mirror of
https://github.com/anchore/syft.git
synced 2026-10-12 06:07:20 +02:00
Compare commits
62
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e21379492 | ||
|
|
d6604adaaf | ||
|
|
34ca9a8412 | ||
|
|
f51b39ca04 | ||
|
|
9bbb42620a | ||
|
|
0b4de3d0c7 | ||
|
|
80d196a8c9 | ||
|
|
ed40833b30 | ||
|
|
93a7d2ee27 | ||
|
|
93a99e36c2 | ||
|
|
b0c88ddea9 | ||
|
|
047e31a969 | ||
|
|
25b55e1704 | ||
|
|
02dc2dfa9b | ||
|
|
5b03788300 | ||
|
|
9901ea8fe9 | ||
|
|
87cd6c8c48 | ||
|
|
d3310a1830 | ||
|
|
8640f978ba | ||
|
|
13b06dad45 | ||
|
|
9604e3dc9c | ||
|
|
f2fc10aa86 | ||
|
|
21b22555d2 | ||
|
|
5326efcef1 | ||
|
|
b90e7f9437 | ||
|
|
fbdd4ee015 | ||
|
|
f154bf570d | ||
|
|
99b58db497 | ||
|
|
8d960e62c6 | ||
|
|
27a8a1be03 | ||
|
|
4ccbd17255 | ||
|
|
891e61a2ef | ||
|
|
f7d3d552ce | ||
|
|
12ea9912b4 | ||
|
|
cf6f92f2c8 | ||
|
|
6440f26b5a | ||
|
|
f2633800ce | ||
|
|
4f227bf447 | ||
|
|
d70eb3d04b | ||
|
|
fe4819bc08 | ||
|
|
b26b38d6c5 | ||
|
|
31969136e3 | ||
|
|
f6845474bd | ||
|
|
e1cadead1d | ||
|
|
3e71f46fc8 | ||
|
|
3b01e13f92 | ||
|
|
25c2e60358 | ||
|
|
dc7fa21980 | ||
|
|
587690b875 | ||
|
|
21eaa5c82b | ||
|
|
081ec04b3f | ||
|
|
dde5d349b1 | ||
|
|
a5d77b9263 | ||
|
|
c9aab4863b | ||
|
|
af1a065d2a | ||
|
|
88cef1e05c | ||
|
|
870d97ca5a | ||
|
|
e681bc4780 | ||
|
|
c31696f131 | ||
|
|
67781e98a2 | ||
|
|
619ace65c3 | ||
|
|
3e4e3bb1d4 |
+3
-3
@@ -42,7 +42,7 @@ tools:
|
||||
# used for signing the checksums file at release
|
||||
- name: cosign
|
||||
version:
|
||||
want: v2.2.3
|
||||
want: v2.2.4
|
||||
method: github-release
|
||||
with:
|
||||
repo: sigstore/cosign
|
||||
@@ -103,7 +103,7 @@ tools:
|
||||
# used for running all local and CI tasks
|
||||
- name: task
|
||||
version:
|
||||
want: v3.35.1
|
||||
want: v3.36.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: go-task/task
|
||||
@@ -111,7 +111,7 @@ tools:
|
||||
# used for triggering a release
|
||||
- name: gh
|
||||
version:
|
||||
want: v2.47.0
|
||||
want: v2.48.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: cli/cli
|
||||
|
||||
@@ -56,6 +56,8 @@ def main(changed_files: str | None = None, merge_base_schema_files: str | None =
|
||||
|
||||
pr_json_schema_files = filter_to_schema_files(pr_changed_files)
|
||||
|
||||
pr_labels = get_pr_labels(pr_number)
|
||||
|
||||
# print("schema files in pr: ", summarize_schema_files(pr_json_schema_files))
|
||||
# print("og schema files: ", summarize_schema_files(og_json_schema_files))
|
||||
|
||||
@@ -76,7 +78,8 @@ def main(changed_files: str | None = None, merge_base_schema_files: str | None =
|
||||
add_label(pr_number, JSON_SCHEMA_LABEL)
|
||||
|
||||
else:
|
||||
remove_label(pr_number, JSON_SCHEMA_LABEL)
|
||||
if JSON_SCHEMA_LABEL in pr_labels:
|
||||
remove_label(pr_number, JSON_SCHEMA_LABEL)
|
||||
|
||||
# new schema files should be scrutinized, comparing the latest and added versions to see if it's a breaking
|
||||
# change (major version bump). Warn about it on the PR via adding a breaking-change label...
|
||||
@@ -84,7 +87,8 @@ def main(changed_files: str | None = None, merge_base_schema_files: str | None =
|
||||
print("\nBreaking change detected...")
|
||||
add_label(pr_number, BREAKING_CHANGE_LABEL)
|
||||
else:
|
||||
remove_label(pr_number, BREAKING_CHANGE_LABEL)
|
||||
if BREAKING_CHANGE_LABEL in pr_labels:
|
||||
remove_label(pr_number, BREAKING_CHANGE_LABEL)
|
||||
|
||||
# modifying an existing schema could be a breaking change, we should warn about it on the PR via a comment...
|
||||
# removing schema files should never be allowed, we should warn about it on the PR via a comment...
|
||||
@@ -119,7 +123,7 @@ def add_label(pr_number: str, label: str):
|
||||
# run "gh pr edit --add-label <label>"
|
||||
result = run(f"gh pr edit {pr_number} --add-label {label}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if result.returncode != 0:
|
||||
print(f"Unable to add {label!r} label to PR with")
|
||||
print(f"Unable to add '{label!r}' label to PR, error:")
|
||||
print(str(result.stderr))
|
||||
sys.exit(1)
|
||||
|
||||
@@ -128,7 +132,7 @@ def remove_label(pr_number: str, label: str):
|
||||
# run "gh pr edit --remove-label <label>"
|
||||
result = run(f"gh pr edit {pr_number} --remove-label {label}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if result.returncode != 0:
|
||||
print(f"Unable to label PR with {label!r}")
|
||||
print(f"Unable to remove '{label!r}' label from PR, error:")
|
||||
print(str(result.stderr))
|
||||
sys.exit(1)
|
||||
|
||||
@@ -165,6 +169,17 @@ def get_pr_changed_files(pr_number: str) -> list[str]:
|
||||
return list_of_files
|
||||
|
||||
|
||||
def get_pr_labels(pr_number: str) -> list[str]:
|
||||
result = run(f"gh pr view {pr_number} --json labels --jq '.labels[].name'", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
if result.returncode != 0:
|
||||
print("Unable to get list of labels on PR")
|
||||
print(str(result.stderr))
|
||||
sys.exit(1)
|
||||
|
||||
list_of_labels = result.stdout.splitlines()
|
||||
return list_of_labels
|
||||
|
||||
|
||||
def filter_to_schema_files(list_of_files: list[str]) -> list[str]:
|
||||
# get files matching "schema/json/schema-*.json"
|
||||
files = []
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
# the job by event.
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
|
||||
echo "result=$OUTPUT" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
- uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
with:
|
||||
name: benchmark-test-results
|
||||
path: test/results/**/*
|
||||
|
||||
@@ -36,16 +36,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 #v5.0.0
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 #v5.0.1
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@1b1aada464948af03b950897e5eb522f92603cc2 #v3.24.9
|
||||
uses: github/codeql-action/init@d39d31e687223d841ef683f52467bd88e9b21c14 #v3.25.3
|
||||
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@1b1aada464948af03b950897e5eb522f92603cc2 #v3.24.9
|
||||
uses: github/codeql-action/autobuild@d39d31e687223d841ef683f52467bd88e9b21c14 #v3.25.3
|
||||
|
||||
# ℹ️ 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@1b1aada464948af03b950897e5eb522f92603cc2 #v3.24.9
|
||||
uses: github/codeql-action/analyze@d39d31e687223d841ef683f52467bd88e9b21c14 #v3.25.3
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- run: python .github/scripts/labeler.py
|
||||
env:
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
with:
|
||||
# we need to be able to get the commit for a tag
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
environment: release
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- name: Check if tag already exists
|
||||
# note: this will fail if the tag already exists
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
# required for goreleaser signs section with cosign
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -143,7 +143,7 @@ jobs:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.TOOLBOX_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }}
|
||||
|
||||
- uses: anchore/sbom-action@ab5d7b5f48981941c4c5d6bf33aeb98fe3bae38c #v0.15.10
|
||||
- uses: anchore/sbom-action@7ccf588e3cf3cc2611714c2eeae48550fbc17552 #v0.15.11
|
||||
continue-on-error: true
|
||||
with:
|
||||
artifact-name: sbom.spdx.json
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'anchore/syft' # only run for main repo
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@70a41aba780001da0a30141984ae2a0c95d8704e #v6.0.2
|
||||
- uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'anchore/syft' # only run for main repo
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@70a41aba780001da0a30141984ae2a0c95d8704e #v6.0.2
|
||||
- uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
|
||||
@@ -17,9 +17,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'anchore/syft' # only run for main repo
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 #v5.0.0
|
||||
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 #v5.0.1
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@70a41aba780001da0a30141984ae2a0c95d8704e #v6.0.2
|
||||
- uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
name: "Static analysis"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
name: "Unit tests"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
name: "Integration tests"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
name: "Build snapshot artifacts"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -139,7 +139,7 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -187,7 +187,7 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -232,7 +232,7 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
@@ -49,6 +49,9 @@ linters-settings:
|
||||
# If lower than 0, disable the check.
|
||||
# Default: 40
|
||||
statements: 50
|
||||
gocritic:
|
||||
enabled-checks:
|
||||
- deferInLoop
|
||||
output:
|
||||
uniq-by-line: false
|
||||
run:
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
<!-- Use this section to tell people about which versions of your project are
|
||||
currently being supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 5.1.x | :white_check_mark: |
|
||||
| 5.0.x | :x: |
|
||||
| 4.0.x | :white_check_mark: |
|
||||
| < 4.0 | :x: |
|
||||
|
||||
-->
|
||||
|
||||
Security updates are applied only to the most recent release, try to always be up to date.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
<!-- Use this section to tell people how to report a vulnerability.
|
||||
|
||||
Tell them where to go, how often they can expect to get an update on a
|
||||
reported vulnerability, what to expect if the vulnerability is accepted or
|
||||
declined, etc. -->
|
||||
|
||||
To report a security issue, please email
|
||||
[security@anchore.com](mailto:security@anchore.com)
|
||||
with a description of the issue, the steps you took to create the issue,
|
||||
affected versions, and, if known, mitigations for the issue.
|
||||
|
||||
All support will be made on the best effort base, so please indicate the "urgency level" of the vulnerability as Critical, High, Medium or Low.
|
||||
+1
-1
@@ -390,7 +390,7 @@ tasks:
|
||||
|
||||
generate-cpe-dictionary-index:
|
||||
desc: Generate the CPE index based off of the latest available CPE dictionary
|
||||
dir: "syft/pkg/cataloger/common/cpe/dictionary"
|
||||
dir: "syft/pkg/cataloger/internal/cpegenerate/dictionary"
|
||||
cmds:
|
||||
- "go generate"
|
||||
|
||||
|
||||
@@ -17,24 +17,24 @@ require (
|
||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20240312213626-055233e539b4
|
||||
github.com/anchore/stereoscope v0.0.2-0.20240229175558-fe426d1b1c84
|
||||
github.com/anchore/stereoscope v0.0.3-0.20240501181043-2e9894674185
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||
// we are hinting brotli to latest due to warning when installing archiver v3:
|
||||
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
|
||||
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||
github.com/charmbracelet/bubbles v0.18.0
|
||||
github.com/charmbracelet/bubbletea v0.25.0
|
||||
github.com/charmbracelet/bubbletea v0.26.1
|
||||
github.com/charmbracelet/lipgloss v0.10.0
|
||||
github.com/dave/jennifer v1.7.0
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/docker v26.0.0+incompatible
|
||||
github.com/docker/docker v26.1.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.2.0
|
||||
github.com/gkampitakis/go-snaps v0.5.2
|
||||
github.com/gkampitakis/go-snaps v0.5.4
|
||||
github.com/go-git/go-billy/v5 v5.5.0
|
||||
github.com/go-git/go-git/v5 v5.12.0
|
||||
github.com/go-test/deep v1.1.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.6
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.8
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||
github.com/knqyf263/go-rpmdb v0.1.0
|
||||
@@ -66,7 +66,7 @@ require (
|
||||
// pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5
|
||||
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
|
||||
github.com/spdx/tools-golang v0.5.3
|
||||
github.com/spdx/tools-golang v0.5.4
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
@@ -77,10 +77,10 @@ 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.16.0
|
||||
golang.org/x/net v0.23.0
|
||||
golang.org/x/mod v0.17.0
|
||||
golang.org/x/net v0.24.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.29.5
|
||||
modernc.org/sqlite v1.29.8
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -103,7 +103,6 @@ require (
|
||||
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||
github.com/containerd/containerd v1.7.11 // indirect
|
||||
github.com/containerd/continuity v0.4.2 // indirect
|
||||
github.com/containerd/fifo v1.1.0 // indirect
|
||||
@@ -122,6 +121,7 @@ require (
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/felixge/fgprof v0.9.3 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
@@ -136,7 +136,7 @@ require (
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
@@ -155,7 +155,7 @@ require (
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/maruel/natural v1.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
@@ -165,7 +165,7 @@ 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/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
@@ -218,13 +218,13 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.19.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/term v0.18.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/term v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.17.0 // indirect
|
||||
golang.org/x/tools v0.19.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
@@ -233,9 +233,9 @@ require (
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
modernc.org/libc v1.41.0 // indirect
|
||||
modernc.org/libc v1.49.3 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.2 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
)
|
||||
|
||||
@@ -111,8 +111,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20240312213626-055233e539b4 h1:SjemQ90fgflz39HG+VMkNfrpUVJpcFW6ZFA3TDXqzBM=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20240312213626-055233e539b4/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
|
||||
github.com/anchore/stereoscope v0.0.2-0.20240229175558-fe426d1b1c84 h1:/E74wU51M87fX5UWHubLZiENXbuAci+xtbSb+JFsIYg=
|
||||
github.com/anchore/stereoscope v0.0.2-0.20240229175558-fe426d1b1c84/go.mod h1:evQiJMQG56Z7/L5uhA8kfhhjF6ESJUZzUH9ms6bQ2Co=
|
||||
github.com/anchore/stereoscope v0.0.3-0.20240501181043-2e9894674185 h1:SuViDJ27nZ+joGdKbAkxAlm7tYMt9NTxTZZ05po4hls=
|
||||
github.com/anchore/stereoscope v0.0.3-0.20240501181043-2e9894674185/go.mod h1:ckIamHiRMp8iBwWoTtE5Xkt9VQ5QC+6+O4VzwqyZr5Q=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
@@ -155,8 +155,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
||||
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
|
||||
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
||||
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
||||
github.com/charmbracelet/bubbletea v0.26.1 h1:xujcQeF73rh4jwu3+zhfQsvV18x+7zIjlw7/CYbzGJ0=
|
||||
github.com/charmbracelet/bubbletea v0.26.1/go.mod h1:FzKr7sKoO8iFVcdIBM9J0sJOcQv5nDQaYwsee3kpbgo=
|
||||
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.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
|
||||
@@ -182,8 +182,6 @@ 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/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw=
|
||||
github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE=
|
||||
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
|
||||
@@ -223,8 +221,8 @@ github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qe
|
||||
github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v26.0.0+incompatible h1:Ng2qi+gdKADUa/VM+6b6YaY2nlZhk/lVJiKR/2bMudU=
|
||||
github.com/docker/docker v26.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v26.1.1+incompatible h1:oI+4kkAgIwwb54b9OC7Xc3hSgu1RlJA/Lln/DF72djQ=
|
||||
github.com/docker/docker v26.1.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=
|
||||
@@ -257,6 +255,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
|
||||
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c/go.mod h1:QGzNH9ujQ2ZUr/CjDGZGWeDAVStrWNjHeEcjJL96Nuk=
|
||||
github.com/facebookincubator/nvdtools v0.1.5 h1:jbmDT1nd6+k+rlvKhnkgMokrCAzHoASWE5LtHbX2qFQ=
|
||||
github.com/facebookincubator/nvdtools v0.1.5/go.mod h1:Kh55SAWnjckS96TBSrXI99KrEKH4iB0OJby3N8GRJO4=
|
||||
@@ -283,8 +283,8 @@ github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvp
|
||||
github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
|
||||
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
|
||||
github.com/gkampitakis/go-snaps v0.5.2 h1:ay/6f7WHwRkOgpBec9DjMLRBAApziJommZ21NkOOCwY=
|
||||
github.com/gkampitakis/go-snaps v0.5.2/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y=
|
||||
github.com/gkampitakis/go-snaps v0.5.4 h1:GX+dkKmVsRenz7SoTbdIEL4KQARZctkMiZ8ZKprRwT8=
|
||||
github.com/gkampitakis/go-snaps v0.5.4/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y=
|
||||
github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4=
|
||||
github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0=
|
||||
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
||||
@@ -398,8 +398,8 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -473,8 +473,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.6 h1:nKXVLqPfAwY7sWcYXdNZZZ2fjqDpAtj9UeWupgfUxSg=
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.6/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg=
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.8 h1:8BCzJdSvUbaDuRba4YVh+SKMGcAAKdkcF3SVFbrHAtQ=
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.8/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=
|
||||
@@ -536,8 +536,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw=
|
||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
@@ -590,8 +590,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA=
|
||||
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
@@ -714,8 +714,8 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
|
||||
github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQraY=
|
||||
github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI=
|
||||
github.com/spdx/tools-golang v0.5.4 h1:fRW4iz16P1ZCUtWStFqS6YiMgnK7WgfTFU/lrsYlvqY=
|
||||
github.com/spdx/tools-golang v0.5.4/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=
|
||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
@@ -867,8 +867,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
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=
|
||||
@@ -909,8 +909,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -958,8 +958,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.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.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
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=
|
||||
@@ -990,8 +990,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.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=
|
||||
@@ -1051,6 +1051,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1071,15 +1072,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.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.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
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=
|
||||
@@ -1155,8 +1156,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1349,18 +1350,28 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk=
|
||||
modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA=
|
||||
modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
||||
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
|
||||
modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg=
|
||||
modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE=
|
||||
modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
|
||||
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/sqlite v1.29.8 h1:nGKglNx9K5v0As+zF0/Gcl1kMkmaU1XynYyq92PbsC8=
|
||||
modernc.org/sqlite v1.29.8/go.mod h1:lQPm27iqa4UNZpmr4Aor0MH0HkCLbt1huYDfWylLZFk=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
@@ -1368,4 +1379,4 @@ modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
@@ -111,10 +111,10 @@ func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string
|
||||
if cfg.DataGenerationConfig.GenerateCPEs {
|
||||
// generate CPEs (note: this is excluded from package ID, so is safe to mutate)
|
||||
// we might have binary classified CPE already with the package so we want to append here
|
||||
dictionaryCPE, ok := cpe.DictionaryFind(p)
|
||||
dictionaryCPEs, ok := cpe.DictionaryFind(p)
|
||||
if ok {
|
||||
log.Tracef("used CPE dictionary to find CPE for %s package %q: %s", p.Type, p.Name, dictionaryCPE.Attributes.BindToFmtString())
|
||||
p.CPEs = append(p.CPEs, dictionaryCPE)
|
||||
log.Tracef("used CPE dictionary to find CPEs for %s package %q: %s", p.Type, p.Name, dictionaryCPEs)
|
||||
p.CPEs = append(p.CPEs, dictionaryCPEs...)
|
||||
} else {
|
||||
p.CPEs = append(p.CPEs, cpe.Generate(p)...)
|
||||
}
|
||||
|
||||
+3262
-2367
File diff suppressed because it is too large
Load Diff
@@ -245,6 +245,8 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
Supplier: helpers.NOASSERTION,
|
||||
},
|
||||
PackageDownloadLocation: helpers.NOASSERTION,
|
||||
PackageLicenseConcluded: helpers.NOASSERTION,
|
||||
PackageLicenseDeclared: helpers.NOASSERTION,
|
||||
}
|
||||
|
||||
if purl != nil {
|
||||
@@ -517,9 +519,7 @@ func toPackageOriginator(p pkg.Package) *spdx.Originator {
|
||||
}
|
||||
|
||||
func toPackageSupplier(p pkg.Package) *spdx.Supplier {
|
||||
// this uses the Originator function for now until
|
||||
// a better distinction can be made for supplier
|
||||
kind, supplier := helpers.Originator(p)
|
||||
kind, supplier := helpers.Supplier(p)
|
||||
if kind == "" || supplier == "" {
|
||||
return &spdx.Supplier{
|
||||
Supplier: helpers.NOASSERTION,
|
||||
@@ -624,6 +624,9 @@ func toFiles(s sbom.SBOM) (results []*spdx.File) {
|
||||
Checksums: toFileChecksums(digests),
|
||||
FileName: coordinates.RealPath,
|
||||
FileTypes: toFileTypes(metadata),
|
||||
LicenseInfoInFiles: []string{ // required in SPDX 2.2
|
||||
helpers.NOASSERTION,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
const (
|
||||
orgType = "Organization"
|
||||
personType = "Person"
|
||||
)
|
||||
|
||||
// Originator needs to conform to the SPDX spec here:
|
||||
// https://spdx.github.io/spdx-spec/v2.2.2/package-information/#76-package-originator-field
|
||||
//
|
||||
// Definition:
|
||||
//
|
||||
// If the package identified in the SPDX document originated from a different person or
|
||||
// organization than identified as Package Supplier (see 7.5 above), this field identifies from
|
||||
// where or whom the package originally came. In some cases, a package may be created and
|
||||
// originally distributed by a different third party than the Package Supplier of the package.
|
||||
// For example, the SPDX document identifies the package as glibc and the Package Supplier as
|
||||
// Red Hat, but the Free Software Foundation is the Package Originator.
|
||||
//
|
||||
// Use NOASSERTION if:
|
||||
//
|
||||
// - the SPDX document creator has attempted to but cannot reach a reasonable objective determination;
|
||||
// - the SPDX document creator has made no attempt to determine this field; or
|
||||
// - the SPDX document creator has intentionally provided no information (no meaning should be implied by doing so).
|
||||
//
|
||||
// Available options are: <omit>, NOASSERTION, Person: <person>, Organization: <org>
|
||||
// return values are: <type>, <value>
|
||||
func Originator(p pkg.Package) (typ string, author string) { // nolint: funlen
|
||||
if !hasMetadata(p) {
|
||||
return typ, author
|
||||
}
|
||||
|
||||
switch metadata := p.Metadata.(type) {
|
||||
case pkg.ApkDBEntry:
|
||||
author = metadata.Maintainer
|
||||
|
||||
case pkg.DotnetPortableExecutableEntry:
|
||||
typ = orgType
|
||||
author = metadata.CompanyName
|
||||
|
||||
case pkg.DpkgDBEntry:
|
||||
author = metadata.Maintainer
|
||||
|
||||
case pkg.JavaArchive:
|
||||
if metadata.Manifest != nil {
|
||||
author = metadata.Manifest.Main.MustGet("Specification-Vendor")
|
||||
if author == "" {
|
||||
author = metadata.Manifest.Main.MustGet("Implementation-Vendor")
|
||||
}
|
||||
}
|
||||
|
||||
case pkg.LinuxKernelModule:
|
||||
author = metadata.Author
|
||||
|
||||
case pkg.PhpComposerLockEntry:
|
||||
if len(metadata.Authors) > 0 {
|
||||
entry := metadata.Authors[0]
|
||||
author = formatPersonOrOrg(entry.Name, entry.Email)
|
||||
}
|
||||
|
||||
case pkg.PhpComposerInstalledEntry:
|
||||
if len(metadata.Authors) > 0 {
|
||||
entry := metadata.Authors[0]
|
||||
author = formatPersonOrOrg(entry.Name, entry.Email)
|
||||
}
|
||||
|
||||
case pkg.RDescription:
|
||||
// this is most likely to have a name and email
|
||||
author = metadata.Maintainer
|
||||
|
||||
if author == "" {
|
||||
author = metadata.Author
|
||||
}
|
||||
|
||||
case pkg.NpmPackage:
|
||||
author = metadata.Author
|
||||
|
||||
case pkg.PythonPackage:
|
||||
author = formatPersonOrOrg(metadata.Author, metadata.AuthorEmail)
|
||||
|
||||
case pkg.RubyGemspec:
|
||||
if len(metadata.Authors) > 0 {
|
||||
author = metadata.Authors[0]
|
||||
}
|
||||
case pkg.RpmDBEntry:
|
||||
typ = orgType
|
||||
author = metadata.Vendor
|
||||
|
||||
case pkg.RpmArchive:
|
||||
typ = orgType
|
||||
author = metadata.Vendor
|
||||
|
||||
case pkg.WordpressPluginEntry:
|
||||
// it seems that the vast majority of the time the author is an org, not a person
|
||||
typ = orgType
|
||||
author = metadata.Author
|
||||
}
|
||||
|
||||
if typ == "" && author != "" {
|
||||
typ = personType
|
||||
}
|
||||
|
||||
return typ, parseAndFormatPersonOrOrg(author)
|
||||
}
|
||||
|
||||
// Supplier needs to conform to the SPDX spec here:
|
||||
// https://spdx.github.io/spdx-spec/v2.2.2/package-information/#75-package-supplier-field
|
||||
//
|
||||
// Definition:
|
||||
//
|
||||
// Identify the actual distribution source for the package/directory identified in the SPDX document. This might
|
||||
// or might not be different from the originating distribution source for the package. The name of the Package Supplier
|
||||
// shall be an organization or recognized author and not a web site. For example, SourceForge is a host website, not a
|
||||
// supplier, the supplier for https://sourceforge.net/projects/bridge/ is “The Linux Foundation.”
|
||||
//
|
||||
// Use NOASSERTION if:
|
||||
//
|
||||
// - the SPDX document creator has attempted to but cannot reach a reasonable objective determination;
|
||||
// - the SPDX document creator has made no attempt to determine this field; or
|
||||
// - the SPDX document creator has intentionally provided no information (no meaning should be implied by doing so).
|
||||
//
|
||||
// Available options are: <omit>, NOASSERTION, Person: <person>, Organization: <org>
|
||||
// return values are: <type>, <value>
|
||||
func Supplier(p pkg.Package) (typ string, author string) {
|
||||
if !hasMetadata(p) {
|
||||
return
|
||||
}
|
||||
|
||||
if metadata, ok := p.Metadata.(pkg.AlpmDBEntry); ok {
|
||||
// most indications here are that this is the person that is simply packaging the upstream software. Most
|
||||
// of the time this is not the original author of the upstream software (which would be the originator).
|
||||
// Though it is possible for users to be both the packager and the author, this code cannot distinct this
|
||||
// case and sticks to the semantically correct interpretation of the "packager" (which says nothing about the
|
||||
// authorship of the upstream software).
|
||||
author = metadata.Packager
|
||||
}
|
||||
|
||||
if author == "" {
|
||||
// TODO: this uses the Originator function for now until a better distinction can be made for supplier
|
||||
return Originator(p)
|
||||
}
|
||||
|
||||
if typ == "" && author != "" {
|
||||
typ = personType
|
||||
}
|
||||
|
||||
return typ, parseAndFormatPersonOrOrg(author)
|
||||
}
|
||||
|
||||
var nameEmailURLPattern = regexp.MustCompile(`^(?P<name>[^<>()]*)( <(?P<email>[^@]+@\w+\.\w+)>)?( \((?P<url>.*)\))?$`)
|
||||
|
||||
func parseAndFormatPersonOrOrg(s string) string {
|
||||
name, email, _ := parseNameEmailURL(s)
|
||||
return formatPersonOrOrg(name, email)
|
||||
}
|
||||
|
||||
func parseNameEmailURL(s string) (name, email, url string) {
|
||||
fields := internal.MatchNamedCaptureGroups(nameEmailURLPattern, s)
|
||||
name = strings.TrimSpace(fields["name"])
|
||||
email = strings.TrimSpace(fields["email"])
|
||||
url = strings.TrimSpace(fields["url"])
|
||||
|
||||
if email == "" {
|
||||
if approximatesAsEmail(url) {
|
||||
email = url
|
||||
url = ""
|
||||
} else if approximatesAsEmail(name) {
|
||||
email = name
|
||||
name = ""
|
||||
}
|
||||
}
|
||||
return name, email, url
|
||||
}
|
||||
|
||||
func approximatesAsEmail(s string) bool {
|
||||
atIndex := strings.Index(s, "@")
|
||||
if atIndex == -1 {
|
||||
return false
|
||||
}
|
||||
dotIndex := strings.Index(s[atIndex:], ".")
|
||||
return dotIndex != -1
|
||||
}
|
||||
|
||||
func formatPersonOrOrg(name, email string) string {
|
||||
name = strings.TrimSpace(name)
|
||||
email = strings.TrimSpace(email)
|
||||
|
||||
blankName := name == ""
|
||||
blankEmail := email == ""
|
||||
|
||||
if !blankEmail && !blankName {
|
||||
return fmt.Sprintf("%s (%s)", name, email)
|
||||
}
|
||||
if !blankName && blankEmail {
|
||||
return name
|
||||
}
|
||||
if blankName && !blankEmail {
|
||||
return email
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,482 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func Test_OriginatorSupplier(t *testing.T) {
|
||||
completionTester := packagemetadata.NewCompletionTester(t,
|
||||
pkg.BinarySignature{},
|
||||
pkg.CocoaPodfileLockEntry{},
|
||||
pkg.ConanV1LockEntry{},
|
||||
pkg.ConanV2LockEntry{}, // the field Username might be the username of either the package originator or the supplier (unclear currently)
|
||||
pkg.ConanfileEntry{},
|
||||
pkg.ConaninfoEntry{},
|
||||
pkg.DartPubspecLockEntry{},
|
||||
pkg.DotnetDepsEntry{},
|
||||
pkg.ELFBinaryPackageNoteJSONPayload{},
|
||||
pkg.ElixirMixLockEntry{},
|
||||
pkg.ErlangRebarLockEntry{},
|
||||
pkg.GolangBinaryBuildinfoEntry{},
|
||||
pkg.GolangModuleEntry{},
|
||||
pkg.HackageStackYamlLockEntry{},
|
||||
pkg.HackageStackYamlEntry{},
|
||||
pkg.LinuxKernel{},
|
||||
pkg.MicrosoftKbPatch{},
|
||||
pkg.NixStoreEntry{},
|
||||
pkg.NpmPackageLockEntry{},
|
||||
pkg.PhpComposerInstalledEntry{},
|
||||
pkg.PhpPeclEntry{},
|
||||
pkg.PortageEntry{},
|
||||
pkg.PythonPipfileLockEntry{},
|
||||
pkg.PythonRequirementsEntry{},
|
||||
pkg.PythonPoetryLockEntry{},
|
||||
pkg.RustBinaryAuditEntry{},
|
||||
pkg.RustCargoLockEntry{},
|
||||
pkg.SwiftPackageManagerResolvedEntry{},
|
||||
pkg.YarnLockEntry{},
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
input pkg.Package
|
||||
originator string
|
||||
supplier string
|
||||
}{
|
||||
{
|
||||
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||
name: "no metadata",
|
||||
input: pkg.Package{},
|
||||
originator: "",
|
||||
supplier: "",
|
||||
},
|
||||
{
|
||||
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||
name: "empty author on existing metadata",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.NpmPackage{
|
||||
Author: "",
|
||||
},
|
||||
},
|
||||
originator: "",
|
||||
supplier: "",
|
||||
},
|
||||
{
|
||||
name: "from apk",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Maintainer: "auth",
|
||||
},
|
||||
},
|
||||
originator: "Person: auth",
|
||||
supplier: "Person: auth",
|
||||
},
|
||||
{
|
||||
name: "from alpm",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Packager: "someone",
|
||||
},
|
||||
},
|
||||
originator: "",
|
||||
supplier: "Person: someone",
|
||||
},
|
||||
{
|
||||
name: "from dotnet -- PE binary",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.DotnetPortableExecutableEntry{
|
||||
CompanyName: "Microsoft Corporation",
|
||||
},
|
||||
},
|
||||
originator: "Organization: Microsoft Corporation",
|
||||
supplier: "Organization: Microsoft Corporation",
|
||||
},
|
||||
{
|
||||
name: "from dpkg",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Maintainer: "auth",
|
||||
},
|
||||
},
|
||||
originator: "Person: auth",
|
||||
supplier: "Person: auth",
|
||||
},
|
||||
{
|
||||
name: "from gem",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.RubyGemspec{
|
||||
Authors: []string{
|
||||
"auth1",
|
||||
"auth2",
|
||||
},
|
||||
},
|
||||
},
|
||||
originator: "Person: auth1",
|
||||
supplier: "Person: auth1",
|
||||
},
|
||||
{
|
||||
name: "from java -- spec > impl cendor in main manifest section",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.JavaArchive{
|
||||
Manifest: &pkg.JavaManifest{
|
||||
Main: pkg.KeyValues{
|
||||
{
|
||||
Key: "Implementation-Vendor",
|
||||
Value: "auth-impl",
|
||||
},
|
||||
{
|
||||
Key: "Specification-Vendor",
|
||||
Value: "auth-spec",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
originator: "Person: auth-spec",
|
||||
supplier: "Person: auth-spec",
|
||||
},
|
||||
{
|
||||
name: "from java -- fallback to impl vendor in main manifest section",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.JavaArchive{
|
||||
Manifest: &pkg.JavaManifest{
|
||||
Main: pkg.KeyValues{
|
||||
{
|
||||
Key: "Implementation-Vendor",
|
||||
Value: "auth-impl",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
originator: "Person: auth-impl",
|
||||
supplier: "Person: auth-impl",
|
||||
},
|
||||
{
|
||||
name: "from java -- non-main manifest sections ignored",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.JavaArchive{
|
||||
Manifest: &pkg.JavaManifest{
|
||||
Sections: []pkg.KeyValues{
|
||||
{
|
||||
{
|
||||
Key: "Implementation-Vendor",
|
||||
Value: "auth-impl",
|
||||
},
|
||||
},
|
||||
},
|
||||
Main: pkg.KeyValues{},
|
||||
},
|
||||
},
|
||||
},
|
||||
// note: empty!
|
||||
},
|
||||
{
|
||||
name: "from linux kernel module",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.LinuxKernelModule{
|
||||
Author: "auth",
|
||||
},
|
||||
},
|
||||
originator: "Person: auth",
|
||||
supplier: "Person: auth",
|
||||
},
|
||||
{
|
||||
name: "from npm",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.NpmPackage{
|
||||
Author: "auth",
|
||||
},
|
||||
},
|
||||
originator: "Person: auth",
|
||||
supplier: "Person: auth",
|
||||
},
|
||||
{
|
||||
name: "from npm -- name, email, and url",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.NpmPackage{
|
||||
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
|
||||
},
|
||||
},
|
||||
originator: "Person: Isaac Z. Schlueter (i@izs.me)",
|
||||
supplier: "Person: Isaac Z. Schlueter (i@izs.me)",
|
||||
},
|
||||
{
|
||||
name: "from npm -- name, email",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.NpmPackage{
|
||||
Author: "Isaac Z. Schlueter <i@izs.me>",
|
||||
},
|
||||
},
|
||||
originator: "Person: Isaac Z. Schlueter (i@izs.me)",
|
||||
supplier: "Person: Isaac Z. Schlueter (i@izs.me)",
|
||||
},
|
||||
{
|
||||
name: "from php composer installed file",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.PhpComposerInstalledEntry{
|
||||
Authors: []pkg.PhpComposerAuthors{
|
||||
{
|
||||
Name: "auth",
|
||||
Email: "me@auth.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
originator: "Person: auth (me@auth.com)",
|
||||
supplier: "Person: auth (me@auth.com)",
|
||||
},
|
||||
{
|
||||
name: "from php composer installed file",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.PhpComposerLockEntry{
|
||||
Authors: []pkg.PhpComposerAuthors{
|
||||
{
|
||||
Name: "auth",
|
||||
Email: "me@auth.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
originator: "Person: auth (me@auth.com)",
|
||||
supplier: "Person: auth (me@auth.com)",
|
||||
},
|
||||
{
|
||||
name: "from python - just name",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.PythonPackage{
|
||||
Author: "auth",
|
||||
},
|
||||
},
|
||||
originator: "Person: auth",
|
||||
supplier: "Person: auth",
|
||||
},
|
||||
{
|
||||
name: "from python - just email",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.PythonPackage{
|
||||
AuthorEmail: "auth@auth.gov",
|
||||
},
|
||||
},
|
||||
originator: "Person: auth@auth.gov",
|
||||
supplier: "Person: auth@auth.gov",
|
||||
},
|
||||
{
|
||||
name: "from python - both name and email",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.PythonPackage{
|
||||
Author: "auth",
|
||||
AuthorEmail: "auth@auth.gov",
|
||||
},
|
||||
},
|
||||
originator: "Person: auth (auth@auth.gov)",
|
||||
supplier: "Person: auth (auth@auth.gov)",
|
||||
},
|
||||
{
|
||||
name: "from r -- maintainer > author",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.RDescription{
|
||||
Author: "author",
|
||||
Maintainer: "maintainer",
|
||||
},
|
||||
},
|
||||
originator: "Person: maintainer",
|
||||
supplier: "Person: maintainer",
|
||||
},
|
||||
{
|
||||
name: "from r -- fallback to author",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.RDescription{
|
||||
Author: "author",
|
||||
},
|
||||
},
|
||||
originator: "Person: author",
|
||||
supplier: "Person: author",
|
||||
},
|
||||
{
|
||||
name: "from rpm archive",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.RpmArchive{
|
||||
Vendor: "auth",
|
||||
},
|
||||
},
|
||||
originator: "Organization: auth",
|
||||
supplier: "Organization: auth",
|
||||
},
|
||||
{
|
||||
name: "from rpm DB",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Vendor: "auth",
|
||||
},
|
||||
},
|
||||
originator: "Organization: auth",
|
||||
supplier: "Organization: auth",
|
||||
},
|
||||
{
|
||||
name: "from wordpress plugin",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.WordpressPluginEntry{
|
||||
Author: "auth",
|
||||
},
|
||||
},
|
||||
originator: "Organization: auth",
|
||||
supplier: "Organization: auth",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
completionTester.Tested(t, test.input.Metadata)
|
||||
|
||||
typ, value := Originator(test.input)
|
||||
if typ != "" {
|
||||
value = typ + ": " + value
|
||||
}
|
||||
assert.Equal(t, test.originator, value)
|
||||
|
||||
typ, value = Supplier(test.input)
|
||||
if typ != "" {
|
||||
value = typ + ": " + value
|
||||
}
|
||||
assert.Equal(t, test.supplier, value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseNameEmailUrl(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantName string
|
||||
wantEmail string
|
||||
wantUrl string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
input: "",
|
||||
},
|
||||
{
|
||||
name: "npm-like: name only",
|
||||
input: "Isaac Z. Schlueter",
|
||||
wantName: "Isaac Z. Schlueter",
|
||||
},
|
||||
{
|
||||
name: "npm-like: name and email",
|
||||
input: "Ray Nos <bogus2@gmail.com>",
|
||||
wantName: "Ray Nos",
|
||||
wantEmail: "bogus2@gmail.com",
|
||||
},
|
||||
{
|
||||
name: "npm-like: name and url",
|
||||
input: "Ray Nos (http://example.com)",
|
||||
wantName: "Ray Nos",
|
||||
wantUrl: "http://example.com",
|
||||
},
|
||||
{
|
||||
name: "npm-like: name, email, and url",
|
||||
input: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
|
||||
wantName: "Isaac Z. Schlueter",
|
||||
wantEmail: "i@izs.me",
|
||||
wantUrl: "http://blog.izs.me",
|
||||
},
|
||||
{
|
||||
name: "mixed input: email only",
|
||||
input: "i@izs.me",
|
||||
wantEmail: "i@izs.me",
|
||||
},
|
||||
{
|
||||
name: "mixed input: email in url",
|
||||
input: "my name (i@izs.me)",
|
||||
wantName: "my name",
|
||||
wantEmail: "i@izs.me",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotName, gotEmail, gotUrl := parseNameEmailURL(tt.input)
|
||||
assert.Equal(t, tt.wantName, gotName)
|
||||
assert.Equal(t, tt.wantEmail, gotEmail)
|
||||
assert.Equal(t, tt.wantUrl, gotUrl)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_formatPersonOrOrg(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
email string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "name only",
|
||||
input: "Isaac Z. Schlueter",
|
||||
want: "Isaac Z. Schlueter",
|
||||
},
|
||||
{
|
||||
name: "email only",
|
||||
email: "i@something.com",
|
||||
want: "i@something.com",
|
||||
},
|
||||
{
|
||||
name: "name and email",
|
||||
input: "Isaac Z. Schlueter",
|
||||
email: "i@something.com",
|
||||
want: "Isaac Z. Schlueter (i@something.com)",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, formatPersonOrOrg(tt.input, tt.email))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_approximatesAsEmail(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
input: "",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no at",
|
||||
input: "something.com",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no dot",
|
||||
input: "something@com",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "dot before at",
|
||||
input: "something.com@nothing",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
input: "something@nothing.com",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, approximatesAsEmail(tt.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func Test_Originator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input pkg.Package
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||
name: "no metadata",
|
||||
input: pkg.Package{},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "from gem",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.RubyGemspec{
|
||||
Authors: []string{
|
||||
"auth1",
|
||||
"auth2",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "Person: auth1",
|
||||
},
|
||||
{
|
||||
name: "from npm",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.NpmPackage{
|
||||
Author: "auth",
|
||||
},
|
||||
},
|
||||
expected: "Person: auth",
|
||||
},
|
||||
{
|
||||
name: "from apk",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Maintainer: "auth",
|
||||
},
|
||||
},
|
||||
expected: "Person: auth",
|
||||
},
|
||||
{
|
||||
name: "from python - just name",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.PythonPackage{
|
||||
Author: "auth",
|
||||
},
|
||||
},
|
||||
expected: "Person: auth",
|
||||
},
|
||||
{
|
||||
name: "from python - just email",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.PythonPackage{
|
||||
AuthorEmail: "auth@auth.gov",
|
||||
},
|
||||
},
|
||||
expected: "Person: auth@auth.gov",
|
||||
},
|
||||
{
|
||||
name: "from python - both name and email",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.PythonPackage{
|
||||
Author: "auth",
|
||||
AuthorEmail: "auth@auth.gov",
|
||||
},
|
||||
},
|
||||
expected: "Person: auth (auth@auth.gov)",
|
||||
},
|
||||
{
|
||||
name: "from rpm",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Vendor: "auth",
|
||||
},
|
||||
},
|
||||
expected: "Organization: auth",
|
||||
},
|
||||
{
|
||||
name: "from dpkg",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Maintainer: "auth",
|
||||
},
|
||||
},
|
||||
expected: "Person: auth",
|
||||
},
|
||||
{
|
||||
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||
name: "empty",
|
||||
input: pkg.Package{
|
||||
Metadata: pkg.NpmPackage{
|
||||
Author: "",
|
||||
},
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
typ, value := Originator(test.input)
|
||||
if typ != "" {
|
||||
value = typ + ": " + value
|
||||
}
|
||||
assert.Equal(t, test.expected, value)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
// Originator needs to conform to the SPDX spec here:
|
||||
// https://spdx.github.io/spdx-spec/package-information/#76-package-originator-field
|
||||
// Available options are: <omit>, NOASSERTION, Person: <person>, Organization: <org>
|
||||
// return values are: <type>, <value>
|
||||
func Originator(p pkg.Package) (string, string) {
|
||||
typ := ""
|
||||
author := ""
|
||||
if hasMetadata(p) {
|
||||
switch metadata := p.Metadata.(type) {
|
||||
case pkg.ApkDBEntry:
|
||||
author = metadata.Maintainer
|
||||
case pkg.NpmPackage:
|
||||
author = metadata.Author
|
||||
case pkg.PythonPackage:
|
||||
author = metadata.Author
|
||||
if author == "" {
|
||||
author = metadata.AuthorEmail
|
||||
} else if metadata.AuthorEmail != "" {
|
||||
author = fmt.Sprintf("%s (%s)", author, metadata.AuthorEmail)
|
||||
}
|
||||
case pkg.RubyGemspec:
|
||||
if len(metadata.Authors) > 0 {
|
||||
author = metadata.Authors[0]
|
||||
}
|
||||
case pkg.RpmDBEntry:
|
||||
typ = "Organization"
|
||||
author = metadata.Vendor
|
||||
case pkg.DpkgDBEntry:
|
||||
author = metadata.Maintainer
|
||||
}
|
||||
if typ == "" && author != "" {
|
||||
typ = "Person"
|
||||
}
|
||||
}
|
||||
return typ, author
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package spdxjson
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -113,6 +115,74 @@ func TestSPDXJSONImageEncoder(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestSPDX22JSONRequredProperties(t *testing.T) {
|
||||
cfg := DefaultEncoderConfig()
|
||||
cfg.Pretty = true
|
||||
cfg.Version = "2.2"
|
||||
|
||||
enc, err := NewFormatEncoderWithConfig(cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
coords := file.Coordinates{
|
||||
RealPath: "/some/file",
|
||||
FileSystemID: "ac897d978b6c38749a1",
|
||||
}
|
||||
|
||||
p1 := pkg.Package{
|
||||
Name: "files-analyzed-true",
|
||||
Version: "v1",
|
||||
Locations: file.NewLocationSet(file.NewLocation(coords.RealPath)),
|
||||
Licenses: pkg.LicenseSet{},
|
||||
Language: pkg.Java,
|
||||
Metadata: pkg.JavaArchive{
|
||||
ArchiveDigests: []file.Digest{
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Value: "a9b87321a9879c79d87987987a97c97b9789ce978dffea987",
|
||||
},
|
||||
},
|
||||
Parent: nil,
|
||||
},
|
||||
}
|
||||
p1.SetID()
|
||||
|
||||
p2 := pkg.Package{
|
||||
Name: "files-analyzed-false",
|
||||
Version: "v2",
|
||||
}
|
||||
p2.SetID()
|
||||
|
||||
testutil.AssertEncoderAgainstGoldenSnapshot(t,
|
||||
testutil.EncoderSnapshotTestConfig{
|
||||
Subject: sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(p1, p2),
|
||||
FileDigests: map[file.Coordinates][]file.Digest{
|
||||
coords: {
|
||||
{
|
||||
Algorithm: "sha1",
|
||||
Value: "3b4ab96c371d913e2a88c269844b6c5fb5cbe761",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Relationships: []artifact.Relationship{
|
||||
{
|
||||
From: p1,
|
||||
To: coords,
|
||||
Type: artifact.ContainsRelationship,
|
||||
},
|
||||
},
|
||||
},
|
||||
Format: enc,
|
||||
UpdateSnapshot: *updateSnapshot,
|
||||
PersistRedactionsInSnapshot: true,
|
||||
IsJSON: true,
|
||||
Redactor: redactor(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestSPDXRelationshipOrder(t *testing.T) {
|
||||
testImage := "image-simple"
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
{
|
||||
"spdxVersion": "SPDX-2.2",
|
||||
"dataLicense": "CC0-1.0",
|
||||
"SPDXID": "SPDXRef-DOCUMENT",
|
||||
"name": "unknown",
|
||||
"documentNamespace":"redacted",
|
||||
"creationInfo": {
|
||||
"licenseListVersion":"redacted",
|
||||
"creators": [
|
||||
"Organization: Anchore, Inc",
|
||||
"Tool: -"
|
||||
],
|
||||
"created":"redacted"
|
||||
},
|
||||
"packages": [
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-files-analyzed-false-7d37ba9d2f7c574b",
|
||||
"copyrightText": "NOASSERTION",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
"filesAnalyzed": false,
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseDeclared": "NOASSERTION",
|
||||
"name": "files-analyzed-false",
|
||||
"sourceInfo": "acquired package info from the following paths: ",
|
||||
"supplier": "NOASSERTION",
|
||||
"versionInfo": "v2"
|
||||
},
|
||||
{
|
||||
"name": "files-analyzed-true",
|
||||
"SPDXID": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
|
||||
"versionInfo": "v1",
|
||||
"supplier": "NOASSERTION",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
"filesAnalyzed": true,
|
||||
"packageVerificationCode": {
|
||||
"packageVerificationCodeValue": "6fe0c471faaaa544e33cae0918eabcdc1c798d18"
|
||||
},
|
||||
"checksums": [
|
||||
{
|
||||
"algorithm": "SHA256",
|
||||
"checksumValue": "a9b87321a9879c79d87987987a97c97b9789ce978dffea987"
|
||||
}
|
||||
],
|
||||
"sourceInfo": "acquired package info from the following paths: /some/file",
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseDeclared": "NOASSERTION",
|
||||
"copyrightText": "NOASSERTION"
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"copyrightText": "",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
"filesAnalyzed": false,
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseDeclared": "NOASSERTION",
|
||||
"name": "",
|
||||
"supplier": "NOASSERTION"
|
||||
}
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"fileName": "/some/file",
|
||||
"SPDXID": "SPDXRef-File-some-file-2c5bc344430decac",
|
||||
"checksums": [
|
||||
{
|
||||
"algorithm": "SHA1",
|
||||
"checksumValue": "3b4ab96c371d913e2a88c269844b6c5fb5cbe761"
|
||||
}
|
||||
],
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseInfoInFiles": [
|
||||
"NOASSERTION"
|
||||
],
|
||||
"copyrightText": "",
|
||||
"comment": "layerID: ac897d978b6c38749a1"
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"spdxElementId": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
|
||||
"relatedSpdxElement": "SPDXRef-File-some-file-2c5bc344430decac",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-false-7d37ba9d2f7c574b",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DOCUMENT",
|
||||
"relatedSpdxElement": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"relationshipType": "DESCRIBES"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -67,6 +67,8 @@
|
||||
"supplier": "NOASSERTION",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
"filesAnalyzed": false,
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseDeclared": "NOASSERTION",
|
||||
"primaryPackagePurpose": "FILE"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -74,6 +74,8 @@
|
||||
"checksumValue": "2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
|
||||
}
|
||||
],
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseDeclared": "NOASSERTION",
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
|
||||
@@ -74,6 +74,8 @@
|
||||
"checksumValue": "2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
|
||||
}
|
||||
],
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseDeclared": "NOASSERTION",
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
@@ -98,6 +100,9 @@
|
||||
}
|
||||
],
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseInfoInFiles": [
|
||||
"NOASSERTION"
|
||||
],
|
||||
"copyrightText": ""
|
||||
},
|
||||
{
|
||||
@@ -113,6 +118,9 @@
|
||||
}
|
||||
],
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseInfoInFiles": [
|
||||
"NOASSERTION"
|
||||
],
|
||||
"copyrightText": ""
|
||||
},
|
||||
{
|
||||
@@ -128,6 +136,9 @@
|
||||
}
|
||||
],
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseInfoInFiles": [
|
||||
"NOASSERTION"
|
||||
],
|
||||
"copyrightText": ""
|
||||
},
|
||||
{
|
||||
@@ -143,6 +154,9 @@
|
||||
}
|
||||
],
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseInfoInFiles": [
|
||||
"NOASSERTION"
|
||||
],
|
||||
"copyrightText": ""
|
||||
},
|
||||
{
|
||||
@@ -158,6 +172,9 @@
|
||||
}
|
||||
],
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseInfoInFiles": [
|
||||
"NOASSERTION"
|
||||
],
|
||||
"copyrightText": ""
|
||||
},
|
||||
{
|
||||
@@ -173,6 +190,9 @@
|
||||
}
|
||||
],
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseInfoInFiles": [
|
||||
"NOASSERTION"
|
||||
],
|
||||
"copyrightText": ""
|
||||
}
|
||||
],
|
||||
|
||||
@@ -16,6 +16,8 @@ PackageSupplier: NOASSERTION
|
||||
PackageDownloadLocation: NOASSERTION
|
||||
PrimaryPackagePurpose: FILE
|
||||
FilesAnalyzed: false
|
||||
PackageLicenseConcluded: NOASSERTION
|
||||
PackageLicenseDeclared: NOASSERTION
|
||||
|
||||
##### Package: @at-sign
|
||||
|
||||
|
||||
@@ -15,36 +15,42 @@ SPDXID: SPDXRef-File-a1-f6-9c2f7510199b17f6
|
||||
FileType: OTHER
|
||||
FileChecksum: SHA1: 0000000000000000000000000000000000000000
|
||||
LicenseConcluded: NOASSERTION
|
||||
LicenseInfoInFile: NOASSERTION
|
||||
|
||||
FileName: /d1/f3
|
||||
SPDXID: SPDXRef-File-d1-f3-c6f5b29dca12661f
|
||||
FileType: OTHER
|
||||
FileChecksum: SHA1: 0000000000000000000000000000000000000000
|
||||
LicenseConcluded: NOASSERTION
|
||||
LicenseInfoInFile: NOASSERTION
|
||||
|
||||
FileName: /d2/f4
|
||||
SPDXID: SPDXRef-File-d2-f4-c641caa71518099f
|
||||
FileType: OTHER
|
||||
FileChecksum: SHA1: 0000000000000000000000000000000000000000
|
||||
LicenseConcluded: NOASSERTION
|
||||
LicenseInfoInFile: NOASSERTION
|
||||
|
||||
FileName: /f1
|
||||
SPDXID: SPDXRef-File-f1-5265a4dde3edbf7c
|
||||
FileType: OTHER
|
||||
FileChecksum: SHA1: 0000000000000000000000000000000000000000
|
||||
LicenseConcluded: NOASSERTION
|
||||
LicenseInfoInFile: NOASSERTION
|
||||
|
||||
FileName: /f2
|
||||
SPDXID: SPDXRef-File-f2-f9e49132a4b96ccd
|
||||
FileType: OTHER
|
||||
FileChecksum: SHA1: 0000000000000000000000000000000000000000
|
||||
LicenseConcluded: NOASSERTION
|
||||
LicenseInfoInFile: NOASSERTION
|
||||
|
||||
FileName: /z1/f5
|
||||
SPDXID: SPDXRef-File-z1-f5-839d99ee67d9d174
|
||||
FileType: OTHER
|
||||
FileChecksum: SHA1: 0000000000000000000000000000000000000000
|
||||
LicenseConcluded: NOASSERTION
|
||||
LicenseInfoInFile: NOASSERTION
|
||||
|
||||
##### Package: user-image-input
|
||||
|
||||
@@ -56,6 +62,8 @@ PackageDownloadLocation: NOASSERTION
|
||||
PrimaryPackagePurpose: CONTAINER
|
||||
FilesAnalyzed: false
|
||||
PackageChecksum: SHA256: 2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368
|
||||
PackageLicenseConcluded: NOASSERTION
|
||||
PackageLicenseDeclared: NOASSERTION
|
||||
ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=
|
||||
|
||||
##### Package: package-2
|
||||
|
||||
+2
@@ -16,6 +16,8 @@ PackageSupplier: NOASSERTION
|
||||
PackageDownloadLocation: NOASSERTION
|
||||
PrimaryPackagePurpose: FILE
|
||||
FilesAnalyzed: false
|
||||
PackageLicenseConcluded: NOASSERTION
|
||||
PackageLicenseDeclared: NOASSERTION
|
||||
|
||||
##### Package: package-2
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ PackageDownloadLocation: NOASSERTION
|
||||
PrimaryPackagePurpose: CONTAINER
|
||||
FilesAnalyzed: false
|
||||
PackageChecksum: SHA256: 2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368
|
||||
PackageLicenseConcluded: NOASSERTION
|
||||
PackageLicenseDeclared: NOASSERTION
|
||||
ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=
|
||||
|
||||
##### Package: package-2
|
||||
|
||||
+8
-40
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
@@ -21,21 +22,16 @@ func GetSource(ctx context.Context, userInput string, cfg *GetSourceConfig) (sou
|
||||
}
|
||||
|
||||
var errs []error
|
||||
var fileNotfound error
|
||||
var fileNotFoundProviders []string
|
||||
|
||||
// call each source provider until we find a valid source
|
||||
for _, p := range providers {
|
||||
src, err := p.Provide(ctx)
|
||||
if err != nil {
|
||||
err = eachError(err, func(err error) error {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
fileNotfound = err
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
fileNotFoundProviders = append(fileNotFoundProviders, p.Name())
|
||||
} else {
|
||||
errs = append(errs, fmt.Errorf("%s: %w", p.Name(), err))
|
||||
}
|
||||
}
|
||||
if src != nil {
|
||||
@@ -52,8 +48,8 @@ func GetSource(ctx context.Context, userInput string, cfg *GetSourceConfig) (sou
|
||||
}
|
||||
}
|
||||
|
||||
if fileNotfound != nil {
|
||||
errs = append([]error{fileNotfound}, errs...)
|
||||
if len(fileNotFoundProviders) > 0 {
|
||||
errs = append(errs, fmt.Errorf("additionally, the following providers failed with %w: %s", os.ErrNotExist, strings.Join(fileNotFoundProviders, ", ")))
|
||||
}
|
||||
return nil, sourceError(userInput, errs...)
|
||||
}
|
||||
@@ -71,31 +67,3 @@ func sourceError(userInput string, errs ...error) error {
|
||||
}
|
||||
return fmt.Errorf("errors occurred attempting to resolve '%s':%s", userInput, errorTexts)
|
||||
}
|
||||
|
||||
func eachError(err error, fn func(error) error) error {
|
||||
out := fn(err)
|
||||
// unwrap singly wrapped errors
|
||||
if e, ok := err.(interface {
|
||||
Unwrap() error
|
||||
}); ok {
|
||||
wrapped := e.Unwrap()
|
||||
got := eachError(wrapped, fn)
|
||||
// return the outer error if received the same wrapped error
|
||||
if errors.Is(got, wrapped) {
|
||||
return err
|
||||
}
|
||||
return got
|
||||
}
|
||||
// unwrap errors from errors.Join
|
||||
if errs, ok := err.(interface {
|
||||
Unwrap() []error
|
||||
}); ok {
|
||||
for _, e := range errs.Unwrap() {
|
||||
e = eachError(e, fn)
|
||||
if e != nil {
|
||||
out = errors.Join(out, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
macho "github.com/anchore/go-macholibre"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
// UnionReader is a single interface with all reading functions needed by multi-arch binary catalogers
|
||||
@@ -44,6 +45,17 @@ func GetUnionReader(readerCloser io.ReadCloser) (UnionReader, error) {
|
||||
return reader, nil
|
||||
}
|
||||
|
||||
// file.LocationReadCloser embeds a ReadCloser, which is likely
|
||||
// to implement UnionReader. Check whether the embedded read closer
|
||||
// implements UnionReader, and just return that if so.
|
||||
r, ok := readerCloser.(file.LocationReadCloser)
|
||||
if ok {
|
||||
ur, ok := r.ReadCloser.(UnionReader)
|
||||
if ok {
|
||||
return ur, nil
|
||||
}
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(readerCloser)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read contents from binary: %w", err)
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
func Test_getUnionReader_notUnionReader(t *testing.T) {
|
||||
@@ -28,3 +30,34 @@ func Test_getUnionReader_notUnionReader(t *testing.T) {
|
||||
|
||||
assert.Equal(t, expectedContents, string(b))
|
||||
}
|
||||
|
||||
type panickingUnionReader struct{}
|
||||
|
||||
func (p2 *panickingUnionReader) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
panic("don't call this in your unit test!")
|
||||
}
|
||||
|
||||
func (p2 *panickingUnionReader) Seek(offset int64, whence int) (int64, error) {
|
||||
panic("don't call this in your unit test!")
|
||||
}
|
||||
|
||||
func (p2 *panickingUnionReader) Read(p []byte) (n int, err error) {
|
||||
panic("don't call this in your unit test!")
|
||||
}
|
||||
|
||||
func (p2 *panickingUnionReader) Close() error {
|
||||
panic("don't call this in your unit test!")
|
||||
}
|
||||
|
||||
var _ UnionReader = (*panickingUnionReader)(nil)
|
||||
|
||||
func Test_getUnionReader_fileLocationReadCloser(t *testing.T) {
|
||||
// panickingUnionReader is a UnionReader
|
||||
p := &panickingUnionReader{}
|
||||
embedsUnionReader := file.NewLocationReadCloser(file.Location{}, p)
|
||||
|
||||
// embedded union reader is returned without "ReadAll" invocation
|
||||
ur, err := GetUnionReader(embedsUnionReader)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, p, ur)
|
||||
}
|
||||
|
||||
@@ -153,6 +153,27 @@ func FileContentsVersionMatcher(pattern string) EvidenceMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
// matchExcluding tests the provided regular expressions against the file, and if matched, DOES NOT return
|
||||
// anything that the matcher would otherwise return
|
||||
func matchExcluding(matcher EvidenceMatcher, contentPatternsToExclude ...string) EvidenceMatcher {
|
||||
var nonMatchPatterns []*regexp.Regexp
|
||||
for _, p := range contentPatternsToExclude {
|
||||
nonMatchPatterns = append(nonMatchPatterns, regexp.MustCompile(p))
|
||||
}
|
||||
return func(resolver file.Resolver, classifier Classifier, location file.Location) ([]pkg.Package, error) {
|
||||
contents, err := getContents(resolver, location)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get read contents for file: %w", err)
|
||||
}
|
||||
for _, nonMatch := range nonMatchPatterns {
|
||||
if nonMatch.Match(contents) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return matcher(resolver, classifier, location)
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:gocognit
|
||||
func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher EvidenceMatcher) EvidenceMatcher {
|
||||
pat := regexp.MustCompile(sharedLibraryPattern)
|
||||
|
||||
@@ -608,10 +608,10 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
{
|
||||
logicalFixture: "java-jre-openjdk/1.8.0_352-b08/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java",
|
||||
Name: "java/jre",
|
||||
Version: "1.8.0_352-b08",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java@1.8.0_352-b08",
|
||||
PURL: "pkg:generic/java/jre@1.8.0_352-b08",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-openjdk", "java"),
|
||||
},
|
||||
@@ -619,10 +619,32 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
{
|
||||
logicalFixture: "java-jre-openjdk/11.0.17/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java",
|
||||
Name: "java/jre",
|
||||
Version: "11.0.17+8-LTS",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java@11.0.17%2B8-LTS",
|
||||
PURL: "pkg:generic/java/jre@11.0.17%2B8-LTS",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-openjdk", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "java-jre-openjdk-eclipse/11.0.22/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java/jre",
|
||||
Version: "11.0.22+7",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/jre@11.0.22%2B7",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-openjdk", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "java-jre-openjdk-arm64-eclipse/11.0.22/linux-arm64",
|
||||
expected: pkg.Package{
|
||||
Name: "java/jre",
|
||||
Version: "11.0.22+7",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/jre@11.0.22%2B7",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-openjdk", "java"),
|
||||
},
|
||||
@@ -630,10 +652,10 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
{
|
||||
logicalFixture: "java-graal-openjdk/17.0.3+7-jvmci-22.1-b06/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java",
|
||||
Name: "java/graalvm",
|
||||
Version: "17.0.3+7-jvmci-22.1-b06",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java@17.0.3%2B7-jvmci-22.1-b06",
|
||||
PURL: "pkg:generic/java/graalvm@17.0.3%2B7-jvmci-22.1-b06",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-graalvm", "java"),
|
||||
},
|
||||
@@ -643,10 +665,10 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
// note: cannot find the original binary, using a custom snippet based on the original snippet in the repo
|
||||
logicalFixture: "java-jre-oracle/19.0.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java",
|
||||
Name: "java/jre",
|
||||
Version: "19.0.1+10-21",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java@19.0.1%2B10-21",
|
||||
PURL: "pkg:generic/java/jre@19.0.1%2B10-21",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-oracle", "java"),
|
||||
},
|
||||
@@ -656,10 +678,10 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
// note: cannot find the original binary, using a custom snippet based on the original snippet in the repo
|
||||
logicalFixture: "java-jre-oracle/19.0.1/darwin",
|
||||
expected: pkg.Package{
|
||||
Name: "java",
|
||||
Name: "java/jre",
|
||||
Version: "19.0.1+10-21",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java@19.0.1%2B10-21",
|
||||
PURL: "pkg:generic/java/jre@19.0.1%2B10-21",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-oracle", "java"),
|
||||
},
|
||||
@@ -667,14 +689,25 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
{
|
||||
logicalFixture: "java-jre-ibm/1.8.0_391/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java",
|
||||
Name: "java/jre",
|
||||
Version: "1.8.0-foreman_2023_10_12_13_27-b00",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java@1.8.0-foreman_2023_10_12_13_27-b00",
|
||||
PURL: "pkg:generic/java/jre@1.8.0-foreman_2023_10_12_13_27-b00",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-ibm", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "java-jdk-openjdk/21.0.2+13-LTS/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java/jdk",
|
||||
Version: "21.0.2+13-LTS",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/jdk@21.0.2%2B13-LTS",
|
||||
Locations: locations("jdb"),
|
||||
Metadata: metadata("java-binary-jdk", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "rust-libstd/1.50.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
|
||||
@@ -87,14 +87,21 @@ func DefaultClassifiers() []Classifier {
|
||||
{
|
||||
Class: "java-binary-openjdk",
|
||||
FileGlob: "**/java",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(
|
||||
// [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
|
||||
// [NUL]openjdk[NUL]java[NUL]1.8[NUL]1.8.0_352-b08[NUL]
|
||||
// Equivalent to the following regexp with lookahead support:
|
||||
// (?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<release>[0-9]+[.0-9]*) (?P<version>[0-9]+[^-\x00]+(-(?!jvmci)[^-\x00]+)+)
|
||||
`(?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^-\s]+(-([^-j\x00][^-\x00]?|[^-\x00][^-v\x00][^-\x00]?|[^-\x00][^-\x00][^-m\x00][^-\x00]?|[^-\x00][^-\x00][^-\x00][^-c\x00][^-\x00]?|[^-\x00][^-\x00][^-\x00][^-\x00][^-i\s].?|[^-\x00]{6,}))+)\x00`),
|
||||
Package: "java",
|
||||
PURL: mustPURL("pkg:generic/java@version"),
|
||||
EvidenceMatcher: matchExcluding(
|
||||
evidenceMatchers(
|
||||
FileContentsVersionMatcher(
|
||||
// [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
|
||||
// [NUL]openjdk[NUL]java[NUL]1.8[NUL]1.8.0_352-b08[NUL]
|
||||
`(?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`),
|
||||
FileContentsVersionMatcher(
|
||||
// arm64 versions: [NUL]0.0[NUL][NUL][NUL][NUL][NUL]11.0.22+7[NUL][NUL][NUL][NUL][NUL][NUL][NUL]openjdk[NUL]java[NUL]
|
||||
`(?m)\x00(?P<release>[0-9]+[.0-9]*)\x00+(?P<version>[0-9]+[^\x00]+)\x00+openjdk\x00java`),
|
||||
),
|
||||
// don't match graalvm
|
||||
"-jvmci-",
|
||||
),
|
||||
Package: "java/jre",
|
||||
PURL: mustPURL("pkg:generic/java/jre@version"),
|
||||
// TODO the updates might need to be part of the CPE Attributes, like: 1.8.0:update152
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
@@ -104,18 +111,22 @@ func DefaultClassifiers() []Classifier {
|
||||
EvidenceMatcher: FileContentsVersionMatcher(
|
||||
// [NUL]java[NUL]1.8[NUL][NUL][NUL][NUL]1.8.0-foreman_2022_09_22_15_30-b00[NUL]
|
||||
`(?m)\x00java\x00(?P<release>[0-9]+[.0-9]+)\x00{4}(?P<version>[0-9]+[-._a-zA-Z0-9]+)\x00`),
|
||||
Package: "java",
|
||||
PURL: mustPURL("pkg:generic/java@version"),
|
||||
Package: "java/jre",
|
||||
PURL: mustPURL("pkg:generic/java/jre@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:ibm:java:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-oracle",
|
||||
FileGlob: "**/java",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(
|
||||
// [NUL]19.0.1+10-21[NUL]
|
||||
`(?m)\x00(?P<version>[0-9]+[.0-9]+[+][-0-9]+)\x00`),
|
||||
Package: "java",
|
||||
PURL: mustPURL("pkg:generic/java@version"),
|
||||
EvidenceMatcher: matchExcluding(
|
||||
FileContentsVersionMatcher(
|
||||
// [NUL]19.0.1+10-21[NUL]
|
||||
`(?m)\x00(?P<version>[0-9]+[.0-9]+[+][-0-9]+)\x00`),
|
||||
// don't match openjdk
|
||||
`\x00openjdk\x00`,
|
||||
),
|
||||
Package: "java/jre",
|
||||
PURL: mustPURL("pkg:generic/java/jre@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:jre:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
@@ -123,10 +134,19 @@ func DefaultClassifiers() []Classifier {
|
||||
FileGlob: "**/java",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(
|
||||
`(?m)\x00(?P<version>[0-9]+[.0-9]+[.0-9]+\+[0-9]+-jvmci-[0-9]+[.0-9]+-b[0-9]+)\x00`),
|
||||
Package: "java",
|
||||
PURL: mustPURL("pkg:generic/java@version"),
|
||||
Package: "java/graalvm",
|
||||
PURL: mustPURL("pkg:generic/java/graalvm@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:graalvm:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-jdk",
|
||||
FileGlob: "**/jdb",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(
|
||||
`(?m)\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(\+[0-9]+)?([-._a-zA-Z0-9]+)?)\x00`),
|
||||
Package: "java/jdk",
|
||||
PURL: mustPURL("pkg:generic/java/jdk@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:jdk:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
Class: "nodejs-binary",
|
||||
FileGlob: "**/node",
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/mimetype"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
@@ -57,31 +58,13 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
|
||||
// first find all ELF binaries that have notes
|
||||
var notesByLocation = make(map[elfPackageKey][]elfBinaryPackageNotes)
|
||||
for _, location := range locations {
|
||||
reader, err := resolver.FileContentsByLocation(location)
|
||||
notes, key, err := parseElfPackageNotes(resolver, location, c)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to get binary contents %q: %w", location.Path(), err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
notes, err := c.parseElfNotes(file.LocationReadCloser{
|
||||
Location: location,
|
||||
ReadCloser: reader,
|
||||
})
|
||||
if err != nil {
|
||||
log.WithFields("file", location.Path(), "error", err).Trace("unable to parse ELF notes")
|
||||
continue
|
||||
}
|
||||
|
||||
if notes == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
notes.Location = location
|
||||
key := elfPackageKey{
|
||||
Name: notes.Name,
|
||||
Version: notes.Version,
|
||||
PURL: notes.PURL,
|
||||
CPE: notes.CPE,
|
||||
}
|
||||
notesByLocation[key] = append(notesByLocation[key], *notes)
|
||||
}
|
||||
|
||||
@@ -105,6 +88,37 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func parseElfPackageNotes(resolver file.Resolver, location file.Location, c *elfPackageCataloger) (*elfBinaryPackageNotes, elfPackageKey, error) {
|
||||
reader, err := resolver.FileContentsByLocation(location)
|
||||
if err != nil {
|
||||
return nil, elfPackageKey{}, fmt.Errorf("unable to get binary contents %q: %w", location.Path(), err)
|
||||
}
|
||||
defer internal.CloseAndLogError(reader, location.AccessPath)
|
||||
|
||||
notes, err := c.parseElfNotes(file.LocationReadCloser{
|
||||
Location: location,
|
||||
ReadCloser: reader,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.WithFields("file", location.Path(), "error", err).Trace("unable to parse ELF notes")
|
||||
return nil, elfPackageKey{}, nil
|
||||
}
|
||||
|
||||
if notes == nil {
|
||||
return nil, elfPackageKey{}, nil
|
||||
}
|
||||
|
||||
notes.Location = location
|
||||
key := elfPackageKey{
|
||||
Name: notes.Name,
|
||||
Version: notes.Version,
|
||||
PURL: notes.PURL,
|
||||
CPE: notes.CPE,
|
||||
}
|
||||
return notes, key, nil
|
||||
}
|
||||
|
||||
func (c *elfPackageCataloger) parseElfNotes(reader file.LocationReadCloser) (*elfBinaryPackageNotes, error) {
|
||||
metadata, err := getELFNotes(reader)
|
||||
if err != nil {
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -203,6 +203,22 @@ from-images:
|
||||
paths:
|
||||
- /usr/lib/jvm/java-11-amazon-corretto/bin/java
|
||||
|
||||
- name: java-jre-openjdk-eclipse
|
||||
version: 11.0.22
|
||||
images:
|
||||
- ref: eclipse-temurin:11.0.22_7-jre@sha256:137960b9ec016cddeb366b7137546d37207d0f284f9492c23abf16a9c171dd51
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /opt/java/openjdk/bin/java
|
||||
|
||||
- name: java-jre-openjdk-arm64-eclipse
|
||||
version: 11.0.22
|
||||
images:
|
||||
- ref: eclipse-temurin:11.0.22_7-jre@sha256:9bb82e8801e12e5377222fde83b5c91d87dc16f76c89e9f8affa678208f56797
|
||||
platform: linux/arm64
|
||||
paths:
|
||||
- /opt/java/openjdk/bin/java
|
||||
|
||||
- name: java-graal-openjdk
|
||||
version: 17.0.3+7-jvmci-22.1-b06
|
||||
images:
|
||||
@@ -211,6 +227,14 @@ from-images:
|
||||
paths:
|
||||
- /opt/java/bin/java
|
||||
|
||||
- name: java-jdk-openjdk
|
||||
version: 21.0.2+13-LTS
|
||||
images:
|
||||
- ref: eclipse-temurin:21.0.2_13-jdk-alpine@sha256:b5d37df8ee5bb964bb340acca83957f9a09291d07768fba1881f6bfc8048e4f5
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- opt/java/openjdk/bin/jdb
|
||||
|
||||
|
||||
# TODO: this is not the original binary used in the test fixture
|
||||
# - version: 5.12.5
|
||||
|
||||
@@ -10,6 +10,6 @@ func Generate(p pkg.Package) []cpe.CPE {
|
||||
return cpegenerate.FromPackageAttributes(p)
|
||||
}
|
||||
|
||||
func DictionaryFind(p pkg.Package) (cpe.CPE, bool) {
|
||||
func DictionaryFind(p pkg.Package) ([]cpe.CPE, bool) {
|
||||
return cpegenerate.FromDictionaryFind(p)
|
||||
}
|
||||
|
||||
@@ -69,13 +69,16 @@ func buildDotNetPackage(versionResources map[string]string, f file.LocationReadC
|
||||
}
|
||||
|
||||
metadata := pkg.DotnetPortableExecutableEntry{
|
||||
AssemblyVersion: versionResources["Assembly Version"],
|
||||
LegalCopyright: versionResources["LegalCopyright"],
|
||||
Comments: versionResources["Comments"],
|
||||
InternalName: versionResources["InternalName"],
|
||||
CompanyName: versionResources["CompanyName"],
|
||||
ProductName: versionResources["ProductName"],
|
||||
ProductVersion: versionResources["ProductVersion"],
|
||||
AssemblyVersion: versionResources["Assembly Version"],
|
||||
LegalCopyright: versionResources["LegalCopyright"],
|
||||
Comments: versionResources["Comments"],
|
||||
InternalName: versionResources["InternalName"],
|
||||
CompanyName: versionResources["CompanyName"],
|
||||
ProductName: versionResources["ProductName"],
|
||||
ProductVersion: versionResources["ProductVersion"],
|
||||
FileDescription: versionResources["FileDescription"],
|
||||
FileVersion: versionResources["FileVersion"],
|
||||
OriginalFilename: versionResources["OriginalFilename"],
|
||||
}
|
||||
|
||||
dnpkg = pkg.Package{
|
||||
@@ -215,7 +218,7 @@ func findName(versionResources map[string]string) string {
|
||||
}
|
||||
|
||||
for _, field := range nameFields {
|
||||
value := spaceNormalize(versionResources[field])
|
||||
value := resolveValue(versionResources[field], versionResources, nil)
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
@@ -225,6 +228,40 @@ func findName(versionResources map[string]string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func resolveValue(value string, collection map[string]string, visited map[string]bool) string {
|
||||
value = spaceNormalize(value)
|
||||
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
if visited == nil {
|
||||
visited = make(map[string]bool)
|
||||
}
|
||||
|
||||
if visited[value] {
|
||||
return value
|
||||
}
|
||||
visited[value] = true
|
||||
|
||||
hasIndirect, nextKey := hasIndirectFieldPrefix(value)
|
||||
if !hasIndirect {
|
||||
return value
|
||||
}
|
||||
|
||||
return resolveValue(collection[nextKey], collection, visited)
|
||||
}
|
||||
|
||||
func hasIndirectFieldPrefix(value string) (bool, string) {
|
||||
for _, prefix := range []string{"f#", "p("} {
|
||||
cleanValue := strings.TrimPrefix(value, prefix)
|
||||
if cleanValue != value {
|
||||
return true, cleanValue
|
||||
}
|
||||
}
|
||||
return false, value
|
||||
}
|
||||
|
||||
// normalizes a string to a trimmed version with all contigous whitespace collapsed to a single space character
|
||||
func spaceNormalize(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
|
||||
@@ -346,3 +346,126 @@ func Test_spaceNormalize(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_resolveValue(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
collection map[string]string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "simple value",
|
||||
value: "value",
|
||||
collection: map[string]string{
|
||||
"key": "value",
|
||||
},
|
||||
want: "value",
|
||||
},
|
||||
{
|
||||
name: "simple value with spaces",
|
||||
value: " value ",
|
||||
collection: map[string]string{
|
||||
"key": "value",
|
||||
},
|
||||
want: "value",
|
||||
},
|
||||
{
|
||||
name: "indirect value - f#",
|
||||
value: "f#other",
|
||||
collection: map[string]string{
|
||||
"key": "f#other",
|
||||
"other": "value",
|
||||
},
|
||||
want: "value",
|
||||
},
|
||||
{
|
||||
name: "indirect value - p(",
|
||||
value: "f#other",
|
||||
collection: map[string]string{
|
||||
"key": "p(other",
|
||||
"other": "value",
|
||||
},
|
||||
want: "value",
|
||||
},
|
||||
{
|
||||
name: "indirect value with cycles",
|
||||
value: "f#other",
|
||||
collection: map[string]string{
|
||||
"key": "f#other",
|
||||
"other": "f#key",
|
||||
},
|
||||
want: "f#other", // this is NOT ideal, but there is no "good" answer here
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, resolveValue(tt.value, tt.collection, nil))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_findVersion(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
versionResources map[string]string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "prefer file version over product version (when both semver)",
|
||||
versionResources: map[string]string{
|
||||
"FileVersion": "1.2.3",
|
||||
"ProductVersion": "4.5.6",
|
||||
},
|
||||
want: "4.5.6",
|
||||
},
|
||||
{
|
||||
name: "prefer file version over product version (when both semver)",
|
||||
versionResources: map[string]string{
|
||||
"FileVersion": "1.2.3",
|
||||
"ProductVersion": "4.5.6.7",
|
||||
},
|
||||
want: "1.2.3",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, findVersion(tt.versionResources))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_keepGreaterSemanticVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
productVersion string
|
||||
fileVersion string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "product semver is greater",
|
||||
productVersion: "3.0.0",
|
||||
fileVersion: "2.0.0",
|
||||
want: "3.0.0",
|
||||
},
|
||||
{
|
||||
name: "file semver is greater",
|
||||
productVersion: "2.0.0",
|
||||
fileVersion: "3.0.0",
|
||||
want: "3.0.0",
|
||||
},
|
||||
{
|
||||
name: "semver preferred over non-semver",
|
||||
productVersion: "3.0.0.2",
|
||||
fileVersion: "3.0.0",
|
||||
want: "3.0.0",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, keepGreaterSemanticVersion(tt.productVersion, tt.fileVersion))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package generic
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/anchore/go-logger"
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
@@ -122,17 +123,9 @@ func (c *Cataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.
|
||||
|
||||
log.WithFields("path", location.RealPath).Trace("parsing file contents")
|
||||
|
||||
contentReader, err := resolver.FileContentsByLocation(location)
|
||||
discoveredPackages, discoveredRelationships, err := invokeParser(ctx, resolver, location, logger, parser, &env)
|
||||
if err != nil {
|
||||
logger.WithFields("location", location.RealPath, "error", err).Warn("unable to fetch contents")
|
||||
continue
|
||||
}
|
||||
|
||||
discoveredPackages, discoveredRelationships, err := parser(ctx, resolver, &env, file.NewLocationReadCloser(location, contentReader))
|
||||
internal.CloseAndLogError(contentReader, location.AccessPath)
|
||||
if err != nil {
|
||||
logger.WithFields("location", location.RealPath, "error", err).Warnf("cataloger failed")
|
||||
continue
|
||||
continue // logging is handled within invokeParser
|
||||
}
|
||||
|
||||
for _, p := range discoveredPackages {
|
||||
@@ -145,6 +138,23 @@ func (c *Cataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.
|
||||
return packages, relationships, nil
|
||||
}
|
||||
|
||||
func invokeParser(ctx context.Context, resolver file.Resolver, location file.Location, logger logger.Logger, parser Parser, env *Environment) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
contentReader, err := resolver.FileContentsByLocation(location)
|
||||
if err != nil {
|
||||
logger.WithFields("location", location.RealPath, "error", err).Warn("unable to fetch contents")
|
||||
return nil, nil, err
|
||||
}
|
||||
defer internal.CloseAndLogError(contentReader, location.AccessPath)
|
||||
|
||||
discoveredPackages, discoveredRelationships, err := parser(ctx, resolver, env, file.NewLocationReadCloser(location, contentReader))
|
||||
if err != nil {
|
||||
logger.WithFields("location", location.RealPath, "error", err).Warnf("cataloger failed")
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return discoveredPackages, discoveredRelationships, nil
|
||||
}
|
||||
|
||||
// selectFiles takes a set of file trees and resolves and file references of interest for future cataloging
|
||||
func (c *Cataloger) selectFiles(resolver file.Resolver) []request {
|
||||
var requests []request
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -86,3 +87,103 @@ func Test_Cataloger(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type spyReturningFileResolver struct {
|
||||
m *file.MockResolver
|
||||
s *spyingIoReadCloser
|
||||
}
|
||||
|
||||
type spyingIoReadCloser struct {
|
||||
rc io.ReadCloser
|
||||
closed bool
|
||||
}
|
||||
|
||||
func newSpyReturningFileResolver(s *spyingIoReadCloser, paths ...string) file.Resolver {
|
||||
m := file.NewMockResolverForPaths(paths...)
|
||||
return spyReturningFileResolver{
|
||||
m: m,
|
||||
s: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spyingIoReadCloser) Read(p []byte) (n int, err error) {
|
||||
return s.Read(p)
|
||||
}
|
||||
|
||||
func (s *spyingIoReadCloser) Close() error {
|
||||
s.closed = true
|
||||
return s.rc.Close()
|
||||
}
|
||||
|
||||
var _ io.ReadCloser = (*spyingIoReadCloser)(nil)
|
||||
|
||||
func (m spyReturningFileResolver) FileContentsByLocation(location file.Location) (io.ReadCloser, error) {
|
||||
return m.s, nil
|
||||
}
|
||||
|
||||
func (m spyReturningFileResolver) HasPath(path string) bool {
|
||||
return m.m.HasPath(path)
|
||||
}
|
||||
|
||||
func (m spyReturningFileResolver) FilesByPath(paths ...string) ([]file.Location, error) {
|
||||
return m.m.FilesByPath(paths...)
|
||||
}
|
||||
|
||||
func (m spyReturningFileResolver) FilesByGlob(patterns ...string) ([]file.Location, error) {
|
||||
return m.m.FilesByGlob(patterns...)
|
||||
}
|
||||
|
||||
func (m spyReturningFileResolver) FilesByMIMEType(types ...string) ([]file.Location, error) {
|
||||
return m.m.FilesByMIMEType(types...)
|
||||
}
|
||||
|
||||
func (m spyReturningFileResolver) RelativeFileByPath(f file.Location, path string) *file.Location {
|
||||
return m.m.RelativeFileByPath(f, path)
|
||||
}
|
||||
|
||||
func (m spyReturningFileResolver) AllLocations(ctx context.Context) <-chan file.Location {
|
||||
return m.m.AllLocations(ctx)
|
||||
}
|
||||
|
||||
func (m spyReturningFileResolver) FileMetadataByLocation(location file.Location) (file.Metadata, error) {
|
||||
return m.m.FileMetadataByLocation(location)
|
||||
}
|
||||
|
||||
var _ file.Resolver = (*spyReturningFileResolver)(nil)
|
||||
|
||||
func TestClosesFileOnParserPanic(t *testing.T) {
|
||||
rc := io.NopCloser(strings.NewReader("some string"))
|
||||
spy := spyingIoReadCloser{
|
||||
rc: rc,
|
||||
}
|
||||
resolver := newSpyReturningFileResolver(&spy, "test-fixtures/another-path.txt")
|
||||
ctx := context.TODO()
|
||||
|
||||
processors := []processor{
|
||||
func(resolver file.Resolver, env Environment) []request {
|
||||
return []request{
|
||||
{
|
||||
Location: file.Location{
|
||||
LocationData: file.LocationData{
|
||||
Coordinates: file.Coordinates{},
|
||||
AccessPath: "/some/access/path",
|
||||
},
|
||||
},
|
||||
Parser: func(context.Context, file.Resolver, *Environment, file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
panic("panic!")
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
c := Cataloger{
|
||||
processor: processors,
|
||||
upstreamCataloger: "unit-test-cataloger",
|
||||
}
|
||||
|
||||
assert.PanicsWithValue(t, "panic!", func() {
|
||||
_, _, _ = c.Catalog(ctx, resolver)
|
||||
})
|
||||
require.True(t, spy.closed)
|
||||
}
|
||||
|
||||
@@ -151,7 +151,10 @@ func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *exten
|
||||
return main
|
||||
}
|
||||
|
||||
var semverPattern = regexp.MustCompile(`\x00(?P<version>v?(\d+\.\d+\.\d+[-\w]*[+\w]*))\x00`)
|
||||
// this is checking for (.L)? because at least one binary seems to have \xA0L preceding the version string, but for some reason
|
||||
// this is unable to be matched by the regex here as \x00\xA0L;
|
||||
// the only thing that seems to work is to just look for version strings following both \x00 and \x00.L for now
|
||||
var semverPattern = regexp.MustCompile(`\x00(.L)?(?P<version>v?(\d+\.\d+\.\d+[-\w]*[+\w]*))\x00`)
|
||||
|
||||
func (c *goBinaryCataloger) findMainModuleVersion(metadata *pkg.GolangBinaryBuildinfoEntry, gbs pkg.KeyValues, reader io.ReadSeekCloser) string {
|
||||
vcsVersion, hasVersion := gbs.Get("vcs.revision")
|
||||
@@ -179,16 +182,8 @@ func (c *goBinaryCataloger) findMainModuleVersion(metadata *pkg.GolangBinaryBuil
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Trace("unable to seek to start of go binary reader")
|
||||
} else {
|
||||
contents, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Trace("unable to read from go binary reader")
|
||||
} else {
|
||||
matchMetadata := internal.MatchNamedCaptureGroups(semverPattern, string(contents))
|
||||
|
||||
version, ok := matchMetadata["version"]
|
||||
if ok {
|
||||
return version
|
||||
}
|
||||
if v := extractVersionFromContents(reader); v != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,6 +205,21 @@ func (c *goBinaryCataloger) findMainModuleVersion(metadata *pkg.GolangBinaryBuil
|
||||
return ""
|
||||
}
|
||||
|
||||
func extractVersionFromContents(reader io.Reader) string {
|
||||
contents, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Trace("unable to read from go binary reader")
|
||||
return ""
|
||||
}
|
||||
matchMetadata := internal.MatchNamedCaptureGroups(semverPattern, string(contents))
|
||||
|
||||
version, ok := matchMetadata["version"]
|
||||
if ok {
|
||||
return version
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func extractVersionFromLDFlags(ldflags string) (majorVersion string, fullVersion string) {
|
||||
if ldflags == "" {
|
||||
return "", ""
|
||||
|
||||
@@ -2,6 +2,8 @@ package golang
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -1090,3 +1092,53 @@ func Test_extractVersionFromLDFlags(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_extractVersionFromContents(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
contents io.Reader
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "empty string on error",
|
||||
contents: &alwaysErrorReader{},
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "empty string on empty reader",
|
||||
contents: bytes.NewReader([]byte{}),
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "null-byte delimited semver",
|
||||
contents: strings.NewReader("\x001.2.3\x00"),
|
||||
want: "1.2.3",
|
||||
},
|
||||
{
|
||||
name: "null-byte delimited semver with v prefix",
|
||||
contents: strings.NewReader("\x00v1.2.3\x00"),
|
||||
want: "v1.2.3",
|
||||
},
|
||||
{
|
||||
// 01a0bfc8: 0e74 5a3b 0000 a04c 7631 2e39 2e35 0000 .tZ;...Lv1.9.5.. from nginx-ingress-controller
|
||||
// at /nginx-ingress-controller in registry.k8s.io/ingress-nginx/controller:v1.9.5
|
||||
// digest: sha256:b3aba22b1da80e7acfc52b115cae1d4c687172cbf2b742d5b502419c25ff340e
|
||||
// TODO: eventually use something for managing snippets, similar to what's used with binary classifier tests
|
||||
name: "null byte, then random byte, then L then semver",
|
||||
contents: strings.NewReader("\x0e\x74\x5a\x3b\x00\x00\xa0\x4cv1.9.5\x00\x00"),
|
||||
want: "v1.9.5",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := extractVersionFromContents(tt.contents)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type alwaysErrorReader struct{}
|
||||
|
||||
func (alwaysErrorReader) Read(_ []byte) (int, error) {
|
||||
return 0, errors.New("read from always error reader")
|
||||
}
|
||||
|
||||
@@ -344,6 +344,17 @@ var defaultCandidateAdditions = buildCandidateLookup(
|
||||
candidateKey{PkgName: "wpa_supplicant"},
|
||||
candidateAddition{AdditionalVendors: []string{"w1.fi"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "dnsmasq", Vendor: "dnsmasq"},
|
||||
candidateAddition{AdditionalVendors: []string{"thekelleys"}},
|
||||
},
|
||||
// Debian packages
|
||||
{
|
||||
pkg.DebPkg,
|
||||
candidateKey{PkgName: "dnsmasq", Vendor: "dnsmasq"},
|
||||
candidateAddition{AdditionalVendors: []string{"thekelleys"}},
|
||||
},
|
||||
//
|
||||
// Binary packages
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -52,11 +52,9 @@ func generateIndexedDictionaryJSON(rawGzipData io.Reader) ([]byte, error) {
|
||||
func filterCpeList(cpeList CpeList) CpeList {
|
||||
var processedCpeList CpeList
|
||||
|
||||
seen := make(map[string]struct{})
|
||||
|
||||
for _, cpeItem := range cpeList.CpeItems {
|
||||
// Skip CPE items that don't have any references.
|
||||
if len(cpeItem.References) == 0 {
|
||||
if len(cpeItem.References.Reference) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -64,6 +62,7 @@ func filterCpeList(cpeList CpeList) CpeList {
|
||||
parsedName, err := wfn.Parse(cpeItem.Name)
|
||||
if err != nil {
|
||||
log.Printf("unable to parse CPE URI %q: %s", cpeItem.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if slices.Contains([]string{"h", "o"}, parsedName.Part) {
|
||||
@@ -71,15 +70,17 @@ func filterCpeList(cpeList CpeList) CpeList {
|
||||
}
|
||||
|
||||
normalizedName := normalizeCPE(parsedName).BindToURI()
|
||||
if _, ok := seen[normalizedName]; ok {
|
||||
continue
|
||||
}
|
||||
seen[normalizedName] = struct{}{}
|
||||
cpeItem.Name = normalizedName
|
||||
|
||||
parsedCPE, err := wfn.Parse(cpeItem.Cpe23Item.Name)
|
||||
cpeName := cpeItem.Cpe23Item.Name
|
||||
if cpeItem.Cpe23Item.Deprecation.DeprecatedBy.Name != "" {
|
||||
cpeName = cpeItem.Cpe23Item.Deprecation.DeprecatedBy.Name
|
||||
}
|
||||
|
||||
parsedCPE, err := wfn.Parse(cpeName)
|
||||
if err != nil {
|
||||
log.Printf("unable to parse CPE value %q: %s", cpeItem.Cpe23Item.Name, err)
|
||||
log.Printf("unable to parse CPE value %q: %s", cpeName, err)
|
||||
continue
|
||||
}
|
||||
|
||||
cpeItem.Cpe23Item.Name = normalizeCPE(parsedCPE).BindToFmtString()
|
||||
@@ -101,13 +102,20 @@ func normalizeCPE(cpe *wfn.Attributes) *wfn.Attributes {
|
||||
}
|
||||
|
||||
const (
|
||||
prefixForNPMPackages = "https://www.npmjs.com/package/"
|
||||
prefixForRubyGems = "https://rubygems.org/gems/"
|
||||
prefixForRubyGemsHTTP = "http://rubygems.org/gems/"
|
||||
prefixForNativeRubyGems = "https://github.com/ruby/"
|
||||
prefixForPyPIPackages = "https://pypi.org/project/"
|
||||
prefixForJenkinsPlugins = "https://github.com/jenkinsci/"
|
||||
prefixForRustCrates = "https://crates.io/crates/"
|
||||
prefixForNPMPackages = "https://www.npmjs.com/package/"
|
||||
prefixForRubyGems = "https://rubygems.org/gems/"
|
||||
prefixForRubyGemsHTTP = "http://rubygems.org/gems/"
|
||||
prefixForNativeRubyGems = "https://github.com/ruby/"
|
||||
prefixForPyPIPackages = "https://pypi.org/project/"
|
||||
prefixForJenkinsPlugins = "https://plugins.jenkins.io/"
|
||||
prefixForJenkinsPluginsGitHub = "https://github.com/jenkinsci/"
|
||||
prefixForRustCrates = "https://crates.io/crates/"
|
||||
prefixForPHPPear = "https://pear.php.net/"
|
||||
prefixForPHPPearHTTP = "http://pear.php.net/"
|
||||
prefixForPHPPecl = "https://pecl.php.net/"
|
||||
prefixForPHPPeclHTTP = "http://pecl.php.net/"
|
||||
prefixForPHPComposer = "https://packagist.org/packages/"
|
||||
prefixForGoModules = "https://pkg.go.dev/"
|
||||
)
|
||||
|
||||
// indexCPEList creates an index of CPEs by ecosystem.
|
||||
@@ -119,8 +127,8 @@ func indexCPEList(list CpeList) *dictionary.Indexed {
|
||||
for _, cpeItem := range list.CpeItems {
|
||||
cpeItemName := cpeItem.Cpe23Item.Name
|
||||
|
||||
for _, reference := range cpeItem.References {
|
||||
ref := reference.Reference.Href
|
||||
for _, reference := range cpeItem.References.Reference {
|
||||
ref := reference.Href
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(ref, prefixForNPMPackages):
|
||||
@@ -135,12 +143,27 @@ func indexCPEList(list CpeList) *dictionary.Indexed {
|
||||
case strings.HasPrefix(ref, prefixForPyPIPackages):
|
||||
addEntryForPyPIPackage(indexed, ref, cpeItemName)
|
||||
|
||||
case strings.HasPrefix(ref, prefixForJenkinsPlugins):
|
||||
case strings.HasPrefix(ref, prefixForJenkinsPluginsGitHub):
|
||||
// It _might_ be a jenkins plugin!
|
||||
addEntryForJenkinsPluginGitHub(indexed, ref, cpeItemName)
|
||||
|
||||
case strings.HasPrefix(ref, prefixForJenkinsPlugins):
|
||||
addEntryForJenkinsPlugin(indexed, ref, cpeItemName)
|
||||
|
||||
case strings.HasPrefix(ref, prefixForRustCrates):
|
||||
addEntryForRustCrate(indexed, ref, cpeItemName)
|
||||
|
||||
case strings.HasPrefix(ref, prefixForPHPPear), strings.HasPrefix(ref, prefixForPHPPearHTTP):
|
||||
addEntryForPHPPearPackage(indexed, ref, cpeItemName)
|
||||
|
||||
case strings.HasPrefix(ref, prefixForPHPPecl), strings.HasPrefix(ref, prefixForPHPPeclHTTP):
|
||||
addEntryForPHPPeclPackage(indexed, ref, cpeItemName)
|
||||
|
||||
case strings.HasPrefix(ref, prefixForPHPComposer):
|
||||
addEntryForPHPComposerPackage(indexed, ref, cpeItemName)
|
||||
|
||||
case strings.HasPrefix(ref, prefixForGoModules):
|
||||
addEntryForGoModulePackage(indexed, ref, cpeItemName)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,21 +171,29 @@ func indexCPEList(list CpeList) *dictionary.Indexed {
|
||||
return indexed
|
||||
}
|
||||
|
||||
func updateIndex(indexed *dictionary.Indexed, ecosystem string, pkgName string, cpe string) {
|
||||
if _, exists := indexed.EcosystemPackages[ecosystem]; !exists {
|
||||
indexed.EcosystemPackages[ecosystem] = make(dictionary.Packages)
|
||||
}
|
||||
|
||||
if indexed.EcosystemPackages[ecosystem][pkgName] == nil {
|
||||
indexed.EcosystemPackages[ecosystem][pkgName] = dictionary.NewSet()
|
||||
}
|
||||
|
||||
indexed.EcosystemPackages[ecosystem][pkgName].Add(cpe)
|
||||
}
|
||||
|
||||
func addEntryForRustCrate(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
// Prune off the non-package-name parts of the URL
|
||||
ref = strings.TrimPrefix(ref, prefixForRustCrates)
|
||||
ref = strings.Split(ref, "/")[0]
|
||||
|
||||
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemRustCrates]; !ok {
|
||||
indexed.EcosystemPackages[dictionary.EcosystemRustCrates] = make(dictionary.Packages)
|
||||
}
|
||||
|
||||
indexed.EcosystemPackages[dictionary.EcosystemRustCrates][ref] = cpeItemName
|
||||
updateIndex(indexed, dictionary.EcosystemRustCrates, ref, cpeItemName)
|
||||
}
|
||||
|
||||
func addEntryForJenkinsPlugin(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
func addEntryForJenkinsPluginGitHub(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
// Prune off the non-package-name parts of the URL
|
||||
ref = strings.TrimPrefix(ref, prefixForJenkinsPlugins)
|
||||
ref = strings.TrimPrefix(ref, prefixForJenkinsPluginsGitHub)
|
||||
ref = strings.Split(ref, "/")[0]
|
||||
|
||||
if !strings.HasSuffix(ref, "-plugin") {
|
||||
@@ -171,12 +202,19 @@ func addEntryForJenkinsPlugin(indexed *dictionary.Indexed, ref string, cpeItemNa
|
||||
}
|
||||
|
||||
ref = strings.TrimSuffix(ref, "-plugin")
|
||||
updateIndex(indexed, dictionary.EcosystemJenkinsPlugins, ref, cpeItemName)
|
||||
}
|
||||
|
||||
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemJenkinsPlugins]; !ok {
|
||||
indexed.EcosystemPackages[dictionary.EcosystemJenkinsPlugins] = make(dictionary.Packages)
|
||||
func addEntryForJenkinsPlugin(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
// Prune off the non-package-name parts of the URL
|
||||
ref = strings.TrimPrefix(ref, prefixForJenkinsPlugins)
|
||||
ref = strings.Split(ref, "/")[0]
|
||||
|
||||
if ref == "" {
|
||||
return
|
||||
}
|
||||
|
||||
indexed.EcosystemPackages[dictionary.EcosystemJenkinsPlugins][ref] = cpeItemName
|
||||
updateIndex(indexed, dictionary.EcosystemJenkinsPlugins, ref, cpeItemName)
|
||||
}
|
||||
|
||||
func addEntryForPyPIPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
@@ -184,11 +222,7 @@ func addEntryForPyPIPackage(indexed *dictionary.Indexed, ref string, cpeItemName
|
||||
ref = strings.TrimPrefix(ref, prefixForPyPIPackages)
|
||||
ref = strings.Split(ref, "/")[0]
|
||||
|
||||
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemPyPI]; !ok {
|
||||
indexed.EcosystemPackages[dictionary.EcosystemPyPI] = make(dictionary.Packages)
|
||||
}
|
||||
|
||||
indexed.EcosystemPackages[dictionary.EcosystemPyPI][ref] = cpeItemName
|
||||
updateIndex(indexed, dictionary.EcosystemPyPI, ref, cpeItemName)
|
||||
}
|
||||
|
||||
func addEntryForNativeRubyGem(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
@@ -196,11 +230,7 @@ func addEntryForNativeRubyGem(indexed *dictionary.Indexed, ref string, cpeItemNa
|
||||
ref = strings.TrimPrefix(ref, prefixForNativeRubyGems)
|
||||
ref = strings.Split(ref, "/")[0]
|
||||
|
||||
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemRubyGems]; !ok {
|
||||
indexed.EcosystemPackages[dictionary.EcosystemRubyGems] = make(dictionary.Packages)
|
||||
}
|
||||
|
||||
indexed.EcosystemPackages[dictionary.EcosystemRubyGems][ref] = cpeItemName
|
||||
updateIndex(indexed, dictionary.EcosystemRubyGems, ref, cpeItemName)
|
||||
}
|
||||
|
||||
func addEntryForRubyGem(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
@@ -209,11 +239,7 @@ func addEntryForRubyGem(indexed *dictionary.Indexed, ref string, cpeItemName str
|
||||
ref = strings.TrimPrefix(ref, prefixForRubyGemsHTTP)
|
||||
ref = strings.Split(ref, "/")[0]
|
||||
|
||||
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemRubyGems]; !ok {
|
||||
indexed.EcosystemPackages[dictionary.EcosystemRubyGems] = make(dictionary.Packages)
|
||||
}
|
||||
|
||||
indexed.EcosystemPackages[dictionary.EcosystemRubyGems][ref] = cpeItemName
|
||||
updateIndex(indexed, dictionary.EcosystemRubyGems, ref, cpeItemName)
|
||||
}
|
||||
|
||||
func addEntryForNPMPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
@@ -222,9 +248,84 @@ func addEntryForNPMPackage(indexed *dictionary.Indexed, ref string, cpeItemName
|
||||
ref = strings.Split(ref, "?")[0]
|
||||
ref = strings.TrimPrefix(ref, prefixForNPMPackages)
|
||||
|
||||
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemNPM]; !ok {
|
||||
indexed.EcosystemPackages[dictionary.EcosystemNPM] = make(dictionary.Packages)
|
||||
updateIndex(indexed, dictionary.EcosystemNPM, ref, cpeItemName)
|
||||
}
|
||||
|
||||
func phpExtensionPackageFromURLFragment(ref string) string {
|
||||
if strings.HasPrefix(ref, "package/") { // package/HTML_QuickForm/download
|
||||
ref = strings.TrimPrefix(ref, "package/")
|
||||
components := strings.Split(ref, "/")
|
||||
|
||||
if len(components) < 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
ref = components[0]
|
||||
} else if strings.Contains(ref, "?package=") { // package-changelog.php?package=xhprof&release=0.9.4
|
||||
components := strings.Split(ref, "?package=")
|
||||
|
||||
if len(components) < 2 {
|
||||
return ""
|
||||
}
|
||||
|
||||
components = strings.Split(components[1], "&")
|
||||
if len(components) < 2 {
|
||||
return ""
|
||||
}
|
||||
|
||||
ref = components[0]
|
||||
}
|
||||
|
||||
indexed.EcosystemPackages[dictionary.EcosystemNPM][ref] = cpeItemName
|
||||
return ref
|
||||
}
|
||||
|
||||
func addEntryForPHPPearPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
ref = strings.TrimPrefix(ref, prefixForPHPPear)
|
||||
ref = strings.TrimPrefix(ref, prefixForPHPPearHTTP)
|
||||
ref = phpExtensionPackageFromURLFragment(ref)
|
||||
|
||||
if ref == "" {
|
||||
return
|
||||
}
|
||||
|
||||
updateIndex(indexed, dictionary.EcosystemPHPPear, ref, cpeItemName)
|
||||
}
|
||||
|
||||
func addEntryForPHPPeclPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
ref = strings.TrimPrefix(ref, prefixForPHPPecl)
|
||||
ref = strings.TrimPrefix(ref, prefixForPHPPeclHTTP)
|
||||
ref = phpExtensionPackageFromURLFragment(ref)
|
||||
|
||||
if ref == "" {
|
||||
return
|
||||
}
|
||||
|
||||
updateIndex(indexed, dictionary.EcosystemPHPPecl, ref, cpeItemName)
|
||||
}
|
||||
|
||||
func addEntryForPHPComposerPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
// Prune off the non-package-name parts of the URL
|
||||
ref = strings.TrimPrefix(ref, prefixForPHPComposer)
|
||||
components := strings.Split(ref, "/")
|
||||
|
||||
if len(components) < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
ref = components[0] + "/" + components[1]
|
||||
|
||||
updateIndex(indexed, dictionary.EcosystemPHPComposer, ref, cpeItemName)
|
||||
}
|
||||
|
||||
func addEntryForGoModulePackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
|
||||
// Prune off the non-package-name parts of the URL
|
||||
ref = strings.Split(ref, "?")[0]
|
||||
ref = strings.TrimPrefix(ref, prefixForGoModules)
|
||||
|
||||
// Ignore the vulnerability reports endpoints
|
||||
if strings.HasPrefix(ref, "vuln/") {
|
||||
return
|
||||
}
|
||||
|
||||
updateIndex(indexed, dictionary.EcosystemGoModules, ref, cpeItemName)
|
||||
}
|
||||
|
||||
+104
-12
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/scylladb/go-set/strset"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -59,33 +60,46 @@ func Test_addEntryFuncs(t *testing.T) {
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemRustCrates: {
|
||||
"unicycle": "cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*",
|
||||
"unicycle": dictionary.NewSet("cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "addEntryForJenkinsPlugin",
|
||||
addEntryFunc: addEntryForJenkinsPlugin,
|
||||
name: "addEntryForJenkinsPluginGitHub",
|
||||
addEntryFunc: addEntryForJenkinsPluginGitHub,
|
||||
inputRef: "https://github.com/jenkinsci/sonarqube-plugin",
|
||||
inputCpeItemName: "cpe:2.3:a:sonarsource:sonarqube_scanner:2.7:*:*:*:*:jenkins:*:*",
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemJenkinsPlugins: {
|
||||
"sonarqube": "cpe:2.3:a:sonarsource:sonarqube_scanner:2.7:*:*:*:*:jenkins:*:*",
|
||||
"sonarqube": dictionary.NewSet("cpe:2.3:a:sonarsource:sonarqube_scanner:2.7:*:*:*:*:jenkins:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "addEntryForJenkinsPlugin: not actually a plugin",
|
||||
addEntryFunc: addEntryForJenkinsPlugin,
|
||||
name: "addEntryForJenkinsPluginGitHub: not actually a plugin",
|
||||
addEntryFunc: addEntryForJenkinsPluginGitHub,
|
||||
inputRef: "https://github.com/jenkinsci/jenkins",
|
||||
inputCpeItemName: "cpe:2.3:a:jenkins:jenkinsci:2.7:*:*:*:*:*:*:*",
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "addEntryForJenkinsPlugin",
|
||||
addEntryFunc: addEntryForJenkinsPlugin,
|
||||
inputRef: "https://plugins.jenkins.io/svn-partial-release-mgr/release",
|
||||
inputCpeItemName: "cpe:2.3:a:jenkins:subversion_partial_release_manager:1.0.1:*:*:*:*:jenkins:*:*",
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemJenkinsPlugins: {
|
||||
"svn-partial-release-mgr": dictionary.NewSet("cpe:2.3:a:jenkins:subversion_partial_release_manager:1.0.1:*:*:*:*:jenkins:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "addEntryForPyPIPackage",
|
||||
addEntryFunc: addEntryForPyPIPackage,
|
||||
@@ -94,7 +108,7 @@ func Test_addEntryFuncs(t *testing.T) {
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemPyPI: {
|
||||
"vault-cli": "cpe:2.3:a:vault-cli_project:vault-cli:*:*:*:*:*:python:*:*",
|
||||
"vault-cli": dictionary.NewSet("cpe:2.3:a:vault-cli_project:vault-cli:*:*:*:*:*:python:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -107,7 +121,7 @@ func Test_addEntryFuncs(t *testing.T) {
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemRubyGems: {
|
||||
"openssl": "cpe:2.3:a:ruby-lang:openssl:-:*:*:*:*:ruby:*:*",
|
||||
"openssl": dictionary.NewSet("cpe:2.3:a:ruby-lang:openssl:-:*:*:*:*:ruby:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -120,7 +134,7 @@ func Test_addEntryFuncs(t *testing.T) {
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemRubyGems: {
|
||||
"actionview": "cpe:2.3:a:action_view_project:action_view:*:*:*:*:*:ruby:*:*",
|
||||
"actionview": dictionary.NewSet("cpe:2.3:a:action_view_project:action_view:*:*:*:*:*:ruby:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -133,7 +147,7 @@ func Test_addEntryFuncs(t *testing.T) {
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemRubyGems: {
|
||||
"rbovirt": "cpe:2.3:a:amos_benari:rbovirt:*:*:*:*:*:ruby:*:*",
|
||||
"rbovirt": dictionary.NewSet("cpe:2.3:a:amos_benari:rbovirt:*:*:*:*:*:ruby:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -146,7 +160,85 @@ func Test_addEntryFuncs(t *testing.T) {
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemNPM: {
|
||||
"@nubosoftware/node-static": "cpe:2.3:a:\\@nubosoftware\\/node-static_project:\\@nubosoftware\\/node-static:-:*:*:*:*:node.js:*:*",
|
||||
"@nubosoftware/node-static": dictionary.NewSet("cpe:2.3:a:\\@nubosoftware\\/node-static_project:\\@nubosoftware\\/node-static:-:*:*:*:*:node.js:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "addEntryForPHPPeclPackage",
|
||||
addEntryFunc: addEntryForPHPPeclPackage,
|
||||
inputRef: "https://pecl.php.net/package/imagick/something/something/v4007.0",
|
||||
inputCpeItemName: "cpe:2.3:a:php:imagick:*:*:*:*:*:*:*:*",
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemPHPPecl: {
|
||||
"imagick": dictionary.NewSet("cpe:2.3:a:php:imagick:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "addEntryForPHPPeclPackage http changelog",
|
||||
addEntryFunc: addEntryForPHPPeclPackage,
|
||||
inputRef: "http://pecl.php.net/package-changelog.php?package=memcached&release",
|
||||
inputCpeItemName: "cpe:2.3:a:php:memcached:*:*:*:*:*:*:*:*",
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemPHPPecl: {
|
||||
"memcached": dictionary.NewSet("cpe:2.3:a:php:memcached:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "addEntryForPHPPearPackage",
|
||||
addEntryFunc: addEntryForPHPPearPackage,
|
||||
inputRef: "https://pear.php.net/package/PEAR/download",
|
||||
inputCpeItemName: "cpe:2.3:a:php:pear:*:*:*:*:*:*:*:*",
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemPHPPear: {
|
||||
"PEAR": dictionary.NewSet("cpe:2.3:a:php:pear:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "addEntryForPHPPearPackage http changelog",
|
||||
addEntryFunc: addEntryForPHPPearPackage,
|
||||
inputRef: "http://pear.php.net/package-changelog.php?package=abcdefg&release",
|
||||
inputCpeItemName: "cpe:2.3:a:php:abcdefg:*:*:*:*:*:*:*:*",
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemPHPPear: {
|
||||
"abcdefg": dictionary.NewSet("cpe:2.3:a:php:abcdefg:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "addEntryForPHPComposerPackage",
|
||||
addEntryFunc: addEntryForPHPComposerPackage,
|
||||
inputRef: "https://packagist.org/packages/frappant/frp-form-answers",
|
||||
inputCpeItemName: "cpe:2.3:a:frappant:forms_export:*:*:*:*:*:*:*:*",
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemPHPComposer: {
|
||||
"frappant/frp-form-answers": dictionary.NewSet("cpe:2.3:a:frappant:forms_export:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "addEntryForGoModulePackage",
|
||||
addEntryFunc: addEntryForGoModulePackage,
|
||||
inputRef: "https://pkg.go.dev/github.com/abc/123?whatever=xvgfhfhf",
|
||||
inputCpeItemName: "cpe:2.3:a:abc:123:*:*:*:*:*:go:*:*",
|
||||
expectedIndexed: dictionary.Indexed{
|
||||
EcosystemPackages: map[string]dictionary.Packages{
|
||||
dictionary.EcosystemGoModules: {
|
||||
"github.com/abc/123": dictionary.NewSet("cpe:2.3:a:abc:123:*:*:*:*:*:go:*:*"),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -161,7 +253,7 @@ func Test_addEntryFuncs(t *testing.T) {
|
||||
|
||||
tt.addEntryFunc(indexed, tt.inputRef, tt.inputCpeItemName)
|
||||
|
||||
if diff := cmp.Diff(tt.expectedIndexed, *indexed); diff != "" {
|
||||
if diff := cmp.Diff(tt.expectedIndexed, *indexed, cmp.AllowUnexported(strset.Set{})); diff != "" {
|
||||
t.Errorf("addEntry* mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,14 +3,19 @@ package main
|
||||
type CpeItem struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Title string `xml:"title"`
|
||||
References []struct {
|
||||
Reference struct {
|
||||
References struct {
|
||||
Reference []struct {
|
||||
Href string `xml:"href,attr"`
|
||||
Body string `xml:",chardata"`
|
||||
} `xml:"reference"`
|
||||
} `xml:"references"`
|
||||
Cpe23Item struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
Deprecation struct {
|
||||
DeprecatedBy struct {
|
||||
Name string `xml:"name,attr"`
|
||||
} `xml:"deprecated-by"`
|
||||
} `xml:"deprecation"`
|
||||
} `xml:"cpe23-item"`
|
||||
}
|
||||
|
||||
|
||||
Vendored
+99
-11
@@ -1,23 +1,111 @@
|
||||
{
|
||||
"ecosystems": {
|
||||
"go_modules": {
|
||||
"aahframe.work": [
|
||||
"cpe:2.3:a:aahframework:aah:*:*:*:*:*:go:*:*"
|
||||
],
|
||||
"github.com/ecnepsnai/web": [
|
||||
"cpe:2.3:a:web_project:web:*:*:*:*:*:go:*:*"
|
||||
],
|
||||
"github.com/square/squalor": [
|
||||
"cpe:2.3:a:square:squalor:*:*:*:*:*:go:*:*"
|
||||
],
|
||||
"gopkg.in/yaml.v3": [
|
||||
"cpe:2.3:a:yaml_project:yaml:*:*:*:*:*:go:*:*"
|
||||
]
|
||||
},
|
||||
"jenkins_plugins": {
|
||||
"sonarqube": "cpe:2.3:a:sonarsource:sonarqube_scanner:*:*:*:*:*:jenkins:*:*"
|
||||
"anchore-container-scanner": [
|
||||
"cpe:2.3:a:anchore:container_image_scanner:*:*:*:*:*:jenkins:*:*",
|
||||
"cpe:2.3:a:jenkins:anchore_container_image_scanner:*:*:*:*:*:jenkins:*:*"
|
||||
],
|
||||
"fireline": [
|
||||
"cpe:2.3:a:jenkins:360_fireline:*:*:*:*:*:jenkins:*:*"
|
||||
],
|
||||
"sonarqube": [
|
||||
"cpe:2.3:a:sonarsource:sonarqube_scanner:*:*:*:*:*:jenkins:*:*"
|
||||
],
|
||||
"svn-partial-release-mgr": [
|
||||
"cpe:2.3:a:jenkins:subversion_partial_release_manager:*:*:*:*:*:jenkins:*:*"
|
||||
]
|
||||
},
|
||||
"npm": {
|
||||
"merge-recursive": "cpe:2.3:a:umbraengineering:merge-recursive:*:*:*:*:*:node.js:*:*",
|
||||
"static-dev-server": "cpe:2.3:a:static-dev-server_project:static-dev-server:*:*:*:*:*:node.js:*:*",
|
||||
"umount": "cpe:2.3:a:umount_project:umount:*:*:*:*:*:node.js:*:*",
|
||||
"undefsafe": "cpe:2.3:a:undefsafe_project:undefsafe:*:*:*:*:*:node.js:*:*",
|
||||
"underscore": "cpe:2.3:a:underscorejs:underscore:*:*:*:*:*:node.js:*:*",
|
||||
"underscore-99xp": "cpe:2.3:a:underscore-99xp_project:underscore-99xp:*:*:*:*:*:node.js:*:*",
|
||||
"unicode": "cpe:2.3:a:unicode_project:unicode:*:*:*:*:*:node.js:*:*",
|
||||
"unicorn-list": "cpe:2.3:a:unicorn-list_project:unicorn-list:*:*:*:*:*:node.js:*:*"
|
||||
"merge-recursive": [
|
||||
"cpe:2.3:a:umbraengineering:merge-recursive:*:*:*:*:*:node.js:*:*"
|
||||
],
|
||||
"ps": [
|
||||
"cpe:2.3:a:umbraengineering:ps:*:*:*:*:*:node.js:*:*"
|
||||
],
|
||||
"static-dev-server": [
|
||||
"cpe:2.3:a:static-dev-server_project:static-dev-server:*:*:*:*:*:node.js:*:*"
|
||||
],
|
||||
"umount": [
|
||||
"cpe:2.3:a:umount_project:umount:*:*:*:*:*:node.js:*:*"
|
||||
],
|
||||
"undefsafe": [
|
||||
"cpe:2.3:a:undefsafe_project:undefsafe:*:*:*:*:*:node.js:*:*"
|
||||
],
|
||||
"underscore": [
|
||||
"cpe:2.3:a:underscorejs:underscore:*:*:*:*:*:node.js:*:*"
|
||||
],
|
||||
"underscore-99xp": [
|
||||
"cpe:2.3:a:underscore-99xp_project:underscore-99xp:*:*:*:*:*:node.js:*:*"
|
||||
],
|
||||
"ungit": [
|
||||
"cpe:2.3:a:ungit_project:ungit:*:*:*:*:*:node.js:*:*"
|
||||
],
|
||||
"unicode": [
|
||||
"cpe:2.3:a:unicode_project:unicode:*:*:*:*:*:node.js:*:*"
|
||||
],
|
||||
"unicode-json": [
|
||||
"cpe:2.3:a:unicode:unicode-json:*:*:*:*:*:node.js:*:*"
|
||||
],
|
||||
"unicorn-list": [
|
||||
"cpe:2.3:a:unicorn-list_project:unicorn-list:*:*:*:*:*:node.js:*:*"
|
||||
]
|
||||
},
|
||||
"php_composer": {
|
||||
"frappant/frp-form-answers": [
|
||||
"cpe:2.3:a:frappant:forms_export:*:*:*:*:*:typo3:*:*"
|
||||
]
|
||||
},
|
||||
"php_pear": {
|
||||
"HTML_QuickForm": [
|
||||
"cpe:2.3:a:html_quickform_project:html_quickform:*:*:*:*:*:*:*:*"
|
||||
],
|
||||
"PEAR": [
|
||||
"cpe:2.3:a:php:pear:*:*:*:*:*:*:*:*"
|
||||
],
|
||||
"XML_RPC": [
|
||||
"cpe:2.3:a:php:xml_rpc:*:*:*:*:*:pear:*:*"
|
||||
]
|
||||
},
|
||||
"php_pecl": {
|
||||
"imagick": [
|
||||
"cpe:2.3:a:php:imagick:*:*:*:*:*:*:*:*"
|
||||
],
|
||||
"memcached": [
|
||||
"cpe:2.3:a:php:memcached:*:*:*:*:*:*:*:*"
|
||||
],
|
||||
"xhprof": [
|
||||
"cpe:2.3:a:php:xhprof:*:*:*:*:*:*:*:*"
|
||||
]
|
||||
},
|
||||
"pypi": {
|
||||
"vault-cli": [
|
||||
"cpe:2.3:a:vault-cli_project:vault-cli:*:*:*:*:*:python:*:*"
|
||||
]
|
||||
},
|
||||
"rubygems": {
|
||||
"openssl": "cpe:2.3:a:ruby-lang:openssl:*:*:*:*:*:*:*:*"
|
||||
"openssl": [
|
||||
"cpe:2.3:a:ruby-lang:openssl:*:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby-lang:openssl:*:*:*:*:*:ruby:*:*"
|
||||
]
|
||||
},
|
||||
"rust_crates": {
|
||||
"unicycle": "cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*"
|
||||
"unicycle": [
|
||||
"cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+222
@@ -24873,4 +24873,226 @@
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:zzzcms:zzzphp:2.1.0:*:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:xhprof:0.9.1">
|
||||
<title xml:lang="en-US">PHP XHProf 0.9.1</title>
|
||||
<references>
|
||||
<reference href="http://pecl.php.net/package-changelog.php?package=xhprof&release=0.9.4">product changelog</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:xhprof:0.9.1:*:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:xhprof:0.9.2">
|
||||
<title xml:lang="en-US">PHP XHProf 0.9.2</title>
|
||||
<references>
|
||||
<reference href="http://pecl.php.net/package-changelog.php?package=xhprof&release=0.9.4">product changelog</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:xhprof:0.9.2:*:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:xhprof:0.9.3">
|
||||
<title xml:lang="en-US">PHP XHProf 0.9.3</title>
|
||||
<references>
|
||||
<reference href="http://pecl.php.net/package-changelog.php?package=xhprof&release=0.9.4">product changelog</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:xhprof:0.9.3:*:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:memcached:0.1.0">
|
||||
<title xml:lang="en-US">PHP memecached 0.1.0</title>
|
||||
<references>
|
||||
<reference href="https://pecl.php.net/package-changelog.php?package=memcached&release">Change Log</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:memcached:0.1.0:*:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:memcached:0.1.2">
|
||||
<title xml:lang="en-US">PHP memecached 0.1.2</title>
|
||||
<references>
|
||||
<reference href="https://pecl.php.net/package-changelog.php?package=memcached&release">Change Log</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:memcached:0.1.2:*:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:memcached:0.1.3">
|
||||
<title xml:lang="en-US">PHP memecached 0.1.3</title>
|
||||
<references>
|
||||
<reference href="https://pecl.php.net/package-changelog.php?package=memcached&release">Change Log</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:memcached:0.1.3:*:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:imagick:3.3.0">
|
||||
<title xml:lang="en-US">PHP Imagick 3.3.0</title>
|
||||
<references>
|
||||
<reference href="http://pecl.php.net/package/imagick">Version</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:imagick:3.3.0:*:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:imagick:3.4.0:-">
|
||||
<title xml:lang="en-US">PHP Imagick 3.4.0</title>
|
||||
<references>
|
||||
<reference href="http://pecl.php.net/package/imagick">Version</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:imagick:3.4.0:-:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:xml_rpc:1.4.4::~~~pear~~">
|
||||
<title xml:lang="en-US">PHP XML_RPC 1.4.4 for Pear</title>
|
||||
<references>
|
||||
<reference href="https://pear.php.net/package/XML_RPC/download/">Version</reference>
|
||||
<reference href="https://pear.php.net/package/XML_RPC/">Product</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:xml_rpc:1.4.4:*:*:*:*:pear:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:xml_rpc:1.4.5::~~~pear~~">
|
||||
<title xml:lang="en-US">PHP XML_RPC 1.4.5 for Pear</title>
|
||||
<references>
|
||||
<reference href="https://pear.php.net/package/XML_RPC/download/">Version</reference>
|
||||
<reference href="https://pear.php.net/package/XML_RPC/">Product</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:xml_rpc:1.4.5:*:*:*:*:pear:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:xml_rpc:1.4.6::~~~pear~~">
|
||||
<title xml:lang="en-US">PHP XML_RPC 1.4.6 for Pear</title>
|
||||
<references>
|
||||
<reference href="https://pear.php.net/package/XML_RPC/download/">Version</reference>
|
||||
<reference href="https://pear.php.net/package/XML_RPC/">Product</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:xml_rpc:1.4.6:*:*:*:*:pear:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:pear:1.0:-">
|
||||
<title xml:lang="en-US">PHP PEAR 1.0</title>
|
||||
<references>
|
||||
<reference href="https://pear.php.net/package/PEAR/download/">Change Log</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:pear:1.0:-:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:php:pear:1.0:b1">
|
||||
<title xml:lang="en-US">PHP PEAR 1.0 Beta 1</title>
|
||||
<references>
|
||||
<reference href="https://pear.php.net/package/PEAR/download/">Change Log</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:php:pear:1.0:b1:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:html_quickform_project:html_quickform:2.0">
|
||||
<title xml:lang="en-US">HTML_QuickForm project HTML_QuickForm 2.0</title>
|
||||
<references>
|
||||
<reference href="http://pear.php.net/package/HTML_QuickForm/">Project</reference>
|
||||
<reference href="http://pear.php.net/package/HTML_QuickForm/download">Version</reference>
|
||||
<reference href="http://some-other-unrelated-reference">Test</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:html_quickform_project:html_quickform:2.0:*:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:html_quickform_project:html_quickform:2.1">
|
||||
<title xml:lang="en-US">HTML_QuickForm project HTML_QuickForm 2.1</title>
|
||||
<references>
|
||||
<reference href="http://pear.php.net/package/HTML_QuickForm/">Project</reference>
|
||||
<reference href="http://pear.php.net/package/HTML_QuickForm/download">Version</reference>
|
||||
<reference href="http://some-other-unrelated-reference">Test</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:html_quickform_project:html_quickform:2.1:*:*:*:*:*:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:frappant:forms_export:-::~~~typo3~~">
|
||||
<title xml:lang="en-US">!Frappant Forms Export for TYPO3</title>
|
||||
<references>
|
||||
<reference href="https://packagist.org/packages/frappant/frp-form-answers">Version</reference>
|
||||
<reference href="https://frappant.ch/">Vendor</reference>
|
||||
<reference href="https://extensions.typo3.org/extension/frp_form_answers">Product</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:frappant:forms_export:-:*:*:*:*:typo3:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:jenkins:_subversion_partial_release_manager:1.0.1::~~~jenkins~~" deprecated="true" deprecation_date="2020-06-04T11:50:37.430Z">
|
||||
<title xml:lang="en-US">Jenkins Subversion Partial Release Manager 1.0.1 for Jenkins</title>
|
||||
<references>
|
||||
<reference href="https://github.com/jenkinsci/svn-partial-release-mgr-plugin">Version</reference>
|
||||
<reference href="https://plugins.jenkins.io/svn-partial-release-mgr">Product</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:jenkins:_subversion_partial_release_manager:1.0.1:*:*:*:*:jenkins:*:*">
|
||||
<cpe-23:deprecation date="2020-06-04T07:50:37.430-04:00">
|
||||
<cpe-23:deprecated-by name="cpe:2.3:a:jenkins:subversion_partial_release_manager:1.0.1:*:*:*:*:jenkins:*:*" type="NAME_CORRECTION"/>
|
||||
</cpe-23:deprecation>
|
||||
</cpe-23:cpe23-item>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:jenkins:360_fireline:1.0::~~~jenkins~~">
|
||||
<title xml:lang="en-US">Jenkins 360 FireLine 1.0 for Jenkins</title>
|
||||
<references>
|
||||
<reference href="https://plugins.jenkins.io/fireline">Product</reference>
|
||||
<reference href="https://github.com/jenkinsci/fireline-plugin">Version</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:jenkins:360_fireline:1.0:*:*:*:*:jenkins:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:anchore:container_image_scanner:-::~~~jenkins~~">
|
||||
<title xml:lang="en-US">Anchore Container Image Scanner for Jenkins</title>
|
||||
<references>
|
||||
<reference href="https://jenkins.io/security/advisory/2018-07-30/#SECURITY-1039">Advisory</reference>
|
||||
<reference href="https://github.com/jenkinsci/anchore-container-scanner-plugin/releases">Version</reference>
|
||||
<reference href="https://plugins.jenkins.io/anchore-container-scanner">Product</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:anchore:container_image_scanner:-:*:*:*:*:jenkins:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:anchore:container_image_scanner:1.0.0::~~~jenkins~~">
|
||||
<title xml:lang="en-US">Anchore Container Image Scanner 1.0.0 for Jenkins</title>
|
||||
<references>
|
||||
<reference href="https://jenkins.io/security/advisory/2018-07-30/#SECURITY-1039">Advisory</reference>
|
||||
<reference href="https://github.com/jenkinsci/anchore-container-scanner-plugin/releases">Version</reference>
|
||||
<reference href="https://plugins.jenkins.io/anchore-container-scanner">Product</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:anchore:container_image_scanner:1.0.0:*:*:*:*:jenkins:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:jenkins:anchore_container_image_scanner:1.0.0::~~~jenkins~~">
|
||||
<title xml:lang="en-US">Jenkins Anchore Container Image Scanner 1.0.0 for Jenkins</title>
|
||||
<references>
|
||||
<reference href="https://plugins.jenkins.io/anchore-container-scanner">Product</reference>
|
||||
<reference href="https://github.com/jenkinsci/anchore-container-scanner-plugin">Version</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:jenkins:anchore_container_image_scanner:1.0.0:*:*:*:*:jenkins:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:jenkins:anchore_container_image_scanner:1.0.1::~~~jenkins~~">
|
||||
<title xml:lang="en-US">Jenkins Anchore Container Image Scanner 1.0.1 for Jenkins</title>
|
||||
<references>
|
||||
<reference href="https://plugins.jenkins.io/anchore-container-scanner">Product</reference>
|
||||
<reference href="https://github.com/jenkinsci/anchore-container-scanner-plugin">Version</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:jenkins:anchore_container_image_scanner:1.0.1:*:*:*:*:jenkins:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:square:squalor:-::~~~go~~">
|
||||
<title xml:lang="en-US">Square Squalor - for Go</title>
|
||||
<references>
|
||||
<reference href="https://github.com/square/squalor/commit/f6f0a47cc344711042eb0970cb423e6950ba3f93">Advisory</reference>
|
||||
<reference href="https://pkg.go.dev/github.com/square/squalor?tab=versions">Version</reference>
|
||||
<reference href="https://square.github.io/">Product</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:square:squalor:-:*:*:*:*:go:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:tar-utils_project:tar-utils:-::~~~go~~">
|
||||
<title xml:lang="en-US">Tar-utils Project Tar-utils - for Go</title>
|
||||
<references>
|
||||
<reference href="https://github.com/whyrusleeping/tar-utils">Project</reference>
|
||||
<reference href="https://pkg.go.dev/vuln/GO-2021-0106">Advisory</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:tar-utils_project:tar-utils:-:*:*:*:*:go:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:web_project:web:1.0.0::~~~go~~">
|
||||
<title xml:lang="en-US">Web Project Web 1.0.0 for Go</title>
|
||||
<references>
|
||||
<reference href="https://github.com/ecnepsnai/web/releases">Change Log</reference>
|
||||
<reference href="https://github.com/ecnepsnai/web">Product</reference>
|
||||
<reference href="https://pkg.go.dev/github.com/ecnepsnai/web">Project</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:web_project:web:1.0.0:*:*:*:*:go:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:aahframework:aah:-::~~~go~~">
|
||||
<title xml:lang="en-US">aah framework aah for Go</title>
|
||||
<references>
|
||||
<reference href="https://pkg.go.dev/aahframe.work">Product</reference>
|
||||
<reference href="https://pkg.go.dev/vuln/GO-2020-0033">Advisory</reference>
|
||||
<reference href="https://aahframework.org/">Vendor</reference>
|
||||
<reference href="https://github.com/go-aah/aah/releases">Change Log</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:aahframework:aah:-:*:*:*:*:go:*:*"/>
|
||||
</cpe-item>
|
||||
<cpe-item name="cpe:/a:yaml_project:yaml:2.3.0::~~~go~~">
|
||||
<title xml:lang="en-US">YAML Project YAML 2.3.0 for Go</title>
|
||||
<references>
|
||||
<reference href="https://pkg.go.dev/gopkg.in/yaml.v3">Project</reference>
|
||||
<reference href="https://github.com/go-yaml/yaml">Project</reference>
|
||||
<reference href="https://github.com/go-yaml/yaml/tags">Version</reference>
|
||||
</references>
|
||||
<cpe-23:cpe23-item name="cpe:2.3:a:yaml_project:yaml:2.3.0:*:*:*:*:go:*:*"/>
|
||||
</cpe-item>
|
||||
</cpe-list>
|
||||
</cpe-list>
|
||||
</cpe-list>
|
||||
|
||||
@@ -1,15 +1,51 @@
|
||||
package dictionary
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"slices"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
)
|
||||
|
||||
const (
|
||||
EcosystemNPM = "npm"
|
||||
EcosystemRubyGems = "rubygems"
|
||||
EcosystemPyPI = "pypi"
|
||||
EcosystemPHPPear = "php_pear"
|
||||
EcosystemPHPPecl = "php_pecl"
|
||||
EcosystemPHPComposer = "php_composer"
|
||||
EcosystemJenkinsPlugins = "jenkins_plugins"
|
||||
EcosystemRustCrates = "rust_crates"
|
||||
EcosystemGoModules = "go_modules"
|
||||
)
|
||||
|
||||
type Indexed struct {
|
||||
EcosystemPackages map[string]Packages `json:"ecosystems"`
|
||||
}
|
||||
|
||||
type Packages map[string]string
|
||||
type Set struct {
|
||||
*strset.Set
|
||||
}
|
||||
|
||||
type Packages map[string]*Set
|
||||
|
||||
func NewSet(ts ...string) *Set {
|
||||
return &Set{strset.New(ts...)}
|
||||
}
|
||||
|
||||
func (s *Set) MarshalJSON() ([]byte, error) {
|
||||
l := s.List()
|
||||
slices.Sort(l)
|
||||
return json.Marshal(l)
|
||||
}
|
||||
|
||||
func (s *Set) UnmarshalJSON(data []byte) error {
|
||||
var strSlice []string
|
||||
|
||||
if err := json.Unmarshal(data, &strSlice); err == nil {
|
||||
*s = *NewSet(strSlice...)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -58,52 +58,69 @@ func GetIndexedDictionary() (_ *dictionary.Indexed, err error) {
|
||||
return indexedCPEDictionary, err
|
||||
}
|
||||
|
||||
func FromDictionaryFind(p pkg.Package) (cpe.CPE, bool) {
|
||||
func FromDictionaryFind(p pkg.Package) ([]cpe.CPE, bool) {
|
||||
dict, err := GetIndexedDictionary()
|
||||
parsedCPEs := []cpe.CPE{}
|
||||
if err != nil {
|
||||
log.Debugf("CPE dictionary lookup not available: %+v", err)
|
||||
return cpe.CPE{}, false
|
||||
return parsedCPEs, false
|
||||
}
|
||||
|
||||
var (
|
||||
cpeString string
|
||||
ok bool
|
||||
cpes *dictionary.Set
|
||||
ok bool
|
||||
)
|
||||
|
||||
switch p.Type {
|
||||
case pkg.NpmPkg:
|
||||
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemNPM][p.Name]
|
||||
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemNPM][p.Name]
|
||||
|
||||
case pkg.GemPkg:
|
||||
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemRubyGems][p.Name]
|
||||
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemRubyGems][p.Name]
|
||||
|
||||
case pkg.PythonPkg:
|
||||
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemPyPI][p.Name]
|
||||
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemPyPI][p.Name]
|
||||
|
||||
case pkg.JenkinsPluginPkg:
|
||||
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemJenkinsPlugins][p.Name]
|
||||
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemJenkinsPlugins][p.Name]
|
||||
|
||||
case pkg.RustPkg:
|
||||
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemRustCrates][p.Name]
|
||||
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemRustCrates][p.Name]
|
||||
|
||||
case pkg.PhpComposerPkg:
|
||||
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemPHPComposer][p.Name]
|
||||
|
||||
case pkg.PhpPeclPkg:
|
||||
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemPHPPecl][p.Name]
|
||||
|
||||
case pkg.GoModulePkg:
|
||||
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemGoModules][p.Name]
|
||||
|
||||
default:
|
||||
// The dictionary doesn't support this package type yet.
|
||||
return cpe.CPE{}, false
|
||||
return parsedCPEs, false
|
||||
}
|
||||
|
||||
if !ok {
|
||||
// The dictionary doesn't have a CPE for this package.
|
||||
return cpe.CPE{}, false
|
||||
return parsedCPEs, false
|
||||
}
|
||||
|
||||
parsedCPE, err := cpe.New(cpeString, cpe.NVDDictionaryLookupSource)
|
||||
if err != nil {
|
||||
return cpe.CPE{}, false
|
||||
for _, c := range cpes.List() {
|
||||
parsedCPE, err := cpe.New(c, cpe.NVDDictionaryLookupSource)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
parsedCPE.Attributes.Version = p.Version
|
||||
parsedCPEs = append(parsedCPEs, parsedCPE)
|
||||
}
|
||||
|
||||
parsedCPE.Attributes.Version = p.Version
|
||||
if len(parsedCPEs) == 0 {
|
||||
return []cpe.CPE{}, false
|
||||
}
|
||||
|
||||
return parsedCPE, true
|
||||
return parsedCPEs, true
|
||||
}
|
||||
|
||||
// FromPackageAttributes Create a list of CPEs for a given package, trying to guess the vendor, product tuple. We should be trying to
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/scylladb/go-set/strset"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
@@ -991,7 +992,7 @@ func TestDictionaryFindIsWired(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pkg pkg.Package
|
||||
want string
|
||||
want []cpe.CPE
|
||||
wantExists bool
|
||||
}{
|
||||
{
|
||||
@@ -1001,7 +1002,10 @@ func TestDictionaryFindIsWired(t *testing.T) {
|
||||
Version: "1.0.2k",
|
||||
Type: pkg.GemPkg,
|
||||
},
|
||||
want: "cpe:2.3:a:ruby-lang:openssl:1.0.2k:*:*:*:*:*:*:*",
|
||||
want: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:ruby-lang:openssl:1.0.2k:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
cpe.Must("cpe:2.3:a:ruby-lang:openssl:1.0.2k:*:*:*:*:ruby:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
// without the cpe data wired up, this would be empty (generation also creates cpe:2.3:a:openssl:openssl:1.0.2k:*:*:*:*:*:*:*)
|
||||
wantExists: true,
|
||||
},
|
||||
@@ -1009,8 +1013,7 @@ func TestDictionaryFindIsWired(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, gotExists := FromDictionaryFind(tt.pkg)
|
||||
|
||||
assert.Equal(t, tt.want, got.Attributes.BindToFmtString())
|
||||
assert.ElementsMatch(t, tt.want, got)
|
||||
assert.Equal(t, tt.wantExists, gotExists)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -181,6 +181,13 @@ func (j *archiveParser) discoverMainPackage(ctx context.Context) (*pkg.Package,
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// check for existence of Weave-Classes manifest key in order to exclude jars getting misrepresented as
|
||||
// their targeted counterparts, e.g. newrelic spring and tomcat instrumentation
|
||||
if _, ok := manifest.Main.Get("Weave-Classes"); ok {
|
||||
log.Debugf("excluding archive due to Weave-Classes manifest entry: %s", j.location)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// grab and assign digest for the entire archive
|
||||
digests, err := getDigestsFromArchive(j.archivePath)
|
||||
if err != nil {
|
||||
|
||||
@@ -1168,7 +1168,6 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
expectedPkgs []pkg.Package
|
||||
expectedRelationships []artifact.Relationship
|
||||
assignParent bool
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "duplicate jar regression - go case (issue #2130)",
|
||||
@@ -1351,6 +1350,11 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude instrumentation jars with Weave-Classes in manifest",
|
||||
fixtureName: "spring-instrumentation-4.3.0-1.0",
|
||||
expectedPkgs: nil, // we expect no packages to be discovered when Weave-Classes present in the manifest
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -26,7 +26,7 @@ func packageURL(name, version string, metadata pkg.JavaArchive) string {
|
||||
return pURL.ToString()
|
||||
}
|
||||
|
||||
// GroupIDFromJavaPackage returns the authoritative group ID for a Java package.
|
||||
// groupIDFromJavaMetadata returns the authoritative group ID for a Java package.
|
||||
// The order of precedence is:
|
||||
// 1. The group ID from the POM properties
|
||||
// 2. The group ID from the POM project
|
||||
|
||||
@@ -230,16 +230,30 @@ func cleanDescription(original *string) (cleaned string) {
|
||||
// resolveProperty emulates some maven property resolution logic by looking in the project's variables
|
||||
// as well as supporting the project expressions like ${project.parent.groupId}.
|
||||
// If no match is found, the entire expression including ${} is returned
|
||||
//
|
||||
//nolint:gocognit
|
||||
func resolveProperty(pom gopom.Project, property *string, propertyName string) string {
|
||||
propertyCase := safeString(property)
|
||||
log.WithFields("existingPropertyValue", propertyCase, "propertyName", propertyName).Trace("resolving property")
|
||||
seenBeforePropertyNames := map[string]struct{}{
|
||||
propertyName: {},
|
||||
}
|
||||
result := recursiveResolveProperty(pom, propertyCase, seenBeforePropertyNames)
|
||||
if propertyMatcher.MatchString(result) {
|
||||
return "" // dereferencing variable failed; fall back to empty string
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
//nolint:gocognit
|
||||
func recursiveResolveProperty(pom gopom.Project, propertyCase string, seenPropertyNames map[string]struct{}) string {
|
||||
return propertyMatcher.ReplaceAllStringFunc(propertyCase, func(match string) string {
|
||||
propertyName := strings.TrimSpace(match[2 : len(match)-1]) // remove leading ${ and trailing }
|
||||
if _, seen := seenPropertyNames[propertyName]; seen {
|
||||
return propertyCase
|
||||
}
|
||||
entries := pomProperties(pom)
|
||||
if value, ok := entries[propertyName]; ok {
|
||||
return value
|
||||
seenPropertyNames[propertyName] = struct{}{}
|
||||
return recursiveResolveProperty(pom, value, seenPropertyNames) // recursively resolve in case a variable points to a variable.
|
||||
}
|
||||
|
||||
// if we don't find anything directly in the pom properties,
|
||||
|
||||
@@ -502,7 +502,7 @@ func Test_resolveProperty(t *testing.T) {
|
||||
pom: gopom.Project{
|
||||
Parent: nil,
|
||||
},
|
||||
expected: "${project.parent.groupId}",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "nil string pointer halts search",
|
||||
@@ -512,7 +512,90 @@ func Test_resolveProperty(t *testing.T) {
|
||||
GroupID: nil,
|
||||
},
|
||||
},
|
||||
expected: "${project.parent.groupId}",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "double dereference",
|
||||
property: "${springboot.version}",
|
||||
pom: gopom.Project{
|
||||
Parent: &gopom.Parent{
|
||||
Version: stringPointer("1.2.3"),
|
||||
},
|
||||
Properties: &gopom.Properties{
|
||||
Entries: map[string]string{
|
||||
"springboot.version": "${project.parent.version}",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "1.2.3",
|
||||
},
|
||||
{
|
||||
name: "map missing stops double dereference",
|
||||
property: "${springboot.version}",
|
||||
pom: gopom.Project{
|
||||
Parent: &gopom.Parent{
|
||||
Version: stringPointer("1.2.3"),
|
||||
},
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "resolution halts even if it resolves to a variable",
|
||||
property: "${springboot.version}",
|
||||
pom: gopom.Project{
|
||||
Parent: &gopom.Parent{
|
||||
Version: stringPointer("${undefined.version}"),
|
||||
},
|
||||
Properties: &gopom.Properties{
|
||||
Entries: map[string]string{
|
||||
"springboot.version": "${project.parent.version}",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "resolution halts even if cyclic",
|
||||
property: "${springboot.version}",
|
||||
pom: gopom.Project{
|
||||
Properties: &gopom.Properties{
|
||||
Entries: map[string]string{
|
||||
"springboot.version": "${springboot.version}",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "resolution halts even if cyclic more steps",
|
||||
property: "${cyclic.version}",
|
||||
pom: gopom.Project{
|
||||
Properties: &gopom.Properties{
|
||||
Entries: map[string]string{
|
||||
"other.version": "${cyclic.version}",
|
||||
"springboot.version": "${other.version}",
|
||||
"cyclic.version": "${springboot.version}",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "resolution halts even if cyclic involving parent",
|
||||
property: "${cyclic.version}",
|
||||
pom: gopom.Project{
|
||||
Parent: &gopom.Parent{
|
||||
Version: stringPointer("${cyclic.version}"),
|
||||
},
|
||||
Properties: &gopom.Properties{
|
||||
Entries: map[string]string{
|
||||
"other.version": "${parent.version}",
|
||||
"springboot.version": "${other.version}",
|
||||
"cyclic.version": "${springboot.version}",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ JACKSON_CORE = jackson-core-2.15.2
|
||||
SBT_JACKSON_CORE = com.fasterxml.jackson.core.jackson-core-2.15.2
|
||||
OPENSAML_CORE = opensaml-core-3.4.6
|
||||
API_ALL_SOURCES = api-all-2.0.0-sources
|
||||
SPRING_INSTRUMENTATION = spring-instrumentation-4.3.0-1.0
|
||||
|
||||
$(CACHE_DIR):
|
||||
mkdir -p $(CACHE_DIR)
|
||||
@@ -19,4 +20,7 @@ $(CACHE_DIR)/$(OPENSAML_CORE).jar: $(CACHE_DIR)
|
||||
cd $(OPENSAML_CORE) && zip -r $(CACHE_PATH)/$(OPENSAML_CORE).jar .
|
||||
|
||||
$(CACHE_DIR)/$(API_ALL_SOURCES).jar: $(CACHE_DIR)
|
||||
cd $(API_ALL_SOURCES) && zip -r $(CACHE_PATH)/$(API_ALL_SOURCES).jar .
|
||||
cd $(API_ALL_SOURCES) && zip -r $(CACHE_PATH)/$(API_ALL_SOURCES).jar .
|
||||
|
||||
$(CACHE_DIR)/$(SPRING_INSTRUMENTATION).jar: $(CACHE_DIR)
|
||||
cd $(SPRING_INSTRUMENTATION) && zip -r $(CACHE_PATH)/$(SPRING_INSTRUMENTATION).jar .
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Manifest-Version: 1.0
|
||||
Implementation-Title: com.newrelic.instrumentation.spring-4.3.0
|
||||
Implementation-Version: 1.0
|
||||
Illegal-Classes:
|
||||
Weave-Violation-Filter: METHOD_MISSING_REQUIRED_ANNOTATIONS,CLASS_MISS
|
||||
ING_REQUIRED_ANNOTATIONS
|
||||
Reference-Classes: org/springframework/core/annotation/AnnotationUtils
|
||||
,org/springframework/web/bind/annotation/DeleteMapping,org/springfram
|
||||
ework/web/bind/annotation/PatchMapping,org/springframework/web/bind/a
|
||||
nnotation/PostMapping,org/springframework/web/bind/annotation/PutMapp
|
||||
ing,org/springframework/web/bind/annotation/RequestMapping,org/spring
|
||||
framework/web/method/HandlerMethod,org/springframework/web/servlet/Mo
|
||||
delAndView
|
||||
Class-Required-Annotations:
|
||||
Method-Required-Annotations:
|
||||
Implementation-Title-Alias: spring_annotations
|
||||
Weave-Classes: org/springframework/web/bind/annotation/GetMapping,org/
|
||||
springframework/web/servlet/mvc/method/AbstractHandlerMethodAdapter
|
||||
Weave-Methods: "handleInternal(Ljavax/servlet/http/HttpServletRequest;
|
||||
Ljavax/servlet/http/HttpServletResponse;Lorg/springframework/web/meth
|
||||
od/HandlerMethod;)Lorg/springframework/web/servlet/ModelAndView;"
|
||||
Implementation-Vendor: New Relic
|
||||
@@ -5,12 +5,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/elliotchance/phpserialize"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
"github.com/elliotchance/phpserialize"
|
||||
)
|
||||
|
||||
// parsePeclSerialized is a parser function for PECL metadata contents, returning "Default" php packages discovered.
|
||||
|
||||
@@ -38,7 +38,7 @@ func parseWheelOrEgg(_ context.Context, resolver file.Resolver, _ *generic.Envir
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
// fetchRecordFiles finds a corresponding installed-files.txt file for the given python package metadata file and returns the set of file records contained.
|
||||
// fetchInstalledFiles finds a corresponding installed-files.txt file for the given python package metadata file and returns the set of file records contained.
|
||||
func fetchInstalledFiles(resolver file.Resolver, metadataLocation file.Location, sitePackagesRootPath string) (files []pkg.PythonFileRecord, sources []file.Location, err error) {
|
||||
// we've been given a file reference to a specific wheel METADATA file. note: this may be for a directory
|
||||
// or for an image... for an image the METADATA file may be present within multiple layers, so it is important
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
// parseWheelOrEggMetadata takes a Python Egg or Wheel (which share the same format and values for our purposes),
|
||||
// parseWheelOrEggRecord takes a Python Egg or Wheel (which share the same format and values for our purposes),
|
||||
// returning all Python packages listed.
|
||||
func parseWheelOrEggRecord(reader io.Reader) []pkg.PythonFileRecord {
|
||||
var records []pkg.PythonFileRecord
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
)
|
||||
|
||||
// parseRpmDb parses an "Packages" RPM DB and returns the Packages listed within it.
|
||||
// nolint:funlen
|
||||
func parseRpmDB(_ context.Context, resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
f, err := os.CreateTemp("", "rpmdb")
|
||||
if err != nil {
|
||||
@@ -24,6 +25,10 @@ func parseRpmDB(_ context.Context, resolver file.Resolver, env *generic.Environm
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
log.Errorf("failed to close temp rpmdb file: %+v", err)
|
||||
}
|
||||
err = os.Remove(f.Name())
|
||||
if err != nil {
|
||||
log.Errorf("failed to remove temp rpmdb file: %+v", err)
|
||||
@@ -39,6 +44,7 @@ func parseRpmDB(_ context.Context, resolver file.Resolver, env *generic.Environm
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
pkgList, err := db.ListPackages()
|
||||
if err != nil {
|
||||
|
||||
+10
-7
@@ -11,11 +11,14 @@ type DotnetDepsEntry struct {
|
||||
|
||||
// DotnetPortableExecutableEntry is a struct that represents a single entry found within "VersionResources" section of a .NET Portable Executable binary file.
|
||||
type DotnetPortableExecutableEntry struct {
|
||||
AssemblyVersion string `json:"assemblyVersion"`
|
||||
LegalCopyright string `json:"legalCopyright"`
|
||||
Comments string `json:"comments,omitempty"`
|
||||
InternalName string `json:"internalName,omitempty"`
|
||||
CompanyName string `json:"companyName"`
|
||||
ProductName string `json:"productName"`
|
||||
ProductVersion string `json:"productVersion"`
|
||||
AssemblyVersion string `json:"assemblyVersion"`
|
||||
LegalCopyright string `json:"legalCopyright"`
|
||||
Comments string `json:"comments,omitempty"`
|
||||
InternalName string `json:"internalName,omitempty"`
|
||||
CompanyName string `json:"companyName"`
|
||||
ProductName string `json:"productName"`
|
||||
ProductVersion string `json:"productVersion"`
|
||||
FileVersion string `json:"fileVersion,omitempty"`
|
||||
FileDescription string `json:"fileDescription,omitempty"`
|
||||
OriginalFilename string `json:"originalFilename,omitempty"`
|
||||
}
|
||||
|
||||
@@ -146,9 +146,6 @@ func (s License) Merge(l License) (*License, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sHash != lHash {
|
||||
return nil, fmt.Errorf("cannot merge licenses with different hash")
|
||||
}
|
||||
|
||||
+5
-3
@@ -3,9 +3,11 @@ package pkg
|
||||
type RDescription struct {
|
||||
/*
|
||||
Fields chosen by:
|
||||
docker run --rm -it rocker/r-ver bash
|
||||
$ install2.r ggplot2 # has a lot of dependencies
|
||||
$ find /usr/local/lib/R -name DESCRIPTION | xargs cat | grep -v '^\s' | cut -d ':' -f 1 | sort | uniq -c | sort -nr
|
||||
docker run --rm -it rocker/r-ver bash
|
||||
$ install2.r ggplot2 # has a lot of dependencies
|
||||
$ find /usr/local/lib/R -name DESCRIPTION | xargs cat | grep -v '^\s' | cut -d ':' -f 1 | sort | uniq -c | sort -nr
|
||||
|
||||
For more information on the DESCRIPTION file see https://r-pkgs.org/description.html
|
||||
*/
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
@@ -40,9 +40,6 @@ func (l stereoscopeImageSourceProvider) Provide(ctx context.Context) (source.Sou
|
||||
Exclude: l.cfg.Exclude,
|
||||
Alias: l.cfg.Alias,
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return New(img, cfg), nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user