Compare commits

...
Author SHA1 Message Date
Alex Goodman 3e21379492 [wip]
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-05-03 16:12:00 -04:00
dependabot[bot] d6604adaaf chore(deps): bump github.com/charmbracelet/bubbletea (#2838)
Bumps [github.com/charmbracelet/bubbletea](https://github.com/charmbracelet/bubbletea) from 0.26.0 to 0.26.1.
- [Release notes](https://github.com/charmbracelet/bubbletea/releases)
- [Commits](https://github.com/charmbracelet/bubbletea/compare/v0.26.0...v0.26.1)

---
updated-dependencies:
- dependency-name: github.com/charmbracelet/bubbletea
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-03 09:50:20 -04:00
Alex Goodman 34ca9a8412 add security policy (#2835)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-05-02 14:45:17 +00:00
dependabot[bot] f51b39ca04 chore(deps): bump actions/setup-go from 5.0.0 to 5.0.1 (#2834)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/0c52d547c9bc32b1aa3301fd7a9cb496313a4491...cdcb36043654635271a94b9a6d1392de5bb323a7)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 10:12:10 -04:00
anchore-actions-token-generator[bot]andkzantow 9bbb42620a chore(deps): update stereoscope to 2e9894674185d121917b283f773c2b5830f8b360 (#2831)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: kzantow <3009477+kzantow@users.noreply.github.com>
2024-05-02 09:38:23 -04:00
dependabot[bot] 0b4de3d0c7 chore(deps): bump github.com/charmbracelet/bubbletea (#2833)
Bumps [github.com/charmbracelet/bubbletea](https://github.com/charmbracelet/bubbletea) from 0.25.0 to 0.26.0.
- [Release notes](https://github.com/charmbracelet/bubbletea/releases)
- [Commits](https://github.com/charmbracelet/bubbletea/compare/v0.25.0...v0.26.0)

---
updated-dependencies:
- dependency-name: github.com/charmbracelet/bubbletea
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-02 09:36:49 -04:00
camcuiandChristopher Phillips 80d196a8c9 chore: fix function name in comment (#2771)
Signed-off-by: camcui <cuishua@sina.cn>
Co-authored-by: Christopher Phillips <christopher.phillips@anchore.com>
2024-05-01 18:04:02 +00:00
William Murphy ed40833b30 chore: enable go-critic deferInLoop lint (#2825)
The goal here is to be more careful about leaking file handles and other
resources.

Signed-off-by: Will Murphy <will.murphy@anchore.com>
2024-05-01 12:59:35 -04:00
William Murphy 93a7d2ee27 fix: better clean up of file handles (#2823)
* always close ELF cataloger file handles

The elf-binary-package-cataloger does its own file IO to account for the
possibility of a logical ELF package being broken across multiple
physical files. However, this casued it to skip the normal invocation
pattern in the generic cataloger code that prevented file leaks. Ensure
this cataloger always closes its file handles.

Signed-off-by: Will Murphy <will.murphy@anchore.com>

* defer closing of generic cataloger file handles

Otherwise, a panicking cataloger could leak file handles.

Signed-off-by: Will Murphy <will.murphy@anchore.com>

* add unit test for file closed on panic parser

Signed-off-by: Will Murphy <will.murphy@anchore.com>

* make invoke parser a static function

Signed-off-by: Will Murphy <will.murphy@anchore.com>

* push error logging down into invoke parser

Signed-off-by: Will Murphy <will.murphy@anchore.com>

---------

Signed-off-by: Will Murphy <will.murphy@anchore.com>
2024-05-01 12:58:17 -04:00
dependabot[bot] 93a99e36c2 chore(deps): bump github.com/docker/docker (#2827)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 26.1.0+incompatible to 26.1.1+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v26.1.0...v26.1.1)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-01 11:03:31 -04:00
Keith Zantow b0c88ddea9 fix(spdx): include required fields (#2168)
* fix(spdx): include required fields

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* chore: missed update due to refactoring

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* chore: update tools-golang

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* chore: add test with packageVerificationCode included and excluded

Signed-off-by: Keith Zantow <kzantow@gmail.com>

---------

Signed-off-by: Keith Zantow <kzantow@gmail.com>
2024-04-30 13:28:42 -04:00
Keith Zantow 047e31a969 fix: add correct vendor for dnsmasq CPE (#2659)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2024-04-30 13:24:01 -04:00
guangwuandChristopher Phillips 25b55e1704 fix: close temp rpmdb file (#2792)
* fix: close temp rpmdb file and db

Signed-off-by: guoguangwu <guoguangwug@gmail.com>

* chore: fix linter

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>

---------

Signed-off-by: guoguangwu <guoguangwug@gmail.com>
Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
Co-authored-by: Christopher Phillips <christopher.phillips@anchore.com>
2024-04-30 12:47:17 -04:00
dependabot[bot]andChristopher Phillips 02dc2dfa9b chore(deps): bump github/codeql-action from 3.25.2 to 3.25.3 (#2817)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.2 to 3.25.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/8f596b4ae3cb3c588a5c46780b86dd53fef16c52...d39d31e687223d841ef683f52467bd88e9b21c14)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Christopher Phillips <christopher.phillips@anchore.com>
2024-04-30 16:27:14 +00:00
Alex Goodman 5b03788300 Fill in SPDX originator for all supported package types (#2822)
* add failing test + beef up doc comments

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* cover more metadata types in spdx originator processing

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-04-29 16:33:00 -04:00
dependabot[bot] 9901ea8fe9 chore(deps): bump anchore/sbom-action from 0.15.10 to 0.15.11 (#2821)
Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.15.10 to 0.15.11.
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Commits](https://github.com/anchore/sbom-action/compare/ab5d7b5f48981941c4c5d6bf33aeb98fe3bae38c...7ccf588e3cf3cc2611714c2eeae48550fbc17552)

---
updated-dependencies:
- dependency-name: anchore/sbom-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-29 12:40:43 -04:00
Alex Goodman 87cd6c8c48 update spdx license list to 3.23 (#2818)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-04-26 14:50:55 +00:00
William Murphy d3310a1830 fix: re-use embedded union reader if possible (#2814)
* fix: re-use embedded union reader if possible

Previously, because file.LocationReadCloser embeds a ReadCloser that
might be a UnionReader, but doesn't implement the interface itself, the
type assertion would fall and Syft would fall back to io.ReadAll to
enable seeking on the underlying reader, resulting in a potentially
large extra allocation.

Instead, check whether the passed ReadCloser is a
file.LocationReadCloser, and if so, try to use the embedded ReadCloser
as a UnionReader.

Signed-off-by: Will Murphy <will.murphy@anchore.com>

* lint fix

Signed-off-by: Will Murphy <will.murphy@anchore.com>

* Assert that underlying reader is returned

Signed-off-by: Will Murphy <will.murphy@anchore.com>

---------

Signed-off-by: Will Murphy <will.murphy@anchore.com>
2024-04-26 10:21:38 -04:00
Weston Steimel 8640f978ba feat: index known CPEs for go modules (#2816)
Signed-off-by: Weston Steimel <commits@weston.slmail.me>
2024-04-26 09:55:05 -04:00
dependabot[bot] 13b06dad45 chore(deps): bump peter-evans/create-pull-request from 6.0.4 to 6.0.5 (#2812) 2024-04-25 10:32:10 -04:00
Weston Steimel 9604e3dc9c feat: support multiple known CPEs in index (#2813)
It is possible that a given package has multiple known "official" CPEs
active in the dictionary at once, so the index should support a slice of
CPE strings per package

Signed-off-by: Weston Steimel <commits@weston.slmail.me>
2024-04-25 15:22:26 +01:00
anchore-actions-token-generator[bot]andkzantow f2fc10aa86 chore(deps): update stereoscope to 8b297badafd5d81fa1187b26ae34dd2a7ce7e425 (#2807)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: kzantow <3009477+kzantow@users.noreply.github.com>
2024-04-24 15:19:13 -04:00
dependabot[bot] 21b22555d2 chore(deps): bump actions/checkout from 4.1.3 to 4.1.4 (#2809)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.3 to 4.1.4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/1d96c772d19495a3b5c517cd2bc0cb401ea0529f...0ad4b8fadaa221de15dcec353f45205ec38ea70b)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-24 15:19:03 -04:00
dependabot[bot] 5326efcef1 chore(deps): bump github.com/gkampitakis/go-snaps from 0.5.3 to 0.5.4 (#2810)
Bumps [github.com/gkampitakis/go-snaps](https://github.com/gkampitakis/go-snaps) from 0.5.3 to 0.5.4.
- [Release notes](https://github.com/gkampitakis/go-snaps/releases)
- [Commits](https://github.com/gkampitakis/go-snaps/compare/v0.5.3...v0.5.4)

---
updated-dependencies:
- dependency-name: github.com/gkampitakis/go-snaps
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-24 12:04:09 -04:00
Gijs Calis b90e7f9437 Fix removing labels in 'Detect schema changes' job (#2772)
* Fix error messages for add & remove label

Signed-off-by: Gijs Calis <51088038+GijsCalis@users.noreply.github.com>

* Check if label exists on PR before removing

Signed-off-by: Gijs Calis <51088038+GijsCalis@users.noreply.github.com>

---------

Signed-off-by: Gijs Calis <51088038+GijsCalis@users.noreply.github.com>
2024-04-23 14:42:57 -04:00
dependabot[bot] fbdd4ee015 chore(deps): bump github.com/docker/docker (#2805)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 26.0.2+incompatible to 26.1.0+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v26.0.2...v26.1.0)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-23 14:32:34 -04:00
William Murphy f154bf570d Display which provider caused which error in output (#2757)
* Display which provider caused which error in output

Otherwise, the output is very difficult to parse.

Signed-off-by: Will Murphy <will.murphy@anchore.com>

* lint fix

Signed-off-by: Will Murphy <will.murphy@anchore.com>

* bump stereoscope to v0.0.2

Signed-off-by: Will Murphy <will.murphy@anchore.com>

---------

Signed-off-by: Will Murphy <will.murphy@anchore.com>
2024-04-23 14:27:39 -04:00
Weston Steimel 99b58db497 fix: prefer non-deprecated CPEs and include jenkins plugins from plugins.jenkins.io (#2806)
Improves the logic for adding known CPEs for Jenkins plugins by
including `plugins.jenkins.io` and prefers non-deprecated CPEs

Signed-off-by: Weston Steimel <commits@weston.slmail.me>
2024-04-23 11:11:47 -04:00
Weston Steimel 8d960e62c6 feat: index known CPEs for PHP Composer packagist.org packages (#2804)
Indexes known CPEs from `packagist.org`

Signed-off-by: Weston Steimel <commits@weston.slmail.me>
2024-04-23 10:42:59 -04:00
dependabot[bot] 27a8a1be03 chore(deps): bump github/codeql-action from 3.25.1 to 3.25.2 (#2802)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.1 to 3.25.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/c7f9125735019aa87cfc361530512d50ea439c71...8f596b4ae3cb3c588a5c46780b86dd53fef16c52)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-23 09:54:36 -04:00
dependabot[bot] 4ccbd17255 chore(deps): bump actions/upload-artifact from 4.3.2 to 4.3.3 (#2803)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.2 to 4.3.3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/1746f4ab65b179e0ea60a494b83293b640dd5bba...65462800fd760344b1a7b4382951275a0abb4808)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-23 09:54:28 -04:00
Weston Steimel 891e61a2ef fix: improvements to known CPE index construction (#2801)
* fix: stop pre-filtering potential known CPE URLs

Previously when building the known CPE index, there was logic to
de-duplicate processing based on the normalized CPE name; however, this
means a significant number of known CPE's don't get indexed because the
first instance of that name didn't have a supported collection url but a
later one did.  This isn't code that executes at runtime in syft so
de-duplicating the processing for performance isn't really necessary
here and it doesn't add much to the total runtime anyways

Signed-off-by: Weston Steimel <commits@weston.slmail.me>

* fix: CPE index builder should extract and consider all reference urls

Previously the struct definition for CpeItem caused only the last URL
reference in the list to be kept and processed for inclusion in the
index

Signed-off-by: Weston Steimel <commits@weston.slmail.me>

---------

Signed-off-by: Weston Steimel <commits@weston.slmail.me>
2024-04-23 09:28:18 -04:00
Keith Zantow f7d3d552ce fix: exclude known instrumentation jars from being erroneously identified (#2796)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2024-04-22 15:03:17 -04:00
Weston Steimel 12ea9912b4 feat: index known cpes for PHP extensions (#2777)
Indexes known CPEs from `pecl.php.net` and `pear.php.net`

Signed-off-by: Weston Steimel <commits@weston.slmail.me>
2024-04-22 13:43:19 -04:00
dependabot[bot] cf6f92f2c8 chore(deps): bump actions/checkout from 4.1.2 to 4.1.3 (#2799)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/9bb56186c3b09b4f86b1c65136769dd318469633...1d96c772d19495a3b5c517cd2bc0cb401ea0529f)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-22 13:29:13 -04:00
59 changed files with 12273 additions and 4091 deletions
+19 -4
View File
@@ -56,6 +56,8 @@ def main(changed_files: str | None = None, merge_base_schema_files: str | None =
pr_json_schema_files = filter_to_schema_files(pr_changed_files)
pr_labels = get_pr_labels(pr_number)
# print("schema files in pr: ", summarize_schema_files(pr_json_schema_files))
# print("og schema files: ", summarize_schema_files(og_json_schema_files))
@@ -76,7 +78,8 @@ def main(changed_files: str | None = None, merge_base_schema_files: str | None =
add_label(pr_number, JSON_SCHEMA_LABEL)
else:
remove_label(pr_number, JSON_SCHEMA_LABEL)
if JSON_SCHEMA_LABEL in pr_labels:
remove_label(pr_number, JSON_SCHEMA_LABEL)
# new schema files should be scrutinized, comparing the latest and added versions to see if it's a breaking
# change (major version bump). Warn about it on the PR via adding a breaking-change label...
@@ -84,7 +87,8 @@ def main(changed_files: str | None = None, merge_base_schema_files: str | None =
print("\nBreaking change detected...")
add_label(pr_number, BREAKING_CHANGE_LABEL)
else:
remove_label(pr_number, BREAKING_CHANGE_LABEL)
if BREAKING_CHANGE_LABEL in pr_labels:
remove_label(pr_number, BREAKING_CHANGE_LABEL)
# modifying an existing schema could be a breaking change, we should warn about it on the PR via a comment...
# removing schema files should never be allowed, we should warn about it on the PR via a comment...
@@ -119,7 +123,7 @@ def add_label(pr_number: str, label: str):
# run "gh pr edit --add-label <label>"
result = run(f"gh pr edit {pr_number} --add-label {label}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode != 0:
print(f"Unable to add {label!r} label to PR with")
print(f"Unable to add '{label!r}' label to PR, error:")
print(str(result.stderr))
sys.exit(1)
@@ -128,7 +132,7 @@ def remove_label(pr_number: str, label: str):
# run "gh pr edit --remove-label <label>"
result = run(f"gh pr edit {pr_number} --remove-label {label}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode != 0:
print(f"Unable to label PR with {label!r}")
print(f"Unable to remove '{label!r}' label from PR, error:")
print(str(result.stderr))
sys.exit(1)
@@ -165,6 +169,17 @@ def get_pr_changed_files(pr_number: str) -> list[str]:
return list_of_files
def get_pr_labels(pr_number: str) -> list[str]:
result = run(f"gh pr view {pr_number} --json labels --jq '.labels[].name'", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode != 0:
print("Unable to get list of labels on PR")
print(str(result.stderr))
sys.exit(1)
list_of_labels = result.stdout.splitlines()
return list_of_labels
def filter_to_schema_files(list_of_files: list[str]) -> list[str]:
# get files matching "schema/json/schema-*.json"
files = []
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
# the job by event.
steps:
- name: Checkout code
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -39,7 +39,7 @@ jobs:
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
echo "result=$OUTPUT" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
- uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: benchmark-test-results
path: test/results/**/*
+5 -5
View File
@@ -36,16 +36,16 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- name: Install Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 #v5.0.0
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 #v5.0.1
with:
go-version-file: go.mod
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 #v3.25.1
uses: github/codeql-action/init@d39d31e687223d841ef683f52467bd88e9b21c14 #v3.25.3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@c7f9125735019aa87cfc361530512d50ea439c71 #v3.25.1
uses: github/codeql-action/autobuild@d39d31e687223d841ef683f52467bd88e9b21c14 #v3.25.3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -70,4 +70,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 #v3.25.1
uses: github/codeql-action/analyze@d39d31e687223d841ef683f52467bd88e9b21c14 #v3.25.3
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- run: python .github/scripts/labeler.py
env:
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
with:
# we need to be able to get the commit for a tag
fetch-depth: 0
+3 -3
View File
@@ -15,7 +15,7 @@ jobs:
environment: release
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- name: Check if tag already exists
# note: this will fail if the tag already exists
@@ -97,7 +97,7 @@ jobs:
# required for goreleaser signs section with cosign
id-token: write
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
with:
fetch-depth: 0
@@ -143,7 +143,7 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.TOOLBOX_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }}
- uses: anchore/sbom-action@ab5d7b5f48981941c4c5d6bf33aeb98fe3bae38c #v0.15.10
- uses: anchore/sbom-action@7ccf588e3cf3cc2611714c2eeae48550fbc17552 #v0.15.11
continue-on-error: true
with:
artifact-name: sbom.spdx.json
+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@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -50,7 +50,7 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83 #v6.0.4
- uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
with:
signoff: true
delete-branch: true
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -33,7 +33,7 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83 #v6.0.4
- uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
with:
signoff: true
delete-branch: true
@@ -17,9 +17,9 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 #v5.0.0
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 #v5.0.1
with:
go-version: ${{ env.GO_VERSION }}
stable: ${{ env.GO_STABLE_VERSION }}
@@ -44,7 +44,7 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83 #v6.0.4
- uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
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@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -31,7 +31,7 @@ jobs:
name: "Unit tests"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -87,7 +87,7 @@ jobs:
name: "Integration tests"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -109,7 +109,7 @@ jobs:
name: "Build snapshot artifacts"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -139,7 +139,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -187,7 +187,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: macos-latest
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -232,7 +232,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b #v4.1.4
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
+3
View File
@@ -49,6 +49,9 @@ linters-settings:
# If lower than 0, disable the check.
# Default: 40
statements: 50
gocritic:
enabled-checks:
- deferInLoop
output:
uniq-by-line: false
run:
+32
View File
@@ -0,0 +1,32 @@
# Security Policy
## Supported Versions
<!-- Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
-->
Security updates are applied only to the most recent release, try to always be up to date.
## Reporting a Vulnerability
<!-- Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc. -->
To report a security issue, please email
[security@anchore.com](mailto:security@anchore.com)
with a description of the issue, the steps you took to create the issue,
affected versions, and, if known, mitigations for the issue.
All support will be made on the best effort base, so please indicate the "urgency level" of the vulnerability as Critical, High, Medium or Low.
+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.2-0.20240229175558-fe426d1b1c84
github.com/anchore/stereoscope v0.0.3-0.20240501181043-2e9894674185
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
// we are hinting brotli to latest due to warning when installing archiver v3:
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/bmatcuk/doublestar/v4 v4.6.1
github.com/charmbracelet/bubbles v0.18.0
github.com/charmbracelet/bubbletea v0.25.0
github.com/charmbracelet/bubbletea v0.26.1
github.com/charmbracelet/lipgloss v0.10.0
github.com/dave/jennifer v1.7.0
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
github.com/distribution/reference v0.6.0
github.com/docker/docker v26.0.2+incompatible
github.com/docker/docker v26.1.1+incompatible
github.com/dustin/go-humanize v1.0.1
github.com/elliotchance/phpserialize v1.4.0
github.com/facebookincubator/nvdtools v0.1.5
github.com/github/go-spdx/v2 v2.2.0
github.com/gkampitakis/go-snaps v0.5.3
github.com/gkampitakis/go-snaps v0.5.4
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.12.0
github.com/go-test/deep v1.1.0
@@ -103,7 +103,6 @@ require (
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/containerd/containerd v1.7.11 // indirect
github.com/containerd/continuity v0.4.2 // indirect
github.com/containerd/fifo v1.1.0 // indirect
@@ -122,6 +121,7 @@ require (
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/felixge/fgprof v0.9.3 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
@@ -165,7 +165,7 @@ require (
github.com/moby/locker v1.0.1 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/signal v0.7.0 // indirect
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
@@ -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.6.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
+15 -14
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.2-0.20240229175558-fe426d1b1c84 h1:/E74wU51M87fX5UWHubLZiENXbuAci+xtbSb+JFsIYg=
github.com/anchore/stereoscope v0.0.2-0.20240229175558-fe426d1b1c84/go.mod h1:evQiJMQG56Z7/L5uhA8kfhhjF6ESJUZzUH9ms6bQ2Co=
github.com/anchore/stereoscope v0.0.3-0.20240501181043-2e9894674185 h1:SuViDJ27nZ+joGdKbAkxAlm7tYMt9NTxTZZ05po4hls=
github.com/anchore/stereoscope v0.0.3-0.20240501181043-2e9894674185/go.mod h1:ckIamHiRMp8iBwWoTtE5Xkt9VQ5QC+6+O4VzwqyZr5Q=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
@@ -155,8 +155,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
github.com/charmbracelet/bubbletea v0.26.1 h1:xujcQeF73rh4jwu3+zhfQsvV18x+7zIjlw7/CYbzGJ0=
github.com/charmbracelet/bubbletea v0.26.1/go.mod h1:FzKr7sKoO8iFVcdIBM9J0sJOcQv5nDQaYwsee3kpbgo=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
@@ -182,8 +182,6 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw=
github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE=
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
@@ -223,8 +221,8 @@ github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qe
github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v26.0.2+incompatible h1:yGVmKUFGgcxA6PXWAokO0sQL22BrQ67cgVjko8tGdXE=
github.com/docker/docker v26.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v26.1.1+incompatible h1:oI+4kkAgIwwb54b9OC7Xc3hSgu1RlJA/Lln/DF72djQ=
github.com/docker/docker v26.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -257,6 +255,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c/go.mod h1:QGzNH9ujQ2ZUr/CjDGZGWeDAVStrWNjHeEcjJL96Nuk=
github.com/facebookincubator/nvdtools v0.1.5 h1:jbmDT1nd6+k+rlvKhnkgMokrCAzHoASWE5LtHbX2qFQ=
github.com/facebookincubator/nvdtools v0.1.5/go.mod h1:Kh55SAWnjckS96TBSrXI99KrEKH4iB0OJby3N8GRJO4=
@@ -283,8 +283,8 @@ github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvp
github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
github.com/gkampitakis/go-snaps v0.5.3 h1:2cJnBgHzJhh0Jk5XBIyDYDe1Ylfncoa9r9bVJ5qvOAE=
github.com/gkampitakis/go-snaps v0.5.3/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y=
github.com/gkampitakis/go-snaps v0.5.4 h1:GX+dkKmVsRenz7SoTbdIEL4KQARZctkMiZ8ZKprRwT8=
github.com/gkampitakis/go-snaps v0.5.4/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y=
github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4=
github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
@@ -590,8 +590,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA=
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
@@ -990,8 +990,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1051,6 +1051,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+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
dictionaryCPE, ok := cpe.DictionaryFind(p)
dictionaryCPEs, ok := cpe.DictionaryFind(p)
if ok {
log.Tracef("used CPE dictionary to find CPE for %s package %q: %s", p.Type, p.Name, dictionaryCPE.Attributes.BindToFmtString())
p.CPEs = append(p.CPEs, dictionaryCPE)
log.Tracef("used CPE dictionary to find CPEs for %s package %q: %s", p.Type, p.Name, dictionaryCPEs)
p.CPEs = append(p.CPEs, dictionaryCPEs...)
} else {
p.CPEs = append(p.CPEs, cpe.Generate(p)...)
}
+3262 -2367
View File
File diff suppressed because it is too large Load Diff
@@ -245,6 +245,8 @@ func toRootPackage(s source.Description) *spdx.Package {
Supplier: helpers.NOASSERTION,
},
PackageDownloadLocation: helpers.NOASSERTION,
PackageLicenseConcluded: helpers.NOASSERTION,
PackageLicenseDeclared: helpers.NOASSERTION,
}
if purl != nil {
@@ -517,9 +519,7 @@ func toPackageOriginator(p pkg.Package) *spdx.Originator {
}
func toPackageSupplier(p pkg.Package) *spdx.Supplier {
// this uses the Originator function for now until
// a better distinction can be made for supplier
kind, supplier := helpers.Originator(p)
kind, supplier := helpers.Supplier(p)
if kind == "" || supplier == "" {
return &spdx.Supplier{
Supplier: helpers.NOASSERTION,
@@ -624,6 +624,9 @@ func toFiles(s sbom.SBOM) (results []*spdx.File) {
Checksums: toFileChecksums(digests),
FileName: coordinates.RealPath,
FileTypes: toFileTypes(metadata),
LicenseInfoInFiles: []string{ // required in SPDX 2.2
helpers.NOASSERTION,
},
})
}
@@ -0,0 +1,210 @@
package helpers
import (
"fmt"
"regexp"
"strings"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/pkg"
)
const (
orgType = "Organization"
personType = "Person"
)
// Originator needs to conform to the SPDX spec here:
// https://spdx.github.io/spdx-spec/v2.2.2/package-information/#76-package-originator-field
//
// Definition:
//
// If the package identified in the SPDX document originated from a different person or
// organization than identified as Package Supplier (see 7.5 above), this field identifies from
// where or whom the package originally came. In some cases, a package may be created and
// originally distributed by a different third party than the Package Supplier of the package.
// For example, the SPDX document identifies the package as glibc and the Package Supplier as
// Red Hat, but the Free Software Foundation is the Package Originator.
//
// Use NOASSERTION if:
//
// - the SPDX document creator has attempted to but cannot reach a reasonable objective determination;
// - the SPDX document creator has made no attempt to determine this field; or
// - the SPDX document creator has intentionally provided no information (no meaning should be implied by doing so).
//
// Available options are: <omit>, NOASSERTION, Person: <person>, Organization: <org>
// return values are: <type>, <value>
func Originator(p pkg.Package) (typ string, author string) { // nolint: funlen
if !hasMetadata(p) {
return typ, author
}
switch metadata := p.Metadata.(type) {
case pkg.ApkDBEntry:
author = metadata.Maintainer
case pkg.DotnetPortableExecutableEntry:
typ = orgType
author = metadata.CompanyName
case pkg.DpkgDBEntry:
author = metadata.Maintainer
case pkg.JavaArchive:
if metadata.Manifest != nil {
author = metadata.Manifest.Main.MustGet("Specification-Vendor")
if author == "" {
author = metadata.Manifest.Main.MustGet("Implementation-Vendor")
}
}
case pkg.LinuxKernelModule:
author = metadata.Author
case pkg.PhpComposerLockEntry:
if len(metadata.Authors) > 0 {
entry := metadata.Authors[0]
author = formatPersonOrOrg(entry.Name, entry.Email)
}
case pkg.PhpComposerInstalledEntry:
if len(metadata.Authors) > 0 {
entry := metadata.Authors[0]
author = formatPersonOrOrg(entry.Name, entry.Email)
}
case pkg.RDescription:
// this is most likely to have a name and email
author = metadata.Maintainer
if author == "" {
author = metadata.Author
}
case pkg.NpmPackage:
author = metadata.Author
case pkg.PythonPackage:
author = formatPersonOrOrg(metadata.Author, metadata.AuthorEmail)
case pkg.RubyGemspec:
if len(metadata.Authors) > 0 {
author = metadata.Authors[0]
}
case pkg.RpmDBEntry:
typ = orgType
author = metadata.Vendor
case pkg.RpmArchive:
typ = orgType
author = metadata.Vendor
case pkg.WordpressPluginEntry:
// it seems that the vast majority of the time the author is an org, not a person
typ = orgType
author = metadata.Author
}
if typ == "" && author != "" {
typ = personType
}
return typ, parseAndFormatPersonOrOrg(author)
}
// Supplier needs to conform to the SPDX spec here:
// https://spdx.github.io/spdx-spec/v2.2.2/package-information/#75-package-supplier-field
//
// Definition:
//
// Identify the actual distribution source for the package/directory identified in the SPDX document. This might
// or might not be different from the originating distribution source for the package. The name of the Package Supplier
// shall be an organization or recognized author and not a web site. For example, SourceForge is a host website, not a
// supplier, the supplier for https://sourceforge.net/projects/bridge/ is “The Linux Foundation.”
//
// Use NOASSERTION if:
//
// - the SPDX document creator has attempted to but cannot reach a reasonable objective determination;
// - the SPDX document creator has made no attempt to determine this field; or
// - the SPDX document creator has intentionally provided no information (no meaning should be implied by doing so).
//
// Available options are: <omit>, NOASSERTION, Person: <person>, Organization: <org>
// return values are: <type>, <value>
func Supplier(p pkg.Package) (typ string, author string) {
if !hasMetadata(p) {
return
}
if metadata, ok := p.Metadata.(pkg.AlpmDBEntry); ok {
// most indications here are that this is the person that is simply packaging the upstream software. Most
// of the time this is not the original author of the upstream software (which would be the originator).
// Though it is possible for users to be both the packager and the author, this code cannot distinct this
// case and sticks to the semantically correct interpretation of the "packager" (which says nothing about the
// authorship of the upstream software).
author = metadata.Packager
}
if author == "" {
// TODO: this uses the Originator function for now until a better distinction can be made for supplier
return Originator(p)
}
if typ == "" && author != "" {
typ = personType
}
return typ, parseAndFormatPersonOrOrg(author)
}
var nameEmailURLPattern = regexp.MustCompile(`^(?P<name>[^<>()]*)( <(?P<email>[^@]+@\w+\.\w+)>)?( \((?P<url>.*)\))?$`)
func parseAndFormatPersonOrOrg(s string) string {
name, email, _ := parseNameEmailURL(s)
return formatPersonOrOrg(name, email)
}
func parseNameEmailURL(s string) (name, email, url string) {
fields := internal.MatchNamedCaptureGroups(nameEmailURLPattern, s)
name = strings.TrimSpace(fields["name"])
email = strings.TrimSpace(fields["email"])
url = strings.TrimSpace(fields["url"])
if email == "" {
if approximatesAsEmail(url) {
email = url
url = ""
} else if approximatesAsEmail(name) {
email = name
name = ""
}
}
return name, email, url
}
func approximatesAsEmail(s string) bool {
atIndex := strings.Index(s, "@")
if atIndex == -1 {
return false
}
dotIndex := strings.Index(s[atIndex:], ".")
return dotIndex != -1
}
func formatPersonOrOrg(name, email string) string {
name = strings.TrimSpace(name)
email = strings.TrimSpace(email)
blankName := name == ""
blankEmail := email == ""
if !blankEmail && !blankName {
return fmt.Sprintf("%s (%s)", name, email)
}
if !blankName && blankEmail {
return name
}
if blankName && !blankEmail {
return email
}
return ""
}
@@ -0,0 +1,482 @@
package helpers
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/internal/packagemetadata"
"github.com/anchore/syft/syft/pkg"
)
func Test_OriginatorSupplier(t *testing.T) {
completionTester := packagemetadata.NewCompletionTester(t,
pkg.BinarySignature{},
pkg.CocoaPodfileLockEntry{},
pkg.ConanV1LockEntry{},
pkg.ConanV2LockEntry{}, // the field Username might be the username of either the package originator or the supplier (unclear currently)
pkg.ConanfileEntry{},
pkg.ConaninfoEntry{},
pkg.DartPubspecLockEntry{},
pkg.DotnetDepsEntry{},
pkg.ELFBinaryPackageNoteJSONPayload{},
pkg.ElixirMixLockEntry{},
pkg.ErlangRebarLockEntry{},
pkg.GolangBinaryBuildinfoEntry{},
pkg.GolangModuleEntry{},
pkg.HackageStackYamlLockEntry{},
pkg.HackageStackYamlEntry{},
pkg.LinuxKernel{},
pkg.MicrosoftKbPatch{},
pkg.NixStoreEntry{},
pkg.NpmPackageLockEntry{},
pkg.PhpComposerInstalledEntry{},
pkg.PhpPeclEntry{},
pkg.PortageEntry{},
pkg.PythonPipfileLockEntry{},
pkg.PythonRequirementsEntry{},
pkg.PythonPoetryLockEntry{},
pkg.RustBinaryAuditEntry{},
pkg.RustCargoLockEntry{},
pkg.SwiftPackageManagerResolvedEntry{},
pkg.YarnLockEntry{},
)
tests := []struct {
name string
input pkg.Package
originator string
supplier string
}{
{
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
name: "no metadata",
input: pkg.Package{},
originator: "",
supplier: "",
},
{
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
name: "empty author on existing metadata",
input: pkg.Package{
Metadata: pkg.NpmPackage{
Author: "",
},
},
originator: "",
supplier: "",
},
{
name: "from apk",
input: pkg.Package{
Metadata: pkg.ApkDBEntry{
Maintainer: "auth",
},
},
originator: "Person: auth",
supplier: "Person: auth",
},
{
name: "from alpm",
input: pkg.Package{
Metadata: pkg.AlpmDBEntry{
Packager: "someone",
},
},
originator: "",
supplier: "Person: someone",
},
{
name: "from dotnet -- PE binary",
input: pkg.Package{
Metadata: pkg.DotnetPortableExecutableEntry{
CompanyName: "Microsoft Corporation",
},
},
originator: "Organization: Microsoft Corporation",
supplier: "Organization: Microsoft Corporation",
},
{
name: "from dpkg",
input: pkg.Package{
Metadata: pkg.DpkgDBEntry{
Maintainer: "auth",
},
},
originator: "Person: auth",
supplier: "Person: auth",
},
{
name: "from gem",
input: pkg.Package{
Metadata: pkg.RubyGemspec{
Authors: []string{
"auth1",
"auth2",
},
},
},
originator: "Person: auth1",
supplier: "Person: auth1",
},
{
name: "from java -- spec > impl cendor in main manifest section",
input: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: pkg.KeyValues{
{
Key: "Implementation-Vendor",
Value: "auth-impl",
},
{
Key: "Specification-Vendor",
Value: "auth-spec",
},
},
},
},
},
originator: "Person: auth-spec",
supplier: "Person: auth-spec",
},
{
name: "from java -- fallback to impl vendor in main manifest section",
input: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: pkg.KeyValues{
{
Key: "Implementation-Vendor",
Value: "auth-impl",
},
},
},
},
},
originator: "Person: auth-impl",
supplier: "Person: auth-impl",
},
{
name: "from java -- non-main manifest sections ignored",
input: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Sections: []pkg.KeyValues{
{
{
Key: "Implementation-Vendor",
Value: "auth-impl",
},
},
},
Main: pkg.KeyValues{},
},
},
},
// note: empty!
},
{
name: "from linux kernel module",
input: pkg.Package{
Metadata: pkg.LinuxKernelModule{
Author: "auth",
},
},
originator: "Person: auth",
supplier: "Person: auth",
},
{
name: "from npm",
input: pkg.Package{
Metadata: pkg.NpmPackage{
Author: "auth",
},
},
originator: "Person: auth",
supplier: "Person: auth",
},
{
name: "from npm -- name, email, and url",
input: pkg.Package{
Metadata: pkg.NpmPackage{
Author: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
},
},
originator: "Person: Isaac Z. Schlueter (i@izs.me)",
supplier: "Person: Isaac Z. Schlueter (i@izs.me)",
},
{
name: "from npm -- name, email",
input: pkg.Package{
Metadata: pkg.NpmPackage{
Author: "Isaac Z. Schlueter <i@izs.me>",
},
},
originator: "Person: Isaac Z. Schlueter (i@izs.me)",
supplier: "Person: Isaac Z. Schlueter (i@izs.me)",
},
{
name: "from php composer installed file",
input: pkg.Package{
Metadata: pkg.PhpComposerInstalledEntry{
Authors: []pkg.PhpComposerAuthors{
{
Name: "auth",
Email: "me@auth.com",
},
},
},
},
originator: "Person: auth (me@auth.com)",
supplier: "Person: auth (me@auth.com)",
},
{
name: "from php composer installed file",
input: pkg.Package{
Metadata: pkg.PhpComposerLockEntry{
Authors: []pkg.PhpComposerAuthors{
{
Name: "auth",
Email: "me@auth.com",
},
},
},
},
originator: "Person: auth (me@auth.com)",
supplier: "Person: auth (me@auth.com)",
},
{
name: "from python - just name",
input: pkg.Package{
Metadata: pkg.PythonPackage{
Author: "auth",
},
},
originator: "Person: auth",
supplier: "Person: auth",
},
{
name: "from python - just email",
input: pkg.Package{
Metadata: pkg.PythonPackage{
AuthorEmail: "auth@auth.gov",
},
},
originator: "Person: auth@auth.gov",
supplier: "Person: auth@auth.gov",
},
{
name: "from python - both name and email",
input: pkg.Package{
Metadata: pkg.PythonPackage{
Author: "auth",
AuthorEmail: "auth@auth.gov",
},
},
originator: "Person: auth (auth@auth.gov)",
supplier: "Person: auth (auth@auth.gov)",
},
{
name: "from r -- maintainer > author",
input: pkg.Package{
Metadata: pkg.RDescription{
Author: "author",
Maintainer: "maintainer",
},
},
originator: "Person: maintainer",
supplier: "Person: maintainer",
},
{
name: "from r -- fallback to author",
input: pkg.Package{
Metadata: pkg.RDescription{
Author: "author",
},
},
originator: "Person: author",
supplier: "Person: author",
},
{
name: "from rpm archive",
input: pkg.Package{
Metadata: pkg.RpmArchive{
Vendor: "auth",
},
},
originator: "Organization: auth",
supplier: "Organization: auth",
},
{
name: "from rpm DB",
input: pkg.Package{
Metadata: pkg.RpmDBEntry{
Vendor: "auth",
},
},
originator: "Organization: auth",
supplier: "Organization: auth",
},
{
name: "from wordpress plugin",
input: pkg.Package{
Metadata: pkg.WordpressPluginEntry{
Author: "auth",
},
},
originator: "Organization: auth",
supplier: "Organization: auth",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
completionTester.Tested(t, test.input.Metadata)
typ, value := Originator(test.input)
if typ != "" {
value = typ + ": " + value
}
assert.Equal(t, test.originator, value)
typ, value = Supplier(test.input)
if typ != "" {
value = typ + ": " + value
}
assert.Equal(t, test.supplier, value)
})
}
}
func Test_parseNameEmailUrl(t *testing.T) {
tests := []struct {
name string
input string
wantName string
wantEmail string
wantUrl string
}{
{
name: "empty",
input: "",
},
{
name: "npm-like: name only",
input: "Isaac Z. Schlueter",
wantName: "Isaac Z. Schlueter",
},
{
name: "npm-like: name and email",
input: "Ray Nos <bogus2@gmail.com>",
wantName: "Ray Nos",
wantEmail: "bogus2@gmail.com",
},
{
name: "npm-like: name and url",
input: "Ray Nos (http://example.com)",
wantName: "Ray Nos",
wantUrl: "http://example.com",
},
{
name: "npm-like: name, email, and url",
input: "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
wantName: "Isaac Z. Schlueter",
wantEmail: "i@izs.me",
wantUrl: "http://blog.izs.me",
},
{
name: "mixed input: email only",
input: "i@izs.me",
wantEmail: "i@izs.me",
},
{
name: "mixed input: email in url",
input: "my name (i@izs.me)",
wantName: "my name",
wantEmail: "i@izs.me",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotName, gotEmail, gotUrl := parseNameEmailURL(tt.input)
assert.Equal(t, tt.wantName, gotName)
assert.Equal(t, tt.wantEmail, gotEmail)
assert.Equal(t, tt.wantUrl, gotUrl)
})
}
}
func Test_formatPersonOrOrg(t *testing.T) {
tests := []struct {
name string
input string
email string
want string
}{
{
name: "empty",
want: "",
},
{
name: "name only",
input: "Isaac Z. Schlueter",
want: "Isaac Z. Schlueter",
},
{
name: "email only",
email: "i@something.com",
want: "i@something.com",
},
{
name: "name and email",
input: "Isaac Z. Schlueter",
email: "i@something.com",
want: "Isaac Z. Schlueter (i@something.com)",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, formatPersonOrOrg(tt.input, tt.email))
})
}
}
func Test_approximatesAsEmail(t *testing.T) {
tests := []struct {
name string
input string
want bool
}{
{
name: "empty",
input: "",
want: false,
},
{
name: "no at",
input: "something.com",
want: false,
},
{
name: "no dot",
input: "something@com",
want: false,
},
{
name: "dot before at",
input: "something.com@nothing",
want: false,
},
{
name: "valid",
input: "something@nothing.com",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, approximatesAsEmail(tt.input))
})
}
}
@@ -1,119 +0,0 @@
package helpers
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/pkg"
)
func Test_Originator(t *testing.T) {
tests := []struct {
name string
input pkg.Package
expected string
}{
{
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
name: "no metadata",
input: pkg.Package{},
expected: "",
},
{
name: "from gem",
input: pkg.Package{
Metadata: pkg.RubyGemspec{
Authors: []string{
"auth1",
"auth2",
},
},
},
expected: "Person: auth1",
},
{
name: "from npm",
input: pkg.Package{
Metadata: pkg.NpmPackage{
Author: "auth",
},
},
expected: "Person: auth",
},
{
name: "from apk",
input: pkg.Package{
Metadata: pkg.ApkDBEntry{
Maintainer: "auth",
},
},
expected: "Person: auth",
},
{
name: "from python - just name",
input: pkg.Package{
Metadata: pkg.PythonPackage{
Author: "auth",
},
},
expected: "Person: auth",
},
{
name: "from python - just email",
input: pkg.Package{
Metadata: pkg.PythonPackage{
AuthorEmail: "auth@auth.gov",
},
},
expected: "Person: auth@auth.gov",
},
{
name: "from python - both name and email",
input: pkg.Package{
Metadata: pkg.PythonPackage{
Author: "auth",
AuthorEmail: "auth@auth.gov",
},
},
expected: "Person: auth (auth@auth.gov)",
},
{
name: "from rpm",
input: pkg.Package{
Metadata: pkg.RpmDBEntry{
Vendor: "auth",
},
},
expected: "Organization: auth",
},
{
name: "from dpkg",
input: pkg.Package{
Metadata: pkg.DpkgDBEntry{
Maintainer: "auth",
},
},
expected: "Person: auth",
},
{
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
name: "empty",
input: pkg.Package{
Metadata: pkg.NpmPackage{
Author: "",
},
},
expected: "",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
typ, value := Originator(test.input)
if typ != "" {
value = typ + ": " + value
}
assert.Equal(t, test.expected, value)
})
}
}
@@ -1,44 +0,0 @@
package helpers
import (
"fmt"
"github.com/anchore/syft/syft/pkg"
)
// Originator needs to conform to the SPDX spec here:
// https://spdx.github.io/spdx-spec/package-information/#76-package-originator-field
// Available options are: <omit>, NOASSERTION, Person: <person>, Organization: <org>
// return values are: <type>, <value>
func Originator(p pkg.Package) (string, string) {
typ := ""
author := ""
if hasMetadata(p) {
switch metadata := p.Metadata.(type) {
case pkg.ApkDBEntry:
author = metadata.Maintainer
case pkg.NpmPackage:
author = metadata.Author
case pkg.PythonPackage:
author = metadata.Author
if author == "" {
author = metadata.AuthorEmail
} else if metadata.AuthorEmail != "" {
author = fmt.Sprintf("%s (%s)", author, metadata.AuthorEmail)
}
case pkg.RubyGemspec:
if len(metadata.Authors) > 0 {
author = metadata.Authors[0]
}
case pkg.RpmDBEntry:
typ = "Organization"
author = metadata.Vendor
case pkg.DpkgDBEntry:
author = metadata.Maintainer
}
if typ == "" && author != "" {
typ = "Person"
}
}
return typ, author
}
+70
View File
@@ -3,6 +3,8 @@ package spdxjson
import (
"bytes"
"flag"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"strings"
"testing"
@@ -113,6 +115,74 @@ func TestSPDXJSONImageEncoder(t *testing.T) {
)
}
func TestSPDX22JSONRequredProperties(t *testing.T) {
cfg := DefaultEncoderConfig()
cfg.Pretty = true
cfg.Version = "2.2"
enc, err := NewFormatEncoderWithConfig(cfg)
require.NoError(t, err)
coords := file.Coordinates{
RealPath: "/some/file",
FileSystemID: "ac897d978b6c38749a1",
}
p1 := pkg.Package{
Name: "files-analyzed-true",
Version: "v1",
Locations: file.NewLocationSet(file.NewLocation(coords.RealPath)),
Licenses: pkg.LicenseSet{},
Language: pkg.Java,
Metadata: pkg.JavaArchive{
ArchiveDigests: []file.Digest{
{
Algorithm: "sha256",
Value: "a9b87321a9879c79d87987987a97c97b9789ce978dffea987",
},
},
Parent: nil,
},
}
p1.SetID()
p2 := pkg.Package{
Name: "files-analyzed-false",
Version: "v2",
}
p2.SetID()
testutil.AssertEncoderAgainstGoldenSnapshot(t,
testutil.EncoderSnapshotTestConfig{
Subject: sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(p1, p2),
FileDigests: map[file.Coordinates][]file.Digest{
coords: {
{
Algorithm: "sha1",
Value: "3b4ab96c371d913e2a88c269844b6c5fb5cbe761",
},
},
},
},
Relationships: []artifact.Relationship{
{
From: p1,
To: coords,
Type: artifact.ContainsRelationship,
},
},
},
Format: enc,
UpdateSnapshot: *updateSnapshot,
PersistRedactionsInSnapshot: true,
IsJSON: true,
Redactor: redactor(),
},
)
}
func TestSPDXRelationshipOrder(t *testing.T) {
testImage := "image-simple"
@@ -0,0 +1,100 @@
{
"spdxVersion": "SPDX-2.2",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "unknown",
"documentNamespace":"redacted",
"creationInfo": {
"licenseListVersion":"redacted",
"creators": [
"Organization: Anchore, Inc",
"Tool: -"
],
"created":"redacted"
},
"packages": [
{
"SPDXID": "SPDXRef-Package-files-analyzed-false-7d37ba9d2f7c574b",
"copyrightText": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"name": "files-analyzed-false",
"sourceInfo": "acquired package info from the following paths: ",
"supplier": "NOASSERTION",
"versionInfo": "v2"
},
{
"name": "files-analyzed-true",
"SPDXID": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
"versionInfo": "v1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": true,
"packageVerificationCode": {
"packageVerificationCodeValue": "6fe0c471faaaa544e33cae0918eabcdc1c798d18"
},
"checksums": [
{
"algorithm": "SHA256",
"checksumValue": "a9b87321a9879c79d87987987a97c97b9789ce978dffea987"
}
],
"sourceInfo": "acquired package info from the following paths: /some/file",
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"copyrightText": "NOASSERTION"
},
{
"SPDXID": "SPDXRef-DocumentRoot-Unknown-",
"copyrightText": "",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"name": "",
"supplier": "NOASSERTION"
}
],
"files": [
{
"fileName": "/some/file",
"SPDXID": "SPDXRef-File-some-file-2c5bc344430decac",
"checksums": [
{
"algorithm": "SHA1",
"checksumValue": "3b4ab96c371d913e2a88c269844b6c5fb5cbe761"
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": "",
"comment": "layerID: ac897d978b6c38749a1"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
"relatedSpdxElement": "SPDXRef-File-some-file-2c5bc344430decac",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-false-7d37ba9d2f7c574b",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relatedSpdxElement": "SPDXRef-DocumentRoot-Unknown-",
"relationshipType": "DESCRIBES"
}
]
}
@@ -67,6 +67,8 @@
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"primaryPackagePurpose": "FILE"
}
],
@@ -74,6 +74,8 @@
"checksumValue": "2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
}
],
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
@@ -74,6 +74,8 @@
"checksumValue": "2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
}
],
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
@@ -98,6 +100,9 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
},
{
@@ -113,6 +118,9 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
},
{
@@ -128,6 +136,9 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
},
{
@@ -143,6 +154,9 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
},
{
@@ -158,6 +172,9 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
},
{
@@ -173,6 +190,9 @@
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": ""
}
],
@@ -16,6 +16,8 @@ PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: FILE
FilesAnalyzed: false
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
##### Package: @at-sign
@@ -15,36 +15,42 @@ SPDXID: SPDXRef-File-a1-f6-9c2f7510199b17f6
FileType: OTHER
FileChecksum: SHA1: 0000000000000000000000000000000000000000
LicenseConcluded: NOASSERTION
LicenseInfoInFile: NOASSERTION
FileName: /d1/f3
SPDXID: SPDXRef-File-d1-f3-c6f5b29dca12661f
FileType: OTHER
FileChecksum: SHA1: 0000000000000000000000000000000000000000
LicenseConcluded: NOASSERTION
LicenseInfoInFile: NOASSERTION
FileName: /d2/f4
SPDXID: SPDXRef-File-d2-f4-c641caa71518099f
FileType: OTHER
FileChecksum: SHA1: 0000000000000000000000000000000000000000
LicenseConcluded: NOASSERTION
LicenseInfoInFile: NOASSERTION
FileName: /f1
SPDXID: SPDXRef-File-f1-5265a4dde3edbf7c
FileType: OTHER
FileChecksum: SHA1: 0000000000000000000000000000000000000000
LicenseConcluded: NOASSERTION
LicenseInfoInFile: NOASSERTION
FileName: /f2
SPDXID: SPDXRef-File-f2-f9e49132a4b96ccd
FileType: OTHER
FileChecksum: SHA1: 0000000000000000000000000000000000000000
LicenseConcluded: NOASSERTION
LicenseInfoInFile: NOASSERTION
FileName: /z1/f5
SPDXID: SPDXRef-File-z1-f5-839d99ee67d9d174
FileType: OTHER
FileChecksum: SHA1: 0000000000000000000000000000000000000000
LicenseConcluded: NOASSERTION
LicenseInfoInFile: NOASSERTION
##### Package: user-image-input
@@ -56,6 +62,8 @@ PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: CONTAINER
FilesAnalyzed: false
PackageChecksum: SHA256: 2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=
##### Package: package-2
@@ -16,6 +16,8 @@ PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: FILE
FilesAnalyzed: false
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
##### Package: package-2
@@ -18,6 +18,8 @@ PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: CONTAINER
FilesAnalyzed: false
PackageChecksum: SHA256: 2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=
##### Package: package-2
+8 -40
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"os"
"strings"
"github.com/anchore/syft/syft/source"
)
@@ -21,21 +22,16 @@ func GetSource(ctx context.Context, userInput string, cfg *GetSourceConfig) (sou
}
var errs []error
var fileNotfound error
var fileNotFoundProviders []string
// call each source provider until we find a valid source
for _, p := range providers {
src, err := p.Provide(ctx)
if err != nil {
err = eachError(err, func(err error) error {
if errors.Is(err, os.ErrNotExist) {
fileNotfound = err
return nil
}
return err
})
if err != nil {
errs = append(errs, err)
if errors.Is(err, os.ErrNotExist) {
fileNotFoundProviders = append(fileNotFoundProviders, p.Name())
} else {
errs = append(errs, fmt.Errorf("%s: %w", p.Name(), err))
}
}
if src != nil {
@@ -52,8 +48,8 @@ func GetSource(ctx context.Context, userInput string, cfg *GetSourceConfig) (sou
}
}
if fileNotfound != nil {
errs = append([]error{fileNotfound}, errs...)
if len(fileNotFoundProviders) > 0 {
errs = append(errs, fmt.Errorf("additionally, the following providers failed with %w: %s", os.ErrNotExist, strings.Join(fileNotFoundProviders, ", ")))
}
return nil, sourceError(userInput, errs...)
}
@@ -71,31 +67,3 @@ func sourceError(userInput string, errs ...error) error {
}
return fmt.Errorf("errors occurred attempting to resolve '%s':%s", userInput, errorTexts)
}
func eachError(err error, fn func(error) error) error {
out := fn(err)
// unwrap singly wrapped errors
if e, ok := err.(interface {
Unwrap() error
}); ok {
wrapped := e.Unwrap()
got := eachError(wrapped, fn)
// return the outer error if received the same wrapped error
if errors.Is(got, wrapped) {
return err
}
return got
}
// unwrap errors from errors.Join
if errs, ok := err.(interface {
Unwrap() []error
}); ok {
for _, e := range errs.Unwrap() {
e = eachError(e, fn)
if e != nil {
out = errors.Join(out, e)
}
}
}
return out
}
+12
View File
@@ -7,6 +7,7 @@ import (
macho "github.com/anchore/go-macholibre"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file"
)
// UnionReader is a single interface with all reading functions needed by multi-arch binary catalogers
@@ -44,6 +45,17 @@ func GetUnionReader(readerCloser io.ReadCloser) (UnionReader, error) {
return reader, nil
}
// file.LocationReadCloser embeds a ReadCloser, which is likely
// to implement UnionReader. Check whether the embedded read closer
// implements UnionReader, and just return that if so.
r, ok := readerCloser.(file.LocationReadCloser)
if ok {
ur, ok := r.ReadCloser.(UnionReader)
if ok {
return ur, nil
}
}
b, err := io.ReadAll(readerCloser)
if err != nil {
return nil, fmt.Errorf("unable to read contents from binary: %w", err)
@@ -7,6 +7,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
)
func Test_getUnionReader_notUnionReader(t *testing.T) {
@@ -28,3 +30,34 @@ func Test_getUnionReader_notUnionReader(t *testing.T) {
assert.Equal(t, expectedContents, string(b))
}
type panickingUnionReader struct{}
func (p2 *panickingUnionReader) ReadAt(p []byte, off int64) (n int, err error) {
panic("don't call this in your unit test!")
}
func (p2 *panickingUnionReader) Seek(offset int64, whence int) (int64, error) {
panic("don't call this in your unit test!")
}
func (p2 *panickingUnionReader) Read(p []byte) (n int, err error) {
panic("don't call this in your unit test!")
}
func (p2 *panickingUnionReader) Close() error {
panic("don't call this in your unit test!")
}
var _ UnionReader = (*panickingUnionReader)(nil)
func Test_getUnionReader_fileLocationReadCloser(t *testing.T) {
// panickingUnionReader is a UnionReader
p := &panickingUnionReader{}
embedsUnionReader := file.NewLocationReadCloser(file.Location{}, p)
// embedded union reader is returned without "ReadAll" invocation
ur, err := GetUnionReader(embedsUnionReader)
require.NoError(t, err)
require.Equal(t, p, ur)
}
@@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/mimetype"
"github.com/anchore/syft/syft/artifact"
@@ -57,31 +58,13 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
// first find all ELF binaries that have notes
var notesByLocation = make(map[elfPackageKey][]elfBinaryPackageNotes)
for _, location := range locations {
reader, err := resolver.FileContentsByLocation(location)
notes, key, err := parseElfPackageNotes(resolver, location, c)
if err != nil {
return nil, nil, fmt.Errorf("unable to get binary contents %q: %w", location.Path(), err)
return nil, nil, err
}
notes, err := c.parseElfNotes(file.LocationReadCloser{
Location: location,
ReadCloser: reader,
})
if err != nil {
log.WithFields("file", location.Path(), "error", err).Trace("unable to parse ELF notes")
continue
}
if notes == nil {
continue
}
notes.Location = location
key := elfPackageKey{
Name: notes.Name,
Version: notes.Version,
PURL: notes.PURL,
CPE: notes.CPE,
}
notesByLocation[key] = append(notesByLocation[key], *notes)
}
@@ -105,6 +88,37 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
return pkgs, nil, nil
}
func parseElfPackageNotes(resolver file.Resolver, location file.Location, c *elfPackageCataloger) (*elfBinaryPackageNotes, elfPackageKey, error) {
reader, err := resolver.FileContentsByLocation(location)
if err != nil {
return nil, elfPackageKey{}, fmt.Errorf("unable to get binary contents %q: %w", location.Path(), err)
}
defer internal.CloseAndLogError(reader, location.AccessPath)
notes, err := c.parseElfNotes(file.LocationReadCloser{
Location: location,
ReadCloser: reader,
})
if err != nil {
log.WithFields("file", location.Path(), "error", err).Trace("unable to parse ELF notes")
return nil, elfPackageKey{}, nil
}
if notes == nil {
return nil, elfPackageKey{}, nil
}
notes.Location = location
key := elfPackageKey{
Name: notes.Name,
Version: notes.Version,
PURL: notes.PURL,
CPE: notes.CPE,
}
return notes, key, nil
}
func (c *elfPackageCataloger) parseElfNotes(reader file.LocationReadCloser) (*elfBinaryPackageNotes, error) {
metadata, err := getELFNotes(reader)
if err != nil {
+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,13 +69,16 @@ func buildDotNetPackage(versionResources map[string]string, f file.LocationReadC
}
metadata := pkg.DotnetPortableExecutableEntry{
AssemblyVersion: versionResources["Assembly Version"],
LegalCopyright: versionResources["LegalCopyright"],
Comments: versionResources["Comments"],
InternalName: versionResources["InternalName"],
CompanyName: versionResources["CompanyName"],
ProductName: versionResources["ProductName"],
ProductVersion: versionResources["ProductVersion"],
AssemblyVersion: versionResources["Assembly Version"],
LegalCopyright: versionResources["LegalCopyright"],
Comments: versionResources["Comments"],
InternalName: versionResources["InternalName"],
CompanyName: versionResources["CompanyName"],
ProductName: versionResources["ProductName"],
ProductVersion: versionResources["ProductVersion"],
FileDescription: versionResources["FileDescription"],
FileVersion: versionResources["FileVersion"],
OriginalFilename: versionResources["OriginalFilename"],
}
dnpkg = pkg.Package{
@@ -215,7 +218,7 @@ func findName(versionResources map[string]string) string {
}
for _, field := range nameFields {
value := spaceNormalize(versionResources[field])
value := resolveValue(versionResources[field], versionResources, nil)
if value == "" {
continue
}
@@ -225,6 +228,40 @@ func findName(versionResources map[string]string) string {
return ""
}
func resolveValue(value string, collection map[string]string, visited map[string]bool) string {
value = spaceNormalize(value)
if value == "" {
return ""
}
if visited == nil {
visited = make(map[string]bool)
}
if visited[value] {
return value
}
visited[value] = true
hasIndirect, nextKey := hasIndirectFieldPrefix(value)
if !hasIndirect {
return value
}
return resolveValue(collection[nextKey], collection, visited)
}
func hasIndirectFieldPrefix(value string) (bool, string) {
for _, prefix := range []string{"f#", "p("} {
cleanValue := strings.TrimPrefix(value, prefix)
if cleanValue != value {
return true, cleanValue
}
}
return false, value
}
// normalizes a string to a trimmed version with all contigous whitespace collapsed to a single space character
func spaceNormalize(value string) string {
value = strings.TrimSpace(value)
@@ -346,3 +346,126 @@ func Test_spaceNormalize(t *testing.T) {
})
}
}
func Test_resolveValue(t *testing.T) {
tests := []struct {
name string
value string
collection map[string]string
want string
}{
{
name: "simple value",
value: "value",
collection: map[string]string{
"key": "value",
},
want: "value",
},
{
name: "simple value with spaces",
value: " value ",
collection: map[string]string{
"key": "value",
},
want: "value",
},
{
name: "indirect value - f#",
value: "f#other",
collection: map[string]string{
"key": "f#other",
"other": "value",
},
want: "value",
},
{
name: "indirect value - p(",
value: "f#other",
collection: map[string]string{
"key": "p(other",
"other": "value",
},
want: "value",
},
{
name: "indirect value with cycles",
value: "f#other",
collection: map[string]string{
"key": "f#other",
"other": "f#key",
},
want: "f#other", // this is NOT ideal, but there is no "good" answer here
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, resolveValue(tt.value, tt.collection, nil))
})
}
}
func Test_findVersion(t *testing.T) {
tests := []struct {
name string
versionResources map[string]string
want string
}{
{
name: "prefer file version over product version (when both semver)",
versionResources: map[string]string{
"FileVersion": "1.2.3",
"ProductVersion": "4.5.6",
},
want: "4.5.6",
},
{
name: "prefer file version over product version (when both semver)",
versionResources: map[string]string{
"FileVersion": "1.2.3",
"ProductVersion": "4.5.6.7",
},
want: "1.2.3",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, findVersion(tt.versionResources))
})
}
}
func Test_keepGreaterSemanticVersion(t *testing.T) {
tests := []struct {
name string
productVersion string
fileVersion string
want string
}{
{
name: "product semver is greater",
productVersion: "3.0.0",
fileVersion: "2.0.0",
want: "3.0.0",
},
{
name: "file semver is greater",
productVersion: "2.0.0",
fileVersion: "3.0.0",
want: "3.0.0",
},
{
name: "semver preferred over non-semver",
productVersion: "3.0.0.2",
fileVersion: "3.0.0",
want: "3.0.0",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, keepGreaterSemanticVersion(tt.productVersion, tt.fileVersion))
})
}
}
+20 -10
View File
@@ -3,6 +3,7 @@ package generic
import (
"context"
"github.com/anchore/go-logger"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
@@ -122,17 +123,9 @@ func (c *Cataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.
log.WithFields("path", location.RealPath).Trace("parsing file contents")
contentReader, err := resolver.FileContentsByLocation(location)
discoveredPackages, discoveredRelationships, err := invokeParser(ctx, resolver, location, logger, parser, &env)
if err != nil {
logger.WithFields("location", location.RealPath, "error", err).Warn("unable to fetch contents")
continue
}
discoveredPackages, discoveredRelationships, err := parser(ctx, resolver, &env, file.NewLocationReadCloser(location, contentReader))
internal.CloseAndLogError(contentReader, location.AccessPath)
if err != nil {
logger.WithFields("location", location.RealPath, "error", err).Warnf("cataloger failed")
continue
continue // logging is handled within invokeParser
}
for _, p := range discoveredPackages {
@@ -145,6 +138,23 @@ func (c *Cataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.
return packages, relationships, nil
}
func invokeParser(ctx context.Context, resolver file.Resolver, location file.Location, logger logger.Logger, parser Parser, env *Environment) ([]pkg.Package, []artifact.Relationship, error) {
contentReader, err := resolver.FileContentsByLocation(location)
if err != nil {
logger.WithFields("location", location.RealPath, "error", err).Warn("unable to fetch contents")
return nil, nil, err
}
defer internal.CloseAndLogError(contentReader, location.AccessPath)
discoveredPackages, discoveredRelationships, err := parser(ctx, resolver, env, file.NewLocationReadCloser(location, contentReader))
if err != nil {
logger.WithFields("location", location.RealPath, "error", err).Warnf("cataloger failed")
return nil, nil, err
}
return discoveredPackages, discoveredRelationships, nil
}
// selectFiles takes a set of file trees and resolves and file references of interest for future cataloging
func (c *Cataloger) selectFiles(resolver file.Resolver) []request {
var requests []request
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -86,3 +87,103 @@ func Test_Cataloger(t *testing.T) {
}
}
}
type spyReturningFileResolver struct {
m *file.MockResolver
s *spyingIoReadCloser
}
type spyingIoReadCloser struct {
rc io.ReadCloser
closed bool
}
func newSpyReturningFileResolver(s *spyingIoReadCloser, paths ...string) file.Resolver {
m := file.NewMockResolverForPaths(paths...)
return spyReturningFileResolver{
m: m,
s: s,
}
}
func (s *spyingIoReadCloser) Read(p []byte) (n int, err error) {
return s.Read(p)
}
func (s *spyingIoReadCloser) Close() error {
s.closed = true
return s.rc.Close()
}
var _ io.ReadCloser = (*spyingIoReadCloser)(nil)
func (m spyReturningFileResolver) FileContentsByLocation(location file.Location) (io.ReadCloser, error) {
return m.s, nil
}
func (m spyReturningFileResolver) HasPath(path string) bool {
return m.m.HasPath(path)
}
func (m spyReturningFileResolver) FilesByPath(paths ...string) ([]file.Location, error) {
return m.m.FilesByPath(paths...)
}
func (m spyReturningFileResolver) FilesByGlob(patterns ...string) ([]file.Location, error) {
return m.m.FilesByGlob(patterns...)
}
func (m spyReturningFileResolver) FilesByMIMEType(types ...string) ([]file.Location, error) {
return m.m.FilesByMIMEType(types...)
}
func (m spyReturningFileResolver) RelativeFileByPath(f file.Location, path string) *file.Location {
return m.m.RelativeFileByPath(f, path)
}
func (m spyReturningFileResolver) AllLocations(ctx context.Context) <-chan file.Location {
return m.m.AllLocations(ctx)
}
func (m spyReturningFileResolver) FileMetadataByLocation(location file.Location) (file.Metadata, error) {
return m.m.FileMetadataByLocation(location)
}
var _ file.Resolver = (*spyReturningFileResolver)(nil)
func TestClosesFileOnParserPanic(t *testing.T) {
rc := io.NopCloser(strings.NewReader("some string"))
spy := spyingIoReadCloser{
rc: rc,
}
resolver := newSpyReturningFileResolver(&spy, "test-fixtures/another-path.txt")
ctx := context.TODO()
processors := []processor{
func(resolver file.Resolver, env Environment) []request {
return []request{
{
Location: file.Location{
LocationData: file.LocationData{
Coordinates: file.Coordinates{},
AccessPath: "/some/access/path",
},
},
Parser: func(context.Context, file.Resolver, *Environment, file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
panic("panic!")
},
},
}
},
}
c := Cataloger{
processor: processors,
upstreamCataloger: "unit-test-cataloger",
}
assert.PanicsWithValue(t, "panic!", func() {
_, _, _ = c.Catalog(ctx, resolver)
})
require.True(t, spy.closed)
}
@@ -344,6 +344,17 @@ var defaultCandidateAdditions = buildCandidateLookup(
candidateKey{PkgName: "wpa_supplicant"},
candidateAddition{AdditionalVendors: []string{"w1.fi"}},
},
{
pkg.ApkPkg,
candidateKey{PkgName: "dnsmasq", Vendor: "dnsmasq"},
candidateAddition{AdditionalVendors: []string{"thekelleys"}},
},
// Debian packages
{
pkg.DebPkg,
candidateKey{PkgName: "dnsmasq", Vendor: "dnsmasq"},
candidateAddition{AdditionalVendors: []string{"thekelleys"}},
},
//
// Binary packages
{
File diff suppressed because it is too large Load Diff
@@ -52,11 +52,9 @@ func generateIndexedDictionaryJSON(rawGzipData io.Reader) ([]byte, error) {
func filterCpeList(cpeList CpeList) CpeList {
var processedCpeList CpeList
seen := make(map[string]struct{})
for _, cpeItem := range cpeList.CpeItems {
// Skip CPE items that don't have any references.
if len(cpeItem.References) == 0 {
if len(cpeItem.References.Reference) == 0 {
continue
}
@@ -64,6 +62,7 @@ func filterCpeList(cpeList CpeList) CpeList {
parsedName, err := wfn.Parse(cpeItem.Name)
if err != nil {
log.Printf("unable to parse CPE URI %q: %s", cpeItem.Name, err)
continue
}
if slices.Contains([]string{"h", "o"}, parsedName.Part) {
@@ -71,15 +70,17 @@ func filterCpeList(cpeList CpeList) CpeList {
}
normalizedName := normalizeCPE(parsedName).BindToURI()
if _, ok := seen[normalizedName]; ok {
continue
}
seen[normalizedName] = struct{}{}
cpeItem.Name = normalizedName
parsedCPE, err := wfn.Parse(cpeItem.Cpe23Item.Name)
cpeName := cpeItem.Cpe23Item.Name
if cpeItem.Cpe23Item.Deprecation.DeprecatedBy.Name != "" {
cpeName = cpeItem.Cpe23Item.Deprecation.DeprecatedBy.Name
}
parsedCPE, err := wfn.Parse(cpeName)
if err != nil {
log.Printf("unable to parse CPE value %q: %s", cpeItem.Cpe23Item.Name, err)
log.Printf("unable to parse CPE value %q: %s", cpeName, err)
continue
}
cpeItem.Cpe23Item.Name = normalizeCPE(parsedCPE).BindToFmtString()
@@ -101,13 +102,20 @@ func normalizeCPE(cpe *wfn.Attributes) *wfn.Attributes {
}
const (
prefixForNPMPackages = "https://www.npmjs.com/package/"
prefixForRubyGems = "https://rubygems.org/gems/"
prefixForRubyGemsHTTP = "http://rubygems.org/gems/"
prefixForNativeRubyGems = "https://github.com/ruby/"
prefixForPyPIPackages = "https://pypi.org/project/"
prefixForJenkinsPlugins = "https://github.com/jenkinsci/"
prefixForRustCrates = "https://crates.io/crates/"
prefixForNPMPackages = "https://www.npmjs.com/package/"
prefixForRubyGems = "https://rubygems.org/gems/"
prefixForRubyGemsHTTP = "http://rubygems.org/gems/"
prefixForNativeRubyGems = "https://github.com/ruby/"
prefixForPyPIPackages = "https://pypi.org/project/"
prefixForJenkinsPlugins = "https://plugins.jenkins.io/"
prefixForJenkinsPluginsGitHub = "https://github.com/jenkinsci/"
prefixForRustCrates = "https://crates.io/crates/"
prefixForPHPPear = "https://pear.php.net/"
prefixForPHPPearHTTP = "http://pear.php.net/"
prefixForPHPPecl = "https://pecl.php.net/"
prefixForPHPPeclHTTP = "http://pecl.php.net/"
prefixForPHPComposer = "https://packagist.org/packages/"
prefixForGoModules = "https://pkg.go.dev/"
)
// indexCPEList creates an index of CPEs by ecosystem.
@@ -119,8 +127,8 @@ func indexCPEList(list CpeList) *dictionary.Indexed {
for _, cpeItem := range list.CpeItems {
cpeItemName := cpeItem.Cpe23Item.Name
for _, reference := range cpeItem.References {
ref := reference.Reference.Href
for _, reference := range cpeItem.References.Reference {
ref := reference.Href
switch {
case strings.HasPrefix(ref, prefixForNPMPackages):
@@ -135,12 +143,27 @@ func indexCPEList(list CpeList) *dictionary.Indexed {
case strings.HasPrefix(ref, prefixForPyPIPackages):
addEntryForPyPIPackage(indexed, ref, cpeItemName)
case strings.HasPrefix(ref, prefixForJenkinsPlugins):
case strings.HasPrefix(ref, prefixForJenkinsPluginsGitHub):
// It _might_ be a jenkins plugin!
addEntryForJenkinsPluginGitHub(indexed, ref, cpeItemName)
case strings.HasPrefix(ref, prefixForJenkinsPlugins):
addEntryForJenkinsPlugin(indexed, ref, cpeItemName)
case strings.HasPrefix(ref, prefixForRustCrates):
addEntryForRustCrate(indexed, ref, cpeItemName)
case strings.HasPrefix(ref, prefixForPHPPear), strings.HasPrefix(ref, prefixForPHPPearHTTP):
addEntryForPHPPearPackage(indexed, ref, cpeItemName)
case strings.HasPrefix(ref, prefixForPHPPecl), strings.HasPrefix(ref, prefixForPHPPeclHTTP):
addEntryForPHPPeclPackage(indexed, ref, cpeItemName)
case strings.HasPrefix(ref, prefixForPHPComposer):
addEntryForPHPComposerPackage(indexed, ref, cpeItemName)
case strings.HasPrefix(ref, prefixForGoModules):
addEntryForGoModulePackage(indexed, ref, cpeItemName)
}
}
}
@@ -148,21 +171,29 @@ func indexCPEList(list CpeList) *dictionary.Indexed {
return indexed
}
func updateIndex(indexed *dictionary.Indexed, ecosystem string, pkgName string, cpe string) {
if _, exists := indexed.EcosystemPackages[ecosystem]; !exists {
indexed.EcosystemPackages[ecosystem] = make(dictionary.Packages)
}
if indexed.EcosystemPackages[ecosystem][pkgName] == nil {
indexed.EcosystemPackages[ecosystem][pkgName] = dictionary.NewSet()
}
indexed.EcosystemPackages[ecosystem][pkgName].Add(cpe)
}
func addEntryForRustCrate(indexed *dictionary.Indexed, ref string, cpeItemName string) {
// Prune off the non-package-name parts of the URL
ref = strings.TrimPrefix(ref, prefixForRustCrates)
ref = strings.Split(ref, "/")[0]
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemRustCrates]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemRustCrates] = make(dictionary.Packages)
}
indexed.EcosystemPackages[dictionary.EcosystemRustCrates][ref] = cpeItemName
updateIndex(indexed, dictionary.EcosystemRustCrates, ref, cpeItemName)
}
func addEntryForJenkinsPlugin(indexed *dictionary.Indexed, ref string, cpeItemName string) {
func addEntryForJenkinsPluginGitHub(indexed *dictionary.Indexed, ref string, cpeItemName string) {
// Prune off the non-package-name parts of the URL
ref = strings.TrimPrefix(ref, prefixForJenkinsPlugins)
ref = strings.TrimPrefix(ref, prefixForJenkinsPluginsGitHub)
ref = strings.Split(ref, "/")[0]
if !strings.HasSuffix(ref, "-plugin") {
@@ -171,12 +202,19 @@ func addEntryForJenkinsPlugin(indexed *dictionary.Indexed, ref string, cpeItemNa
}
ref = strings.TrimSuffix(ref, "-plugin")
updateIndex(indexed, dictionary.EcosystemJenkinsPlugins, ref, cpeItemName)
}
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemJenkinsPlugins]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemJenkinsPlugins] = make(dictionary.Packages)
func addEntryForJenkinsPlugin(indexed *dictionary.Indexed, ref string, cpeItemName string) {
// Prune off the non-package-name parts of the URL
ref = strings.TrimPrefix(ref, prefixForJenkinsPlugins)
ref = strings.Split(ref, "/")[0]
if ref == "" {
return
}
indexed.EcosystemPackages[dictionary.EcosystemJenkinsPlugins][ref] = cpeItemName
updateIndex(indexed, dictionary.EcosystemJenkinsPlugins, ref, cpeItemName)
}
func addEntryForPyPIPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
@@ -184,11 +222,7 @@ func addEntryForPyPIPackage(indexed *dictionary.Indexed, ref string, cpeItemName
ref = strings.TrimPrefix(ref, prefixForPyPIPackages)
ref = strings.Split(ref, "/")[0]
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemPyPI]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemPyPI] = make(dictionary.Packages)
}
indexed.EcosystemPackages[dictionary.EcosystemPyPI][ref] = cpeItemName
updateIndex(indexed, dictionary.EcosystemPyPI, ref, cpeItemName)
}
func addEntryForNativeRubyGem(indexed *dictionary.Indexed, ref string, cpeItemName string) {
@@ -196,11 +230,7 @@ func addEntryForNativeRubyGem(indexed *dictionary.Indexed, ref string, cpeItemNa
ref = strings.TrimPrefix(ref, prefixForNativeRubyGems)
ref = strings.Split(ref, "/")[0]
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemRubyGems]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemRubyGems] = make(dictionary.Packages)
}
indexed.EcosystemPackages[dictionary.EcosystemRubyGems][ref] = cpeItemName
updateIndex(indexed, dictionary.EcosystemRubyGems, ref, cpeItemName)
}
func addEntryForRubyGem(indexed *dictionary.Indexed, ref string, cpeItemName string) {
@@ -209,11 +239,7 @@ func addEntryForRubyGem(indexed *dictionary.Indexed, ref string, cpeItemName str
ref = strings.TrimPrefix(ref, prefixForRubyGemsHTTP)
ref = strings.Split(ref, "/")[0]
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemRubyGems]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemRubyGems] = make(dictionary.Packages)
}
indexed.EcosystemPackages[dictionary.EcosystemRubyGems][ref] = cpeItemName
updateIndex(indexed, dictionary.EcosystemRubyGems, ref, cpeItemName)
}
func addEntryForNPMPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
@@ -222,9 +248,84 @@ func addEntryForNPMPackage(indexed *dictionary.Indexed, ref string, cpeItemName
ref = strings.Split(ref, "?")[0]
ref = strings.TrimPrefix(ref, prefixForNPMPackages)
if _, ok := indexed.EcosystemPackages[dictionary.EcosystemNPM]; !ok {
indexed.EcosystemPackages[dictionary.EcosystemNPM] = make(dictionary.Packages)
updateIndex(indexed, dictionary.EcosystemNPM, ref, cpeItemName)
}
func phpExtensionPackageFromURLFragment(ref string) string {
if strings.HasPrefix(ref, "package/") { // package/HTML_QuickForm/download
ref = strings.TrimPrefix(ref, "package/")
components := strings.Split(ref, "/")
if len(components) < 1 {
return ""
}
ref = components[0]
} else if strings.Contains(ref, "?package=") { // package-changelog.php?package=xhprof&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]
}
indexed.EcosystemPackages[dictionary.EcosystemNPM][ref] = cpeItemName
return ref
}
func addEntryForPHPPearPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
ref = strings.TrimPrefix(ref, prefixForPHPPear)
ref = strings.TrimPrefix(ref, prefixForPHPPearHTTP)
ref = phpExtensionPackageFromURLFragment(ref)
if ref == "" {
return
}
updateIndex(indexed, dictionary.EcosystemPHPPear, ref, cpeItemName)
}
func addEntryForPHPPeclPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
ref = strings.TrimPrefix(ref, prefixForPHPPecl)
ref = strings.TrimPrefix(ref, prefixForPHPPeclHTTP)
ref = phpExtensionPackageFromURLFragment(ref)
if ref == "" {
return
}
updateIndex(indexed, dictionary.EcosystemPHPPecl, ref, cpeItemName)
}
func addEntryForPHPComposerPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
// Prune off the non-package-name parts of the URL
ref = strings.TrimPrefix(ref, prefixForPHPComposer)
components := strings.Split(ref, "/")
if len(components) < 2 {
return
}
ref = components[0] + "/" + components[1]
updateIndex(indexed, dictionary.EcosystemPHPComposer, ref, cpeItemName)
}
func addEntryForGoModulePackage(indexed *dictionary.Indexed, ref string, cpeItemName string) {
// Prune off the non-package-name parts of the URL
ref = strings.Split(ref, "?")[0]
ref = strings.TrimPrefix(ref, prefixForGoModules)
// Ignore the vulnerability reports endpoints
if strings.HasPrefix(ref, "vuln/") {
return
}
updateIndex(indexed, dictionary.EcosystemGoModules, ref, cpeItemName)
}
@@ -8,6 +8,7 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -59,33 +60,46 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemRustCrates: {
"unicycle": "cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*",
"unicycle": dictionary.NewSet("cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*"),
},
},
},
},
{
name: "addEntryForJenkinsPlugin",
addEntryFunc: addEntryForJenkinsPlugin,
name: "addEntryForJenkinsPluginGitHub",
addEntryFunc: addEntryForJenkinsPluginGitHub,
inputRef: "https://github.com/jenkinsci/sonarqube-plugin",
inputCpeItemName: "cpe:2.3:a:sonarsource:sonarqube_scanner:2.7:*:*:*:*:jenkins:*:*",
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemJenkinsPlugins: {
"sonarqube": "cpe:2.3:a:sonarsource:sonarqube_scanner:2.7:*:*:*:*:jenkins:*:*",
"sonarqube": dictionary.NewSet("cpe:2.3:a:sonarsource:sonarqube_scanner:2.7:*:*:*:*:jenkins:*:*"),
},
},
},
},
{
name: "addEntryForJenkinsPlugin: not actually a plugin",
addEntryFunc: addEntryForJenkinsPlugin,
name: "addEntryForJenkinsPluginGitHub: not actually a plugin",
addEntryFunc: addEntryForJenkinsPluginGitHub,
inputRef: "https://github.com/jenkinsci/jenkins",
inputCpeItemName: "cpe:2.3:a:jenkins:jenkinsci:2.7:*:*:*:*:*:*:*",
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{},
},
},
{
name: "addEntryForJenkinsPlugin",
addEntryFunc: addEntryForJenkinsPlugin,
inputRef: "https://plugins.jenkins.io/svn-partial-release-mgr/release",
inputCpeItemName: "cpe:2.3:a:jenkins:subversion_partial_release_manager:1.0.1:*:*:*:*:jenkins:*:*",
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemJenkinsPlugins: {
"svn-partial-release-mgr": dictionary.NewSet("cpe:2.3:a:jenkins:subversion_partial_release_manager:1.0.1:*:*:*:*:jenkins:*:*"),
},
},
},
},
{
name: "addEntryForPyPIPackage",
addEntryFunc: addEntryForPyPIPackage,
@@ -94,7 +108,7 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemPyPI: {
"vault-cli": "cpe:2.3:a:vault-cli_project:vault-cli:*:*:*:*:*:python:*:*",
"vault-cli": dictionary.NewSet("cpe:2.3:a:vault-cli_project:vault-cli:*:*:*:*:*:python:*:*"),
},
},
},
@@ -107,7 +121,7 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemRubyGems: {
"openssl": "cpe:2.3:a:ruby-lang:openssl:-:*:*:*:*:ruby:*:*",
"openssl": dictionary.NewSet("cpe:2.3:a:ruby-lang:openssl:-:*:*:*:*:ruby:*:*"),
},
},
},
@@ -120,7 +134,7 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemRubyGems: {
"actionview": "cpe:2.3:a:action_view_project:action_view:*:*:*:*:*:ruby:*:*",
"actionview": dictionary.NewSet("cpe:2.3:a:action_view_project:action_view:*:*:*:*:*:ruby:*:*"),
},
},
},
@@ -133,7 +147,7 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemRubyGems: {
"rbovirt": "cpe:2.3:a:amos_benari:rbovirt:*:*:*:*:*:ruby:*:*",
"rbovirt": dictionary.NewSet("cpe:2.3:a:amos_benari:rbovirt:*:*:*:*:*:ruby:*:*"),
},
},
},
@@ -146,7 +160,85 @@ func Test_addEntryFuncs(t *testing.T) {
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemNPM: {
"@nubosoftware/node-static": "cpe:2.3:a:\\@nubosoftware\\/node-static_project:\\@nubosoftware\\/node-static:-:*:*:*:*:node.js:*:*",
"@nubosoftware/node-static": dictionary.NewSet("cpe:2.3:a:\\@nubosoftware\\/node-static_project:\\@nubosoftware\\/node-static:-:*:*:*:*:node.js:*:*"),
},
},
},
},
{
name: "addEntryForPHPPeclPackage",
addEntryFunc: addEntryForPHPPeclPackage,
inputRef: "https://pecl.php.net/package/imagick/something/something/v4007.0",
inputCpeItemName: "cpe:2.3:a:php:imagick:*:*:*:*:*:*:*:*",
expectedIndexed: dictionary.Indexed{
EcosystemPackages: map[string]dictionary.Packages{
dictionary.EcosystemPHPPecl: {
"imagick": dictionary.NewSet("cpe:2.3:a:php:imagick:*:*:*:*:*:*:*:*"),
},
},
},
},
{
name: "addEntryForPHPPeclPackage http changelog",
addEntryFunc: addEntryForPHPPeclPackage,
inputRef: "http://pecl.php.net/package-changelog.php?package=memcached&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:*:*"),
},
},
},
@@ -161,7 +253,7 @@ func Test_addEntryFuncs(t *testing.T) {
tt.addEntryFunc(indexed, tt.inputRef, tt.inputCpeItemName)
if diff := cmp.Diff(tt.expectedIndexed, *indexed); diff != "" {
if diff := cmp.Diff(tt.expectedIndexed, *indexed, cmp.AllowUnexported(strset.Set{})); diff != "" {
t.Errorf("addEntry* mismatch (-want +got):\n%s", diff)
}
})
@@ -3,14 +3,19 @@ package main
type CpeItem struct {
Name string `xml:"name,attr"`
Title string `xml:"title"`
References []struct {
Reference struct {
References struct {
Reference []struct {
Href string `xml:"href,attr"`
Body string `xml:",chardata"`
} `xml:"reference"`
} `xml:"references"`
Cpe23Item struct {
Name string `xml:"name,attr"`
Name string `xml:"name,attr"`
Deprecation struct {
DeprecatedBy struct {
Name string `xml:"name,attr"`
} `xml:"deprecated-by"`
} `xml:"deprecation"`
} `xml:"cpe23-item"`
}
@@ -1,23 +1,111 @@
{
"ecosystems": {
"go_modules": {
"aahframe.work": [
"cpe:2.3:a:aahframework:aah:*:*:*:*:*:go:*:*"
],
"github.com/ecnepsnai/web": [
"cpe:2.3:a:web_project:web:*:*:*:*:*:go:*:*"
],
"github.com/square/squalor": [
"cpe:2.3:a:square:squalor:*:*:*:*:*:go:*:*"
],
"gopkg.in/yaml.v3": [
"cpe:2.3:a:yaml_project:yaml:*:*:*:*:*:go:*:*"
]
},
"jenkins_plugins": {
"sonarqube": "cpe:2.3:a:sonarsource:sonarqube_scanner:*:*:*:*:*:jenkins:*:*"
"anchore-container-scanner": [
"cpe:2.3:a:anchore:container_image_scanner:*:*:*:*:*:jenkins:*:*",
"cpe:2.3:a:jenkins:anchore_container_image_scanner:*:*:*:*:*:jenkins:*:*"
],
"fireline": [
"cpe:2.3:a:jenkins:360_fireline:*:*:*:*:*:jenkins:*:*"
],
"sonarqube": [
"cpe:2.3:a:sonarsource:sonarqube_scanner:*:*:*:*:*:jenkins:*:*"
],
"svn-partial-release-mgr": [
"cpe:2.3:a:jenkins:subversion_partial_release_manager:*:*:*:*:*:jenkins:*:*"
]
},
"npm": {
"merge-recursive": "cpe:2.3:a:umbraengineering:merge-recursive:*:*:*:*:*:node.js:*:*",
"static-dev-server": "cpe:2.3:a:static-dev-server_project:static-dev-server:*:*:*:*:*:node.js:*:*",
"umount": "cpe:2.3:a:umount_project:umount:*:*:*:*:*:node.js:*:*",
"undefsafe": "cpe:2.3:a:undefsafe_project:undefsafe:*:*:*:*:*:node.js:*:*",
"underscore": "cpe:2.3:a:underscorejs:underscore:*:*:*:*:*:node.js:*:*",
"underscore-99xp": "cpe:2.3:a:underscore-99xp_project:underscore-99xp:*:*:*:*:*:node.js:*:*",
"unicode": "cpe:2.3:a:unicode_project:unicode:*:*:*:*:*:node.js:*:*",
"unicorn-list": "cpe:2.3:a:unicorn-list_project:unicorn-list:*:*:*:*:*:node.js:*:*"
"merge-recursive": [
"cpe:2.3:a:umbraengineering:merge-recursive:*:*:*:*:*:node.js:*:*"
],
"ps": [
"cpe:2.3:a:umbraengineering:ps:*:*:*:*:*:node.js:*:*"
],
"static-dev-server": [
"cpe:2.3:a:static-dev-server_project:static-dev-server:*:*:*:*:*:node.js:*:*"
],
"umount": [
"cpe:2.3:a:umount_project:umount:*:*:*:*:*:node.js:*:*"
],
"undefsafe": [
"cpe:2.3:a:undefsafe_project:undefsafe:*:*:*:*:*:node.js:*:*"
],
"underscore": [
"cpe:2.3:a:underscorejs:underscore:*:*:*:*:*:node.js:*:*"
],
"underscore-99xp": [
"cpe:2.3:a:underscore-99xp_project:underscore-99xp:*:*:*:*:*:node.js:*:*"
],
"ungit": [
"cpe:2.3:a:ungit_project:ungit:*:*:*:*:*:node.js:*:*"
],
"unicode": [
"cpe:2.3:a:unicode_project:unicode:*:*:*:*:*:node.js:*:*"
],
"unicode-json": [
"cpe:2.3:a:unicode:unicode-json:*:*:*:*:*:node.js:*:*"
],
"unicorn-list": [
"cpe:2.3:a:unicorn-list_project:unicorn-list:*:*:*:*:*:node.js:*:*"
]
},
"php_composer": {
"frappant/frp-form-answers": [
"cpe:2.3:a:frappant:forms_export:*:*:*:*:*:typo3:*:*"
]
},
"php_pear": {
"HTML_QuickForm": [
"cpe:2.3:a:html_quickform_project:html_quickform:*:*:*:*:*:*:*:*"
],
"PEAR": [
"cpe:2.3:a:php:pear:*:*:*:*:*:*:*:*"
],
"XML_RPC": [
"cpe:2.3:a:php:xml_rpc:*:*:*:*:*:pear:*:*"
]
},
"php_pecl": {
"imagick": [
"cpe:2.3:a:php:imagick:*:*:*:*:*:*:*:*"
],
"memcached": [
"cpe:2.3:a:php:memcached:*:*:*:*:*:*:*:*"
],
"xhprof": [
"cpe:2.3:a:php:xhprof:*:*:*:*:*:*:*:*"
]
},
"pypi": {
"vault-cli": [
"cpe:2.3:a:vault-cli_project:vault-cli:*:*:*:*:*:python:*:*"
]
},
"rubygems": {
"openssl": "cpe:2.3:a:ruby-lang:openssl:*:*:*:*:*:*:*:*"
"openssl": [
"cpe:2.3:a:ruby-lang:openssl:*:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby-lang:openssl:*:*:*:*:*:ruby:*:*"
]
},
"rust_crates": {
"unicycle": "cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*"
"unicycle": [
"cpe:2.3:a:unicycle_project:unicycle:*:*:*:*:*:rust:*:*"
]
}
}
}
@@ -24873,4 +24873,226 @@
</references>
<cpe-23:cpe23-item name="cpe:2.3:a:zzzcms:zzzphp:2.1.0:*:*:*:*:*:*:*"/>
</cpe-item>
<cpe-item name="cpe:/a:php:xhprof:0.9.1">
<title xml:lang="en-US">PHP XHProf 0.9.1</title>
<references>
<reference href="http://pecl.php.net/package-changelog.php?package=xhprof&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,15 +1,51 @@
package dictionary
import (
"encoding/json"
"slices"
"github.com/scylladb/go-set/strset"
)
const (
EcosystemNPM = "npm"
EcosystemRubyGems = "rubygems"
EcosystemPyPI = "pypi"
EcosystemPHPPear = "php_pear"
EcosystemPHPPecl = "php_pecl"
EcosystemPHPComposer = "php_composer"
EcosystemJenkinsPlugins = "jenkins_plugins"
EcosystemRustCrates = "rust_crates"
EcosystemGoModules = "go_modules"
)
type Indexed struct {
EcosystemPackages map[string]Packages `json:"ecosystems"`
}
type Packages map[string]string
type Set struct {
*strset.Set
}
type Packages map[string]*Set
func NewSet(ts ...string) *Set {
return &Set{strset.New(ts...)}
}
func (s *Set) MarshalJSON() ([]byte, error) {
l := s.List()
slices.Sort(l)
return json.Marshal(l)
}
func (s *Set) UnmarshalJSON(data []byte) error {
var strSlice []string
if err := json.Unmarshal(data, &strSlice); err == nil {
*s = *NewSet(strSlice...)
} else {
return err
}
return nil
}
@@ -58,52 +58,69 @@ func GetIndexedDictionary() (_ *dictionary.Indexed, err error) {
return indexedCPEDictionary, err
}
func FromDictionaryFind(p pkg.Package) (cpe.CPE, bool) {
func FromDictionaryFind(p pkg.Package) ([]cpe.CPE, bool) {
dict, err := GetIndexedDictionary()
parsedCPEs := []cpe.CPE{}
if err != nil {
log.Debugf("CPE dictionary lookup not available: %+v", err)
return cpe.CPE{}, false
return parsedCPEs, false
}
var (
cpeString string
ok bool
cpes *dictionary.Set
ok bool
)
switch p.Type {
case pkg.NpmPkg:
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemNPM][p.Name]
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemNPM][p.Name]
case pkg.GemPkg:
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemRubyGems][p.Name]
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemRubyGems][p.Name]
case pkg.PythonPkg:
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemPyPI][p.Name]
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemPyPI][p.Name]
case pkg.JenkinsPluginPkg:
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemJenkinsPlugins][p.Name]
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemJenkinsPlugins][p.Name]
case pkg.RustPkg:
cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemRustCrates][p.Name]
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemRustCrates][p.Name]
case pkg.PhpComposerPkg:
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemPHPComposer][p.Name]
case pkg.PhpPeclPkg:
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemPHPPecl][p.Name]
case pkg.GoModulePkg:
cpes, ok = dict.EcosystemPackages[dictionary.EcosystemGoModules][p.Name]
default:
// The dictionary doesn't support this package type yet.
return cpe.CPE{}, false
return parsedCPEs, false
}
if !ok {
// The dictionary doesn't have a CPE for this package.
return cpe.CPE{}, false
return parsedCPEs, false
}
parsedCPE, err := cpe.New(cpeString, cpe.NVDDictionaryLookupSource)
if err != nil {
return cpe.CPE{}, false
for _, c := range cpes.List() {
parsedCPE, err := cpe.New(c, cpe.NVDDictionaryLookupSource)
if err != nil {
continue
}
parsedCPE.Attributes.Version = p.Version
parsedCPEs = append(parsedCPEs, parsedCPE)
}
parsedCPE.Attributes.Version = p.Version
if len(parsedCPEs) == 0 {
return []cpe.CPE{}, false
}
return parsedCPE, true
return parsedCPEs, true
}
// FromPackageAttributes Create a list of CPEs for a given package, trying to guess the vendor, product tuple. We should be trying to
@@ -10,6 +10,7 @@ import (
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
)
@@ -991,7 +992,7 @@ func TestDictionaryFindIsWired(t *testing.T) {
tests := []struct {
name string
pkg pkg.Package
want string
want []cpe.CPE
wantExists bool
}{
{
@@ -1001,7 +1002,10 @@ func TestDictionaryFindIsWired(t *testing.T) {
Version: "1.0.2k",
Type: pkg.GemPkg,
},
want: "cpe:2.3:a:ruby-lang:openssl:1.0.2k:*:*:*:*:*:*:*",
want: []cpe.CPE{
cpe.Must("cpe:2.3:a:ruby-lang:openssl:1.0.2k:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
cpe.Must("cpe:2.3:a:ruby-lang:openssl:1.0.2k:*:*:*:*:ruby:*:*", cpe.NVDDictionaryLookupSource),
},
// without the cpe data wired up, this would be empty (generation also creates cpe:2.3:a:openssl:openssl:1.0.2k:*:*:*:*:*:*:*)
wantExists: true,
},
@@ -1009,8 +1013,7 @@ func TestDictionaryFindIsWired(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, gotExists := FromDictionaryFind(tt.pkg)
assert.Equal(t, tt.want, got.Attributes.BindToFmtString())
assert.ElementsMatch(t, tt.want, got)
assert.Equal(t, tt.wantExists, gotExists)
})
}
@@ -181,6 +181,13 @@ func (j *archiveParser) discoverMainPackage(ctx context.Context) (*pkg.Package,
return nil, nil
}
// check for existence of Weave-Classes manifest key in order to exclude jars getting misrepresented as
// their targeted counterparts, e.g. newrelic spring and tomcat instrumentation
if _, ok := manifest.Main.Get("Weave-Classes"); ok {
log.Debugf("excluding archive due to Weave-Classes manifest entry: %s", j.location)
return nil, nil
}
// grab and assign digest for the entire archive
digests, err := getDigestsFromArchive(j.archivePath)
if err != nil {
@@ -1168,7 +1168,6 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
expectedPkgs []pkg.Package
expectedRelationships []artifact.Relationship
assignParent bool
want bool
}{
{
name: "duplicate jar regression - go case (issue #2130)",
@@ -1351,6 +1350,11 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
},
},
},
{
name: "exclude instrumentation jars with Weave-Classes in manifest",
fixtureName: "spring-instrumentation-4.3.0-1.0",
expectedPkgs: nil, // we expect no packages to be discovered when Weave-Classes present in the manifest
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+1 -1
View File
@@ -26,7 +26,7 @@ func packageURL(name, version string, metadata pkg.JavaArchive) string {
return pURL.ToString()
}
// GroupIDFromJavaPackage returns the authoritative group ID for a Java package.
// groupIDFromJavaMetadata returns the authoritative group ID for a Java package.
// The order of precedence is:
// 1. The group ID from the POM properties
// 2. The group ID from the POM project
@@ -5,6 +5,7 @@ JACKSON_CORE = jackson-core-2.15.2
SBT_JACKSON_CORE = com.fasterxml.jackson.core.jackson-core-2.15.2
OPENSAML_CORE = opensaml-core-3.4.6
API_ALL_SOURCES = api-all-2.0.0-sources
SPRING_INSTRUMENTATION = spring-instrumentation-4.3.0-1.0
$(CACHE_DIR):
mkdir -p $(CACHE_DIR)
@@ -19,4 +20,7 @@ $(CACHE_DIR)/$(OPENSAML_CORE).jar: $(CACHE_DIR)
cd $(OPENSAML_CORE) && zip -r $(CACHE_PATH)/$(OPENSAML_CORE).jar .
$(CACHE_DIR)/$(API_ALL_SOURCES).jar: $(CACHE_DIR)
cd $(API_ALL_SOURCES) && zip -r $(CACHE_PATH)/$(API_ALL_SOURCES).jar .
cd $(API_ALL_SOURCES) && zip -r $(CACHE_PATH)/$(API_ALL_SOURCES).jar .
$(CACHE_DIR)/$(SPRING_INSTRUMENTATION).jar: $(CACHE_DIR)
cd $(SPRING_INSTRUMENTATION) && zip -r $(CACHE_PATH)/$(SPRING_INSTRUMENTATION).jar .
@@ -0,0 +1,22 @@
Manifest-Version: 1.0
Implementation-Title: com.newrelic.instrumentation.spring-4.3.0
Implementation-Version: 1.0
Illegal-Classes:
Weave-Violation-Filter: METHOD_MISSING_REQUIRED_ANNOTATIONS,CLASS_MISS
ING_REQUIRED_ANNOTATIONS
Reference-Classes: org/springframework/core/annotation/AnnotationUtils
,org/springframework/web/bind/annotation/DeleteMapping,org/springfram
ework/web/bind/annotation/PatchMapping,org/springframework/web/bind/a
nnotation/PostMapping,org/springframework/web/bind/annotation/PutMapp
ing,org/springframework/web/bind/annotation/RequestMapping,org/spring
framework/web/method/HandlerMethod,org/springframework/web/servlet/Mo
delAndView
Class-Required-Annotations:
Method-Required-Annotations:
Implementation-Title-Alias: spring_annotations
Weave-Classes: org/springframework/web/bind/annotation/GetMapping,org/
springframework/web/servlet/mvc/method/AbstractHandlerMethodAdapter
Weave-Methods: "handleInternal(Ljavax/servlet/http/HttpServletRequest;
Ljavax/servlet/http/HttpServletResponse;Lorg/springframework/web/meth
od/HandlerMethod;)Lorg/springframework/web/servlet/ModelAndView;"
Implementation-Vendor: New Relic
+1 -1
View File
@@ -38,7 +38,7 @@ func parseWheelOrEgg(_ context.Context, resolver file.Resolver, _ *generic.Envir
return pkgs, nil, nil
}
// fetchRecordFiles finds a corresponding installed-files.txt file for the given python package metadata file and returns the set of file records contained.
// fetchInstalledFiles finds a corresponding installed-files.txt file for the given python package metadata file and returns the set of file records contained.
func fetchInstalledFiles(resolver file.Resolver, metadataLocation file.Location, sitePackagesRootPath string) (files []pkg.PythonFileRecord, sources []file.Location, err error) {
// we've been given a file reference to a specific wheel METADATA file. note: this may be for a directory
// or for an image... for an image the METADATA file may be present within multiple layers, so it is important
@@ -13,7 +13,7 @@ import (
"github.com/anchore/syft/syft/pkg"
)
// parseWheelOrEggMetadata takes a Python Egg or Wheel (which share the same format and values for our purposes),
// parseWheelOrEggRecord takes a Python Egg or Wheel (which share the same format and values for our purposes),
// returning all Python packages listed.
func parseWheelOrEggRecord(reader io.Reader) []pkg.PythonFileRecord {
var records []pkg.PythonFileRecord
@@ -17,6 +17,7 @@ import (
)
// parseRpmDb parses an "Packages" RPM DB and returns the Packages listed within it.
// nolint:funlen
func parseRpmDB(_ context.Context, resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
f, err := os.CreateTemp("", "rpmdb")
if err != nil {
@@ -24,6 +25,10 @@ func parseRpmDB(_ context.Context, resolver file.Resolver, env *generic.Environm
}
defer func() {
err = f.Close()
if err != nil {
log.Errorf("failed to close temp rpmdb file: %+v", err)
}
err = os.Remove(f.Name())
if err != nil {
log.Errorf("failed to remove temp rpmdb file: %+v", err)
@@ -39,6 +44,7 @@ func parseRpmDB(_ context.Context, resolver file.Resolver, env *generic.Environm
if err != nil {
return nil, nil, err
}
defer db.Close()
pkgList, err := db.ListPackages()
if err != nil {
+10 -7
View File
@@ -11,11 +11,14 @@ type DotnetDepsEntry struct {
// DotnetPortableExecutableEntry is a struct that represents a single entry found within "VersionResources" section of a .NET Portable Executable binary file.
type DotnetPortableExecutableEntry struct {
AssemblyVersion string `json:"assemblyVersion"`
LegalCopyright string `json:"legalCopyright"`
Comments string `json:"comments,omitempty"`
InternalName string `json:"internalName,omitempty"`
CompanyName string `json:"companyName"`
ProductName string `json:"productName"`
ProductVersion string `json:"productVersion"`
AssemblyVersion string `json:"assemblyVersion"`
LegalCopyright string `json:"legalCopyright"`
Comments string `json:"comments,omitempty"`
InternalName string `json:"internalName,omitempty"`
CompanyName string `json:"companyName"`
ProductName string `json:"productName"`
ProductVersion string `json:"productVersion"`
FileVersion string `json:"fileVersion,omitempty"`
FileDescription string `json:"fileDescription,omitempty"`
OriginalFilename string `json:"originalFilename,omitempty"`
}
+5 -3
View File
@@ -3,9 +3,11 @@ package pkg
type RDescription struct {
/*
Fields chosen by:
docker run --rm -it rocker/r-ver bash
$ install2.r ggplot2 # has a lot of dependencies
$ find /usr/local/lib/R -name DESCRIPTION | xargs cat | grep -v '^\s' | cut -d ':' -f 1 | sort | uniq -c | sort -nr
docker run --rm -it rocker/r-ver bash
$ install2.r ggplot2 # has a lot of dependencies
$ find /usr/local/lib/R -name DESCRIPTION | xargs cat | grep -v '^\s' | cut -d ':' -f 1 | sort | uniq -c | sort -nr
For more information on the DESCRIPTION file see https://r-pkgs.org/description.html
*/
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`