Compare commits

..
Author SHA1 Message Date
Will Murphy 6676bb7459 fix lint
Signed-off-by: Will Murphy <will.murphy@anchore.com>
2024-04-24 13:31:08 -04:00
Will Murphy 20b692df04 newLazyUnionReader cannot return err
Signed-off-by: Will Murphy <will.murphy@anchore.com>
2024-04-24 12:51:49 -04:00
Will Murphy 434f100add clean up lazy union reader
Signed-off-by: Will Murphy <will.murphy@anchore.com>
2024-04-24 12:48:55 -04:00
Will Murphy bad5cf2af8 more passing lazy union reader tests
Signed-off-by: Will Murphy <will.murphy@anchore.com>
2024-04-24 11:00:50 -04:00
Will Murphy 20a26a0dfe very WIP: lazy union reader
Signed-off-by: Will Murphy <will.murphy@anchore.com>
2024-04-24 10:34:14 -04:00
61 changed files with 4397 additions and 12252 deletions
+4 -19
View File
@@ -56,8 +56,6 @@ 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))
@@ -78,8 +76,7 @@ def main(changed_files: str | None = None, merge_base_schema_files: str | None =
add_label(pr_number, JSON_SCHEMA_LABEL)
else:
if JSON_SCHEMA_LABEL in pr_labels:
remove_label(pr_number, JSON_SCHEMA_LABEL)
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...
@@ -87,8 +84,7 @@ 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:
if BREAKING_CHANGE_LABEL in pr_labels:
remove_label(pr_number, BREAKING_CHANGE_LABEL)
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...
@@ -123,7 +119,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, error:")
print(f"Unable to add {label!r} label to PR with")
print(str(result.stderr))
sys.exit(1)
@@ -132,7 +128,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 remove '{label!r}' label from PR, error:")
print(f"Unable to label PR with {label!r}")
print(str(result.stderr))
sys.exit(1)
@@ -169,17 +165,6 @@ 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 = []
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
# the job by event.
steps:
- name: Checkout code
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- 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@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
- uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
with:
name: benchmark-test-results
path: test/results/**/*
+5 -5
View File
@@ -36,16 +36,16 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- name: Install Go
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 #v5.0.1
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 #v5.0.0
with:
go-version-file: go.mod
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@d39d31e687223d841ef683f52467bd88e9b21c14 #v3.25.3
uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 #v3.25.1
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@d39d31e687223d841ef683f52467bd88e9b21c14 #v3.25.3
uses: github/codeql-action/autobuild@c7f9125735019aa87cfc361530512d50ea439c71 #v3.25.1
# ℹ️ 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@d39d31e687223d841ef683f52467bd88e9b21c14 #v3.25.3
uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 #v3.25.1
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- run: python .github/scripts/labeler.py
env:
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
with:
# we need to be able to get the commit for a tag
fetch-depth: 0
+3 -3
View File
@@ -15,7 +15,7 @@ jobs:
environment: release
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- 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@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
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@7ccf588e3cf3cc2611714c2eeae48550fbc17552 #v0.15.11
- uses: anchore/sbom-action@ab5d7b5f48981941c4c5d6bf33aeb98fe3bae38c #v0.15.10
continue-on-error: true
with:
artifact-name: sbom.spdx.json
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- 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@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
- uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83 #v6.0.4
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@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- 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@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
- uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83 #v6.0.4
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@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 #v5.0.1
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 #v5.0.0
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@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
- uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83 #v6.0.4
with:
signoff: true
delete-branch: true
+7 -7
View File
@@ -17,7 +17,7 @@ jobs:
name: "Static analysis"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -31,7 +31,7 @@ jobs:
name: "Unit tests"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -87,7 +87,7 @@ jobs:
name: "Integration tests"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- 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@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- 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@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -187,7 +187,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: macos-latest
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- 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@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
-3
View File
@@ -49,9 +49,6 @@ linters-settings:
# If lower than 0, disable the check.
# Default: 40
statements: 50
gocritic:
enabled-checks:
- deferInLoop
output:
uniq-by-line: false
run:
-32
View File
@@ -1,32 +0,0 @@
# 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.
+7 -7
View File
@@ -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.3-0.20240501181043-2e9894674185
github.com/anchore/stereoscope v0.0.2-0.20240229175558-fe426d1b1c84
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
// we are hinting brotli to latest due to warning when installing archiver v3:
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/bmatcuk/doublestar/v4 v4.6.1
github.com/charmbracelet/bubbles v0.18.0
github.com/charmbracelet/bubbletea v0.26.1
github.com/charmbracelet/bubbletea v0.25.0
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.1.1+incompatible
github.com/docker/docker v26.0.2+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.4
github.com/gkampitakis/go-snaps v0.5.3
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
@@ -103,6 +103,7 @@ 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
@@ -121,7 +122,6 @@ 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
@@ -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-20230316100256-276c6243b2f6 // indirect
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // 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
@@ -220,7 +220,7 @@ require (
go.uber.org/multierr v1.9.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.7.0 // indirect
golang.org/x/sync v0.6.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
+14 -15
View File
@@ -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.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/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/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.26.1 h1:xujcQeF73rh4jwu3+zhfQsvV18x+7zIjlw7/CYbzGJ0=
github.com/charmbracelet/bubbletea v0.26.1/go.mod h1:FzKr7sKoO8iFVcdIBM9J0sJOcQv5nDQaYwsee3kpbgo=
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
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,6 +182,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/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=
@@ -221,8 +223,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.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 v26.0.2+incompatible h1:yGVmKUFGgcxA6PXWAokO0sQL22BrQ67cgVjko8tGdXE=
github.com/docker/docker v26.0.2+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=
@@ -255,8 +257,6 @@ 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.4 h1:GX+dkKmVsRenz7SoTbdIEL4KQARZctkMiZ8ZKprRwT8=
github.com/gkampitakis/go-snaps v0.5.4/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y=
github.com/gkampitakis/go-snaps v0.5.3 h1:2cJnBgHzJhh0Jk5XBIyDYDe1Ylfncoa9r9bVJ5qvOAE=
github.com/gkampitakis/go-snaps v0.5.3/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=
@@ -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-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
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/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=
@@ -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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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/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,7 +1051,6 @@ 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=
+3 -3
View File
@@ -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
dictionaryCPEs, ok := cpe.DictionaryFind(p)
dictionaryCPE, ok := cpe.DictionaryFind(p)
if ok {
log.Tracef("used CPE dictionary to find CPEs for %s package %q: %s", p.Type, p.Name, dictionaryCPEs)
p.CPEs = append(p.CPEs, dictionaryCPEs...)
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)
} else {
p.CPEs = append(p.CPEs, cpe.Generate(p)...)
}
+2318 -3213
View File
File diff suppressed because it is too large Load Diff
@@ -245,8 +245,6 @@ func toRootPackage(s source.Description) *spdx.Package {
Supplier: helpers.NOASSERTION,
},
PackageDownloadLocation: helpers.NOASSERTION,
PackageLicenseConcluded: helpers.NOASSERTION,
PackageLicenseDeclared: helpers.NOASSERTION,
}
if purl != nil {
@@ -519,7 +517,9 @@ func toPackageOriginator(p pkg.Package) *spdx.Originator {
}
func toPackageSupplier(p pkg.Package) *spdx.Supplier {
kind, supplier := helpers.Supplier(p)
// this uses the Originator function for now until
// a better distinction can be made for supplier
kind, supplier := helpers.Originator(p)
if kind == "" || supplier == "" {
return &spdx.Supplier{
Supplier: helpers.NOASSERTION,
@@ -624,9 +624,6 @@ 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,
},
})
}
@@ -1,210 +0,0 @@
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 ""
}
@@ -1,482 +0,0 @@
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))
})
}
}
@@ -0,0 +1,119 @@
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)
})
}
}
@@ -0,0 +1,44 @@
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
}
-70
View File
@@ -3,8 +3,6 @@ package spdxjson
import (
"bytes"
"flag"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"strings"
"testing"
@@ -115,74 +113,6 @@ 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"
@@ -1,100 +0,0 @@
{
"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,8 +67,6 @@
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"primaryPackagePurpose": "FILE"
}
],
@@ -74,8 +74,6 @@
"checksumValue": "2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
}
],
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
@@ -74,8 +74,6 @@
"checksumValue": "2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
}
],
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
@@ -100,9 +98,6 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
},
{
@@ -118,9 +113,6 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
},
{
@@ -136,9 +128,6 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
},
{
@@ -154,9 +143,6 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
},
{
@@ -172,9 +158,6 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
},
{
@@ -190,9 +173,6 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
}
],
@@ -16,8 +16,6 @@ PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: FILE
FilesAnalyzed: false
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
##### Package: @at-sign
@@ -15,42 +15,36 @@ 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
@@ -62,8 +56,6 @@ 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
@@ -16,8 +16,6 @@ PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: FILE
FilesAnalyzed: false
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
##### Package: package-2
@@ -18,8 +18,6 @@ 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
+40 -8
View File
@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"os"
"strings"
"github.com/anchore/syft/syft/source"
)
@@ -22,16 +21,21 @@ func GetSource(ctx context.Context, userInput string, cfg *GetSourceConfig) (sou
}
var errs []error
var fileNotFoundProviders []string
var fileNotfound error
// call each source provider until we find a valid source
for _, p := range providers {
src, err := p.Provide(ctx)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
fileNotFoundProviders = append(fileNotFoundProviders, p.Name())
} else {
errs = append(errs, fmt.Errorf("%s: %w", p.Name(), err))
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 src != nil {
@@ -48,8 +52,8 @@ func GetSource(ctx context.Context, userInput string, cfg *GetSourceConfig) (sou
}
}
if len(fileNotFoundProviders) > 0 {
errs = append(errs, fmt.Errorf("additionally, the following providers failed with %w: %s", os.ErrNotExist, strings.Join(fileNotFoundProviders, ", ")))
if fileNotfound != nil {
errs = append([]error{fileNotfound}, errs...)
}
return nil, sourceError(userInput, errs...)
}
@@ -67,3 +71,31 @@ 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
}
@@ -0,0 +1,135 @@
package unionreader
import (
"bytes"
"errors"
"fmt"
"io"
"math"
"sync"
)
// lazyUnionReader must implement UnionReader
var _ UnionReader = (*lazyUnionReader)(nil)
// lazyUnionReader wraps an io.Reader to make it into a logical ReadSeeker
// The reader maintains a []byte, which is everything that has been read so far.
// Otherwise, callers needing a ReadSeeker might copy the entire reader into
// a buffer in order to have a seeker.
type lazyUnionReader struct {
buf []byte // the bytes that have been read so far
cursor int64 // the current position where Read() will take place
rc io.ReadCloser // the underlying reader
mu sync.Mutex // exported methods must acquire this lock before changing any field. Unexported methods assume their caller acquired the lock
}
func (c *lazyUnionReader) Read(p []byte) (n int, err error) {
c.mu.Lock()
defer c.mu.Unlock()
needBytes := int64(len(p))
newOffset := c.cursor + needBytes
err = c.ensureReadUntil(newOffset)
if err != nil && !errors.Is(err, io.EOF) {
return 0, err
}
// stop reading either at cursor + length p, or the end of the underlying reader,
// whichever is sooner
end := min(c.cursor+int64(len(p)), int64(len(c.buf)))
copy(p, c.buf[c.cursor:end])
n = int(end - c.cursor)
c.cursor = end
return n, err
}
func (c *lazyUnionReader) ReadAt(p []byte, off int64) (n int, err error) {
c.mu.Lock()
defer c.mu.Unlock()
needUntil := int64(len(p)) + off
err = c.ensureReadUntil(needUntil)
end := min(off+int64(len(p)), int64(len(c.buf)))
start := min(off, c.maxRead())
if off > start {
return 0, io.EOF
}
copy(p, c.buf[start:end])
return int(end - start), err
}
func (c *lazyUnionReader) Seek(offset int64, whence int) (int64, error) {
c.mu.Lock()
defer c.mu.Unlock()
var trueOffset int64
var err error
switch whence {
case io.SeekStart:
trueOffset = offset
case io.SeekCurrent:
trueOffset = offset + c.cursor
err = c.ensureReadUntil(trueOffset)
case io.SeekEnd:
err = c.readAll()
trueOffset = c.maxRead() + offset
}
if err != nil {
return 0, err
}
if trueOffset < 0 {
return 0, fmt.Errorf("request to read negative offset impossible %v", trueOffset)
}
trueOffset = min(c.maxRead(), trueOffset)
c.cursor = trueOffset
return c.cursor, nil
}
func (c *lazyUnionReader) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
return c.rc.Close()
}
func (c *lazyUnionReader) readAll() error {
buf, err := io.ReadAll(c.rc)
switch {
case err != nil && errors.Is(err, io.EOF):
err = nil
case err != nil:
return err
}
c.buf = append(c.buf, buf...)
return err
}
func (c *lazyUnionReader) ensureReadUntil(offset int64) error {
readN := offset - c.maxRead()
if readN <= 0 {
return nil
}
var buf bytes.Buffer
_, err := io.CopyN(&buf, c.rc, readN)
if err != nil && !errors.Is(err, io.EOF) {
return err
}
c.buf = append(c.buf, buf.Bytes()...)
return err
}
func (c *lazyUnionReader) maxRead() int64 {
return int64(len(c.buf))
}
func min(ints ...int64) int64 {
minSeeen := int64(math.MaxInt64) // really? math.MaxInt64 has type int?
for _, n := range ints {
if n < minSeeen {
minSeeen = n
}
}
return minSeeen
}
func newLazyUnionReader(readCloser io.ReadCloser) UnionReader {
return &lazyUnionReader{
rc: readCloser,
mu: sync.Mutex{},
}
}
@@ -0,0 +1,212 @@
package unionreader
import (
"bytes"
"io"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type spyingCloser struct {
closed bool
io.Reader
}
func (s *spyingCloser) Close() error {
s.closed = true
return nil
}
func Test_lazyUnionReader_Close(t *testing.T) {
r := strings.NewReader("some string")
sc := &spyingCloser{
false,
r,
}
subject := newLazyUnionReader(sc)
require.NoError(t, subject.Close())
assert.True(t, sc.closed)
}
func Test_lazyUnionReader_ReadAll(t *testing.T) {
rc := io.NopCloser(strings.NewReader("some data"))
subject := newLazyUnionReader(rc)
b, err := io.ReadAll(subject)
require.NoError(t, err)
assert.Equal(t, "some data", string(b))
}
func Test_lazyUnionReader_RepeatedlyRead(t *testing.T) {
data := "some data for our reader that we need to read!"
rc := io.NopCloser(strings.NewReader(data))
subject := newLazyUnionReader(rc)
var readErr error
var readResult []byte
for readErr == nil {
buf := make([]byte, 2)
var n int
n, readErr = subject.Read(buf)
readResult = append(readResult, buf[:n]...)
}
assert.Equal(t, data, string(readResult))
assert.ErrorIs(t, readErr, io.EOF)
}
func Test_lazyUnionReader_ReadAt(t *testing.T) {
readStart := make([]byte, 4)
readMid := make([]byte, 4)
readEnd := make([]byte, 4)
tests := []struct {
name string
dst []byte
off int64
wantN int
wantBytes []byte
wantEOF bool
}{
{
name: "read first 4 bytes",
dst: readStart,
off: 0,
wantN: 4,
wantBytes: []byte("0123"),
},
{
name: "read 4 bytes from middle",
dst: readMid,
off: 4,
wantN: 4,
wantBytes: []byte("4567"),
},
{
name: "read last 4 bytes",
dst: readEnd,
off: 12,
wantN: 4,
wantBytes: []byte("cdef"),
},
{
name: "read past end",
dst: make([]byte, 4),
off: 14,
wantN: 2,
wantBytes: []byte("ef"),
wantEOF: true,
},
{
name: "read way out of bounds",
dst: make([]byte, 4),
off: 512,
wantN: 0,
wantEOF: true,
},
{
name: "buffer more than available",
dst: make([]byte, 512),
off: 0,
wantN: 16,
wantBytes: []byte("0123456789abcdef"),
wantEOF: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rc := io.NopCloser(strings.NewReader("0123456789abcdef"))
subject := newLazyUnionReader(rc)
n, err := subject.ReadAt(tt.dst, tt.off)
assert.Equal(t, tt.wantN, n)
assert.Equal(t, string(tt.wantBytes), string(tt.dst[:tt.wantN]))
if tt.wantEOF {
assert.ErrorIs(t, err, io.EOF)
}
})
}
}
func Test_lazyUnionReader_Seek(t *testing.T) {
//const seek = 0
//const read = 1
type command struct {
seekOffset int64
seekWhence int
readDst []byte
}
data := []byte("this is a string of data that I'm very excited to share")
tests := []struct {
name string
commands []command
wantBytes []byte
wantEOF bool
}{
{
name: "read the first 4 bytes twice",
commands: []command{
{
readDst: make([]byte, 4),
},
{
seekOffset: 0,
seekWhence: io.SeekStart,
},
{
readDst: make([]byte, 4),
},
},
wantBytes: []byte("thisthis"),
},
{
name: "read the last 4 bytes twice",
commands: []command{
{
seekWhence: io.SeekEnd,
seekOffset: -4,
},
{
readDst: make([]byte, 4),
},
{
seekWhence: io.SeekEnd,
seekOffset: -4,
},
{
readDst: make([]byte, 4),
},
},
wantBytes: []byte("harehare"),
},
{
name: "read 4, skip 4, read 4",
commands: []command{
{readDst: make([]byte, 4)},
{seekOffset: 4, seekWhence: io.SeekCurrent},
{readDst: make([]byte, 4)},
},
wantBytes: []byte("thisa st"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rc := io.NopCloser(bytes.NewReader(data))
subject := newLazyUnionReader(rc)
var readSeekErr error
var readResult []byte
for _, c := range tt.commands {
var n int
if len(c.readDst) > 0 {
n, readSeekErr = subject.Read(c.readDst)
readResult = append(readResult, c.readDst[:n]...)
} else {
_, readSeekErr = subject.Seek(c.seekOffset, c.seekWhence)
}
}
if tt.wantEOF {
assert.ErrorIs(t, readSeekErr, io.EOF)
}
assert.Equal(t, string(tt.wantBytes), string(readResult))
})
}
}
+1 -33
View File
@@ -1,13 +1,10 @@
package unionreader
import (
"bytes"
"fmt"
"io"
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,34 +41,5 @@ func GetUnionReader(readerCloser io.ReadCloser) (UnionReader, error) {
if ok {
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)
}
bytesReader := bytes.NewReader(b)
reader = struct {
io.ReadCloser
io.ReaderAt
io.Seeker
}{
ReadCloser: io.NopCloser(bytesReader),
ReaderAt: bytesReader,
Seeker: bytesReader,
}
return reader, nil
return newLazyUnionReader(readerCloser), nil
}
@@ -7,8 +7,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
)
func Test_getUnionReader_notUnionReader(t *testing.T) {
@@ -30,34 +28,3 @@ 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)
}
@@ -6,7 +6,6 @@ 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"
@@ -58,13 +57,31 @@ 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 {
notes, key, err := parseElfPackageNotes(resolver, location, c)
reader, err := resolver.FileContentsByLocation(location)
if err != nil {
return nil, nil, err
return nil, nil, fmt.Errorf("unable to get binary contents %q: %w", location.Path(), 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)
}
@@ -88,37 +105,6 @@ 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 {
+1 -1
View File
@@ -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,16 +69,13 @@ 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"],
FileDescription: versionResources["FileDescription"],
FileVersion: versionResources["FileVersion"],
OriginalFilename: versionResources["OriginalFilename"],
AssemblyVersion: versionResources["Assembly Version"],
LegalCopyright: versionResources["LegalCopyright"],
Comments: versionResources["Comments"],
InternalName: versionResources["InternalName"],
CompanyName: versionResources["CompanyName"],
ProductName: versionResources["ProductName"],
ProductVersion: versionResources["ProductVersion"],
}
dnpkg = pkg.Package{
@@ -218,7 +215,7 @@ func findName(versionResources map[string]string) string {
}
for _, field := range nameFields {
value := resolveValue(versionResources[field], versionResources, nil)
value := spaceNormalize(versionResources[field])
if value == "" {
continue
}
@@ -228,40 +225,6 @@ 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,126 +346,3 @@ 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))
})
}
}
+10 -20
View File
@@ -3,7 +3,6 @@ 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"
@@ -123,9 +122,17 @@ func (c *Cataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.
log.WithFields("path", location.RealPath).Trace("parsing file contents")
discoveredPackages, discoveredRelationships, err := invokeParser(ctx, resolver, location, logger, parser, &env)
contentReader, err := resolver.FileContentsByLocation(location)
if err != nil {
continue // logging is handled within invokeParser
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
}
for _, p := range discoveredPackages {
@@ -138,23 +145,6 @@ 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,7 +4,6 @@ import (
"context"
"fmt"
"io"
"strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -87,103 +86,3 @@ 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)
}
@@ -344,17 +344,6 @@ 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,9 +52,11 @@ 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.Reference) == 0 {
if len(cpeItem.References) == 0 {
continue
}
@@ -62,7 +64,6 @@ 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) {
@@ -70,17 +71,15 @@ func filterCpeList(cpeList CpeList) CpeList {
}
normalizedName := normalizeCPE(parsedName).BindToURI()
if _, ok := seen[normalizedName]; ok {
continue
}
seen[normalizedName] = struct{}{}
cpeItem.Name = normalizedName
cpeName := cpeItem.Cpe23Item.Name
if cpeItem.Cpe23Item.Deprecation.DeprecatedBy.Name != "" {
cpeName = cpeItem.Cpe23Item.Deprecation.DeprecatedBy.Name
}
parsedCPE, err := wfn.Parse(cpeName)
parsedCPE, err := wfn.Parse(cpeItem.Cpe23Item.Name)
if err != nil {
log.Printf("unable to parse CPE value %q: %s", cpeName, err)
continue
log.Printf("unable to parse CPE value %q: %s", cpeItem.Cpe23Item.Name, err)
}
cpeItem.Cpe23Item.Name = normalizeCPE(parsedCPE).BindToFmtString()
@@ -102,20 +101,13 @@ 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://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/"
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/"
)
// indexCPEList creates an index of CPEs by ecosystem.
@@ -127,8 +119,8 @@ func indexCPEList(list CpeList) *dictionary.Indexed {
for _, cpeItem := range list.CpeItems {
cpeItemName := cpeItem.Cpe23Item.Name
for _, reference := range cpeItem.References.Reference {
ref := reference.Href
for _, reference := range cpeItem.References {
ref := reference.Reference.Href
switch {
case strings.HasPrefix(ref, prefixForNPMPackages):
@@ -143,27 +135,12 @@ func indexCPEList(list CpeList) *dictionary.Indexed {
case strings.HasPrefix(ref, prefixForPyPIPackages):
addEntryForPyPIPackage(indexed, ref, cpeItemName)
case strings.HasPrefix(ref, prefixForJenkinsPluginsGitHub):
// It _might_ be a jenkins plugin!
addEntryForJenkinsPluginGitHub(indexed, ref, cpeItemName)
case strings.HasPrefix(ref, prefixForJenkinsPlugins):
// It _might_ be a jenkins plugin!
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)
}
}
}
@@ -171,29 +148,21 @@ 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]
updateIndex(indexed, dictionary.EcosystemRustCrates, ref, cpeItemName)
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemRustCrates]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemRustCrates] = make(dictionary.Packages)
}
indexed.EcosystemPackages[dictionary.EcosystemRustCrates][ref] = cpeItemName
}
func addEntryForJenkinsPluginGitHub(indexed *dictionary.Indexed, ref string, cpeItemName string) {
func addEntryForJenkinsPlugin(indexed *dictionary.Indexed, ref string, cpeItemName string) {
// Prune off the non-package-name parts of the URL
ref = strings.TrimPrefix(ref, prefixForJenkinsPluginsGitHub)
ref = strings.TrimPrefix(ref, prefixForJenkinsPlugins)
ref = strings.Split(ref, "/")[0]
if !strings.HasSuffix(ref, "-plugin") {
@@ -202,19 +171,12 @@ func addEntryForJenkinsPluginGitHub(indexed *dictionary.Indexed, ref string, cpe
}
ref = strings.TrimSuffix(ref, "-plugin")
updateIndex(indexed, dictionary.EcosystemJenkinsPlugins, ref, cpeItemName)
}
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
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemJenkinsPlugins]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemJenkinsPlugins] = make(dictionary.Packages)
}
updateIndex(indexed, dictionary.EcosystemJenkinsPlugins, ref, cpeItemName)
indexed.EcosystemPackages[dictionary.EcosystemJenkinsPlugins][ref] = cpeItemName
}
func addEntryForPyPIPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
@@ -222,7 +184,11 @@ func addEntryForPyPIPackage(indexed *dictionary.Indexed, ref string, cpeItemName
ref = strings.TrimPrefix(ref, prefixForPyPIPackages)
ref = strings.Split(ref, "/")[0]
updateIndex(indexed, dictionary.EcosystemPyPI, ref, cpeItemName)
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemPyPI]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemPyPI] = make(dictionary.Packages)
}
indexed.EcosystemPackages[dictionary.EcosystemPyPI][ref] = cpeItemName
}
func addEntryForNativeRubyGem(indexed *dictionary.Indexed, ref string, cpeItemName string) {
@@ -230,7 +196,11 @@ func addEntryForNativeRubyGem(indexed *dictionary.Indexed, ref string, cpeItemNa
ref = strings.TrimPrefix(ref, prefixForNativeRubyGems)
ref = strings.Split(ref, "/")[0]
updateIndex(indexed, dictionary.EcosystemRubyGems, ref, cpeItemName)
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemRubyGems]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemRubyGems] = make(dictionary.Packages)
}
indexed.EcosystemPackages[dictionary.EcosystemRubyGems][ref] = cpeItemName
}
func addEntryForRubyGem(indexed *dictionary.Indexed, ref string, cpeItemName string) {
@@ -239,7 +209,11 @@ func addEntryForRubyGem(indexed *dictionary.Indexed, ref string, cpeItemName str
ref = strings.TrimPrefix(ref, prefixForRubyGemsHTTP)
ref = strings.Split(ref, "/")[0]
updateIndex(indexed, dictionary.EcosystemRubyGems, ref, cpeItemName)
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemRubyGems]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemRubyGems] = make(dictionary.Packages)
}
indexed.EcosystemPackages[dictionary.EcosystemRubyGems][ref] = cpeItemName
}
func addEntryForNPMPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
@@ -248,84 +222,9 @@ func addEntryForNPMPackage(indexed *dictionary.Indexed, ref string, cpeItemName
ref = strings.Split(ref, "?")[0]
ref = strings.TrimPrefix(ref, prefixForNPMPackages)
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&amp;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]
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemNPM]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemNPM] = make(dictionary.Packages)
}
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)
indexed.EcosystemPackages[dictionary.EcosystemNPM][ref] = cpeItemName
}
@@ -8,7 +8,6 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -60,46 +59,33 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemRustCrates: {
"unicycle": dictionary.NewSet("cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*"),
"unicycle": "cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*",
},
},
},
},
{
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": dictionary.NewSet("cpe:2.3:a:sonarsource:sonarqube_scanner:2.7:*:*:*:*:jenkins:*:*"),
},
},
},
},
{
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:*:*",
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: {
"svn-partial-release-mgr": dictionary.NewSet("cpe:2.3:a:jenkins:subversion_partial_release_manager:1.0.1:*:*:*:*:jenkins:*:*"),
"sonarqube": "cpe:2.3:a:sonarsource:sonarqube_scanner:2.7:*:*:*:*:jenkins:*:*",
},
},
},
},
{
name: "addEntryForJenkinsPlugin: not actually a plugin",
addEntryFunc: addEntryForJenkinsPlugin,
inputRef: "https://github.com/jenkinsci/jenkins",
inputCpeItemName: "cpe:2.3:a:jenkins:jenkinsci:2.7:*:*:*:*:*:*:*",
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{},
},
},
{
name: "addEntryForPyPIPackage",
addEntryFunc: addEntryForPyPIPackage,
@@ -108,7 +94,7 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemPyPI: {
"vault-cli": dictionary.NewSet("cpe:2.3:a:vault-cli_project:vault-cli:*:*:*:*:*:python:*:*"),
"vault-cli": "cpe:2.3:a:vault-cli_project:vault-cli:*:*:*:*:*:python:*:*",
},
},
},
@@ -121,7 +107,7 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemRubyGems: {
"openssl": dictionary.NewSet("cpe:2.3:a:ruby-lang:openssl:-:*:*:*:*:ruby:*:*"),
"openssl": "cpe:2.3:a:ruby-lang:openssl:-:*:*:*:*:ruby:*:*",
},
},
},
@@ -134,7 +120,7 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemRubyGems: {
"actionview": dictionary.NewSet("cpe:2.3:a:action_view_project:action_view:*:*:*:*:*:ruby:*:*"),
"actionview": "cpe:2.3:a:action_view_project:action_view:*:*:*:*:*:ruby:*:*",
},
},
},
@@ -147,7 +133,7 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemRubyGems: {
"rbovirt": dictionary.NewSet("cpe:2.3:a:amos_benari:rbovirt:*:*:*:*:*:ruby:*:*"),
"rbovirt": "cpe:2.3:a:amos_benari:rbovirt:*:*:*:*:*:ruby:*:*",
},
},
},
@@ -160,85 +146,7 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemNPM: {
"@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&amp;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&amp;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:*:*"),
"@nubosoftware/node-static": "cpe:2.3:a:\\@nubosoftware\\/node-static_project:\\@nubosoftware\\/node-static:-:*:*:*:*:node.js:*:*",
},
},
},
@@ -253,7 +161,7 @@ func Test_addEntryFuncs(t *testing.T) {
tt.addEntryFunc(indexed, tt.inputRef, tt.inputCpeItemName)
if diff := cmp.Diff(tt.expectedIndexed, *indexed, cmp.AllowUnexported(strset.Set{})); diff != "" {
if diff := cmp.Diff(tt.expectedIndexed, *indexed); diff != "" {
t.Errorf("addEntry* mismatch (-want +got):\n%s", diff)
}
})
@@ -3,19 +3,14 @@ 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"`
Deprecation struct {
DeprecatedBy struct {
Name string `xml:"name,attr"`
} `xml:"deprecated-by"`
} `xml:"deprecation"`
Name string `xml:"name,attr"`
} `xml:"cpe23-item"`
}
@@ -1,111 +1,23 @@
{
"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": {
"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:*:*"
]
"sonarqube": "cpe:2.3:a:sonarsource:sonarqube_scanner:*:*:*:*:*:jenkins:*:*"
},
"npm": {
"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:*:*"
]
"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:*:*"
},
"rubygems": {
"openssl": [
"cpe:2.3:a:ruby-lang:openssl:*:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby-lang:openssl:*:*:*:*:*:ruby:*:*"
]
"openssl": "cpe:2.3:a:ruby-lang:openssl:*:*:*:*:*:*:*:*"
},
"rust_crates": {
"unicycle": [
"cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*"
]
"unicycle": "cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*"
}
}
}
@@ -24873,226 +24873,4 @@
</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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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,51 +1,15 @@
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 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
}
type Packages map[string]string
@@ -58,69 +58,52 @@ 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 parsedCPEs, false
return cpe.CPE{}, false
}
var (
cpes *dictionary.Set
ok bool
cpeString string
ok bool
)
switch p.Type {
case pkg.NpmPkg:
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemNPM][p.Name]
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemNPM][p.Name]
case pkg.GemPkg:
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemRubyGems][p.Name]
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemRubyGems][p.Name]
case pkg.PythonPkg:
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemPyPI][p.Name]
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemPyPI][p.Name]
case pkg.JenkinsPluginPkg:
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemJenkinsPlugins][p.Name]
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemJenkinsPlugins][p.Name]
case pkg.RustPkg:
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]
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemRustCrates][p.Name]
default:
// The dictionary doesn't support this package type yet.
return parsedCPEs, false
return cpe.CPE{}, false
}
if !ok {
// The dictionary doesn't have a CPE for this package.
return parsedCPEs, false
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, err := cpe.New(cpeString, cpe.NVDDictionaryLookupSource)
if err != nil {
return cpe.CPE{}, false
}
if len(parsedCPEs) == 0 {
return []cpe.CPE{}, false
}
parsedCPE.Attributes.Version = p.Version
return parsedCPEs, true
return parsedCPE, true
}
// FromPackageAttributes Create a list of CPEs for a given package, trying to guess the vendor, product tuple. We should be trying to
@@ -10,7 +10,6 @@ import (
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
)
@@ -992,7 +991,7 @@ func TestDictionaryFindIsWired(t *testing.T) {
tests := []struct {
name string
pkg pkg.Package
want []cpe.CPE
want string
wantExists bool
}{
{
@@ -1002,10 +1001,7 @@ func TestDictionaryFindIsWired(t *testing.T) {
Version: "1.0.2k",
Type: pkg.GemPkg,
},
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),
},
want: "cpe:2.3:a:ruby-lang:openssl:1.0.2k:*:*:*:*:*:*:*",
// without the cpe data wired up, this would be empty (generation also creates cpe:2.3:a:openssl:openssl:1.0.2k:*:*:*:*:*:*:*)
wantExists: true,
},
@@ -1013,7 +1009,8 @@ func TestDictionaryFindIsWired(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, gotExists := FromDictionaryFind(tt.pkg)
assert.ElementsMatch(t, tt.want, got)
assert.Equal(t, tt.want, got.Attributes.BindToFmtString())
assert.Equal(t, tt.wantExists, gotExists)
})
}
@@ -181,13 +181,6 @@ 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,6 +1168,7 @@ 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)",
@@ -1350,11 +1351,6 @@ 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) {
+1 -1
View File
@@ -26,7 +26,7 @@ func packageURL(name, version string, metadata pkg.JavaArchive) string {
return pURL.ToString()
}
// groupIDFromJavaMetadata returns the authoritative group ID for a Java package.
// GroupIDFromJavaPackage 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
@@ -5,7 +5,6 @@ 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)
@@ -20,7 +19,4 @@ $(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 .
$(CACHE_DIR)/$(SPRING_INSTRUMENTATION).jar: $(CACHE_DIR)
cd $(SPRING_INSTRUMENTATION) && zip -r $(CACHE_PATH)/$(SPRING_INSTRUMENTATION).jar .
cd $(API_ALL_SOURCES) && zip -r $(CACHE_PATH)/$(API_ALL_SOURCES).jar .
@@ -1,22 +0,0 @@
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
+1 -1
View File
@@ -38,7 +38,7 @@ func parseWheelOrEgg(_ context.Context, resolver file.Resolver, _ *generic.Envir
return pkgs, nil, nil
}
// fetchInstalledFiles finds a corresponding installed-files.txt file for the given python package metadata file and returns the set of file records contained.
// fetchRecordFiles 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"
)
// parseWheelOrEggRecord takes a Python Egg or Wheel (which share the same format and values for our purposes),
// parseWheelOrEggMetadata 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,7 +17,6 @@ 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 {
@@ -25,10 +24,6 @@ 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)
@@ -44,7 +39,6 @@ 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 {
+7 -10
View File
@@ -11,14 +11,11 @@ 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"`
FileVersion string `json:"fileVersion,omitempty"`
FileDescription string `json:"fileDescription,omitempty"`
OriginalFilename string `json:"originalFilename,omitempty"`
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"`
}
+3 -5
View File
@@ -3,11 +3,9 @@ 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
For more information on the DESCRIPTION file see https://r-pkgs.org/description.html
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
*/
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`