Compare commits

...
19 Commits
Author SHA1 Message Date
Alex Goodman 8f6bdde666 Label PRs when the json schema changes (#2240)
* label PRs when the json schema changes

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

* moderate pr comments

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

* be more strict about processing file names

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2023-10-20 13:00:15 -04:00
Christopher Angelo PhillipsandAuston-Zhang ef43294d0e Add download location when cataloging directory npm package lock (#2238)
* added download locatoin (resolved) when cataloging a directory - javascript ecosystem- npm - packag-lock
	Signed-off by Auston(Aoxiang) Zhang <auston.zhang@dal.ca>

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

* chore: get DCO to fire

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

---------

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
Co-authored-by: Auston-Zhang <ax706429@dal.ca>
2023-10-20 11:40:38 -04:00
Christopher Angelo Phillips e1ad340c2d fix: allow packages to be captured from DIST/EGG case (#2239)
* fix: allow packages to be captured from DIST/EGG case

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

* test: update expected glob paths

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

---------

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2023-10-20 11:29:13 -04:00
07f13049da Account for maven bundle plugin and fix filename matching (#2220)
* account for maven bundle plugin and fix filename matching

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

* add in-repo jar tests based on metadata to cover #2130

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

* tests: fix test merge commit

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
Co-authored-by: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <christopher.phillips@anchore.com>
2023-10-19 17:57:23 -04:00
dependabot[bot] 6c7900f5b8 chore(deps): bump actions/checkout from 4.1.0 to 4.1.1 (#2236)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.0 to 4.1.1.
- [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/8ade135a41bc03ea155e62e844d188df1ea18608...b4ffde65f46336ab88eb53be808477a3936bae11)

---
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>
2023-10-18 09:54:26 -04:00
Alex Goodman 7018573bf7 Remove internal string set (#2219)
* remove internal string set

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

* incorporate changes from #2227

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

* beef up the pkg.License.Merg() doc string

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2023-10-17 12:52:11 -04:00
Alex Goodman f3ad8cf250 bump clio to get stderr reporting fix (#2232)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2023-10-16 12:47:48 -04:00
Alex Goodman 31f1d7dbf0 Fix panic for empty input to Swift cataloger (#2226)
* survive invalid input in swift parser

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

* add empty file

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2023-10-16 11:04:33 -04:00
Colm O hEigeartaighandAlex Goodman 144ed725a7 Add additional license filenames (#2227)
* Add additional license filenames

Signed-off-by: Colm O hEigeartaigh <coheigea@apache.org>

* add comment about the license list being manually updated

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

---------

Signed-off-by: Colm O hEigeartaigh <coheigea@apache.org>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2023-10-16 13:20:00 +00:00
dependabot[bot] dcec2bc352 chore(deps): bump github/codeql-action from 2.22.2 to 2.22.3 (#2229)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.22.2 to 2.22.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/d90b8d79de6dc1f58e83a1499aa58d6c93dc28de...0116bc2df50751f9724a2e35ef1f24d22f90e4e1)

---
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>
2023-10-16 08:59:39 -04:00
dependabot[bot] 1fe0921a5b chore(deps): bump github.com/charmbracelet/lipgloss from 0.9.0 to 0.9.1 (#2222)
Bumps [github.com/charmbracelet/lipgloss](https://github.com/charmbracelet/lipgloss) from 0.9.0 to 0.9.1.
- [Release notes](https://github.com/charmbracelet/lipgloss/releases)
- [Commits](https://github.com/charmbracelet/lipgloss/compare/v0.9.0...v0.9.1)

---
updated-dependencies:
- dependency-name: github.com/charmbracelet/lipgloss
  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>
2023-10-12 11:10:56 -04:00
dependabot[bot] 538fe5ee1d chore(deps): bump github/codeql-action from 2.22.1 to 2.22.2 (#2224)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.22.1 to 2.22.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/fdcae64e1484d349b3366718cdfef3d404390e85...d90b8d79de6dc1f58e83a1499aa58d6c93dc28de)

---
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>
2023-10-12 11:10:45 -04:00
Colm O hEigeartaigh 2687100e6a Detect a license file in the root directory or META-INF of a jar (#2213)
Signed-off-by: Colm O hEigeartaigh <coheigea@apache.org>
2023-10-12 11:09:53 -04:00
Benji VisserandAlex Goodman fe7a417fb2 Parse donet dependency trees (#2143)
* add dependency information for .NET pkgs

Signed-off-by: Benji Visser <benji@093b.org>

* update pkg coverage directory test

Signed-off-by: Benji Visser <benji@093b.org>

* reverse dependsOn relationship

Signed-off-by: Benji Visser <benji@093b.org>

* update root pkg parsing

Signed-off-by: Benji Visser <benji@093b.org>

* add comments about the test relationships represented

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

* add docs around relationship sorting functions + update test helpers

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

---------

Signed-off-by: Benji Visser <benji@093b.org>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2023-10-11 18:01:24 +00:00
dependabot[bot] 7732cd3b48 chore(deps): bump golang.org/x/net from 0.16.0 to 0.17.0 (#2214)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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>
2023-10-11 13:52:07 -04:00
dependabot[bot] 0302fc5b48 chore(deps): bump github.com/google/go-cmp from 0.5.9 to 0.6.0 (#2215)
Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.5.9 to 0.6.0.
- [Release notes](https://github.com/google/go-cmp/releases)
- [Commits](https://github.com/google/go-cmp/compare/v0.5.9...v0.6.0)

---
updated-dependencies:
- dependency-name: github.com/google/go-cmp
  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>
2023-10-11 09:53:00 -04:00
dependabot[bot] b899536814 chore(deps): bump github.com/charmbracelet/lipgloss from 0.8.0 to 0.9.0 (#2216)
Bumps [github.com/charmbracelet/lipgloss](https://github.com/charmbracelet/lipgloss) from 0.8.0 to 0.9.0.
- [Release notes](https://github.com/charmbracelet/lipgloss/releases)
- [Commits](https://github.com/charmbracelet/lipgloss/compare/v0.8.0...v0.9.0)

---
updated-dependencies:
- dependency-name: github.com/charmbracelet/lipgloss
  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>
2023-10-11 09:52:42 -04:00
Christopher Angelo PhillipsandAlex Goodman d1120ad56e chore: add automated homebrew action (#2164)
* chore: add automated homebrew action

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

* migrate homebrew publish step to separate post-release workflow

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

---------

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2023-10-11 09:52:22 -04:00
Alex Goodman ef759038f5 Add relationships for dpkg packages (#2212)
* add relationships for deb packages

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

* update snapshots

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

* bump json schema

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

* small refactor to remove duplicate code

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2023-10-11 08:56:26 -04:00
96 changed files with 5350 additions and 515 deletions
+224
View File
@@ -0,0 +1,224 @@
from __future__ import annotations
import sys
import glob
import subprocess
import os
import re
DRY_RUN = False
def main(changed_files: str | None = None, merge_base_schema_files: str | None = None):
global DRY_RUN
pr_number = os.environ.get("GITHUB_PR_NUMBER")
comment_file_path = os.environ.get("CI_COMMENT_FILE")
if not comment_file_path:
print("CI_COMMENT_FILE not set")
sys.exit(1)
if not pr_number:
DRY_RUN = True
if changed_files:
DRY_RUN = True
# read lines from file... this is useful for local testing
with open(changed_files) as f:
pr_changed_files = f.read().splitlines()
with open(merge_base_schema_files) as f:
og_json_schema_files = sort_json_schema_files(f.read().splitlines())
else:
if not is_ci():
print("Not in CI")
sys.exit(1)
if not pr_number:
print("Not a PR")
sys.exit(1)
pr_changed_files = get_pr_changed_files(pr_number)
# since we are running this in the context of the pull_request_target, the checkout is the merge base..
# that is the main branch of the original repo, NOT the branch in the forked repo (or branch in the target
# repo for non-forked PRs). This means we just need to list the current checkedout files to get a sense of
# the changes before a merge.
og_json_schema_files = list_json_schema_files()
pr_json_schema_files = filter_to_schema_files(pr_changed_files)
# print("schema files in pr: ", summarize_schema_files(pr_json_schema_files))
# print("og schema files: ", summarize_schema_files(og_json_schema_files))
if not og_json_schema_files:
print("No schema files found in merge base")
sys.exit(1)
# pr_json_schema_files = set of PR files are added, removed, and changed files
new_schema_files = set(pr_json_schema_files) - set(og_json_schema_files)
removed_or_modified_schema_files = set(pr_json_schema_files) - set(new_schema_files)
print("new schemas: ", summarize_schema_files(new_schema_files))
print("removed or modified schemas:", summarize_schema_files(removed_or_modified_schema_files))
# if there is a new or modified schema, we should add the "json-schema" label to the PR...
if new_schema_files or removed_or_modified_schema_files:
print("\nAdding json-schema label...")
add_label(pr_number, "json-schema")
else:
remove_label(pr_number, "json-schema")
# 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...
if is_breaking_change(new_schema_files, og_json_schema_files[-1]):
print("\nBreaking change detected...")
add_label(pr_number, "breaking-change")
else:
remove_label(pr_number, "breaking-change")
# 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...
if removed_or_modified_schema_files:
print("\nRemoved or modified schema detected...")
schemas = sort_json_schema_files(list(removed_or_modified_schema_files))
schemas_str = "\n".join([f" - {schema}" for schema in schemas])
add_comment(comment_file_path, f"Detected modification or removal of existing json schemas:\n{schemas_str}", warning=True)
def add_comment(comment_file_path: str, comment: str, warning: bool = False, important: bool = False):
if warning or important:
comment_lines = comment.splitlines()
comment = "\n".join([f"> {line}" for line in comment_lines])
if warning:
comment = f"> [!WARNING]\n{comment}"
elif important:
comment = f"> [!IMPORTANT]\n{comment}"
# create any parent directories if they don't exist
os.makedirs(os.path.dirname(comment_file_path), exist_ok=True)
with open(comment_file_path, "w") as f:
f.write(comment)
print(f"Comment file contents: {comment_file_path}")
print(comment)
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(str(result.stderr))
sys.exit(1)
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(str(result.stderr))
sys.exit(1)
def major_version(semver: str) -> int:
return int(semver.split(".")[0])
def is_breaking_change(new_schema_files: set[str], latest_schema_file: str) -> bool:
latest_major_version = major_version(get_semver(latest_schema_file))
for file in new_schema_files:
change_major_version = major_version(get_semver(file))
if change_major_version > latest_major_version:
return True
return False
def summarize_schema_files(files: list[str]) -> list[str]:
return [get_semver(file) for file in files]
def is_ci() -> bool:
return "CI" in os.environ
def get_pr_changed_files(pr_number: str) -> list[str]:
result = run(f"gh pr view {pr_number} --json files --jq '.files.[].path'", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode != 0:
print("Unable to get list of changed files in PR")
print(str(result.stderr))
sys.exit(1)
list_of_files = result.stdout.splitlines()
return list_of_files
def filter_to_schema_files(list_of_files: list[str]) -> list[str]:
# get files matching "schema/json/schema-*.json"
files = []
for file in list_of_files:
if re.match(r"^schema/json/schema-\d+\.\d+\.\d+\.json$", file):
files.append(file)
return sort_json_schema_files(files)
def list_json_schema_files() -> list[str]:
# list files in "schema/json" directory matching the pattern of "schema-*.json"
return sort_json_schema_files(list(glob.glob("schema/json/schema-*.json")))
def run(command: str, **kwargs) -> subprocess.CompletedProcess:
if DRY_RUN:
print(f"[DRY RUN] {command}")
return subprocess.CompletedProcess(args=[command], returncode=0)
print(f"[RUN] {command}")
return subprocess.run(command, **kwargs)
def get_semver(input_file: str) -> str:
return input_file.split("-")[1].split(".json")[0]
def sort_json_schema_files(files: list[str]) -> list[str]:
# sort files by schema version, where the input looks like "schema/json/schema-1.12.1.json"
# we should sort by the semantic version embedded within the basename, not the string
# so that "schema/json/schema-1.2.1.json" comes before "schema/json/schema-1.12.1.json".
versions = [get_semver(file) for file in files if file]
versions = sorted(versions, key=lambda s: [int(u) for u in s.split('.')])
return [f"schema/json/schema-{version}.json" for version in versions]
# allow for test files that have line-by-line list of files:
# .binny.yaml
# .github/actions/bootstrap/action.yaml
# .github/scripts/goreleaser-install.sh
# .github/workflows/release.yaml
# .github/workflows/update-bootstrap-tools.yml
# .github/workflows/update-cpe-dictionary-index.yml
# .github/workflows/update-stereoscope-release.yml
# .github/workflows/validations.yaml
# .gitignore
# .goreleaser.yaml
# Makefile
# Taskfile.yaml
# schema/cyclonedx/Makefile
if __name__ == "__main__":
# these are variables for a single file name that contains a list of files (line separated)
changed_files = None
merge_base_schema_files = None
if len(sys.argv) > 2:
changed_files = sys.argv[1]
merge_base_schema_files = sys.argv[2]
main(changed_files, merge_base_schema_files)
+65
View File
@@ -0,0 +1,65 @@
import unittest
from unittest.mock import patch
import subprocess
import labeler
class Labeler(unittest.TestCase):
def test_major_version(self):
self.assertEqual(labeler.major_version("1.2.3"), 1)
self.assertEqual(labeler.major_version("2.0.0"), 2)
def test_is_breaking_change(self):
new_schema_files = ["schema/json/schema-2.0.0.json"]
latest_schema_file = "schema/json/schema-1.2.0.json"
self.assertTrue(labeler.is_breaking_change(new_schema_files, latest_schema_file))
new_schema_files = ["schema/json/schema-1.3.0.json"]
latest_schema_file = "schema/json/schema-1.2.0.json"
self.assertFalse(labeler.is_breaking_change(new_schema_files, latest_schema_file))
def test_summarize_schema_files(self):
files = ["schema/json/schema-1.0.0.json", "schema/json/schema-2.0.0.json"]
expected = ["1.0.0", "2.0.0"]
self.assertEqual(labeler.summarize_schema_files(files), expected)
def test_is_ci(self):
# Mock os.environ to simulate CI environment
with patch.dict("os.environ", {"CI": "true"}):
self.assertTrue(labeler.is_ci())
def test_get_pr_changed_files(self):
expected_command = "gh pr view 123 --json files --jq '.files.[].path'"
expected_output = "file1.json\nfile2.json\n"
subprocess.CompletedProcess.returncode = 0
subprocess.CompletedProcess.stdout = expected_output
with patch("labeler.run", return_value=subprocess.CompletedProcess) as mock_run:
result = labeler.get_pr_changed_files("123")
mock_run.assert_called_with(expected_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
self.assertEqual(result, ["file1.json", "file2.json"])
def test_filter_to_schema_files(self):
input_files = ["schema/json/schema-1.0.0.json", "not_schema.txt", "schema/json/schema-2.0.0.json"]
expected_files = ["schema/json/schema-1.0.0.json", "schema/json/schema-2.0.0.json"]
self.assertEqual(labeler.filter_to_schema_files(input_files), expected_files)
# we should be strict about what files are allowed to be processed
input_files = ["schema/json/schema-1.0.0extracontent.json", "schema/json/schema-1.0.0.md", "schema/json/schema-1.0.0.json.extracontent"]
expected_files = []
self.assertEqual(labeler.filter_to_schema_files(input_files), expected_files)
def test_get_semver(self):
input_file = "schema/json/schema-1.0.0.json"
expected_semver = "1.0.0"
self.assertEqual(labeler.get_semver(input_file), expected_semver)
def test_sort_json_schema_files(self):
files = ["schema/json/schema-1.12.1.json", "schema/json/schema-1.2.1.json"]
expected_sorted_files = ["schema/json/schema-1.2.1.json", "schema/json/schema-1.12.1.json"]
self.assertEqual(labeler.sort_json_schema_files(files), expected_sorted_files)
if __name__ == "__main__":
unittest.main()
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
# the job by event.
steps:
- name: Checkout code
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
+4 -4
View File
@@ -36,7 +36,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- name: Install Go
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0
@@ -45,7 +45,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@fdcae64e1484d349b3366718cdfef3d404390e85 #v2.22.1
uses: github/codeql-action/init@0116bc2df50751f9724a2e35ef1f24d22f90e4e1 #v2.22.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@fdcae64e1484d349b3366718cdfef3d404390e85 #v2.22.1
uses: github/codeql-action/autobuild@0116bc2df50751f9724a2e35ef1f24d22f90e4e1 #v2.22.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@fdcae64e1484d349b3366718cdfef3d404390e85 #v2.22.1
uses: github/codeql-action/analyze@0116bc2df50751f9724a2e35ef1f24d22f90e4e1 #v2.22.3
+54
View File
@@ -0,0 +1,54 @@
name: "Detect schema changes"
on:
# IMPORTANT! This workflow is triggered by the `pull_request_target` event
# which means that forked PRs will run with access secrets from the repo
# it's forked from (the "target" repo).
#
# For this reason we only NEVER checkout the code from the pull request
# (e.g. "ref: ${{ github.event.pull_request.head.sha }}") to prevent
# accidentally running potentially untrusted code.
#
# By default the checkout will be:
# - GITHUB_SHA: Last commit on the PR base branch
# - GITHUB_REF: PR base branch
#
# ...unlike a typical PR where:
# - GITHUB_SHA: Last merge commit on the GITHUB_REF branch
# - GITHUB_REF: PR merge branch refs/pull/:prNumber/merge
pull_request_target:
env:
# note: this is used within hashFiles() so must be within the GITHUB_WORKSPACE path (or will silently fail)
CI_COMMENT_FILE: .tmp/labeler-comment.txt
# needs to be any string to uniquely identify the comment on a PR across multiple runs
COMMENT_HEADER: "label-commentary"
jobs:
label:
name: "Label changes"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- run: python .github/scripts/labeler.py
env:
# note: this token has write access to the repo
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_PR_NUMBER: ${{ github.event.number }}
- name: Delete existing comment
if: ${{ hashFiles( env.CI_COMMENT_FILE ) == '' }}
uses: marocchino/sticky-pull-request-comment@efaaab3fd41a9c3de579aba759d2552635e590fd #v2.8.0
with:
header: ${{ env.COMMENT_HEADER }}
hide: true
hide_classify: "OUTDATED"
- name: Add comment
if: ${{ hashFiles( env.CI_COMMENT_FILE ) != '' }}
uses: marocchino/sticky-pull-request-comment@efaaab3fd41a9c3de579aba759d2552635e590fd #v2.8.0
with:
header: ${{ env.COMMENT_HEADER }}
path: ${{ env.CI_COMMENT_FILE }}
+25
View File
@@ -0,0 +1,25 @@
name: "Release Homebrew"
on:
release:
types: [published]
jobs:
homebrew:
runs-on: ubuntu-20.04
steps:
- name: Update Homebrew formula
uses: dawidd6/action-homebrew-bump-formula@d3667e5ae14df19579e4414897498e3e88f2f458 # v3.10.0
with:
token: ${{ secrets.HOMEBREW_TOKEN }}
org: anchore
formula: syft
- uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 #v3.15.1
if: ${{ failure() }}
with:
status: ${{ job.status }}
fields: repo,workflow,action,eventName
text: "Post-release homebrew publish workflow has failed: https://github.com/anchore/syft/actions/workflows/release-homebrew.yaml"
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
environment: release
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- name: Check if tag already exists
# note: this will fail if the tag already exists
@@ -95,7 +95,7 @@ jobs:
contents: write
packages: write
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
with:
fetch-depth: 0
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0
with:
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- uses: actions/setup-go@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0
with:
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0
with:
+7 -7
View File
@@ -16,7 +16,7 @@ jobs:
name: "Static analysis"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -30,7 +30,7 @@ jobs:
name: "Unit tests"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -74,7 +74,7 @@ jobs:
name: "Integration tests"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -96,7 +96,7 @@ jobs:
name: "Build snapshot artifacts"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -127,7 +127,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- name: Download snapshot build
uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
@@ -163,7 +163,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: macos-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- name: Download snapshot build
uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
@@ -191,7 +191,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
+5
View File
@@ -64,3 +64,8 @@ test/integration/test-fixtures/**/go.sum
# attestation
cosign.key
cosign.pub
# Byte-compiled object files for python
__pycache__/
*.py[cod]
*$py.class
+1
View File
@@ -6,6 +6,7 @@ In order to test and develop in this repo you will need the following dependenci
- Golang
- docker
- make
- Python (>= 3.9)
### Docker settings for getting started
Make sure you've updated your docker settings so the default docker socket path is available.
+5 -1
View File
@@ -70,7 +70,7 @@ all: static-analysis test ## Run all linux-based checks (linting, license check,
static-analysis: check-go-mod-tidy lint check-licenses check-json-schema-drift ## Run all static analysis checks
.PHONY: test
test: unit integration validate-cyclonedx-schema benchmark cli ## Run all tests (currently unit, integration, linux compare, and cli tests)
test: unit integration validate-cyclonedx-schema benchmark test-utils cli ## Run all tests (currently unit, integration, linux compare, and cli tests)
## Bootstrapping targets #################################
@@ -167,6 +167,10 @@ cli: $(SNAPSHOT_DIR) ## Run CLI tests
SYFT_BINARY_LOCATION='$(SNAPSHOT_BIN)' \
go test -count=1 -timeout=15m -v ./test/cli
.PHONY: test-utils
test-utils:
python .github/scripts/labeler_test.py
## Benchmark test targets #################################
+3 -3
View File
@@ -7,10 +7,10 @@ import (
"github.com/iancoleman/strcase"
"github.com/mitchellh/go-homedir"
"github.com/scylladb/go-set/strset"
"github.com/anchore/clio"
"github.com/anchore/fangs"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/pkg/cataloger"
golangCataloger "github.com/anchore/syft/syft/pkg/cataloger/golang"
@@ -147,8 +147,8 @@ func (cfg Catalog) ToCatalogerConfig() cataloger.Config {
var validDefaultSourceValues = []string{"registry", "docker", "podman", ""}
func checkDefaultSourceValues(source string) error {
validValues := internal.NewStringSet(validDefaultSourceValues...)
if !validValues.Contains(source) {
validValues := strset.New(validDefaultSourceValues...)
if !validValues.Has(source) {
validValuesString := strings.Join(validDefaultSourceValues, ", ")
return fmt.Errorf("%s is not a valid default source; please use one of the following: %s''", source, validValuesString)
}
+5 -5
View File
@@ -9,7 +9,7 @@ require (
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/acobaugh/osrelease v0.1.0
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461
github.com/anchore/clio v0.0.0-20230823172630-c42d666061af
github.com/anchore/clio v0.0.0-20231016125544-c98a83e1c7fc
github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
@@ -22,7 +22,7 @@ require (
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/bmatcuk/doublestar/v4 v4.6.0
github.com/charmbracelet/bubbletea v0.24.2
github.com/charmbracelet/lipgloss v0.8.0
github.com/charmbracelet/lipgloss v0.9.1
github.com/dave/jennifer v1.7.0
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
github.com/docker/distribution v2.8.3+incompatible // indirect
@@ -34,7 +34,7 @@ require (
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.9.0
github.com/go-test/deep v1.1.0
github.com/google/go-cmp v0.5.9
github.com/google/go-cmp v0.6.0
github.com/google/go-containerregistry v0.16.1
github.com/google/licensecheck v0.3.1
github.com/google/uuid v1.3.1
@@ -70,7 +70,7 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
golang.org/x/mod v0.13.0
golang.org/x/net v0.16.0
golang.org/x/net v0.17.0
golang.org/x/term v0.13.0 // indirect
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.26.0
@@ -153,7 +153,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
+10 -9
View File
@@ -96,8 +96,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461 h1:xGu4/uMWucwWV0YV3fpFIQZ6KVfS/Wfhmma8t0s0vRo=
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461/go.mod h1:Ger02eh5NpPm2IqkPAy396HU1KlK3BhOeCljDYXySSk=
github.com/anchore/clio v0.0.0-20230823172630-c42d666061af h1:dBVKZyMZeA0oZK0+aCCRoqxhxUvx/7xy/VEaLMMMnb0=
github.com/anchore/clio v0.0.0-20230823172630-c42d666061af/go.mod h1:XryJ3CIF1T7SbacQV+OPykfKKIbfXnBssYfpjy2peUg=
github.com/anchore/clio v0.0.0-20231016125544-c98a83e1c7fc h1:A1KFO+zZZmbNlz1+WKsCF0RKVx6XRoxsAG3lrqH9hUQ=
github.com/anchore/clio v0.0.0-20231016125544-c98a83e1c7fc/go.mod h1:QeWvNzxsrUNxcs6haQo3OtISfXUXW0qAuiG4EQiz0GU=
github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe h1:pVpLCGWdNeskAw7vGNdCAcGMezrNljHIqOc9HaOja5M=
github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe/go.mod h1:82EGoxZTfBXSW0/zollEP+Qs3wkiKmip5yBT5j+eZpY=
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a h1:nJ2G8zWKASyVClGVgG7sfM5mwoZlZ2zYpIzN2OhjWkw=
@@ -156,8 +156,8 @@ github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
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.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU=
github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU=
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@@ -366,8 +366,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ=
github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -533,8 +534,8 @@ github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZ
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -935,8 +936,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+1 -1
View File
@@ -3,5 +3,5 @@ package internal
const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "11.0.0"
JSONSchemaVersion = "11.0.1"
)
+4 -3
View File
@@ -6,7 +6,8 @@ import (
"sort"
"strings"
"github.com/anchore/syft/internal"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal/log"
)
@@ -40,7 +41,7 @@ func (z ZipFileManifest) Add(entry string, info os.FileInfo) {
// GlobMatch returns the path keys that match the given value(s).
func (z ZipFileManifest) GlobMatch(patterns ...string) []string {
uniqueMatches := internal.NewStringSet()
uniqueMatches := strset.New()
for _, pattern := range patterns {
for entry := range z {
@@ -54,7 +55,7 @@ func (z ZipFileManifest) GlobMatch(patterns ...string) []string {
}
}
results := uniqueMatches.ToSlice()
results := uniqueMatches.List()
sort.Strings(results)
return results
+9 -8
View File
@@ -1,23 +1,26 @@
package licenses
import "github.com/anchore/syft/internal"
// all of these taken from https://github.com/golang/pkgsite/blob/8996ff632abee854aef1b764ca0501f262f8f523/internal/licenses/licenses.go#L338
// which unfortunately is not exported. But fortunately is under BSD-style license.
// which unfortunately is not exported. But fortunately is under BSD-style license. Take note that this list has
// been manually updated to include more license filenames (see https://github.com/anchore/syft/pull/2227).
var (
FileNames = []string{
func FileNames() []string {
return []string{
"AL2.0",
"COPYING",
"COPYING.md",
"COPYING.markdown",
"COPYING.txt",
"LGPL2.1",
"LICENCE",
"LICENCE.md",
"LICENCE.markdown",
"licence.txt",
"LICENCE.txt",
"LICENSE",
"LICENSE.md",
"LICENSE.markdown",
"license.txt",
"LICENSE.txt",
"LICENSE-2.0.txt",
"LICENCE-2.0.txt",
@@ -48,6 +51,4 @@ var (
"UNLICENSE",
"UNLICENCE",
}
FileNameSet = internal.NewStringSet(FileNames...)
)
}
File diff suppressed because it is too large Load Diff
@@ -95,7 +95,7 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression
// singular expression case
// only ID field here since we guarantee that the license is valid
if value, exists := spdxlicense.ID(l.SPDXExpression); exists {
if !l.URLs.Empty() {
if len(l.URLs) > 0 {
processLicenseURLs(l, value, &spdxc)
continue
}
@@ -123,7 +123,7 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression
// license string that are not valid spdx expressions or ids
// we only use license Name here since we cannot guarantee that the license is a valid SPDX expression
if !l.URLs.Empty() {
if len(l.URLs) > 0 {
processLicenseURLs(l, "", &otherc)
continue
}
@@ -137,7 +137,7 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression
}
func processLicenseURLs(l pkg.License, spdxID string, populate *cyclonedx.Licenses) {
for _, url := range l.URLs.ToSlice() {
for _, url := range l.URLs {
if spdxID == "" {
*populate = append(*populate, cyclonedx.LicenseChoice{
License: &cyclonedx.License{
@@ -4,9 +4,9 @@ import (
"testing"
"github.com/CycloneDX/cyclonedx-go"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/pkg"
)
@@ -191,7 +191,9 @@ func Test_encodeLicense(t *testing.T) {
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
assert.Equal(t, test.expected, encodeLicenses(test.input))
if d := cmp.Diff(test.expected, encodeLicenses(test.input)); d != "" {
t.Errorf("unexpected license (-want +got):\n%s", d)
}
})
}
}
@@ -223,7 +225,7 @@ func TestDecodeLicenses(t *testing.T) {
Value: "RandomLicense",
// CycloneDX specification doesn't give a field for determining the license type
Type: license.Declared,
URLs: internal.NewStringSet(),
URLs: []string{},
},
},
},
@@ -243,7 +245,7 @@ func TestDecodeLicenses(t *testing.T) {
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: internal.NewStringSet(),
URLs: []string{},
},
},
},
@@ -262,7 +264,7 @@ func TestDecodeLicenses(t *testing.T) {
Value: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0",
SPDXExpression: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0",
Type: license.Declared,
URLs: internal.NewStringSet(),
URLs: []string{},
},
},
},
@@ -20,6 +20,8 @@ func DownloadLocation(p pkg.Package) string {
return NoneIfEmpty(metadata.URL)
case pkg.NpmPackageJSONMetadata:
return NoneIfEmpty(metadata.URL)
case pkg.NpmPackageLockJSONMetadata:
return NoneIfEmpty(metadata.Resolved)
}
}
return NOASSERTION
@@ -46,6 +46,24 @@ func Test_DownloadLocation(t *testing.T) {
},
expected: NONE,
},
{
name: "from npm package-lock should include resolved",
input: pkg.Package{
Metadata: pkg.NpmPackageLockJSONMetadata{
Resolved: "http://package-lock.test",
},
},
expected: "http://package-lock.test",
},
{
name: "from npm package-lock empty should be NONE",
input: pkg.Package{
Metadata: pkg.NpmPackageLockJSONMetadata{
Resolved: "",
},
},
expected: NONE,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
+1 -1
View File
@@ -57,7 +57,7 @@ func redactor(values ...string) testutils.Redactor {
`sha256:[A-Za-z0-9]{64}`: `sha256:redacted`,
// BOM refs
`bom-ref="[a-zA-Z0-9\-:]+"`: `bom-ref:redacted`,
`bom-ref="[a-zA-Z0-9\-:]+"`: `bom-ref="redacted"`,
},
)
}
@@ -32,7 +32,7 @@
<property name="syft:location:0:path">/some/path/pkg1</property>
</properties>
</component>
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=db4abfe497c180d3" type="library">
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=ad5013466727018f" type="library">
<name>package-2</name>
<version>2.0.1</version>
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
@@ -34,7 +34,7 @@
<property name="syft:location:0:path">/somefile-1.txt</property>
</properties>
</component>
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=958443e2d9304af4" type="library">
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=f27313b22a5ba330" type="library">
<name>package-2</name>
<version>2.0.1</version>
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
@@ -39,7 +39,7 @@
},
{
"name": "package-2",
"SPDXID": "SPDXRef-Package-deb-package-2-db4abfe497c180d3",
"SPDXID": "SPDXRef-Package-deb-package-2-ad5013466727018f",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -78,7 +78,7 @@
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-db4abfe497c180d3",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-ad5013466727018f",
"relationshipType": "CONTAINS"
},
{
@@ -39,7 +39,7 @@
},
{
"name": "package-2",
"SPDXID": "SPDXRef-Package-deb-package-2-958443e2d9304af4",
"SPDXID": "SPDXRef-Package-deb-package-2-f27313b22a5ba330",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -92,7 +92,7 @@
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-958443e2d9304af4",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-f27313b22a5ba330",
"relationshipType": "CONTAINS"
},
{
@@ -39,7 +39,7 @@
},
{
"name": "package-2",
"SPDXID": "SPDXRef-Package-deb-package-2-958443e2d9304af4",
"SPDXID": "SPDXRef-Package-deb-package-2-f27313b22a5ba330",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -214,7 +214,7 @@
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-958443e2d9304af4",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-f27313b22a5ba330",
"relationshipType": "CONTAINS"
},
{
@@ -61,7 +61,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951
##### Package: package-2
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-958443e2d9304af4
SPDXID: SPDXRef-Package-deb-package-2-f27313b22a5ba330
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -97,6 +97,6 @@ Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef
Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f
Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-125840abc1c66dd7
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-958443e2d9304af4
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-f27313b22a5ba330
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
@@ -20,7 +20,7 @@ FilesAnalyzed: false
##### Package: package-2
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-db4abfe497c180d3
SPDXID: SPDXRef-Package-deb-package-2-ad5013466727018f
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -50,6 +50,6 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-2
##### Relationships
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-9265397e5e15168a
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-db4abfe497c180d3
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-ad5013466727018f
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-some-path
@@ -23,7 +23,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951
##### Package: package-2
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-958443e2d9304af4
SPDXID: SPDXRef-Package-deb-package-2-f27313b22a5ba330
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -53,6 +53,6 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
##### Relationships
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-125840abc1c66dd7
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-958443e2d9304af4
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-f27313b22a5ba330
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
+1 -2
View File
@@ -12,7 +12,6 @@ import (
"github.com/stretchr/testify/require"
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/formats/internal/testutils"
@@ -118,7 +117,7 @@ func Test_encodeDecodeFileMetadata(t *testing.T) {
Value: "MIT",
SPDXExpression: "MIT",
Type: "MIT",
URLs: internal.NewStringSet("https://example.org/license"),
URLs: []string{"https://example.org/license"},
Locations: file.LocationSet{},
}),
Language: "language",
@@ -41,7 +41,7 @@
}
},
{
"id": "db4abfe497c180d3",
"id": "ad5013466727018f",
"name": "package-2",
"version": "2.0.1",
"type": "deb",
@@ -36,7 +36,7 @@
}
},
{
"id": "9fd0b9f41034991d",
"id": "aa0ca2c331576dfd",
"name": "package-2",
"version": "2.0.1",
"type": "deb",
@@ -37,7 +37,7 @@
}
},
{
"id": "958443e2d9304af4",
"id": "f27313b22a5ba330",
"name": "package-2",
"version": "2.0.1",
"type": "deb",
+8 -1
View File
@@ -214,11 +214,18 @@ func toLicenseModel(pkgLicenses []pkg.License) (modelLicenses []model.License) {
if v := l.Locations.ToSlice(); v != nil {
locations = v
}
// format model must have allocated collections
urls := l.URLs
if urls == nil {
urls = []string{}
}
modelLicenses = append(modelLicenses, model.License{
Value: l.Value,
SPDXExpression: l.SPDXExpression,
Type: l.Type,
URLs: l.URLs.ToSlice(),
URLs: urls,
Locations: locations,
})
}
+1 -2
View File
@@ -10,7 +10,6 @@ import (
"github.com/google/go-cmp/cmp"
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/cpe"
@@ -136,7 +135,7 @@ func toSyftLicenses(m []model.License) (p []pkg.License) {
Value: l.Value,
SPDXExpression: l.SPDXExpression,
Type: l.Type,
URLs: internal.NewStringSet(l.URLs...),
URLs: l.URLs,
Locations: file.NewLocationSet(l.Locations...),
})
}
+7 -7
View File
@@ -4,8 +4,8 @@ import (
"sync"
"github.com/jinzhu/copier"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
)
@@ -124,13 +124,13 @@ func (c *Collection) addTypeToIndex(p Package) {
}
func (c *Collection) addPathsToIndex(p Package) {
observedPaths := internal.NewStringSet()
observedPaths := strset.New()
for _, l := range p.Locations.ToSlice() {
if l.RealPath != "" && !observedPaths.Contains(l.RealPath) {
if l.RealPath != "" && !observedPaths.Has(l.RealPath) {
c.addPathToIndex(p.id, l.RealPath)
observedPaths.Add(l.RealPath)
}
if l.VirtualPath != "" && l.RealPath != l.VirtualPath && !observedPaths.Contains(l.VirtualPath) {
if l.VirtualPath != "" && l.RealPath != l.VirtualPath && !observedPaths.Has(l.VirtualPath) {
c.addPathToIndex(p.id, l.VirtualPath)
observedPaths.Add(l.VirtualPath)
}
@@ -173,13 +173,13 @@ func (c *Collection) deleteTypeFromIndex(p Package) {
}
func (c *Collection) deletePathsFromIndex(p Package) {
observedPaths := internal.NewStringSet()
observedPaths := strset.New()
for _, l := range p.Locations.ToSlice() {
if l.RealPath != "" && !observedPaths.Contains(l.RealPath) {
if l.RealPath != "" && !observedPaths.Has(l.RealPath) {
c.deletePathFromIndex(p.id, l.RealPath)
observedPaths.Add(l.RealPath)
}
if l.VirtualPath != "" && l.RealPath != l.VirtualPath && !observedPaths.Contains(l.VirtualPath) {
if l.VirtualPath != "" && l.RealPath != l.VirtualPath && !observedPaths.Has(l.VirtualPath) {
c.deletePathFromIndex(p.id, l.VirtualPath)
observedPaths.Add(l.VirtualPath)
}
+2 -3
View File
@@ -13,7 +13,6 @@ import (
"github.com/facebookincubator/nvdtools/wfn"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
@@ -118,13 +117,13 @@ func Generate(p pkg.Package) []cpe.CPE {
return nil
}
keys := internal.NewStringSet()
keys := strset.New()
cpes := make([]cpe.CPE, 0)
for _, product := range products {
for _, vendor := range vendors {
// prevent duplicate entries...
key := fmt.Sprintf("%s|%s|%s", product, vendor, p.Version)
if keys.Contains(key) {
if keys.Has(key) {
continue
}
keys.Add(key)
+6
View File
@@ -44,6 +44,11 @@ func TestDpkgCataloger(t *testing.T) {
Contains configuration files and directories required for
authentication to work on Debian systems. This package is required
on almost all installations.`,
Depends: []string{
"debconf (>= 0.5) | debconf-2.0",
"debconf (>= 1.5.19) | cdebconf",
"libpam-modules (>= 1.0.1-6)",
},
Files: []pkg.DpkgFileRecord{
{
Path: "/etc/pam.conf",
@@ -112,6 +117,7 @@ func TestDpkgCataloger(t *testing.T) {
SQLite is a C library that implements an SQL database engine.
Programs that link with the SQLite library can have SQL database
access without running a separate RDBMS process.`,
Depends: []string{"libc6 (>= 2.29)"},
Files: []pkg.DpkgFileRecord{
{Path: "/usr/lib/aarch64-linux-gnu/libsqlite3.so.0.8.6", Digest: &file.Digest{
Algorithm: "md5",
+8 -6
View File
@@ -36,13 +36,15 @@ func newDpkgPackage(d pkg.DpkgMetadata, dbLocation file.Location, resolver file.
Metadata: d,
}
// the current entry only has what may have been listed in the status file, however, there are additional
// files that are listed in multiple other locations. We should retrieve them all and merge the file lists
// together.
mergeFileListing(resolver, dbLocation, &p)
if resolver != nil {
// the current entry only has what may have been listed in the status file, however, there are additional
// files that are listed in multiple other locations. We should retrieve them all and merge the file lists
// together.
mergeFileListing(resolver, dbLocation, &p)
// fetch additional data from the copyright file to derive the license information
addLicenses(resolver, dbLocation, &p)
// fetch additional data from the copyright file to derive the license information
addLicenses(resolver, dbLocation, &p)
}
p.SetID()
+4 -2
View File
@@ -7,6 +7,8 @@ import (
"sort"
"strings"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal"
)
@@ -18,7 +20,7 @@ var (
)
func parseLicensesFromCopyright(reader io.Reader) []string {
findings := internal.NewStringSet()
findings := strset.New()
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
@@ -31,7 +33,7 @@ func parseLicensesFromCopyright(reader io.Reader) []string {
}
}
results := findings.ToSlice()
results := findings.List()
sort.Strings(results)
+124 -7
View File
@@ -35,7 +35,7 @@ func parseDpkgDB(resolver file.Resolver, env *generic.Environment, reader file.L
pkgs = append(pkgs, newDpkgPackage(m, reader.Location, resolver, env.LinuxRelease))
}
return pkgs, nil, nil
return pkgs, associateRelationships(pkgs), nil
}
// parseDpkgStatus is a parser function for Debian DB status contents, returning all Debian packages listed.
@@ -63,6 +63,22 @@ func parseDpkgStatus(reader io.Reader) ([]pkg.DpkgMetadata, error) {
return metadata, nil
}
// dpkgExtractedMetadata is an adapter struct to capture the fields from the dpkg status file, however, the final
// pkg.DpkgMetadata struct has different types for some fields (e.g. Provides, Depends, and PreDepends is []string, not a string).
type dpkgExtractedMetadata struct {
Package string `mapstructure:"Package"`
Source string `mapstructure:"Source"`
Version string `mapstructure:"Version"`
SourceVersion string `mapstructure:"SourceVersion"`
Architecture string `mapstructure:"Architecture"`
Maintainer string `mapstructure:"Maintainer"`
InstalledSize int `mapstructure:"InstalledSize"`
Description string `mapstructure:"Description"`
Provides string `mapstructure:"Provides"`
Depends string `mapstructure:"Depends"`
PreDepends string `mapstructure:"PreDepends"` // note: original doc is Pre-Depends
}
// parseDpkgStatusEntry returns an individual Dpkg entry, or returns errEndOfPackages if there are no more packages to parse from the reader.
func parseDpkgStatusEntry(reader *bufio.Reader) (*pkg.DpkgMetadata, error) {
var retErr error
@@ -77,22 +93,36 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (*pkg.DpkgMetadata, error) {
retErr = err
}
entry := pkg.DpkgMetadata{}
err = mapstructure.Decode(dpkgFields, &entry)
raw := dpkgExtractedMetadata{}
err = mapstructure.Decode(dpkgFields, &raw)
if err != nil {
return nil, err
}
sourceName, sourceVersion := extractSourceVersion(entry.Source)
sourceName, sourceVersion := extractSourceVersion(raw.Source)
if sourceVersion != "" {
entry.SourceVersion = sourceVersion
entry.Source = sourceName
raw.SourceVersion = sourceVersion
raw.Source = sourceName
}
if entry.Package == "" {
if raw.Package == "" {
return nil, retErr
}
entry := pkg.DpkgMetadata{
Package: raw.Package,
Source: raw.Source,
Version: raw.Version,
SourceVersion: raw.SourceVersion,
Architecture: raw.Architecture,
Maintainer: raw.Maintainer,
InstalledSize: raw.InstalledSize,
Description: raw.Description,
Provides: splitPkgList(raw.Provides),
Depends: splitPkgList(raw.Depends),
PreDepends: splitPkgList(raw.PreDepends),
}
// there may be an optional conffiles section that we should persist as files
if conffilesSection, exists := dpkgFields["Conffiles"]; exists && conffilesSection != nil {
if sectionStr, ok := conffilesSection.(string); ok {
@@ -108,6 +138,17 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (*pkg.DpkgMetadata, error) {
return &entry, retErr
}
func splitPkgList(pkgList string) (ret []string) {
fields := strings.Split(pkgList, ",")
for _, field := range fields {
field = strings.TrimSpace(field)
if field != "" {
ret = append(ret, field)
}
}
return ret
}
func extractAllFields(reader *bufio.Reader) (map[string]interface{}, error) {
dpkgFields := make(map[string]interface{})
var key string
@@ -195,3 +236,79 @@ func handleNewKeyValue(line string) (key string, val interface{}, err error) {
return "", nil, fmt.Errorf("cannot parse field from line: '%s'", line)
}
// associateRelationships will create relationships between packages based on the "Depends", "Pre-Depends", and "Provides"
// fields for installed packages. if there is an installed package that has a dependency that is (somehow) not installed,
// then that relationship (between the installed and uninstalled package) will NOT be created.
func associateRelationships(pkgs []pkg.Package) (relationships []artifact.Relationship) {
// map["provides" + "package"] -> packages that provide that package
lookup := make(map[string][]pkg.Package)
// read provided and add as keys for lookup keys as well as package names
for _, p := range pkgs {
meta, ok := p.Metadata.(pkg.DpkgMetadata)
if !ok {
log.Warnf("cataloger failed to extract dpkg 'provides' metadata for package %+v", p.Name)
continue
}
lookup[p.Name] = append(lookup[p.Name], p)
for _, provides := range meta.Provides {
k := stripVersionSpecifier(provides)
lookup[k] = append(lookup[k], p)
}
}
// read "Depends" and "Pre-Depends" and match with keys
for _, p := range pkgs {
meta, ok := p.Metadata.(pkg.DpkgMetadata)
if !ok {
log.Warnf("cataloger failed to extract dpkg 'dependency' metadata for package %+v", p.Name)
continue
}
var allDeps []string
allDeps = append(allDeps, meta.Depends...)
allDeps = append(allDeps, meta.PreDepends...)
for _, depSpecifier := range allDeps {
deps := splitPackageChoice(depSpecifier)
for _, dep := range deps {
for _, depPkg := range lookup[dep] {
relationships = append(relationships, artifact.Relationship{
From: depPkg,
To: p,
Type: artifact.DependencyOfRelationship,
})
}
}
}
}
return relationships
}
func stripVersionSpecifier(s string) string {
// examples:
// libgmp10 (>= 2:6.2.1+dfsg1) --> libgmp10
// libgmp10 --> libgmp10
// foo [i386] --> foo
// default-mta | mail-transport-agent --> default-mta | mail-transport-agent
// kernel-headers-2.2.10 [!hurd-i386] --> kernel-headers-2.2.10
items := internal.SplitAny(s, "[(<>=")
if len(items) == 0 {
return s
}
return strings.TrimSpace(items[0])
}
func splitPackageChoice(s string) (ret []string) {
fields := strings.Split(s, "|")
for _, field := range fields {
field = strings.TrimSpace(field)
if field != "" {
ret = append(ret, stripVersionSpecifier(field))
}
}
return ret
}
+156 -1
View File
@@ -11,9 +11,11 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
@@ -48,6 +50,18 @@ func Test_parseDpkgStatus(t *testing.T) {
* apt-cdrom to use removable media as a source for packages
* apt-config as an interface to the configuration settings
* apt-key as an interface to manage authentication keys`,
Provides: []string{"apt-transport-https (= 1.8.2)"},
Depends: []string{
"adduser",
"gpgv | gpgv2 | gpgv1",
"debian-archive-keyring",
"libapt-pkg5.0 (>= 1.7.0~alpha3~)",
"libc6 (>= 2.15)",
"libgcc1 (>= 1:3.0)",
"libgnutls30 (>= 3.6.6)",
"libseccomp2 (>= 1.0.1)",
"libstdc++6 (>= 5.2)",
},
Files: []pkg.DpkgFileRecord{
{
Path: "/etc/apt/apt.conf.d/01autoremove",
@@ -110,6 +124,18 @@ func Test_parseDpkgStatus(t *testing.T) {
* apt-cdrom to use removable media as a source for packages
* apt-config as an interface to the configuration settings
* apt-key as an interface to manage authentication keys`,
Provides: []string{"apt-transport-https (= 1.8.2)"},
Depends: []string{
"adduser",
"gpgv | gpgv2 | gpgv1",
"debian-archive-keyring",
"libapt-pkg5.0 (>= 1.7.0~alpha3~)",
"libc6 (>= 2.15)",
"libgcc1 (>= 1:3.0)",
"libgnutls30 (>= 3.6.6)",
"libseccomp2 (>= 1.0.1)",
"libstdc++6 (>= 5.2)",
},
Files: []pkg.DpkgFileRecord{},
},
},
@@ -135,7 +161,9 @@ func Test_parseDpkgStatus(t *testing.T) {
globe. It is updated periodically to reflect changes made by
political bodies to time zone boundaries, UTC offsets, and
daylight-saving rules.`,
Files: []pkg.DpkgFileRecord{},
Provides: []string{"tzdata-buster"},
Depends: []string{"debconf (>= 0.5) | debconf-2.0"},
Files: []pkg.DpkgFileRecord{},
},
{
Package: "util-linux",
@@ -149,6 +177,14 @@ func Test_parseDpkgStatus(t *testing.T) {
important utilities included in this package allow you to view kernel
messages, create new filesystems, view block device information,
interface with real time clock, etc.`,
Depends: []string{"fdisk", "login (>= 1:4.5-1.1~)"},
PreDepends: []string{
"libaudit1 (>= 1:2.2.1)", "libblkid1 (>= 2.31.1)", "libc6 (>= 2.25)",
"libcap-ng0 (>= 0.7.9)", "libmount1 (>= 2.25)", "libpam0g (>= 0.99.7.1)",
"libselinux1 (>= 2.6-3~)", "libsmartcols1 (>= 2.33)", "libsystemd0",
"libtinfo6 (>= 6)", "libudev1 (>= 183)", "libuuid1 (>= 2.16)",
"zlib1g (>= 1:1.1.4)",
},
Files: []pkg.DpkgFileRecord{
{
Path: "/etc/default/hwclock",
@@ -397,3 +433,122 @@ func Test_handleNewKeyValue(t *testing.T) {
})
}
}
func Test_stripVersionSpecifier(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{
name: "package name only",
input: "test",
want: "test",
},
{
name: "with version",
input: "test (1.2.3)",
want: "test",
},
{
name: "multiple packages",
input: "test | other",
want: "test | other",
},
{
name: "with architecture specifiers",
input: "test [amd64 i386]",
want: "test",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, stripVersionSpecifier(tt.input))
})
}
}
func Test_associateRelationships(t *testing.T) {
tests := []struct {
name string
fixture string
wantRelationships map[string][]string
}{
{
name: "relationships for coreutils",
fixture: "test-fixtures/status/coreutils-relationships",
wantRelationships: map[string][]string{
"coreutils": {"libacl1", "libattr1", "libc6", "libgmp10", "libselinux1"},
"libacl1": {"libc6"},
"libattr1": {"libc6"},
"libc6": {"libgcc-s1"},
"libgcc-s1": {"gcc-12-base", "libc6"},
"libgmp10": {"libc6"},
"libpcre2-8-0": {"libc6"},
"libselinux1": {"libc6", "libpcre2-8-0"},
},
},
{
name: "relationships from dpkg example docs",
fixture: "test-fixtures/status/doc-examples",
wantRelationships: map[string][]string{
"made-up-package-1": {"kernel-headers-2.2.10", "hurd-dev", "gnumach-dev"},
"made-up-package-2": {"libluajit5.1-dev", "liblua5.1-dev"},
"made-up-package-3": {"foo", "bar"},
// note that the "made-up-package-4" depends on "made-up-package-5" but not via the direct
// package name, but through the "provides" virtual package name "virtual-package-5".
"made-up-package-4": {"made-up-package-5"},
// note that though there is a "default-mta | mail-transport-agent | not-installed"
// dependency choice we raise up the packages that are installed for every choice.
// In this case that means that "default-mta" and "mail-transport-agent".
"mutt": {"libc6", "default-mta", "mail-transport-agent"},
},
},
{
name: "relationships for libpam-runtime",
fixture: "test-fixtures/status/libpam-runtime",
wantRelationships: map[string][]string{
"libpam-runtime": {"debconf1", "debconf-2.0", "debconf2", "cdebconf", "libpam-modules"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, err := os.Open(tt.fixture)
require.NoError(t, err)
reader := file.NewLocationReadCloser(file.NewLocation(tt.fixture), f)
pkgs, relationships, err := parseDpkgDB(nil, &generic.Environment{}, reader)
require.NotEmpty(t, pkgs)
require.NotEmpty(t, relationships)
require.NoError(t, err)
if d := cmp.Diff(tt.wantRelationships, abstractRelationships(t, relationships)); d != "" {
t.Errorf("unexpected relationships (-want +got):\n%s", d)
}
})
}
}
func abstractRelationships(t testing.TB, relationships []artifact.Relationship) map[string][]string {
t.Helper()
abstracted := make(map[string][]string)
for _, relationship := range relationships {
fromPkg, ok := relationship.From.(pkg.Package)
if !ok {
continue
}
toPkg, ok := relationship.To.(pkg.Package)
if !ok {
continue
}
// we build this backwards since we use DependencyOfRelationship instead of DependsOn
abstracted[toPkg.Name] = append(abstracted[toPkg.Name], fromPkg.Name)
}
return abstracted
}
@@ -0,0 +1,114 @@
Package: coreutils
Essential: yes
Status: install ok installed
Priority: required
Section: utils
Installed-Size: 20272
Maintainer: Michael Stone <mstone@debian.org>
Architecture: arm64
Multi-Arch: foreign
Version: 9.1-1
Pre-Depends: libacl1 (>= 2.2.23), libattr1 (>= 1:2.4.44), libc6 (>= 2.34), libgmp10 (>= 2:6.2.1+dfsg1), libselinux1 (>= 3.1~)
Package: libacl1
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 101
Maintainer: Guillem Jover <guillem@debian.org>
Architecture: arm64
Multi-Arch: same
Source: acl
Version: 2.3.1-3
Depends: libc6 (>= 2.33)
Package: libc6
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 23127
Maintainer: GNU Libc Maintainers <debian-glibc@lists.debian.org>
Architecture: arm64
Multi-Arch: same
Source: glibc
Version: 2.36-9+deb12u1
Depends: libgcc-s1
Recommends: libidn2-0 (>= 2.0.5~)
Suggests: glibc-doc, debconf | debconf-2.0, libc-l10n, locales, libnss-nis, libnss-nisplus
Breaks: aide (<< 0.17.3-4+b3), busybox (<< 1.30.1-6), chrony (<< 4.2-3~), fakechroot (<< 2.19-3.5), firefox (<< 91~), firefox-esr (<< 91~), gnumach-image-1.8-486 (<< 2:1.8+git20210923~), gnumach-image-1.8-486-dbg (<< 2:1.8+git20210923~), gnumach-image-1.8-xen-486 (<< 2:1.8+git20210923~), gnumach-image-1.8-xen-486-dbg (<< 2:1.8+git20210923~), hurd (<< 1:0.9.git20220301-2), ioquake3 (<< 1.36+u20200211.f2c61c1~dfsg-2~), iraf-fitsutil (<< 2018.07.06-4), libgegl-0.4-0 (<< 0.4.18), libtirpc1 (<< 0.2.3), locales (<< 2.36), locales-all (<< 2.36), macs (<< 2.2.7.1-3~), nocache (<< 1.1-1~), nscd (<< 2.36), openarena (<< 0.8.8+dfsg-4~), openssh-server (<< 1:8.1p1-5), python3-iptables (<< 1.0.0-2), r-cran-later (<< 0.7.5+dfsg-2), tinydns (<< 1:1.05-14), valgrind (<< 1:3.19.0-1~), wcc (<< 0.0.2+dfsg-3)
Package: libgcc-s1
Protected: yes
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 147
Maintainer: Debian GCC Maintainers <debian-gcc@lists.debian.org>
Architecture: arm64
Multi-Arch: same
Source: gcc-12
Version: 12.2.0-14
Replaces: libgcc1 (<< 1:10)
Provides: libgcc1 (= 1:12.2.0-14)
Depends: gcc-12-base (= 12.2.0-14), libc6 (>= 2.35)
Package: gcc-12-base
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 100
Maintainer: Debian GCC Maintainers <debian-gcc@lists.debian.org>
Architecture: arm64
Multi-Arch: same
Source: gcc-12
Version: 12.2.0-14
Breaks: gnat (<< 7)
Package: libattr1
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 99
Maintainer: Guillem Jover <guillem@debian.org>
Architecture: arm64
Multi-Arch: same
Source: attr
Version: 1:2.5.1-4
Depends: libc6 (>= 2.17)
Package: libgmp10
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 855
Maintainer: Debian Science Team <debian-science-maintainers@lists.alioth.debian.org>
Architecture: arm64
Multi-Arch: same
Source: gmp
Version: 2:6.2.1+dfsg1-1.1
Depends: libc6 (>= 2.17)
Breaks: libmath-gmp-perl (<< 2.20-1), libmath-prime-util-gmp-perl (<< 0.51-2), postgresql-pgmp (<< 1.0.3-1)
Package: libselinux1
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 223
Maintainer: Debian SELinux maintainers <selinux-devel@lists.alioth.debian.org>
Architecture: arm64
Multi-Arch: same
Source: libselinux (3.4-1)
Version: 3.4-1+b6
Depends: libc6 (>= 2.34), libpcre2-8-0 (>= 10.22)
Package: libpcre2-8-0
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 649
Maintainer: Matthew Vernon <matthew@debian.org>
Architecture: arm64
Multi-Arch: same
Source: pcre2
Version: 10.42-1
Depends: libc6 (>= 2.34)
@@ -0,0 +1,46 @@
Package: mutt
Version: 1.3.17-1
Depends: libc6 (>= 2.2.1), default-mta | mail-transport-agent | not-installed
Package: made-up-package-1
Version: 1.0.0
Source: glibc
Depends: kernel-headers-2.2.10 [!hurd-i386],
hurd-dev [hurd-i386], gnumach-dev [hurd-i386]
Package: made-up-package-2
Version: 2.0.0
Depends:
libluajit5.1-dev [i386 amd64 kfreebsd-i386 armel armhf powerpc mips],
liblua5.1-dev [hurd-i386 ia64 kfreebsd-amd64 s390x sparc],
Package: made-up-package-3
Version: 3.0.0
Depends: foo [i386], bar [amd64]
Package: made-up-package-4
Version: 3.0.0
Depends: virtual-package-5
Package: made-up-package-5
Provides: virtual-package-5 (=1.0)
Package: foo
Package: bar
Package: kernel-headers-2.2.10
Package: hurd-dev
Package: gnumach-dev
Package: default-mta
Package: mail-transport-agent
Package: libc6
Package: libluajit5.1-dev
Package: liblua5.1-dev
@@ -0,0 +1,23 @@
Package: libpam-runtime
Status: install ok installed
Priority: required
Section: admin
Installed-Size: 876
Maintainer: Sam Hartman <hartmans@debian.org>
Architecture: all
Multi-Arch: foreign
Source: pam
Version: 1.5.2-6+deb12u1
Replaces: libpam0g-dev, libpam0g-util
Depends: debconf1 (>= 0.5) | debconf-2.0, debconf2 (>= 1.5.19) | cdebconf, libpam-modules (>= 1.0.1-6)
Conflicts: libpam0g-util
Package: debconf1
Package: debconf2
Package: debconf-2.0
Package: cdebconf
Package: libpam-modules
+24 -7
View File
@@ -1,6 +1,8 @@
package dotnet
import (
"fmt"
"regexp"
"strings"
"github.com/anchore/packageurl-go"
@@ -9,13 +11,7 @@ import (
)
func newDotnetDepsPackage(nameVersion string, lib dotnetDepsLibrary, locations ...file.Location) *pkg.Package {
if lib.Type != "package" {
return nil
}
fields := strings.Split(nameVersion, "/")
name := fields[0]
version := fields[1]
name, version := extractNameAndVersion(nameVersion)
m := pkg.DotnetDepsMetadata{
Name: name,
@@ -41,6 +37,27 @@ func newDotnetDepsPackage(nameVersion string, lib dotnetDepsLibrary, locations .
return p
}
func getDepsJSONFilePrefix(p string) string {
r := regexp.MustCompile(`([^\/]+)\.deps\.json$`)
match := r.FindStringSubmatch(p)
if len(match) > 1 {
return match[1]
}
return ""
}
func extractNameAndVersion(nameVersion string) (name, version string) {
fields := strings.Split(nameVersion, "/")
name = fields[0]
version = fields[1]
return
}
func createNameAndVersion(name, version string) (nameVersion string) {
nameVersion = fmt.Sprintf("%s/%s", name, version)
return
}
func packageURL(m pkg.DotnetDepsMetadata) string {
var qualifiers packageurl.Qualifiers
+75 -8
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"sort"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -13,8 +14,18 @@ import (
var _ generic.Parser = parseDotnetDeps
type dotnetRuntimeTarget struct {
Name string `json:"name"`
}
type dotnetDepsTarget struct {
Dependencies map[string]string `json:"dependencies"`
Runtime map[string]struct{} `json:"runtime"`
}
type dotnetDeps struct {
Libraries map[string]dotnetDepsLibrary `json:"libraries"`
RuntimeTarget dotnetRuntimeTarget `json:"runtimeTarget"`
Targets map[string]map[string]dotnetDepsTarget `json:"targets"`
Libraries map[string]dotnetDepsLibrary `json:"libraries"`
}
type dotnetDepsLibrary struct {
@@ -24,27 +35,55 @@ type dotnetDepsLibrary struct {
HashPath string `json:"hashPath"`
}
//nolint:funlen
func parseDotnetDeps(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
var pkgs []pkg.Package
var pkgMap = make(map[string]pkg.Package)
var relationships []artifact.Relationship
dec := json.NewDecoder(reader)
var p dotnetDeps
if err := dec.Decode(&p); err != nil {
var depsDoc dotnetDeps
if err := dec.Decode(&depsDoc); err != nil {
return nil, nil, fmt.Errorf("failed to parse deps.json file: %w", err)
}
var names []string
rootName := getDepsJSONFilePrefix(reader.AccessPath())
if rootName == "" {
return nil, nil, fmt.Errorf("unable to determine root package name from deps.json file: %s", reader.AccessPath())
}
var rootPkg *pkg.Package
for nameVersion, lib := range depsDoc.Libraries {
name, _ := extractNameAndVersion(nameVersion)
if lib.Type == "project" && name == rootName {
rootPkg = newDotnetDepsPackage(
nameVersion,
lib,
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
)
}
}
if rootPkg == nil {
return nil, nil, fmt.Errorf("unable to determine root package from deps.json file: %s", reader.AccessPath())
}
pkgs = append(pkgs, *rootPkg)
pkgMap[createNameAndVersion(rootPkg.Name, rootPkg.Version)] = *rootPkg
for nameVersion := range p.Libraries {
var names []string
for nameVersion := range depsDoc.Libraries {
names = append(names, nameVersion)
}
// sort the names so that the order of the packages is deterministic
sort.Strings(names)
for _, nameVersion := range names {
lib := p.Libraries[nameVersion]
// skip the root package
name, version := extractNameAndVersion(nameVersion)
if name == rootPkg.Name && version == rootPkg.Version {
continue
}
lib := depsDoc.Libraries[nameVersion]
dotnetPkg := newDotnetDepsPackage(
nameVersion,
lib,
@@ -53,8 +92,36 @@ func parseDotnetDeps(_ file.Resolver, _ *generic.Environment, reader file.Locati
if dotnetPkg != nil {
pkgs = append(pkgs, *dotnetPkg)
pkgMap[nameVersion] = *dotnetPkg
}
}
return pkgs, nil, nil
for pkgNameVersion, target := range depsDoc.Targets[depsDoc.RuntimeTarget.Name] {
for depName, depVersion := range target.Dependencies {
depNameVersion := createNameAndVersion(depName, depVersion)
depPkg, ok := pkgMap[depNameVersion]
if !ok {
log.Debug("unable to find package in map", depNameVersion)
continue
}
p, ok := pkgMap[pkgNameVersion]
if !ok {
log.Debug("unable to find package in map", pkgNameVersion)
continue
}
rel := artifact.Relationship{
From: depPkg,
To: p,
Type: artifact.DependencyOfRelationship,
}
relationships = append(relationships, rel)
}
}
// sort the relationships for deterministic output
// TODO: ideally this would be replaced with artifact.SortRelationships when one exists and is type agnostic.
// this will only consider package-to-package relationships.
pkg.SortRelationships(relationships)
return pkgs, relationships, nil
}
@@ -12,202 +12,363 @@ import (
func TestParseDotnetDeps(t *testing.T) {
fixture := "test-fixtures/TestLibrary.deps.json"
fixtureLocationSet := file.NewLocationSet(file.NewLocation(fixture))
expected := []pkg.Package{
{
Name: "AWSSDK.Core",
Version: "3.7.10.6",
PURL: "pkg:nuget/AWSSDK.Core@3.7.10.6",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "AWSSDK.Core",
Version: "3.7.10.6",
Sha512: "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==",
Path: "awssdk.core/3.7.10.6",
HashPath: "awssdk.core.3.7.10.6.nupkg.sha512",
},
rootPkg := pkg.Package{
Name: "TestLibrary",
Version: "1.0.0",
PURL: "pkg:nuget/TestLibrary@1.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "TestLibrary",
Version: "1.0.0",
},
{
Name: "Microsoft.Extensions.DependencyInjection.Abstractions",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.DependencyInjection.Abstractions",
Version: "6.0.0",
Sha512: "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==",
Path: "microsoft.extensions.dependencyinjection.abstractions/6.0.0",
HashPath: "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512",
},
}
testCommon := pkg.Package{
Name: "TestCommon",
Version: "1.0.0",
PURL: "pkg:nuget/TestCommon@1.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "TestCommon",
Version: "1.0.0",
},
{
Name: "Microsoft.Extensions.DependencyInjection",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.DependencyInjection",
Version: "6.0.0",
Sha512: "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==",
Path: "microsoft.extensions.dependencyinjection/6.0.0",
HashPath: "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512",
},
}
awssdkcore := pkg.Package{
Name: "AWSSDK.Core",
Version: "3.7.10.6",
PURL: "pkg:nuget/AWSSDK.Core@3.7.10.6",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "AWSSDK.Core",
Version: "3.7.10.6",
Sha512: "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==",
Path: "awssdk.core/3.7.10.6",
HashPath: "awssdk.core.3.7.10.6.nupkg.sha512",
},
{
Name: "Microsoft.Extensions.Logging.Abstractions",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Logging.Abstractions",
Version: "6.0.0",
Sha512: "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==",
Path: "microsoft.extensions.logging.abstractions/6.0.0",
HashPath: "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512",
},
}
msftDependencyInjectionAbstractions := pkg.Package{
Name: "Microsoft.Extensions.DependencyInjection.Abstractions",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.DependencyInjection.Abstractions",
Version: "6.0.0",
Sha512: "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==",
Path: "microsoft.extensions.dependencyinjection.abstractions/6.0.0",
HashPath: "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512",
},
{
Name: "Microsoft.Extensions.Logging",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.Logging@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Logging",
Version: "6.0.0",
Sha512: "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==",
Path: "microsoft.extensions.logging/6.0.0",
HashPath: "microsoft.extensions.logging.6.0.0.nupkg.sha512",
},
}
msftDependencyInjection := pkg.Package{
Name: "Microsoft.Extensions.DependencyInjection",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.DependencyInjection",
Version: "6.0.0",
Sha512: "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==",
Path: "microsoft.extensions.dependencyinjection/6.0.0",
HashPath: "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512",
},
{
Name: "Microsoft.Extensions.Options",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.Options@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Options",
Version: "6.0.0",
Sha512: "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==",
Path: "microsoft.extensions.options/6.0.0",
HashPath: "microsoft.extensions.options.6.0.0.nupkg.sha512",
},
}
msftLoggingAbstractions := pkg.Package{
Name: "Microsoft.Extensions.Logging.Abstractions",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Logging.Abstractions",
Version: "6.0.0",
Sha512: "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==",
Path: "microsoft.extensions.logging.abstractions/6.0.0",
HashPath: "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512",
},
{
Name: "Microsoft.Extensions.Primitives",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.Primitives@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Primitives",
Version: "6.0.0",
Sha512: "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==",
Path: "microsoft.extensions.primitives/6.0.0",
HashPath: "microsoft.extensions.primitives.6.0.0.nupkg.sha512",
},
}
msftExtensionsLogging := pkg.Package{
Name: "Microsoft.Extensions.Logging",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.Logging@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Logging",
Version: "6.0.0",
Sha512: "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==",
Path: "microsoft.extensions.logging/6.0.0",
HashPath: "microsoft.extensions.logging.6.0.0.nupkg.sha512",
},
{
Name: "Newtonsoft.Json",
Version: "13.0.1",
PURL: "pkg:nuget/Newtonsoft.Json@13.0.1",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Newtonsoft.Json",
Version: "13.0.1",
Sha512: "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
Path: "newtonsoft.json/13.0.1",
HashPath: "newtonsoft.json.13.0.1.nupkg.sha512",
},
}
msftExtensionsOptions := pkg.Package{
Name: "Microsoft.Extensions.Options",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.Options@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Options",
Version: "6.0.0",
Sha512: "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==",
Path: "microsoft.extensions.options/6.0.0",
HashPath: "microsoft.extensions.options.6.0.0.nupkg.sha512",
},
{
Name: "Serilog.Sinks.Console",
Version: "4.0.1",
PURL: "pkg:nuget/Serilog.Sinks.Console@4.0.1",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Serilog.Sinks.Console",
Version: "4.0.1",
Sha512: "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==",
Path: "serilog.sinks.console/4.0.1",
HashPath: "serilog.sinks.console.4.0.1.nupkg.sha512",
},
}
msftExtensionsPrimitives := pkg.Package{
Name: "Microsoft.Extensions.Primitives",
Version: "6.0.0",
PURL: "pkg:nuget/Microsoft.Extensions.Primitives@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Primitives",
Version: "6.0.0",
Sha512: "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==",
Path: "microsoft.extensions.primitives/6.0.0",
HashPath: "microsoft.extensions.primitives.6.0.0.nupkg.sha512",
},
{
Name: "Serilog",
Version: "2.10.0",
PURL: "pkg:nuget/Serilog@2.10.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Serilog",
Version: "2.10.0",
Sha512: "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==",
Path: "serilog/2.10.0",
HashPath: "serilog.2.10.0.nupkg.sha512",
},
}
newtonsoftJson := pkg.Package{
Name: "Newtonsoft.Json",
Version: "13.0.1",
PURL: "pkg:nuget/Newtonsoft.Json@13.0.1",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Newtonsoft.Json",
Version: "13.0.1",
Sha512: "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
Path: "newtonsoft.json/13.0.1",
HashPath: "newtonsoft.json.13.0.1.nupkg.sha512",
},
{
Name: "System.Diagnostics.DiagnosticSource",
Version: "6.0.0",
PURL: "pkg:nuget/System.Diagnostics.DiagnosticSource@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "System.Diagnostics.DiagnosticSource",
Version: "6.0.0",
Sha512: "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==",
Path: "system.diagnostics.diagnosticsource/6.0.0",
HashPath: "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512",
},
}
serilogSinksConsole := pkg.Package{
Name: "Serilog.Sinks.Console",
Version: "4.0.1",
PURL: "pkg:nuget/Serilog.Sinks.Console@4.0.1",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Serilog.Sinks.Console",
Version: "4.0.1",
Sha512: "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==",
Path: "serilog.sinks.console/4.0.1",
HashPath: "serilog.sinks.console.4.0.1.nupkg.sha512",
},
{
Name: "System.Runtime.CompilerServices.Unsafe",
Version: "6.0.0",
PURL: "pkg:nuget/System.Runtime.CompilerServices.Unsafe@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "System.Runtime.CompilerServices.Unsafe",
Version: "6.0.0",
Sha512: "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
Path: "system.runtime.compilerservices.unsafe/6.0.0",
HashPath: "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512",
},
}
serilog := pkg.Package{
Name: "Serilog",
Version: "2.10.0",
PURL: "pkg:nuget/Serilog@2.10.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Serilog",
Version: "2.10.0",
Sha512: "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==",
Path: "serilog/2.10.0",
HashPath: "serilog.2.10.0.nupkg.sha512",
},
}
systemDiagnosticsDiagnosticsource := pkg.Package{
Name: "System.Diagnostics.DiagnosticSource",
Version: "6.0.0",
PURL: "pkg:nuget/System.Diagnostics.DiagnosticSource@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "System.Diagnostics.DiagnosticSource",
Version: "6.0.0",
Sha512: "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==",
Path: "system.diagnostics.diagnosticsource/6.0.0",
HashPath: "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512",
},
}
systemRuntimeCompilerServicesUnsafe := pkg.Package{
Name: "System.Runtime.CompilerServices.Unsafe",
Version: "6.0.0",
PURL: "pkg:nuget/System.Runtime.CompilerServices.Unsafe@6.0.0",
Locations: fixtureLocationSet,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "System.Runtime.CompilerServices.Unsafe",
Version: "6.0.0",
Sha512: "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
Path: "system.runtime.compilerservices.unsafe/6.0.0",
HashPath: "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512",
},
}
var expectedRelationships []artifact.Relationship
pkgtest.TestFileParser(t, fixture, parseDotnetDeps, expected, expectedRelationships)
expectedPkgs := []pkg.Package{
awssdkcore,
msftDependencyInjection,
msftDependencyInjectionAbstractions,
msftExtensionsLogging,
msftLoggingAbstractions,
msftExtensionsOptions,
msftExtensionsPrimitives,
newtonsoftJson,
serilog,
serilogSinksConsole,
systemDiagnosticsDiagnosticsource,
systemRuntimeCompilerServicesUnsafe,
testCommon,
rootPkg,
}
// ┌── (✓ = is represented in the test)
// ↓
//
// ✓ TestLibrary/1.0.0 (project)
// ✓ ├── [a] Microsoft.Extensions.DependencyInjection/6.0.0 [file version: 6.0.21.52210]
// ✓ │ ├── [b] Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0 [file version: 6.0.21.52210]
// ✓ │ └── [c!] System.Runtime.CompilerServices.Unsafe/6.0.0 [NO TARGET INFO]
// ✓ ├── Microsoft.Extensions.Logging/6.0.0 [file version: 6.0.21.52210]
// ✓ │ ├── Microsoft.Extensions.DependencyInjection/6.0.0 ...to [a]
// ✓ │ ├── Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0 ...to [b]
// ✓ │ ├── Microsoft.Extensions.Logging.Abstractions/6.0.0 [file version: 6.0.21.52210]
// ✓ │ ├── Microsoft.Extensions.Options/6.0.0 [file version: 6.0.21.52210]
// ✓ │ │ ├── Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0 ...to [b]
// ✓ │ │ └── Microsoft.Extensions.Primitives/6.0.0 [file version: 6.0.21.52210]
// ✓ │ │ └── System.Runtime.CompilerServices.Unsafe/6.0.0 ...to [c!]
// ✓ │ └── System.Diagnostics.DiagnosticSource/6.0.0 [NO RUNTIME INFO]
// ✓ │ └── System.Runtime.CompilerServices.Unsafe/6.0.0 ...to [c!]
// ✓ ├── Newtonsoft.Json/13.0.1 [file version: 13.0.1.25517]
// ✓ ├── [d] Serilog/2.10.0 [file version: 2.10.0.0]
// ✓ ├── Serilog.Sinks.Console/4.0.1 [file version: 4.0.1.0]
// ✓ │ └── Serilog/2.10.0 ...to [d]
// ✓ └── [e!] TestCommon/1.0.0 [NOT SERVICEABLE / NO SHA]
// ✓ └── AWSSDK.Core/3.7.10.6 [file version: 3.7.10.6]
expectedRelationships := []artifact.Relationship{
{
From: awssdkcore,
To: testCommon,
Type: artifact.DependencyOfRelationship,
},
{
From: msftDependencyInjection,
To: msftExtensionsLogging,
Type: artifact.DependencyOfRelationship,
},
{
From: msftDependencyInjection,
To: rootPkg,
Type: artifact.DependencyOfRelationship,
},
{
From: msftDependencyInjectionAbstractions,
To: msftDependencyInjection,
Type: artifact.DependencyOfRelationship,
},
{
From: msftDependencyInjectionAbstractions,
To: msftExtensionsLogging,
Type: artifact.DependencyOfRelationship,
},
{
From: msftDependencyInjectionAbstractions,
To: msftExtensionsOptions,
Type: artifact.DependencyOfRelationship,
},
{
From: msftExtensionsLogging,
To: rootPkg,
Type: artifact.DependencyOfRelationship,
},
{
From: msftLoggingAbstractions,
To: msftExtensionsLogging,
Type: artifact.DependencyOfRelationship,
},
{
From: msftExtensionsOptions,
To: msftExtensionsLogging,
Type: artifact.DependencyOfRelationship,
},
{
From: msftExtensionsPrimitives,
To: msftExtensionsOptions,
Type: artifact.DependencyOfRelationship,
},
{
From: newtonsoftJson,
To: rootPkg,
Type: artifact.DependencyOfRelationship,
},
{
From: serilog,
To: serilogSinksConsole,
Type: artifact.DependencyOfRelationship,
},
{
From: serilog,
To: rootPkg,
Type: artifact.DependencyOfRelationship,
},
{
From: serilogSinksConsole,
To: rootPkg,
Type: artifact.DependencyOfRelationship,
},
{
From: systemDiagnosticsDiagnosticsource,
To: msftExtensionsLogging,
Type: artifact.DependencyOfRelationship,
},
{
From: systemRuntimeCompilerServicesUnsafe,
To: msftDependencyInjection,
Type: artifact.DependencyOfRelationship,
},
{
From: systemRuntimeCompilerServicesUnsafe,
To: msftExtensionsPrimitives,
Type: artifact.DependencyOfRelationship,
},
{
From: systemRuntimeCompilerServicesUnsafe,
To: systemDiagnosticsDiagnosticsource,
Type: artifact.DependencyOfRelationship,
},
{
From: testCommon,
To: rootPkg,
Type: artifact.DependencyOfRelationship,
},
}
pkgtest.TestFileParser(t, fixture, parseDotnetDeps, expectedPkgs, expectedRelationships)
}
@@ -232,4 +232,4 @@
"sha512": ""
}
}
}
}
+36 -33
View File
@@ -18,6 +18,7 @@ import (
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/storage/memory"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/log"
@@ -31,6 +32,7 @@ type goLicenses struct {
opts GoCatalogerOpts
localModCacheResolver file.WritableResolver
progress *monitor.CatalogerTask
licenseFileNames *strset.Set
}
func newGoLicenses(opts GoCatalogerOpts) goLicenses {
@@ -42,6 +44,7 @@ func newGoLicenses(opts GoCatalogerOpts) goLicenses {
RemoveOnCompletion: true,
Title: "Downloading go mod",
},
licenseFileNames: strset.New(licenses.FileNames()...),
}
}
@@ -77,7 +80,7 @@ func modCacheResolver(modCacheDir string) file.WritableResolver {
}
func (c *goLicenses) getLicenses(resolver file.Resolver, moduleName, moduleVersion string) (licenses []pkg.License, err error) {
licenses, err = findLicenses(resolver,
licenses, err = c.findLicenses(resolver,
fmt.Sprintf(`**/go/pkg/mod/%s@%s/*`, processCaps(moduleName), moduleVersion),
)
if err != nil || len(licenses) > 0 {
@@ -102,7 +105,7 @@ func (c *goLicenses) getLicensesFromLocal(moduleName, moduleVersion string) ([]p
// if we're running against a directory on the filesystem, it may not include the
// user's homedir / GOPATH, so we defer to using the localModCacheResolver
return findLicenses(c.localModCacheResolver, moduleSearchGlob(moduleName, moduleVersion))
return c.findLicenses(c.localModCacheResolver, moduleSearchGlob(moduleName, moduleVersion))
}
func (c *goLicenses) getLicensesFromRemote(moduleName, moduleVersion string) ([]pkg.License, error) {
@@ -139,7 +142,37 @@ func (c *goLicenses) getLicensesFromRemote(moduleName, moduleVersion string) ([]
log.Tracef("remote proxy walk failed for: %s", moduleName)
}
return findLicenses(c.localModCacheResolver, moduleSearchGlob(moduleName, moduleVersion))
return c.findLicenses(c.localModCacheResolver, moduleSearchGlob(moduleName, moduleVersion))
}
func (c *goLicenses) findLicenses(resolver file.Resolver, globMatch string) (out []pkg.License, err error) {
out = make([]pkg.License, 0)
if resolver == nil {
return
}
locations, err := resolver.FilesByGlob(globMatch)
if err != nil {
return nil, err
}
for _, l := range locations {
fileName := path.Base(l.RealPath)
if c.licenseFileNames.Has(fileName) {
contents, err := resolver.FileContentsByLocation(l)
if err != nil {
return nil, err
}
parsed, err := licenses.Parse(contents, l)
if err != nil {
return nil, err
}
out = append(out, parsed...)
}
}
return
}
func moduleDir(moduleName, moduleVersion string) string {
@@ -157,36 +190,6 @@ func requireCollection(licenses []pkg.License) []pkg.License {
return licenses
}
func findLicenses(resolver file.Resolver, globMatch string) (out []pkg.License, err error) {
out = make([]pkg.License, 0)
if resolver == nil {
return
}
locations, err := resolver.FilesByGlob(globMatch)
if err != nil {
return nil, err
}
for _, l := range locations {
fileName := path.Base(l.RealPath)
if licenses.FileNameSet.Contains(fileName) {
contents, err := resolver.FileContentsByLocation(l)
if err != nil {
return nil, err
}
parsed, err := licenses.Parse(contents, l)
if err != nil {
return nil, err
}
out = append(out, parsed...)
}
}
return
}
var capReplacer = regexp.MustCompile("[A-Z]")
func processCaps(s string) string {
@@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/fileresolver"
"github.com/anchore/syft/syft/license"
@@ -37,7 +36,6 @@ func Test_LocalLicenseSearch(t *testing.T) {
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
Locations: file.NewLocationSet(loc1),
URLs: internal.NewStringSet(),
},
},
{
@@ -48,7 +46,6 @@ func Test_LocalLicenseSearch(t *testing.T) {
SPDXExpression: "MIT",
Type: license.Concluded,
Locations: file.NewLocationSet(loc2),
URLs: internal.NewStringSet(),
},
},
}
@@ -128,7 +125,6 @@ func Test_RemoteProxyLicenseSearch(t *testing.T) {
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
Locations: file.NewLocationSet(loc1),
URLs: internal.NewStringSet(),
},
},
{
@@ -139,7 +135,6 @@ func Test_RemoteProxyLicenseSearch(t *testing.T) {
SPDXExpression: "MIT",
Type: license.Concluded,
Locations: file.NewLocationSet(loc2),
URLs: internal.NewStringSet(),
},
},
}
@@ -329,6 +329,10 @@ func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationshi
opts = append(opts, p.compareOptions...)
opts = append(opts, cmp.Reporter(&r))
// order should not matter
pkg.Sort(p.expectedPkgs)
pkg.Sort(pkgs)
if diff := cmp.Diff(p.expectedPkgs, pkgs, opts...); diff != "" {
t.Log("Specific Differences:\n" + r.String())
t.Errorf("unexpected packages from parsing (-expected +actual)\n%s", diff)
@@ -341,6 +345,10 @@ func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationshi
opts = append(opts, p.compareOptions...)
opts = append(opts, cmp.Reporter(&r))
// order should not matter
pkg.SortRelationships(p.expectedRelationships)
pkg.SortRelationships(relationships)
if diff := cmp.Diff(p.expectedRelationships, relationships, opts...); diff != "" {
t.Log("Specific Differences:\n" + r.String())
+77 -18
View File
@@ -8,6 +8,7 @@ import (
"strings"
intFile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
@@ -177,6 +178,31 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
return nil, err
}
licenses, name, version, err := j.parseLicenses(manifest)
if err != nil {
return nil, err
}
return &pkg.Package{
// TODO: maybe select name should just have a pom properties in it?
Name: name,
Version: version,
Language: pkg.Java,
Licenses: pkg.NewLicenseSet(licenses...),
Locations: file.NewLocationSet(
j.location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
),
Type: j.fileInfo.pkgType(),
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
VirtualPath: j.location.AccessPath(),
Manifest: manifest,
ArchiveDigests: digests,
},
}, nil
}
func (j *archiveParser) parseLicenses(manifest *pkg.JavaManifest) ([]pkg.License, string, string, error) {
// we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest
// TODO: when we support locations of paths within archives we should start passing the specific manifest location object instead of the top jar
licenses := pkg.NewLicensesFromLocation(j.location, selectLicenses(manifest)...)
@@ -201,23 +227,17 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
licenses = append(licenses, pomLicenses...)
}
return &pkg.Package{
// TODO: maybe select name should just have a pom properties in it?
Name: name,
Version: version,
Language: pkg.Java,
Licenses: pkg.NewLicenseSet(licenses...),
Locations: file.NewLocationSet(
j.location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
),
Type: j.fileInfo.pkgType(),
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
VirtualPath: j.location.AccessPath(),
Manifest: manifest,
ArchiveDigests: digests,
},
}, nil
if len(licenses) == 0 {
fileLicenses, err := j.getLicenseFromFileInArchive()
if err != nil {
return nil, "", "", err
}
if fileLicenses != nil {
licenses = append(licenses, fileLicenses...)
}
}
return licenses, name, version, nil
}
type parsedPomProject struct {
@@ -237,7 +257,7 @@ func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (name, versi
projects, _ := pomProjectByParentPath(j.archivePath, j.location, pomMatches)
for parentPath, propertiesObj := range properties {
if propertiesObj.ArtifactID != "" && j.fileInfo.name != "" && strings.HasPrefix(propertiesObj.ArtifactID, j.fileInfo.name) {
if artifactIDMatchesFilename(propertiesObj.ArtifactID, j.fileInfo.name) {
pomPropertiesObject = propertiesObj
if proj, exists := projects[parentPath]; exists {
pomProjectObject = proj
@@ -256,6 +276,13 @@ func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (name, versi
return name, version, pomProjectObject.Licenses
}
func artifactIDMatchesFilename(artifactID, fileName string) bool {
if artifactID == "" || fileName == "" {
return false
}
return strings.HasPrefix(artifactID, fileName) || strings.HasSuffix(fileName, artifactID)
}
// discoverPkgsFromAllMavenFiles parses Maven POM properties/xml for a given
// parent package, returning all listed Java packages found for each pom
// properties discovered and potentially updating the given parentPkg with new
@@ -310,6 +337,38 @@ func getDigestsFromArchive(archivePath string) ([]file.Digest, error) {
return digests, nil
}
func (j *archiveParser) getLicenseFromFileInArchive() ([]pkg.License, error) {
var fileLicenses []pkg.License
for _, filename := range licenses.FileNames() {
licenseMatches := j.fileManifest.GlobMatch("/META-INF/" + filename)
if len(licenseMatches) == 0 {
// Try the root directory if it's not in META-INF
licenseMatches = j.fileManifest.GlobMatch("/" + filename)
}
if len(licenseMatches) > 0 {
contents, err := intFile.ContentsFromZip(j.archivePath, licenseMatches...)
if err != nil {
return nil, fmt.Errorf("unable to extract java license (%s): %w", j.location, err)
}
for _, licenseMatch := range licenseMatches {
licenseContents := contents[licenseMatch]
parsed, err := licenses.Parse(strings.NewReader(licenseContents), j.location)
if err != nil {
return nil, err
}
if len(parsed) > 0 {
fileLicenses = append(fileLicenses, parsed...)
}
}
}
}
return fileLicenses, nil
}
func (j *archiveParser) discoverPkgsFromNestedArchives(parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) {
// we know that all java archives are zip formatted files, so we can use the shared zip helper
return discoverPkgsFromZip(j.location, j.archivePath, j.contentPath, j.fileManifest, parentPkg)
+255 -51
View File
@@ -11,10 +11,13 @@ import (
"syscall"
"testing"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/gookit/color"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/pkg"
@@ -38,47 +41,7 @@ func generateJavaBuildFixture(t *testing.T, fixturePath string) {
cmd := exec.Command("make", makeTask)
cmd.Dir = filepath.Join(cwd, "test-fixtures/java-builds/")
stderr, err := cmd.StderrPipe()
if err != nil {
t.Fatalf("could not get stderr: %+v", err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
t.Fatalf("could not get stdout: %+v", err)
}
err = cmd.Start()
if err != nil {
t.Fatalf("failed to start cmd: %+v", err)
}
show := func(label string, reader io.ReadCloser) {
scanner := bufio.NewScanner(reader)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
t.Logf("%s: %s", label, scanner.Text())
}
}
go show("out", stdout)
go show("err", stderr)
if err := cmd.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
// The program has exited with an exit code != 0
// This works on both Unix and Windows. Although package
// syscall is generally platform dependent, WaitStatus is
// defined for both Unix and Windows and in both cases has
// an ExitStatus() method with the same signature.
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
if status.ExitStatus() != 0 {
t.Fatalf("failed to generate fixture: rc=%d", status.ExitStatus())
}
}
} else {
t.Fatalf("unable to get generate fixture result: %+v", err)
}
}
run(t, cmd)
}
func TestParseJar(t *testing.T) {
@@ -158,6 +121,14 @@ func TestParseJar(t *testing.T) {
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
Licenses: pkg.NewLicenseSet(
pkg.License{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar")),
},
),
Metadata: pkg.JavaMetadata{
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar",
Manifest: &pkg.JavaManifest{
@@ -223,6 +194,14 @@ func TestParseJar(t *testing.T) {
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
Licenses: pkg.NewLicenseSet(
pkg.License{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar")),
},
),
Metadata: pkg.JavaMetadata{
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
Manifest: &pkg.JavaManifest{
@@ -576,7 +555,7 @@ func TestParseNestedJar(t *testing.T) {
})
require.NoError(t, err)
expectedNameVersionPairSet := internal.NewStringSet()
expectedNameVersionPairSet := strset.New()
makeKey := func(p *pkg.Package) string {
if p == nil {
@@ -589,24 +568,24 @@ func TestParseNestedJar(t *testing.T) {
expectedNameVersionPairSet.Add(makeKey(&e))
}
actualNameVersionPairSet := internal.NewStringSet()
actualNameVersionPairSet := strset.New()
for _, a := range actual {
a := a
key := makeKey(&a)
actualNameVersionPairSet.Add(key)
if !expectedNameVersionPairSet.Contains(key) {
if !expectedNameVersionPairSet.Has(key) {
t.Errorf("extra package: %s", a)
}
}
for _, key := range expectedNameVersionPairSet.ToSlice() {
if !actualNameVersionPairSet.Contains(key) {
for _, key := range expectedNameVersionPairSet.List() {
if !actualNameVersionPairSet.Has(key) {
t.Errorf("missing package: %s", key)
}
}
if len(actual) != len(expectedNameVersionPairSet) {
t.Fatalf("unexpected package count: %d!=%d", len(actual), len(expectedNameVersionPairSet))
if len(actual) != expectedNameVersionPairSet.Size() {
t.Fatalf("unexpected package count: %d!=%d", len(actual), expectedNameVersionPairSet.Size())
}
for _, a := range actual {
@@ -730,7 +709,7 @@ func Test_newPackageFromMavenData(t *testing.T) {
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: internal.NewStringSet("https://opensource.org/licenses/MIT"),
URLs: []string{"https://opensource.org/licenses/MIT"},
Locations: file.NewLocationSet(file.NewLocation("some-license-path")),
},
},
@@ -767,7 +746,7 @@ func Test_newPackageFromMavenData(t *testing.T) {
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: internal.NewStringSet("https://opensource.org/licenses/MIT"),
URLs: []string{"https://opensource.org/licenses/MIT"},
Locations: file.NewLocationSet(file.NewLocation("some-license-path")),
},
),
@@ -1004,3 +983,228 @@ func Test_newPackageFromMavenData(t *testing.T) {
})
}
}
func Test_artifactIDMatchesFilename(t *testing.T) {
tests := []struct {
name string
artifactID string
fileName string // without version or extension
want bool
}{
{
name: "artifact id within file name",
artifactID: "atlassian-extras-api",
fileName: "com.atlassian.extras_atlassian-extras-api",
want: true,
},
{
name: "file name within artifact id",
artifactID: "atlassian-extras-api-something",
fileName: "atlassian-extras-api",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, artifactIDMatchesFilename(tt.artifactID, tt.fileName))
})
}
}
func Test_parseJavaArchive_regressions(t *testing.T) {
tests := []struct {
name string
fixtureName string
expectedPkgs []pkg.Package
expectedRelationships []artifact.Relationship
want bool
}{
{
name: "duplicate jar regression - go case (issue #2130)",
fixtureName: "jackson-core-2.15.2",
expectedPkgs: []pkg.Package{
{
Name: "jackson-core",
Version: "2.15.2",
Type: pkg.JavaPkg,
Language: pkg.Java,
MetadataType: pkg.JavaMetadataType,
PURL: "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.15.2",
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/jar-metadata/cache/jackson-core-2.15.2.jar")),
Licenses: pkg.NewLicenseSet(
pkg.NewLicensesFromLocation(
file.NewLocation("test-fixtures/jar-metadata/cache/jackson-core-2.15.2.jar"),
"https://www.apache.org/licenses/LICENSE-2.0.txt",
)...,
),
Metadata: pkg.JavaMetadata{
VirtualPath: "test-fixtures/jar-metadata/cache/jackson-core-2.15.2.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Build-Jdk-Spec": "1.8",
"Bundle-Description": "Core Jackson processing abstractions",
"Bundle-DocURL": "https://github.com/FasterXML/jackson-core",
"Bundle-License": "https://www.apache.org/licenses/LICENSE-2.0.txt",
"Bundle-ManifestVersion": "2",
"Bundle-Name": "Jackson-core",
"Bundle-SymbolicName": "com.fasterxml.jackson.core.jackson-core",
"Bundle-Vendor": "FasterXML",
"Bundle-Version": "2.15.2",
"Created-By": "Apache Maven Bundle Plugin 5.1.8",
"Export-Package": "com.fasterxml.jackson.core;version...snip",
"Implementation-Title": "Jackson-core",
"Implementation-Vendor": "FasterXML",
"Implementation-Vendor-Id": "com.fasterxml.jackson.core",
"Implementation-Version": "2.15.2",
"Import-Package": "com.fasterxml.jackson.core;version=...snip",
"Manifest-Version": "1.0",
"Multi-Release": "true",
"Require-Capability": `osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"`,
"Specification-Title": "Jackson-core",
"Specification-Vendor": "FasterXML",
"Specification-Version": "2.15.2",
"Tool": "Bnd-6.3.1.202206071316",
"X-Compile-Source-JDK": "1.8",
"X-Compile-Target-JDK": "1.8",
},
},
// not under test
//ArchiveDigests: []file.Digest{{Algorithm: "sha1", Value: "d8bc1d9c428c96fe447e2c429fc4304d141024df"}},
},
},
},
},
{
name: "duplicate jar regression - bad case (issue #2130)",
fixtureName: "com.fasterxml.jackson.core.jackson-core-2.15.2",
expectedPkgs: []pkg.Package{
{
Name: "jackson-core",
Version: "2.15.2",
Type: pkg.JavaPkg,
Language: pkg.Java,
MetadataType: pkg.JavaMetadataType,
PURL: "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.15.2",
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/jar-metadata/cache/com.fasterxml.jackson.core.jackson-core-2.15.2.jar")),
Licenses: pkg.NewLicenseSet(
pkg.NewLicensesFromLocation(
file.NewLocation("test-fixtures/jar-metadata/cache/com.fasterxml.jackson.core.jackson-core-2.15.2.jar"),
"https://www.apache.org/licenses/LICENSE-2.0.txt",
)...,
),
Metadata: pkg.JavaMetadata{
VirtualPath: "test-fixtures/jar-metadata/cache/com.fasterxml.jackson.core.jackson-core-2.15.2.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Build-Jdk-Spec": "1.8",
"Bundle-Description": "Core Jackson processing abstractions",
"Bundle-DocURL": "https://github.com/FasterXML/jackson-core",
"Bundle-License": "https://www.apache.org/licenses/LICENSE-2.0.txt",
"Bundle-ManifestVersion": "2",
"Bundle-Name": "Jackson-core",
"Bundle-SymbolicName": "com.fasterxml.jackson.core.jackson-core",
"Bundle-Vendor": "FasterXML",
"Bundle-Version": "2.15.2",
"Created-By": "Apache Maven Bundle Plugin 5.1.8",
"Export-Package": "com.fasterxml.jackson.core;version...snip",
"Implementation-Title": "Jackson-core",
"Implementation-Vendor": "FasterXML",
"Implementation-Vendor-Id": "com.fasterxml.jackson.core",
"Implementation-Version": "2.15.2",
"Import-Package": "com.fasterxml.jackson.core;version=...snip",
"Manifest-Version": "1.0",
"Multi-Release": "true",
"Require-Capability": `osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"`,
"Specification-Title": "Jackson-core",
"Specification-Vendor": "FasterXML",
"Specification-Version": "2.15.2",
"Tool": "Bnd-6.3.1.202206071316",
"X-Compile-Source-JDK": "1.8",
"X-Compile-Target-JDK": "1.8",
},
},
// not under test
//ArchiveDigests: []file.Digest{{Algorithm: "sha1", Value: "abd3e329270fc54a2acaceb45420fd5710ecefd5"}},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pkgtest.NewCatalogTester().
FromFile(t, generateJavaMetadataJarFixture(t, tt.fixtureName)).
Expects(tt.expectedPkgs, tt.expectedRelationships).
WithCompareOptions(cmpopts.IgnoreFields(pkg.JavaMetadata{}, "ArchiveDigests")).
TestParser(t, parseJavaArchive)
})
}
}
func generateJavaMetadataJarFixture(t *testing.T, fixtureName string) string {
fixturePath := filepath.Join("test-fixtures/jar-metadata/cache/", fixtureName+".jar")
if _, err := os.Stat(fixturePath); !os.IsNotExist(err) {
// fixture already exists...
return fixturePath
}
makeTask := filepath.Join("cache", fixtureName+".jar")
t.Logf(color.Bold.Sprintf("Generating Fixture from 'make %s'", makeTask))
cwd, err := os.Getwd()
if err != nil {
t.Errorf("unable to get cwd: %+v", err)
}
cmd := exec.Command("make", makeTask)
cmd.Dir = filepath.Join(cwd, "test-fixtures/jar-metadata")
run(t, cmd)
return fixturePath
}
func run(t testing.TB, cmd *exec.Cmd) {
stderr, err := cmd.StderrPipe()
if err != nil {
t.Fatalf("could not get stderr: %+v", err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
t.Fatalf("could not get stdout: %+v", err)
}
err = cmd.Start()
if err != nil {
t.Fatalf("failed to start cmd: %+v", err)
}
show := func(label string, reader io.ReadCloser) {
scanner := bufio.NewScanner(reader)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
t.Logf("%s: %s", label, scanner.Text())
}
}
go show("out", stdout)
go show("err", stderr)
if err := cmd.Wait(); err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
// The program has exited with an exit code != 0
// This works on both Unix and Windows. Although package
// syscall is generally platform dependent, WaitStatus is
// defined for both Unix and Windows and in both cases has
// an ExitStatus() method with the same signature.
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
if status.ExitStatus() != 0 {
t.Fatalf("failed to generate fixture: rc=%d", status.ExitStatus())
}
}
} else {
t.Fatalf("unable to get generate fixture result: %+v", err)
}
}
}
+64 -20
View File
@@ -109,27 +109,71 @@ func parseJavaManifest(path string, reader io.Reader) (*pkg.JavaManifest, error)
}
func selectName(manifest *pkg.JavaManifest, filenameObj archiveFilename) string {
var name string
switch {
case filenameObj.name != "":
name = filenameObj.name
case manifest.Main["Name"] != "":
// Manifest original spec...
name = manifest.Main["Name"]
case manifest.Main["Bundle-Name"] != "":
// BND tooling...
name = manifest.Main["Bundle-Name"]
case manifest.Main["Short-Name"] != "":
// Jenkins...
name = manifest.Main["Short-Name"]
case manifest.Main["Extension-Name"] != "":
// Jenkins...
name = manifest.Main["Extension-Name"]
case manifest.Main["Implementation-Title"] != "":
// last ditch effort...
name = manifest.Main["Implementation-Title"]
// special case: from https://svn.apache.org/repos/asf/felix/releases/maven-bundle-plugin-1.2.0/doc/maven-bundle-plugin-bnd.html
// "<Bundle-SymbolicName> is assumed to be "${groupId}.${artifactId}"."
//
// documentation from https://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html
// agrees this is the default behavior:
//
// - [1] if artifact.getFile is not null and the jar contains a OSGi Manifest with Bundle-SymbolicName property then that value is returned
//
// - [2] if groupId has only one section (no dots) and artifact.getFile is not null then the first package name with classes
// is returned. eg. commons-logging:commons-logging -> org.apache.commons.logging
//
// - [3] if artifactId is equal to last section of groupId then groupId is returned. eg. org.apache.maven:maven -> org.apache.maven
//
// - [4] if artifactId starts with last section of groupId that portion is removed. eg. org.apache.maven:maven-core -> org.apache.maven.core
// The computed symbolic name is also stored in the $(maven-symbolicname) property in case you want to add attributes or directives to it.
//
if manifest != nil {
if strings.Contains(manifest.Main["Created-By"], "Apache Maven Bundle Plugin") {
if v := manifest.Main["Bundle-SymbolicName"]; v != "" {
// the problem with this approach is that we don't have a strong indication of the artifactId
// not having a "." in it. However, by convention it is unlikely that an artifactId would have a ".".
fields := strings.Split(v, ".")
// grab the last field, this is the artifactId. Note: because of [3] we do not know if this value is
// correct. That is, a group id of "commons-logging" may have caused BND to swap out the reference to
// "org.apache.commons.logging", which means we'd interpret this as an artifact id of "logging",
// which is not correct.
// [correct] https://mvnrepository.com/artifact/commons-logging/commons-logging
// [still incorrect] https://mvnrepository.com/artifact/org.apache.commons.logging/org.apache.commons.logging
return fields[len(fields)-1]
}
}
}
return name
// the filename tends to be the next-best reference for the package name
if filenameObj.name != "" {
if strings.Contains(filenameObj.name, ".") {
// special case: this *might* be a group id + artifact id. By convention artifact ids do not have "." in them.
fields := strings.Split(filenameObj.name, ".")
return fields[len(fields)-1]
}
return filenameObj.name
}
// remaining fields in the manifest is a bit of a free-for-all depending on the build tooling used and package maintainer preferences
if manifest != nil {
switch {
case manifest.Main["Name"] != "":
// Manifest original spec...
return manifest.Main["Name"]
case manifest.Main["Bundle-Name"] != "":
// BND tooling... TODO: this does not seem accurate (I don't see a reference in the BND tooling docs for this)
return manifest.Main["Bundle-Name"]
case manifest.Main["Short-Name"] != "":
// Jenkins...
return manifest.Main["Short-Name"]
case manifest.Main["Extension-Name"] != "":
// Jenkins...
return manifest.Main["Extension-Name"]
case manifest.Main["Implementation-Title"] != "":
// last ditch effort...
return manifest.Main["Implementation-Title"]
}
}
return ""
}
func selectVersion(manifest *pkg.JavaManifest, filenameObj archiveFilename) string {
@@ -160,6 +160,44 @@ func TestSelectName(t *testing.T) {
archive: newJavaArchiveFilename("/something/omg.jar"),
expected: "omg",
},
{
desc: "Use the artifact ID baked by the Apache Maven Bundle Plugin",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Created-By": "Apache Maven Bundle Plugin",
"Bundle-SymbolicName": "com.atlassian.gadgets.atlassian-gadgets-api",
"Name": "foo",
"Implementation-Title": "maven-wrapper",
},
},
archive: newJavaArchiveFilename("/something/omg.jar"),
expected: "atlassian-gadgets-api",
},
{
// example: pkg:maven/org.apache.servicemix.bundles/org.apache.servicemix.bundles.spring-beans@5.3.26_1
desc: "Apache Maven Bundle Plugin might bake a version in the created-by field",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Created-By": "Apache Maven Bundle Plugin 5.1.6",
"Bundle-SymbolicName": "com.atlassian.gadgets.atlassian-gadgets-api",
"Name": "foo",
"Implementation-Title": "maven-wrapper",
},
},
archive: newJavaArchiveFilename("/something/omg.jar"),
expected: "atlassian-gadgets-api",
},
{
desc: "Filename looks like a groupid + artifact id",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Name": "foo",
"Implementation-Title": "maven-wrapper",
},
},
archive: newJavaArchiveFilename("/something/com.atlassian.gadgets.atlassian-gadgets-api.jar"),
expected: "atlassian-gadgets-api",
},
}
for _, test := range tests {
@@ -11,7 +11,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/vifraa/gopom"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/pkg"
@@ -342,19 +341,18 @@ func Test_parsePomXMLProject(t *testing.T) {
Value: "The Apache Software License, Version 2.0",
SPDXExpression: "", // TODO: ideally we would parse this title to get Apache-2.0 (created issue #2210 https://github.com/anchore/syft/issues/2210)
Type: license.Declared,
URLs: internal.NewStringSet("http://www.apache.org/licenses/LICENSE-2.0.txt"),
URLs: []string{"http://www.apache.org/licenses/LICENSE-2.0.txt"},
Locations: file.NewLocationSet(jarLocation),
},
{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: internal.NewStringSet(),
Locations: file.NewLocationSet(jarLocation),
},
{
Type: license.Declared,
URLs: internal.NewStringSet("https://opensource.org/license/unlicense/"),
URLs: []string{"https://opensource.org/license/unlicense/"},
Locations: file.NewLocationSet(jarLocation),
},
},
@@ -0,0 +1 @@
/cache
@@ -0,0 +1,14 @@
CACHE_DIR = cache
CACHE_PATH = $(shell pwd)/cache
JACKSON_CORE = jackson-core-2.15.2
SBT_JACKSON_CORE = com.fasterxml.jackson.core.jackson-core-2.15.2
$(CACHE_DIR):
mkdir -p $(CACHE_DIR)
$(CACHE_DIR)/$(JACKSON_CORE).jar: $(CACHE_DIR)
cd $(JACKSON_CORE) && zip -r $(CACHE_PATH)/$(JACKSON_CORE).jar .
$(CACHE_DIR)/$(SBT_JACKSON_CORE).jar: $(CACHE_DIR)
cd $(SBT_JACKSON_CORE) && zip -r $(CACHE_PATH)/$(SBT_JACKSON_CORE).jar .
@@ -0,0 +1,5 @@
# Jar-Metadata test fixtures
Each directory is the name of a jar to be created (simply a zip) based on the contents of the directory.
This prevents us from having to create real jars by hand or keep binaries in the repo. This also means we dont need the
entire jar, only the necessary metadata for testing.
@@ -0,0 +1,25 @@
Manifest-Version: 1.0
Bundle-License: https://www.apache.org/licenses/LICENSE-2.0.txt
Bundle-SymbolicName: com.fasterxml.jackson.core.jackson-core
Implementation-Vendor-Id: com.fasterxml.jackson.core
Specification-Title: Jackson-core
Bundle-DocURL: https://github.com/FasterXML/jackson-core
Import-Package: com.fasterxml.jackson.core;version=...snip
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
Export-Package: com.fasterxml.jackson.core;version...snip
Bundle-Name: Jackson-core
Multi-Release: true
Build-Jdk-Spec: 1.8
Bundle-Description: Core Jackson processing abstractions
Implementation-Title: Jackson-core
Implementation-Version: 2.15.2
Bundle-ManifestVersion: 2
Specification-Vendor: FasterXML
Bundle-Vendor: FasterXML
Tool: Bnd-6.3.1.202206071316
Implementation-Vendor: FasterXML
Bundle-Version: 2.15.2
X-Compile-Target-JDK: 1.8
X-Compile-Source-JDK: 1.8
Created-By: Apache Maven Bundle Plugin 5.1.8
Specification-Version: 2.15.2
@@ -0,0 +1,323 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-base</artifactId>
<version>2.15.2</version>
</parent>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<name>Jackson-core</name>
<version>2.15.2</version>
<packaging>jar</packaging>
<description>Core Jackson processing abstractions (aka Streaming API), implementation for JSON</description>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<inceptionYear>2008</inceptionYear>
<url>https://github.com/FasterXML/jackson-core</url>
<scm>
<connection>scm:git:git@github.com:FasterXML/jackson-core.git</connection>
<developerConnection>scm:git:git@github.com:FasterXML/jackson-core.git</developerConnection>
<url>https://github.com/FasterXML/jackson-core</url>
<tag>jackson-core-2.15.2</tag>
</scm>
<properties>
<!-- 03-May-2022: Change Java compatibility for Jackson-Core 2.14 from Java6 to Java8,
still use Moditect to get JDK9+ module info support; need newer bundle plugin as well
(can just defaults from `jackson-parent`)
-->
<!-- 16-Nov-2022, tatu: [core#838] Verify Android SDK compatibility.
Baseline compatibility:
* Jackson 2.13 compatible with Android SDK 19 and up
* Jackson 2.14 compatible with Android SDK 26 and up
* Jackson 2.15 compatible with Android SDK 26 and up
-->
<version.android.sdk>26</version.android.sdk>
<version.android.sdk.signature>0.5.1</version.android.sdk.signature>
<osgi.export>com.fasterxml.jackson.core;version=${project.version},
com.fasterxml.jackson.core.*;version=${project.version}
</osgi.export>
<osgi.import>!ch.randelshofer.fastdoubleparser, *</osgi.import>
<!-- Generate PackageVersion.java into this directory. -->
<packageVersion.dir>com/fasterxml/jackson/core/json</packageVersion.dir>
<packageVersion.package>${project.groupId}.json</packageVersion.package>
<!-- for Reproducible Builds -->
<project.build.outputTimestamp>2023-05-30T22:15:40Z</project.build.outputTimestamp>
</properties>
<!-- Alas, need to include snapshot reference since otherwise can not find
snapshot of parent... -->
<repositories>
<repository>
<id>sonatype-nexus-snapshots</id>
<name>Sonatype Nexus Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases><enabled>false</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.9.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- 26-Aug-2019, tatu: JaCoCo for code coverage -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Important: enable enforcer plug-in: -->
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<executions> <!-- or? combine.children="merge"> -->
<execution>
<id>enforce-properties</id>
<phase>validate</phase>
<goals><goal>enforce</goal></goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${version.plugin.surefire}</version>
<configuration>
<redirectTestOutputToFile>${surefire.redirectTestOutputToFile}</redirectTestOutputToFile>
<excludes>
<exclude>**/failing/**/*.java</exclude>
</excludes>
<!-- 13-Apr-2018, tatu: for debugging [core#400]
<systemPropertyVariables>
<com.fasterxml.jackson.core.util.BufferRecyclers.trackReusableBuffers>true</com.fasterxml.jackson.core.util.BufferRecyclers.trackReusableBuffers>
</systemPropertyVariables>
-->
</configuration>
</plugin>
<!-- settings are fine, but needed to trigger execution! -->
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
</plugin>
<!-- 04-Mar-2019, tatu: Add rudimentary JDK9+ module info. To build with JDK 8
will have to use `moduleInfoFile` as anything else requires JDK 9+
-->
<plugin>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
</plugin>
<!-- 03-Nov-2020, tatu: Add LICENSE from main level -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<promoteTransitiveDependencies>true</promoteTransitiveDependencies>
<createDependencyReducedPom>true</createDependencyReducedPom>
<minimizeJar>true</minimizeJar>
</configuration>
<executions>
<execution>
<id>shade-jackson-core</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>ch.randelshofer:fastdoubleparser</artifact>
<excludes>
<exclude>META-INF/versions/**/module-info.*</exclude>
</excludes>
</filter>
</filters>
<relocations>
<relocation>
<pattern>ch/randelshofer/fastdoubleparser</pattern>
<shadedPattern>com/fasterxml/jackson/core/io/doubleparser</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/LICENSE</pattern>
<shadedPattern>META-INF/FastDoubleParser-LICENSE</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/NOTICE</pattern>
<shadedPattern>META-INF/FastDoubleParser-NOTICE</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/jackson-core-LICENSE</pattern>
<shadedPattern>META-INF/LICENSE</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/jackson-core-NOTICE</pattern>
<shadedPattern>META-INF/NOTICE</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/versions/11/ch/randelshofer/fastdoubleparser</pattern>
<shadedPattern>META-INF/versions/11/com/fasterxml/jackson/core/io/doubleparser</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/versions/17/ch/randelshofer/fastdoubleparser</pattern>
<shadedPattern>META-INF/versions/17/com/fasterxml/jackson/core/io/doubleparser</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/versions/19/ch/randelshofer/fastdoubleparser</pattern>
<shadedPattern>META-INF/versions/19/com/fasterxml/jackson/core/io/doubleparser</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>de.jjohannes</groupId>
<artifactId>gradle-module-metadata-maven-plugin</artifactId>
<!-- 27-Apr-2023, tatu: [core#999]: Need to exclude shaded FDP
dependency from GMM
-->
<configuration>
<removedDependencies>
<dependency>
<groupId>ch.randelshofer</groupId>
<artifactId>fastdoubleparser</artifactId>
</dependency>
</removedDependencies>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
</archive>
</configuration>
</plugin>
<!-- 23-Mar-2023, tatu: [core#965] Need to put back Gradle module metadata marker -->
<plugin>
<groupId>io.github.floverfelt</groupId>
<artifactId>find-and-replace-maven-plugin</artifactId>
<version>1.1.0</version>
<executions>
<execution>
<id>exec</id>
<phase>package</phase>
<goals>
<goal>find-and-replace</goal>
</goals>
<configuration>
<replacementType>file-contents</replacementType>
<baseDir>${basedir}</baseDir>
<findRegex><![CDATA[<modelVersion>4.0.0</modelVersion>]]></findRegex>
<fileMask>dependency-reduced-pom.xml</fileMask>
<replaceValue><![CDATA[ <!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>]]></replaceValue>
<recursive>false</recursive>
</configuration>
</execution>
</executions>
</plugin>
<!-- 16-Nov-2022, tatu: [core#838] add verification of compatibility
wrt Android SDK versions using AnimalSniffer with "gummy bears" signatures.
To be run from CI, but manually with:
mvn clean package animal-sniffer:check
-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.22</version>
<configuration>
<signature>
<groupId>com.toasttab.android</groupId>
<artifactId>gummy-bears-api-${version.android.sdk}</artifactId>
<version>${version.android.sdk.signature}</version>
</signature>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>ch.randelshofer</groupId>
<artifactId>fastdoubleparser</artifactId>
<version>0.9.0</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,25 @@
Manifest-Version: 1.0
Bundle-License: https://www.apache.org/licenses/LICENSE-2.0.txt
Bundle-SymbolicName: com.fasterxml.jackson.core.jackson-core
Implementation-Vendor-Id: com.fasterxml.jackson.core
Specification-Title: Jackson-core
Bundle-DocURL: https://github.com/FasterXML/jackson-core
Import-Package: com.fasterxml.jackson.core;version=...snip
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
Export-Package: com.fasterxml.jackson.core;version...snip
Bundle-Name: Jackson-core
Multi-Release: true
Build-Jdk-Spec: 1.8
Bundle-Description: Core Jackson processing abstractions
Implementation-Title: Jackson-core
Implementation-Version: 2.15.2
Bundle-ManifestVersion: 2
Specification-Vendor: FasterXML
Bundle-Vendor: FasterXML
Tool: Bnd-6.3.1.202206071316
Implementation-Vendor: FasterXML
Bundle-Version: 2.15.2
X-Compile-Target-JDK: 1.8
X-Compile-Source-JDK: 1.8
Created-By: Apache Maven Bundle Plugin 5.1.8
Specification-Version: 2.15.2
@@ -0,0 +1,323 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-base</artifactId>
<version>2.15.2</version>
</parent>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<name>Jackson-core</name>
<version>2.15.2</version>
<packaging>jar</packaging>
<description>Core Jackson processing abstractions (aka Streaming API), implementation for JSON</description>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<inceptionYear>2008</inceptionYear>
<url>https://github.com/FasterXML/jackson-core</url>
<scm>
<connection>scm:git:git@github.com:FasterXML/jackson-core.git</connection>
<developerConnection>scm:git:git@github.com:FasterXML/jackson-core.git</developerConnection>
<url>https://github.com/FasterXML/jackson-core</url>
<tag>jackson-core-2.15.2</tag>
</scm>
<properties>
<!-- 03-May-2022: Change Java compatibility for Jackson-Core 2.14 from Java6 to Java8,
still use Moditect to get JDK9+ module info support; need newer bundle plugin as well
(can just defaults from `jackson-parent`)
-->
<!-- 16-Nov-2022, tatu: [core#838] Verify Android SDK compatibility.
Baseline compatibility:
* Jackson 2.13 compatible with Android SDK 19 and up
* Jackson 2.14 compatible with Android SDK 26 and up
* Jackson 2.15 compatible with Android SDK 26 and up
-->
<version.android.sdk>26</version.android.sdk>
<version.android.sdk.signature>0.5.1</version.android.sdk.signature>
<osgi.export>com.fasterxml.jackson.core;version=${project.version},
com.fasterxml.jackson.core.*;version=${project.version}
</osgi.export>
<osgi.import>!ch.randelshofer.fastdoubleparser, *</osgi.import>
<!-- Generate PackageVersion.java into this directory. -->
<packageVersion.dir>com/fasterxml/jackson/core/json</packageVersion.dir>
<packageVersion.package>${project.groupId}.json</packageVersion.package>
<!-- for Reproducible Builds -->
<project.build.outputTimestamp>2023-05-30T22:15:40Z</project.build.outputTimestamp>
</properties>
<!-- Alas, need to include snapshot reference since otherwise can not find
snapshot of parent... -->
<repositories>
<repository>
<id>sonatype-nexus-snapshots</id>
<name>Sonatype Nexus Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases><enabled>false</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.9.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- 26-Aug-2019, tatu: JaCoCo for code coverage -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Important: enable enforcer plug-in: -->
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<executions> <!-- or? combine.children="merge"> -->
<execution>
<id>enforce-properties</id>
<phase>validate</phase>
<goals><goal>enforce</goal></goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${version.plugin.surefire}</version>
<configuration>
<redirectTestOutputToFile>${surefire.redirectTestOutputToFile}</redirectTestOutputToFile>
<excludes>
<exclude>**/failing/**/*.java</exclude>
</excludes>
<!-- 13-Apr-2018, tatu: for debugging [core#400]
<systemPropertyVariables>
<com.fasterxml.jackson.core.util.BufferRecyclers.trackReusableBuffers>true</com.fasterxml.jackson.core.util.BufferRecyclers.trackReusableBuffers>
</systemPropertyVariables>
-->
</configuration>
</plugin>
<!-- settings are fine, but needed to trigger execution! -->
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
</plugin>
<!-- 04-Mar-2019, tatu: Add rudimentary JDK9+ module info. To build with JDK 8
will have to use `moduleInfoFile` as anything else requires JDK 9+
-->
<plugin>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
</plugin>
<!-- 03-Nov-2020, tatu: Add LICENSE from main level -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<promoteTransitiveDependencies>true</promoteTransitiveDependencies>
<createDependencyReducedPom>true</createDependencyReducedPom>
<minimizeJar>true</minimizeJar>
</configuration>
<executions>
<execution>
<id>shade-jackson-core</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>ch.randelshofer:fastdoubleparser</artifact>
<excludes>
<exclude>META-INF/versions/**/module-info.*</exclude>
</excludes>
</filter>
</filters>
<relocations>
<relocation>
<pattern>ch/randelshofer/fastdoubleparser</pattern>
<shadedPattern>com/fasterxml/jackson/core/io/doubleparser</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/LICENSE</pattern>
<shadedPattern>META-INF/FastDoubleParser-LICENSE</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/NOTICE</pattern>
<shadedPattern>META-INF/FastDoubleParser-NOTICE</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/jackson-core-LICENSE</pattern>
<shadedPattern>META-INF/LICENSE</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/jackson-core-NOTICE</pattern>
<shadedPattern>META-INF/NOTICE</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/versions/11/ch/randelshofer/fastdoubleparser</pattern>
<shadedPattern>META-INF/versions/11/com/fasterxml/jackson/core/io/doubleparser</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/versions/17/ch/randelshofer/fastdoubleparser</pattern>
<shadedPattern>META-INF/versions/17/com/fasterxml/jackson/core/io/doubleparser</shadedPattern>
</relocation>
<relocation>
<pattern>META-INF/versions/19/ch/randelshofer/fastdoubleparser</pattern>
<shadedPattern>META-INF/versions/19/com/fasterxml/jackson/core/io/doubleparser</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>de.jjohannes</groupId>
<artifactId>gradle-module-metadata-maven-plugin</artifactId>
<!-- 27-Apr-2023, tatu: [core#999]: Need to exclude shaded FDP
dependency from GMM
-->
<configuration>
<removedDependencies>
<dependency>
<groupId>ch.randelshofer</groupId>
<artifactId>fastdoubleparser</artifactId>
</dependency>
</removedDependencies>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Multi-Release>true</Multi-Release>
</manifestEntries>
</archive>
</configuration>
</plugin>
<!-- 23-Mar-2023, tatu: [core#965] Need to put back Gradle module metadata marker -->
<plugin>
<groupId>io.github.floverfelt</groupId>
<artifactId>find-and-replace-maven-plugin</artifactId>
<version>1.1.0</version>
<executions>
<execution>
<id>exec</id>
<phase>package</phase>
<goals>
<goal>find-and-replace</goal>
</goals>
<configuration>
<replacementType>file-contents</replacementType>
<baseDir>${basedir}</baseDir>
<findRegex><![CDATA[<modelVersion>4.0.0</modelVersion>]]></findRegex>
<fileMask>dependency-reduced-pom.xml</fileMask>
<replaceValue><![CDATA[ <!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>]]></replaceValue>
<recursive>false</recursive>
</configuration>
</execution>
</executions>
</plugin>
<!-- 16-Nov-2022, tatu: [core#838] add verification of compatibility
wrt Android SDK versions using AnimalSniffer with "gummy bears" signatures.
To be run from CI, but manually with:
mvn clean package animal-sniffer:check
-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.22</version>
<configuration>
<signature>
<groupId>com.toasttab.android</groupId>
<artifactId>gummy-bears-api-${version.android.sdk}</artifactId>
<version>${version.android.sdk.signature}</version>
</signature>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>ch.randelshofer</groupId>
<artifactId>fastdoubleparser</artifactId>
<version>0.9.0</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -5,7 +5,8 @@ import (
"fmt"
"regexp"
"github.com/anchore/syft/internal"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -51,7 +52,7 @@ func parseYarnLock(resolver file.Resolver, _ *generic.Environment, reader file.L
var pkgs []pkg.Package
scanner := bufio.NewScanner(reader)
parsedPackages := internal.NewStringSet()
parsedPackages := strset.New()
currentPackage := noPackage
currentVersion := noVersion
@@ -60,7 +61,7 @@ func parseYarnLock(resolver file.Resolver, _ *generic.Environment, reader file.L
if packageName := findPackageName(line); packageName != noPackage {
// When we find a new package, check if we have unsaved identifiers
if currentPackage != noPackage && currentVersion != noVersion && !parsedPackages.Contains(currentPackage+"@"+currentVersion) {
if currentPackage != noPackage && currentVersion != noVersion && !parsedPackages.Has(currentPackage+"@"+currentVersion) {
pkgs = append(pkgs, newYarnLockPackage(resolver, reader.Location, currentPackage, currentVersion))
parsedPackages.Add(currentPackage + "@" + currentVersion)
}
@@ -68,7 +69,7 @@ func parseYarnLock(resolver file.Resolver, _ *generic.Environment, reader file.L
currentPackage = packageName
} else if version := findPackageVersion(line); version != noVersion {
currentVersion = version
} else if packageName, version := findPackageAndVersion(line); packageName != noPackage && version != noVersion && !parsedPackages.Contains(packageName+"@"+version) {
} else if packageName, version := findPackageAndVersion(line); packageName != noPackage && version != noVersion && !parsedPackages.Has(packageName+"@"+version) {
pkgs = append(pkgs, newYarnLockPackage(resolver, reader.Location, packageName, version))
parsedPackages.Add(packageName + "@" + version)
@@ -79,7 +80,7 @@ func parseYarnLock(resolver file.Resolver, _ *generic.Environment, reader file.L
}
// check if we have valid unsaved data after end-of-file has reached
if currentPackage != noPackage && currentVersion != noVersion && !parsedPackages.Contains(currentPackage+"@"+currentVersion) {
if currentPackage != noPackage && currentVersion != noVersion && !parsedPackages.Has(currentPackage+"@"+currentVersion) {
pkgs = append(pkgs, newYarnLockPackage(resolver, reader.Location, currentPackage, currentVersion))
parsedPackages.Add(currentPackage + "@" + currentVersion)
}
@@ -9,7 +9,8 @@ import (
"strconv"
"strings"
"github.com/anchore/syft/internal"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
@@ -105,7 +106,7 @@ func addLicenses(resolver file.Resolver, dbLocation file.Location, p *pkg.Packag
return
}
findings := internal.NewStringSet()
findings := strset.New()
scanner := bufio.NewScanner(licenseReader)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
@@ -115,7 +116,7 @@ func addLicenses(resolver file.Resolver, dbLocation file.Location, p *pkg.Packag
}
}
licenseCandidates := findings.ToSlice()
licenseCandidates := findings.List()
p.Licenses = pkg.NewLicenseSet(pkg.NewLicensesFromLocation(*location, licenseCandidates...)...)
p.Locations.Add(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
}
+8 -1
View File
@@ -29,5 +29,12 @@ func NewPythonIndexCataloger(cfg CatalogerConfig) *generic.Cataloger {
// NewPythonPackageCataloger returns a new cataloger for python packages within egg or wheel installation directories.
func NewPythonPackageCataloger() *generic.Cataloger {
return generic.NewCataloger("python-package-cataloger").
WithParserByGlobs(parseWheelOrEgg, eggInfoGlob, "**/*dist-info/METADATA", "**/*egg-info/PKG-INFO")
WithParserByGlobs(
parseWheelOrEgg,
eggInfoGlob,
"**/*dist-info/METADATA",
"**/*egg-info/PKG-INFO",
"**/*DIST-INFO/METADATA",
"**/*EGG-INFO/PKG-INFO",
)
}
@@ -69,6 +69,43 @@ func Test_PackageCataloger(t *testing.T) {
},
},
},
{
name: "egg-info directory case sensitive",
fixtures: []string{
"test-fixtures/casesensitive/EGG-INFO/PKG-INFO",
"test-fixtures/casesensitive/EGG-INFO/RECORD",
"test-fixtures/casesensitive/EGG-INFO/top_level.txt",
},
expectedPackage: pkg.Package{
Name: "requests",
Version: "2.22.0",
PURL: "pkg:pypi/requests@2.22.0",
Type: pkg.PythonPkg,
Language: pkg.Python,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocations("Apache 2.0", file.NewLocation("test-fixtures/casesensitive/EGG-INFO/PKG-INFO")),
),
FoundBy: "python-package-cataloger",
MetadataType: pkg.PythonPackageMetadataType,
Metadata: pkg.PythonPackageMetadata{
Name: "requests",
Version: "2.22.0",
Platform: "UNKNOWN",
Author: "Kenneth Reitz",
AuthorEmail: "me@kennethreitz.org",
SitePackagesRootPath: "test-fixtures/casesensitive",
Files: []pkg.PythonFileRecord{
{Path: "requests-2.22.0.dist-info/INSTALLER", Digest: &pkg.PythonFileDigest{"sha256", "zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg"}, Size: "4"},
{Path: "requests/__init__.py", Digest: &pkg.PythonFileDigest{"sha256", "PnKCgjcTq44LaAMzB-7--B2FdewRrE8F_vjZeaG9NhA"}, Size: "3921"},
{Path: "requests/__pycache__/__version__.cpython-38.pyc"},
{Path: "requests/__pycache__/utils.cpython-38.pyc"},
{Path: "requests/__version__.py", Digest: &pkg.PythonFileDigest{"sha256", "Bm-GFstQaFezsFlnmEMrJDe8JNROz9n2XXYtODdvjjc"}, Size: "436"},
{Path: "requests/utils.py", Digest: &pkg.PythonFileDigest{"sha256", "LtPJ1db6mJff2TJSJWKi7rBpzjPS3mSOrjC9zRhoD3A"}, Size: "30049"},
},
TopLevelPackages: []string{"requests"},
},
},
},
{
name: "dist-info directory",
fixtures: []string{
@@ -109,6 +146,46 @@ func Test_PackageCataloger(t *testing.T) {
},
},
},
{
name: "dist-info directory case sensitive",
fixtures: []string{
"test-fixtures/casesensitive/DIST-INFO/METADATA",
"test-fixtures/casesensitive/DIST-INFO/RECORD",
"test-fixtures/casesensitive/DIST-INFO/top_level.txt",
"test-fixtures/casesensitive/DIST-INFO/direct_url.json",
},
expectedPackage: pkg.Package{
Name: "Pygments",
Version: "2.6.1",
PURL: "pkg:pypi/Pygments@2.6.1?vcs_url=git+https://github.com/python-test/test.git%40aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
Type: pkg.PythonPkg,
Language: pkg.Python,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromLocations("BSD License", file.NewLocation("test-fixtures/casesensitive/DIST-INFO/METADATA")),
),
FoundBy: "python-package-cataloger",
MetadataType: pkg.PythonPackageMetadataType,
Metadata: pkg.PythonPackageMetadata{
Name: "Pygments",
Version: "2.6.1",
Platform: "any",
Author: "Georg Brandl",
AuthorEmail: "georg@python.org",
SitePackagesRootPath: "test-fixtures/casesensitive",
Files: []pkg.PythonFileRecord{
{Path: "../../../bin/pygmentize", Digest: &pkg.PythonFileDigest{"sha256", "dDhv_U2jiCpmFQwIRHpFRLAHUO4R1jIJPEvT_QYTFp8"}, Size: "220"},
{Path: "Pygments-2.6.1.dist-info/AUTHORS", Digest: &pkg.PythonFileDigest{"sha256", "PVpa2_Oku6BGuiUvutvuPnWGpzxqFy2I8-NIrqCvqUY"}, Size: "8449"},
{Path: "Pygments-2.6.1.dist-info/RECORD"},
{Path: "pygments/__pycache__/__init__.cpython-38.pyc"},
{Path: "pygments/util.py", Digest: &pkg.PythonFileDigest{"sha256", "586xXHiJGGZxqk5PMBu3vBhE68DLuAe5MBARWrSPGxA"}, Size: "10778"},
{Path: "pygments/x_util.py", Digest: &pkg.PythonFileDigest{"sha256", "qpzzsOW31KT955agi-7NS--90I0iNiJCyLJQnRCHgKI="}, Size: "10778"},
},
TopLevelPackages: []string{"pygments", "something_else"},
DirectURLOrigin: &pkg.PythonDirectURLOriginInfo{URL: "https://github.com/python-test/test.git", VCS: "git", CommitID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
},
},
},
{
name: "malformed-record",
fixtures: []string{
@@ -278,6 +355,8 @@ func Test_PackageCataloger_Globs(t *testing.T) {
name: "obtain index files",
fixture: "test-fixtures/glob-paths",
expected: []string{
"site-packages/v.DIST-INFO/METADATA",
"site-packages/w.EGG-INFO/PKG-INFO",
"site-packages/x.dist-info/METADATA",
"site-packages/y.egg-info/PKG-INFO",
"site-packages/z.egg-info",
@@ -0,0 +1,47 @@
Metadata-Version: 2.1
Name: Pygments
Version: 2.6.1
Summary: Pygments is a syntax highlighting package written in Python.
Home-page: https://pygments.org/
Author: Georg Brandl
Author-email: georg@python.org
License: BSD License
Keywords: syntax highlighting
Platform: any
Classifier: License :: OSI Approved :: BSD License
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: Intended Audience :: System Administrators
Classifier: Development Status :: 6 - Mature
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Operating System :: OS Independent
Classifier: Topic :: Text Processing :: Filters
Classifier: Topic :: Utilities
Requires-Python: >=3.5
Pygments
~~~~~~~~
Pygments is a syntax highlighting package written in Python.
It is a generic syntax highlighter suitable for use in code hosting, forums,
wikis or other applications that need to prettify source code. Highlights
are:
* a wide range of over 500 languages and other text formats is supported
* special attention is paid to details, increasing quality by a fair amount
* support for new languages and formats are added easily
* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image formats that PIL supports and ANSI sequences
* it is usable as a command-line tool and as a library
:copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.
:license: BSD, see LICENSE for details.
@@ -0,0 +1,6 @@
../../../bin/pygmentize,sha256=dDhv_U2jiCpmFQwIRHpFRLAHUO4R1jIJPEvT_QYTFp8,220
Pygments-2.6.1.dist-info/AUTHORS,sha256=PVpa2_Oku6BGuiUvutvuPnWGpzxqFy2I8-NIrqCvqUY,8449
Pygments-2.6.1.dist-info/RECORD,,
pygments/__pycache__/__init__.cpython-38.pyc,,
pygments/util.py,sha256=586xXHiJGGZxqk5PMBu3vBhE68DLuAe5MBARWrSPGxA,10778
pygments/x_util.py,sha256=qpzzsOW31KT955agi-7NS--90I0iNiJCyLJQnRCHgKI=,10778
@@ -0,0 +1 @@
{"url": "https://github.com/python-test/test.git", "vcs_info": {"commit_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "vcs": "git"}}
@@ -0,0 +1,2 @@
pygments
something_else
@@ -0,0 +1,134 @@
Metadata-Version: 2.1
Name: requests
Version: 2.22.0
Summary: Python HTTP for Humans.
Home-page: http://python-requests.org
Author: Kenneth Reitz
Author-email: me@kennethreitz.org
License: Apache 2.0
Description: Requests: HTTP for Humans™
==========================
[![image](https://img.shields.io/pypi/v/requests.svg)](https://pypi.org/project/requests/)
[![image](https://img.shields.io/pypi/l/requests.svg)](https://pypi.org/project/requests/)
[![image](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests/)
[![codecov.io](https://codecov.io/github/requests/requests/coverage.svg?branch=master)](https://codecov.io/github/requests/requests)
[![image](https://img.shields.io/github/contributors/requests/requests.svg)](https://github.com/requests/requests/graphs/contributors)
[![image](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://saythanks.io/to/kennethreitz)
Requests is the only *Non-GMO* HTTP library for Python, safe for human
consumption.
![image](https://farm5.staticflickr.com/4317/35198386374_1939af3de6_k_d.jpg)
Behold, the power of Requests:
``` {.sourceCode .python}
>>> import requests
>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
>>> r.status_code
200
>>> r.headers['content-type']
'application/json; charset=utf8'
>>> r.encoding
'utf-8'
>>> r.text
u'{"type":"User"...'
>>> r.json()
{u'disk_usage': 368627, u'private_gists': 484, ...}
```
See [the similar code, sans Requests](https://gist.github.com/973705).
[![image](https://raw.githubusercontent.com/requests/requests/master/docs/_static/requests-logo-small.png)](http://docs.python-requests.org/)
Requests allows you to send *organic, grass-fed* HTTP/1.1 requests,
without the need for manual labor. There's no need to manually add query
strings to your URLs, or to form-encode your POST data. Keep-alive and
HTTP connection pooling are 100% automatic, thanks to
[urllib3](https://github.com/shazow/urllib3).
Besides, all the cool kids are doing it. Requests is one of the most
downloaded Python packages of all time, pulling in over 11,000,000
downloads every month. You don't want to be left out!
Feature Support
---------------
Requests is ready for today's web.
- International Domains and URLs
- Keep-Alive & Connection Pooling
- Sessions with Cookie Persistence
- Browser-style SSL Verification
- Basic/Digest Authentication
- Elegant Key/Value Cookies
- Automatic Decompression
- Automatic Content Decoding
- Unicode Response Bodies
- Multipart File Uploads
- HTTP(S) Proxy Support
- Connection Timeouts
- Streaming Downloads
- `.netrc` Support
- Chunked Requests
Requests officially supports Python 2.7 & 3.4–3.7, and runs great on
PyPy.
Installation
------------
To install Requests, simply use [pipenv](http://pipenv.org/) (or pip, of
course):
``` {.sourceCode .bash}
$ pipenv install requests
✨🍰✨
```
Satisfaction guaranteed.
Documentation
-------------
Fantastic documentation is available at
<http://docs.python-requests.org/>, for a limited time only.
How to Contribute
-----------------
1. Become more familiar with the project by reading our [Contributor's Guide](http://docs.python-requests.org/en/latest/dev/contributing/) and our [development philosophy](http://docs.python-requests.org/en/latest/dev/philosophy/).
2. Check for open issues or open a fresh issue to start a discussion
around a feature idea or a bug. There is a [Contributor
Friendly](https://github.com/requests/requests/issues?direction=desc&labels=Contributor+Friendly&page=1&sort=updated&state=open)
tag for issues that should be ideal for people who are not very
familiar with the codebase yet.
3. Fork [the repository](https://github.com/requests/requests) on
GitHub to start making your changes to the **master** branch (or
branch off of it).
4. Write a test which shows that the bug was fixed or that the feature
works as expected.
5. Send a pull request and bug the maintainer until it gets merged and
published. :) Make sure to add yourself to
[AUTHORS](https://github.com/requests/requests/blob/master/AUTHORS.rst).
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
Description-Content-Type: text/markdown
Provides-Extra: security
Provides-Extra: socks
@@ -0,0 +1,69 @@
Metadata-Version: 1.1
Name: mxnet
Version: 1.8.0
Summary: <!--- Licensed to the Apache Software Foundation (ASF) under one -->
<!--- or more contributor license agreements. See the NOTICE file -->
<!--- distributed with this work for additional information -->
<!--- regarding copyright ownership. The ASF licenses this file -->
<!--- to you under the Apache License, Version 2.0 (the -->
<!--- "License"); you may not use this file except in compliance -->
<!--- with the License. You may obtain a copy of the License at -->
<!--- http://www.apache.org/licenses/LICENSE-2.0 -->
<!--- Unless required by applicable law or agreed to in writing, -->
<!--- software distributed under the License is distributed on an -->
<!--- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -->
<!--- KIND, either express or implied. See the License for the -->
<!--- specific language governing permissions and limitations -->
<!--- under the License. -->
MXNet Python Package
====================
This directory and nested files contain MXNet Python package and language binding.
## Installation
To install MXNet Python package, visit MXNet [Install Instruction](https://mxnet.apache.org/get_started)
## Running the unit tests
For running unit tests, you will need the [nose PyPi package](https://pypi.python.org/pypi/nose). To install:
```bash
pip install --upgrade nose
```
Once ```nose``` is installed, run the following from MXNet root directory (please make sure the installation path of ```nosetests``` is included in your ```$PATH``` environment variable):
```
nosetests tests/python/unittest
nosetests tests/python/train
```
Home-page: https://github.com/apache/incubator-mxnet
Author: UNKNOWN
Author-email: UNKNOWN
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: C++
Classifier: Programming Language :: Cython
Classifier: Programming Language :: Other
Classifier: Programming Language :: Perl
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -0,0 +1,6 @@
requests-2.22.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
requests/__init__.py,sha256=PnKCgjcTq44LaAMzB-7--B2FdewRrE8F_vjZeaG9NhA,3921
requests/__pycache__/__version__.cpython-38.pyc,,
requests/__pycache__/utils.cpython-38.pyc,,
requests/__version__.py,sha256=Bm-GFstQaFezsFlnmEMrJDe8JNROz9n2XXYtODdvjjc,436
requests/utils.py,sha256=LtPJ1db6mJff2TJSJWKi7rBpzjPS3mSOrjC9zRhoD3A,30049
@@ -4,7 +4,8 @@ import (
"bufio"
"strings"
"github.com/anchore/syft/internal"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -13,7 +14,7 @@ import (
var _ generic.Parser = parseGemFileLockEntries
var sectionsOfInterest = internal.NewStringSet("GEM", "GIT", "PATH", "PLUGIN SOURCE")
var sectionsOfInterest = strset.New("GEM", "GIT", "PATH", "PLUGIN SOURCE")
// parseGemFileLockEntries is a parser function for Gemfile.lock contents, returning all Gems discovered.
func parseGemFileLockEntries(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
@@ -30,7 +31,7 @@ func parseGemFileLockEntries(_ file.Resolver, _ *generic.Environment, reader fil
// start of section
currentSection = sanitizedLine
continue
} else if !sectionsOfInterest.Contains(currentSection) {
} else if !sectionsOfInterest.Has(currentSection) {
// skip this line, we're in the wrong section
continue
}
@@ -6,6 +6,7 @@ import (
"fmt"
"io"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -67,7 +68,17 @@ func parsePackageResolved(_ file.Resolver, _ *generic.Environment, reader file.L
}
}
var pins, err = pinsForVersion(packageResolvedData, packageResolvedData["version"].(float64))
if packageResolvedData["version"] == nil {
log.Trace("no version found in Package.resolved file, skipping")
return nil, nil, nil
}
version, ok := packageResolvedData["version"].(float64)
if !ok {
return nil, nil, fmt.Errorf("failed to parse Package.resolved file: version is not a number")
}
var pins, err = pinsForVersion(packageResolvedData, version)
if err != nil {
return nil, nil, err
}
@@ -1,8 +1,12 @@
package swift
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -80,3 +84,24 @@ func TestParsePackageResolved(t *testing.T) {
pkgtest.TestFileParser(t, fixture, parsePackageResolved, expectedPkgs, expectedRelationships)
}
func TestParsePackageResolved_empty(t *testing.T) {
// regression for https://github.com/anchore/syft/issues/2225
fixture := "test-fixtures/empty-packages.resolved"
pkgtest.TestFileParser(t, fixture, parsePackageResolved, nil, nil)
dir := t.TempDir()
fixture = filepath.Join(dir, "Package.resolved")
_, err := os.Create(fixture)
require.NoError(t, err)
pkgtest.TestFileParser(t, fixture, parsePackageResolved, nil, nil)
}
func TestParsePackageResolved_versionNotANumber(t *testing.T) {
// regression for https://github.com/anchore/syft/issues/2225
fixture := "test-fixtures/bad-version-packages.resolved"
pkgtest.NewCatalogTester().FromFile(t, fixture).WithError().TestParser(t, parsePackageResolved)
}
@@ -0,0 +1,3 @@
{
"version" : "2"
}
@@ -0,0 +1 @@
{}
+42 -9
View File
@@ -14,16 +14,49 @@ var _ FileOwner = (*DpkgMetadata)(nil)
// DpkgMetadata represents all captured data for a Debian package DB entry; available fields are described
// at http://manpages.ubuntu.com/manpages/xenial/man1/dpkg-query.1.html in the --showformat section.
// Additional information about how these fields are used can be found at
// - https://www.debian.org/doc/debian-policy/ch-controlfields.html
// - https://www.debian.org/doc/debian-policy/ch-relationships.html
// - https://www.debian.org/doc/debian-policy/ch-binary.html#s-virtual-pkg
// - https://www.debian.org/doc/debian-policy/ch-relationships.html#s-virtual
type DpkgMetadata struct {
Package string `mapstructure:"Package" json:"package"`
Source string `mapstructure:"Source" json:"source" cyclonedx:"source"`
Version string `mapstructure:"Version" json:"version"`
SourceVersion string `mapstructure:"SourceVersion" json:"sourceVersion" cyclonedx:"sourceVersion"`
Architecture string `mapstructure:"Architecture" json:"architecture"`
Maintainer string `mapstructure:"Maintainer" json:"maintainer"`
InstalledSize int `mapstructure:"InstalledSize" json:"installedSize" cyclonedx:"installedSize"`
Description string `mapstructure:"Description" hash:"ignore" json:"-"`
Files []DpkgFileRecord `json:"files"`
Package string `json:"package"`
Source string `json:"source" cyclonedx:"source"`
Version string `json:"version"`
SourceVersion string `json:"sourceVersion" cyclonedx:"sourceVersion"`
// Architecture can include the following sets of values depending on context and the control file used:
// - a unique single word identifying a Debian machine architecture as described in Architecture specification string (https://www.debian.org/doc/debian-policy/ch-customized-programs.html#s-arch-spec) .
// - an architecture wildcard identifying a set of Debian machine architectures, see Architecture wildcards (https://www.debian.org/doc/debian-policy/ch-customized-programs.html#s-arch-wildcard-spec). any matches all Debian machine architectures and is the most frequently used.
// - "all", which indicates an architecture-independent package.
// - "source", which indicates a source package.
Architecture string `json:"architecture"`
// Maintainer is the package maintainer’s name and email address. The name must come first, then the email
// address inside angle brackets <> (in RFC822 format).
Maintainer string `json:"maintainer"`
InstalledSize int `json:"installedSize" cyclonedx:"installedSize"`
// Description contains a description of the binary package, consisting of two parts, the synopsis or the short
// description, and the long description (in a multiline format).
Description string `hash:"ignore" json:"-"`
// Provides is a virtual package that is provided by one or more packages. A virtual package is one which appears
// in the Provides control field of another package. The effect is as if the package(s) which provide a particular
// virtual package name had been listed by name everywhere the virtual package name appears. (See also Virtual packages)
Provides []string `json:"provides,omitempty"`
// Depends This declares an absolute dependency. A package will not be configured unless all of the packages listed in
// its Depends field have been correctly configured (unless there is a circular dependency).
Depends []string `json:"depends,omitempty"`
// PreDepends is like Depends, except that it also forces dpkg to complete installation of the packages named
// before even starting the installation of the package which declares the pre-dependency.
PreDepends []string `json:"preDepends,omitempty"`
Files []DpkgFileRecord `json:"files"`
}
// DpkgFileRecord represents a single file attributed to a debian package.
+36 -15
View File
@@ -4,7 +4,8 @@ import (
"fmt"
"sort"
"github.com/anchore/syft/internal"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
@@ -23,11 +24,11 @@ var _ sort.Interface = (*Licenses)(nil)
// this is different for licenses since we're only looking for evidence
// of where a license was declared/concluded for a given package
type License struct {
Value string `json:"value"`
SPDXExpression string `json:"spdxExpression"`
Type license.Type `json:"type"`
URLs internal.StringSet `hash:"ignore"`
Locations file.LocationSet `hash:"ignore"`
Value string
SPDXExpression string
Type license.Type
URLs []string `hash:"ignore"`
Locations file.LocationSet `hash:"ignore"`
}
type Licenses []License
@@ -77,7 +78,6 @@ func NewLicenseFromType(value string, t license.Type) License {
Value: value,
SPDXExpression: spdxExpression,
Type: t,
URLs: internal.NewStringSet(),
Locations: file.NewLocationSet(),
}
}
@@ -109,11 +109,16 @@ func NewLicenseFromLocations(value string, locations ...file.Location) License {
func NewLicenseFromURLs(value string, urls ...string) License {
l := NewLicense(value)
for _, u := range urls {
if u != "" {
l.URLs.Add(u)
s := strset.New()
for _, url := range urls {
if url != "" {
s.Add(url)
}
}
l.URLs = s.List()
sort.Strings(l.URLs)
return l
}
@@ -123,12 +128,15 @@ func NewLicenseFromFields(value, url string, location *file.Location) License {
l.Locations.Add(*location)
}
if url != "" {
l.URLs.Add(url)
l.URLs = append(l.URLs, url)
}
return l
}
// this is a bit of a hack to not infinitely recurse when hashing a license
// Merge two licenses into a new license object. If the merge is not possible due to unmergeable fields
// (e.g. different values for Value, SPDXExpression, Type, or any non-collection type) an error is returned.
// TODO: this is a bit of a hack to not infinitely recurse when hashing a license
func (s License) Merge(l License) (*License, error) {
sHash, err := artifact.IDByHash(s)
if err != nil {
@@ -145,11 +153,24 @@ func (s License) Merge(l License) (*License, error) {
return nil, fmt.Errorf("cannot merge licenses with different hash")
}
s.URLs.Add(l.URLs.ToSlice()...)
if s.Locations.Empty() && l.Locations.Empty() {
// try to keep s.URLs unallocated unless necessary (which is the default state from the constructor)
if len(l.URLs) > 0 {
s.URLs = append(s.URLs, l.URLs...)
}
if len(s.URLs) > 0 {
s.URLs = strset.New(s.URLs...).List()
sort.Strings(s.URLs)
}
if l.Locations.Empty() {
return &s, nil
}
s.Locations.Add(l.Locations.ToSlice()...)
// since the set instance has a reference type (map) we must make a new instance
locations := file.NewLocationSet(s.Locations.ToSlice()...)
locations.Add(l.Locations.ToSlice()...)
s.Locations = locations
return &s, nil
}
+29 -6
View File
@@ -3,9 +3,8 @@ package pkg
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/google/go-cmp/cmp"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/license"
)
@@ -97,7 +96,7 @@ func TestLicenseSet_Add(t *testing.T) {
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: internal.NewStringSet("https://example.com"),
URLs: []string{"https://example.com"},
Locations: file.NewLocationSet(file.NewLocation("/place")),
},
},
@@ -115,14 +114,13 @@ func TestLicenseSet_Add(t *testing.T) {
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Concluded,
URLs: internal.NewStringSet(),
Locations: file.NewLocationSet(),
},
{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: internal.NewStringSet("https://example.com"),
URLs: []string{"https://example.com"},
Locations: file.NewLocationSet(file.NewLocation("/place")),
},
},
@@ -133,7 +131,32 @@ func TestLicenseSet_Add(t *testing.T) {
s := NewLicenseSet()
s.Add(tt.licenses...)
testMe := s.ToSlice()
assert.Equal(t, tt.want, testMe)
if d := cmp.Diff(tt.want, testMe, cmp.Comparer(defaultLicenseComparer)); d != "" {
t.Errorf("unexpected license set (-want +got):\n%s", d)
}
})
}
}
func defaultLocationComparer(x, y file.Location) bool {
return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.VirtualPath, y.VirtualPath)
}
func defaultLicenseComparer(x, y License) bool {
return cmp.Equal(x, y, cmp.Comparer(defaultLocationComparer), cmp.Comparer(
func(x, y file.LocationSet) bool {
xs := x.ToSlice()
ys := y.ToSlice()
if len(xs) != len(ys) {
return false
}
for i, xe := range xs {
ye := ys[i]
if !defaultLocationComparer(xe, ye) {
return false
}
}
return true
},
))
}
+134 -5
View File
@@ -9,6 +9,7 @@ import (
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/license"
)
func Test_Hash(t *testing.T) {
@@ -18,11 +19,8 @@ func Test_Hash(t *testing.T) {
loc2 := file.NewLocation("place!")
loc2.FileSystemID = "fs2" // important! there is a different file system ID
lic1 := NewLicenseFromLocations("MIT", loc1)
lic2 := NewLicenseFromLocations("MIT", loc2)
lic1.URLs.Add("foo")
lic2.URLs.Add("bar") // we also want to check the URLs are ignored
lic1 := NewLicenseFromFields("MIT", "foo", &loc1)
lic2 := NewLicenseFromFields("MIT", "bar", &loc2)
hash1, err := artifact.IDByHash(lic1)
require.NoError(t, err)
@@ -97,3 +95,134 @@ func Test_Sort(t *testing.T) {
}
}
func TestLicense_Merge(t *testing.T) {
locA := file.NewLocation("a")
locB := file.NewLocation("b")
tests := []struct {
name string
subject License
other License
want License
wantErr require.ErrorAssertionFunc
}{
{
name: "valid merge",
subject: License{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: []string{
"b", "a",
},
Locations: file.NewLocationSet(locA),
},
other: License{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: []string{
"c", "d",
},
Locations: file.NewLocationSet(locB),
},
want: License{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: []string{
"a", "b", "c", "d",
},
Locations: file.NewLocationSet(locA, locB),
},
},
{
name: "mismatched value",
subject: License{
Value: "DIFFERENT!!",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: []string{
"b", "a",
},
Locations: file.NewLocationSet(locA),
},
other: License{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: []string{
"c", "d",
},
Locations: file.NewLocationSet(locB),
},
wantErr: require.Error,
},
{
name: "mismatched spdx expression",
subject: License{
Value: "MIT",
SPDXExpression: "DIFFERENT!!",
Type: license.Declared,
URLs: []string{
"b", "a",
},
Locations: file.NewLocationSet(locA),
},
other: License{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: []string{
"c", "d",
},
Locations: file.NewLocationSet(locB),
},
wantErr: require.Error,
},
{
name: "mismatched type",
subject: License{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Concluded,
URLs: []string{
"b", "a",
},
Locations: file.NewLocationSet(locA),
},
other: License{
Value: "MIT",
SPDXExpression: "MIT",
Type: license.Declared,
URLs: []string{
"c", "d",
},
Locations: file.NewLocationSet(locB),
},
wantErr: require.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = require.NoError
}
subjectLocationLen := len(tt.subject.Locations.ToSlice())
subjectURLLen := len(tt.subject.URLs)
got, err := tt.subject.Merge(tt.other)
tt.wantErr(t, err)
if err != nil {
return
}
require.NotNilf(t, got, "expected a non-nil license")
assert.Equal(t, tt.want, *got)
// prove we don't modify the subject
assert.Equal(t, subjectLocationLen, len(tt.subject.Locations.ToSlice()))
assert.Equal(t, subjectURLLen, len(tt.subject.URLs))
})
}
}
+39 -1
View File
@@ -1,9 +1,47 @@
package pkg
import "github.com/anchore/syft/syft/artifact"
import (
"sort"
"github.com/anchore/syft/syft/artifact"
)
func NewRelationships(catalog *Collection) []artifact.Relationship {
rels := RelationshipsByFileOwnership(catalog)
rels = append(rels, RelationshipsEvidentBy(catalog)...)
return rels
}
// SortRelationships takes a set of package-to-package relationships and sorts them in a stable order by name and version.
// Note: this does not consider package-to-other, other-to-package, or other-to-other relationships.
// TODO: ideally this should be replaced with a more type-agnostic sort function that resides in the artifact package.
func SortRelationships(rels []artifact.Relationship) {
sort.SliceStable(rels, func(i, j int) bool {
return relationshipLess(rels[i], rels[j])
})
}
func relationshipLess(i, j artifact.Relationship) bool {
iFrom, ok1 := i.From.(Package)
iTo, ok2 := i.To.(Package)
jFrom, ok3 := j.From.(Package)
jTo, ok4 := j.To.(Package)
if !(ok1 && ok2 && ok3 && ok4) {
return false
}
if iFrom.Name != jFrom.Name {
return iFrom.Name < jFrom.Name
}
if iFrom.Version != jFrom.Version {
return iFrom.Version < jFrom.Version
}
if iTo.Name != jTo.Name {
return iTo.Name < jTo.Name
}
if iTo.Version != jTo.Version {
return iTo.Version < jTo.Version
}
return i.Type < j.Type
}
@@ -247,6 +247,8 @@ var dirOnlyTestCases = []testCase{
"Serilog.Sinks.Console": "4.0.1",
"System.Diagnostics.DiagnosticSource": "6.0.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
"TestCommon": "1.0.0",
"TestLibrary": "1.0.0",
},
},
{
+28 -22
View File
@@ -4,13 +4,11 @@ import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger"
@@ -59,8 +57,8 @@ func BenchmarkImagePackageCatalogers(b *testing.B) {
func TestPkgCoverageImage(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope, nil)
observedLanguages := internal.NewStringSet()
definedLanguages := internal.NewStringSet()
observedLanguages := strset.New()
definedLanguages := strset.New()
for _, l := range pkg.AllLanguages {
definedLanguages.Add(l.String())
}
@@ -76,8 +74,8 @@ func TestPkgCoverageImage(t *testing.T) {
definedLanguages.Remove(pkg.Erlang.String())
definedLanguages.Remove(pkg.Elixir.String())
observedPkgs := internal.NewStringSet()
definedPkgs := internal.NewStringSet()
observedPkgs := strset.New()
definedPkgs := strset.New()
for _, p := range pkg.AllPkgs {
definedPkgs.Add(string(p))
}
@@ -148,29 +146,37 @@ func TestPkgCoverageImage(t *testing.T) {
observedPkgs.Remove(string(pkg.UnknownPkg))
definedPkgs.Remove(string(pkg.UnknownPkg))
missingLang := strset.Difference(definedLanguages, observedLanguages)
extraLang := strset.Difference(observedLanguages, definedLanguages)
// ensure that integration test cases stay in sync with the available catalogers
if diff := cmp.Diff(definedLanguages, observedLanguages); diff != "" {
t.Errorf("language coverage incomplete (languages=%d, coverage=%d)", len(definedLanguages), len(observedLanguages))
t.Errorf("definedLanguages mismatch observedLanguages (-want +got):\n%s", diff)
if missingLang.Size() > 0 || extraLang.Size() > 0 {
t.Errorf("language coverage incomplete (languages=%d, coverage=%d)", definedLanguages.Size(), observedLanguages.Size())
t.Errorf("unexpected languages: %s", extraLang.List())
t.Errorf("missing languages: %s", missingLang.List())
}
if diff := cmp.Diff(definedPkgs, observedPkgs); diff != "" {
t.Errorf("package coverage incomplete (packages=%d, coverage=%d)", len(definedPkgs), len(observedPkgs))
t.Errorf("definedPkgs mismatch observedPkgs (-want +got):\n%s", diff)
missingPkgs := strset.Difference(definedPkgs, observedPkgs)
extraPkgs := strset.Difference(observedPkgs, definedPkgs)
if missingPkgs.Size() > 0 || extraPkgs.Size() > 0 {
t.Errorf("package coverage incomplete (packages=%d, coverage=%d)", definedPkgs.Size(), observedPkgs.Size())
t.Errorf("unexpected packages: %s", extraPkgs.List())
t.Errorf("missing packages: %s", missingPkgs.List())
}
}
func TestPkgCoverageDirectory(t *testing.T) {
sbom, _ := catalogDirectory(t, "test-fixtures/image-pkg-coverage")
observedLanguages := internal.NewStringSet()
definedLanguages := internal.NewStringSet()
observedLanguages := strset.New()
definedLanguages := strset.New()
for _, l := range pkg.AllLanguages {
definedLanguages.Add(l.String())
}
observedPkgs := internal.NewStringSet()
definedPkgs := internal.NewStringSet()
observedPkgs := strset.New()
definedPkgs := strset.New()
for _, p := range pkg.AllPkgs {
definedPkgs.Add(string(p))
}
@@ -237,12 +243,12 @@ func TestPkgCoverageDirectory(t *testing.T) {
definedPkgs.Remove(string(pkg.KbPkg))
// ensure that integration test commonTestCases stay in sync with the available catalogers
if len(observedLanguages) < len(definedLanguages) {
t.Errorf("language coverage incomplete (languages=%d, coverage=%d)", len(definedLanguages), len(observedLanguages))
if observedLanguages.Size() < definedLanguages.Size() {
t.Errorf("language coverage incomplete (languages=%d, coverage=%d)", definedLanguages.Size(), observedLanguages.Size())
}
if len(observedPkgs) < len(definedPkgs) {
t.Errorf("package coverage incomplete (packages=%d, coverage=%d)", len(definedPkgs), len(observedPkgs))
if observedPkgs.Size() < definedPkgs.Size() {
t.Errorf("package coverage incomplete (packages=%d, coverage=%d)", definedPkgs.Size(), observedPkgs.Size())
}
}
@@ -251,8 +257,8 @@ func TestPkgCoverageCatalogerConfiguration(t *testing.T) {
// for which that cataloger isn't enabled by defauly
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope, []string{"rust"})
observedLanguages := internal.NewStringSet()
definedLanguages := internal.NewStringSet()
observedLanguages := strset.New()
definedLanguages := strset.New()
definedLanguages.Add("rust")
for actualPkg := range sbom.Artifacts.Packages.Enumerate() {
+10 -9
View File
@@ -5,14 +5,15 @@ import (
"strings"
"testing"
"github.com/anchore/syft/internal"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/syft/pkg"
)
func TestNpmPackageLockDirectory(t *testing.T) {
sbom, _ := catalogDirectory(t, "test-fixtures/npm-lock")
foundPackages := internal.NewStringSet()
foundPackages := strset.New()
for actualPkg := range sbom.Artifacts.Packages.Enumerate(pkg.NpmPkg) {
for _, actualLocation := range actualPkg.Locations.ToSlice() {
@@ -25,16 +26,16 @@ func TestNpmPackageLockDirectory(t *testing.T) {
// ensure that integration test commonTestCases stay in sync with the available catalogers
const expectedPackageCount = 6
if len(foundPackages) != expectedPackageCount {
t.Errorf("found the wrong set of npm package-lock.json packages (expected: %d, actual: %d)", expectedPackageCount, len(foundPackages))
if foundPackages.Size() != expectedPackageCount {
t.Errorf("found the wrong set of npm package-lock.json packages (expected: %d, actual: %d)", expectedPackageCount, foundPackages.Size())
}
}
func TestYarnPackageLockDirectory(t *testing.T) {
sbom, _ := catalogDirectory(t, "test-fixtures/yarn-lock")
foundPackages := internal.NewStringSet()
expectedPackages := internal.NewStringSet("async@0.9.2", "async@3.2.3", "merge-objects@1.0.5", "should-type@1.3.0", "@4lolo/resize-observer-polyfill@1.5.2")
foundPackages := strset.New()
expectedPackages := strset.New("async@0.9.2", "async@3.2.3", "merge-objects@1.0.5", "should-type@1.3.0", "@4lolo/resize-observer-polyfill@1.5.2")
for actualPkg := range sbom.Artifacts.Packages.Enumerate(pkg.NpmPkg) {
for _, actualLocation := range actualPkg.Locations.ToSlice() {
@@ -46,9 +47,9 @@ func TestYarnPackageLockDirectory(t *testing.T) {
}
// ensure that integration test commonTestCases stay in sync with the available catalogers
if len(foundPackages) != len(expectedPackages) {
t.Errorf("found the wrong set of yarn.lock packages (expected: %d, actual: %d)", len(expectedPackages), len(foundPackages))
if foundPackages.Size() != expectedPackages.Size() {
t.Errorf("found the wrong set of yarn.lock packages (expected: %d, actual: %d)", expectedPackages.Size(), foundPackages.Size())
} else if !reflect.DeepEqual(foundPackages, expectedPackages) {
t.Errorf("found the wrong set of yarn.lock packages (expected: %+q, actual: %+q)", expectedPackages.ToSlice(), foundPackages.ToSlice())
t.Errorf("found the wrong set of yarn.lock packages (expected: %+q, actual: %+q)", expectedPackages.List(), foundPackages.List())
}
}