Compare commits

..
Author SHA1 Message Date
Keith Zantow 4c3e49957c chore: more lenient java groupID lookups
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-08-17 14:38:28 -04:00
Keith Zantow 4762ba0943 feat: use java package names to determine known groupids (#2032)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-08-17 12:55:25 -04:00
Keith Zantow d1635971a1 fix: inconsistent removal of binaries by overlap (#2036)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-08-17 15:27:31 +00:00
9467bd66c2 fix: CycloneDX relationships not output or decoded properly (#1974)
Signed-off-by: Mark Galpin <mark@tidelift.com>
Signed-off-by: Keith Zantow <kzantow@gmail.com>
Co-authored-by: Mark Galpin <mark@tidelift.com>
Co-authored-by: Keith Zantow <kzantow@gmail.com>
2023-08-17 11:02:12 -04:00
Keith Zantow 59107324ce chore: restore cataloger.DefaultConfig (#2028)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-08-14 20:28:07 +00:00
Keith Zantow b3d7ba569b fix: read direct package files when decoding SPDX tag-value (#2014)
* fix: read direct package files when decoding SPDX tag-value

---------

Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-08-14 11:37:24 -04:00
anchore-actions-token-generator[bot]andspiffcs c7fe58683d chore(deps): update bootstrap tools to latest versions (#2022)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: spiffcs <spiffcs@users.noreply.github.com>
2023-08-14 11:36:15 -04:00
anchore-actions-token-generator[bot]andwagoodman 28b06dae25 chore(deps): update CPE dictionary index (#2025)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: wagoodman <wagoodman@users.noreply.github.com>
2023-08-14 11:35:57 -04:00
anchore-actions-token-generator[bot]andspiffcs a90cff1cd2 chore(deps): update bootstrap tools to latest versions (#2012)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: spiffcs <spiffcs@users.noreply.github.com>
2023-08-10 13:20:09 -04:00
dependabot[bot]andChristopher Phillips 82eafeaf4a chore(deps): bump github.com/vifraa/gopom from 0.2.2 to 1.0.0 (#2008)
* chore(deps): bump github.com/vifraa/gopom from 0.2.2 to 1.0.0
* refactor: update consumer code to use new optional values

Bumps [github.com/vifraa/gopom](https://github.com/vifraa/gopom) from 0.2.2 to 1.0.0.
- [Release notes](https://github.com/vifraa/gopom/releases)
- [Commits](https://github.com/vifraa/gopom/compare/v0.2.2...v1.0.0)

---
updated-dependencies:
- dependency-name: github.com/vifraa/gopom
  dependency-type: direct:production
  update-type: version-update:semver-major
...
---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Christopher Phillips <christopher.phillips@anchore.com>
2023-08-09 17:22:51 -04:00
Christopher Angelo Phillips 541c8d339b 1948-filter-pkg-by-type (#2011)
---------

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2023-08-09 16:05:52 -04:00
dependabot[bot] 6bf6f85584 chore(deps): bump github.com/dave/jennifer from 1.6.1 to 1.7.0 (#2009)
Bumps [github.com/dave/jennifer](https://github.com/dave/jennifer) from 1.6.1 to 1.7.0.
- [Commits](https://github.com/dave/jennifer/compare/v1.6.1...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/dave/jennifer
  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-08-09 14:46:11 -04:00
Keith Zantow c7272fd6a5 fix: SPDX license values and download location (#2007)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-08-08 15:55:50 -04:00
Christopher Angelo Phillips 466da7cbda 931: binary cataloger exclusion defaults for ownership by overlap (#1948)
Fixes #931

PR #1948 introduces a new implicit exclusion for binary packages that overlap by file ownership and have certain characteristics:

1) the relationship between packages is OwnershipByFileOverlap
2) the parent package is an "os" package - see changelog for included catalogers
3) the child is a synthetic package generated by the binary cataloger - see changelog for included catalogers
4) the package names are identical

---------

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2023-08-08 13:00:52 -04:00
dependabot[bot] 2fc65094b7 chore(deps): bump golang.org/x/net from 0.13.0 to 0.14.0 (#2004)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/net/compare/v0.13.0...v0.14.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-08-07 10:34:00 -04:00
dependabot[bot] d7ff77072a chore(deps): bump modernc.org/sqlite from 1.24.0 to 1.25.0 (#1998)
Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.24.0 to 1.25.0.
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.24.0...v1.25.0)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  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-08-04 14:24:31 -04:00
Christopher Angelo Phillips 78660022bf test: add coverage for new rpmdb paths (#1999)
Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2023-08-04 13:04:36 -04:00
Keith Zantow aaf767f8d3 chore: improve spdx purl decoding (#1996)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-08-04 11:43:21 -04:00
Keith Zantow 79014ed8c8 fix: gradle lockfile parser groupId handling (#1995)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-08-04 11:42:26 -04:00
Christopher Angelo Phillips e774006052 fix: update glob to use newer usr/lib/sysimage path (#1997)
See this link for details on the path migration for the rpmdb
https://fedoraproject.org/wiki/Changes/RelocateRPMToUsr

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2023-08-03 16:23:50 -07:00
Nicholas R. Smith 1d6d5f7f5f fix: opkg search glob (#1994)
Signed-off-by: Nicholas R. Smith <nicholas_smith@selinc.com>
2023-08-03 19:33:11 +00:00
Sem Provoost 433a7b8a42 feat: nginx binary classifier (#1988)
Signed-off-by: SemProvoost <27961543+SemProvoost@users.noreply.github.com>
2023-08-03 13:09:31 -04:00
Nicholas R. SmithandNicholas R. Smith e55277f26d Expand deb cataloger to include opkg (#1985)
* Add opkg info directory and status file to deb cataloger

opkg uses the same or nearly the same metadata and structure as Debian:
**/lib/opkg/status lists status information for all packages
**/lib/opkg/info/opkg.conffiles is a list of configuration files
**/lib/opkg/info/*.list contains files and directories installed by the package
**/lib/opkg/info/*.preinst are scripts to run before installation
**/lib/opkg/info/*.postinst are scripts to run after installation
**/lib/opkg/info/*.postrm are scripts to run after package removal
**/lib/opkg/info/*.control provides package metadata

Signed-off-by: Nicholas R. Smith <nicholas_smith@selinc.com>

---------

Signed-off-by: Nicholas R. Smith <nicholas_smith@selinc.com>
Co-authored-by: Nicholas R. Smith <nicholas_smith@selinc.com>
2023-08-03 12:33:14 -04:00
anchore-actions-token-generator[bot]andspiffcs c2b4231cc3 chore(deps): update bootstrap tools to latest versions (#1991)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: spiffcs <spiffcs@users.noreply.github.com>
2023-08-03 10:53:29 -04:00
dependabot[bot] c150b4e358 chore(deps): bump github.com/google/go-containerregistry (#1993)
Bumps [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) from 0.15.2 to 0.16.1.
- [Release notes](https://github.com/google/go-containerregistry/releases)
- [Changelog](https://github.com/google/go-containerregistry/blob/main/.goreleaser.yml)
- [Commits](https://github.com/google/go-containerregistry/compare/v0.15.2...v0.16.1)

---
updated-dependencies:
- dependency-name: github.com/google/go-containerregistry
  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-08-03 10:53:09 -04:00
Keith Zantow 3f0475efb7 chore: update bubbly to fix hanging (#1990)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-08-02 10:28:35 -04:00
dependabot[bot] 2e376d067f chore(deps): bump golang.org/x/net from 0.12.0 to 0.13.0 (#1989) 2023-08-02 14:16:49 +00:00
Christopher Angelo Phillips 8e893dfc20 feat: use originator logic to fill supplier (#1980)
* feat: use Originator to fill supplier for NTIA minimum
---------
Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2023-08-01 17:19:49 -04:00
Alex Goodman 756d0f29af add metadata types to all cpe test fixtures (#1982)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2023-07-31 16:35:09 -04:00
Keith Zantow e2f7befbfb fix: default image source name to user input (#1979)
* fix: default image source name to user input

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

* chore: add test

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

---------

Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-07-31 17:29:18 +00:00
68 changed files with 1409 additions and 349 deletions
+3 -3
View File
@@ -10,11 +10,11 @@ CHRONICLE_CMD = $(TEMP_DIR)/chronicle
GLOW_CMD = $(TEMP_DIR)/glow
# Tool versions #################################
GOLANGCILINT_VERSION := v1.53.3
GOLANGCILINT_VERSION := v1.54.1
GOSIMPORTS_VERSION := v0.3.8
BOUNCER_VERSION := v0.4.0
CHRONICLE_VERSION := v0.6.0
GORELEASER_VERSION := v1.19.2
CHRONICLE_VERSION := v0.7.0
GORELEASER_VERSION := v1.20.0
YAJSV_VERSION := v1.4.1
COSIGN_VERSION := v2.1.1
QUILL_VERSION := v0.2.0
+4
View File
@@ -482,6 +482,10 @@ default-image-pull-source: ""
# - "./out/**/*.json"
exclude: []
# allows users to exclude synthetic binary packages from the sbom
# these packages are removed if an overlap with a non-synthetic package is found
exclude-binary-overlap-by-ownership: true
# os and/or architecture to use when referencing container images (e.g. "windows/armv6" or "arm64")
# same as --platform; SYFT_PLATFORM env var
platform: ""
+15 -15
View File
@@ -37,13 +37,13 @@ require (
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.4
github.com/vifraa/gopom v0.2.2
github.com/vifraa/gopom v1.0.0
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/mod v0.12.0
golang.org/x/net v0.12.0
golang.org/x/term v0.10.0
golang.org/x/net v0.14.0
golang.org/x/term v0.11.0
gopkg.in/yaml.v2 v2.4.0
)
@@ -51,14 +51,14 @@ require (
github.com/CycloneDX/cyclonedx-go v0.7.1
github.com/Masterminds/semver v1.5.0
github.com/Masterminds/sprig/v3 v3.2.3
github.com/anchore/bubbly v0.0.0-20230712165553-812110ab0a10
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461
github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0
github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe
github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/charmbracelet/bubbletea v0.24.2
github.com/charmbracelet/lipgloss v0.7.1
github.com/dave/jennifer v1.6.1
github.com/dave/jennifer v1.7.0
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
github.com/docker/distribution v2.8.2+incompatible
github.com/docker/docker v24.0.5+incompatible
@@ -66,7 +66,7 @@ require (
github.com/gkampitakis/go-snaps v0.4.7
github.com/go-git/go-billy/v5 v5.4.1
github.com/go-git/go-git/v5 v5.8.1
github.com/google/go-containerregistry v0.15.2
github.com/google/go-containerregistry v0.16.1
github.com/google/licensecheck v0.3.1
github.com/invopop/jsonschema v0.7.0
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b
@@ -77,7 +77,7 @@ require (
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.24.0
modernc.org/sqlite v1.25.0
)
require (
@@ -101,7 +101,7 @@ require (
github.com/containerd/containerd v1.7.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/cli v23.0.5+incompatible // indirect
github.com/docker/cli v24.0.0+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
@@ -178,10 +178,10 @@ require (
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/tools v0.8.0 // indirect
golang.org/x/sync v0.2.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/tools v0.9.1 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.55.0 // indirect
@@ -191,9 +191,9 @@ require (
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/libc v1.24.1 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/memory v1.6.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
@@ -204,7 +204,7 @@ require (
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
)
retract (
+30 -29
View File
@@ -88,8 +88,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anchore/bubbly v0.0.0-20230712165553-812110ab0a10 h1:Wrqt9fd8ygEMyFtxncZU7RgW2qBu5CL1x876xIyUlPU=
github.com/anchore/bubbly v0.0.0-20230712165553-812110ab0a10/go.mod h1:Ger02eh5NpPm2IqkPAy396HU1KlK3BhOeCljDYXySSk=
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-20230602170917-e747e60c4aa0 h1:g0UqRW60JDrf5fb40RUyIwwcfQ3nAJqGj4aUCVTwFE4=
github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0/go.mod h1:0IQVIROfgRX4WZFMfgsbNZmMgLKqW/KgByyJDYvWiDE=
github.com/anchore/fangs v0.0.0-20230531202914-48a718c6b4ba h1:tJ186HK8e0Lf+hhNWX4fJrq14yj3mw8JQkkLhA0nFhE=
@@ -179,16 +179,16 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/dave/jennifer v1.6.1 h1:T4T/67t6RAA5AIV6+NP8Uk/BIsXgDoqEowgycdQQLuk=
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE=
github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE=
github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qeXMx3O0wqM=
github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
@@ -325,8 +325,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
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-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE=
github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q=
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=
github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs=
github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY=
@@ -687,8 +687,8 @@ github.com/vbatts/go-mtree v0.5.3 h1:S/jYlfG8rZ+a0bhZd+RANXejy7M4Js8fq9U+XoWTd5w
github.com/vbatts/go-mtree v0.5.3/go.mod h1:eXsdoPMdL2jcJx6HweWi9lYQxBsTp4lNhqqAjgkZUg8=
github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck=
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
github.com/vifraa/gopom v0.2.2 h1:zrqoCUVIplcsETouv3xxHPvfI/WV1GUPrdX2+Diahzo=
github.com/vifraa/gopom v0.2.2/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0=
github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA=
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20=
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 h1:lwgTsTy18nYqASnH58qyfRW/ldj7Gt2zzBvgYPzdA4s=
@@ -750,8 +750,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -843,8 +843,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.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
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=
@@ -874,8 +874,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -957,16 +958,16 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -979,8 +980,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1043,8 +1044,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1243,16 +1244,16 @@ modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.24.0 h1:EsClRIWHGhLTCX44p+Ri/JLD+vFGo0QGjasg2/F9TlI=
modernc.org/sqlite v1.24.0/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
+30 -27
View File
@@ -42,31 +42,32 @@ type Application struct {
ConfigPath string `yaml:"configPath,omitempty" json:"configPath" mapstructure:"config"`
Verbosity uint `yaml:"verbosity,omitempty" json:"verbosity" mapstructure:"verbosity"`
// -q, indicates to not show any status output to stderr (ETUI or logging UI)
Quiet bool `yaml:"quiet" json:"quiet" mapstructure:"quiet"`
Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, the format to use for output
OutputTemplatePath string `yaml:"output-template-path" json:"output-template-path" mapstructure:"output-template-path"` // -t template file to use for output
File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to
CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not
Dev development `yaml:"dev" json:"dev" mapstructure:"dev"`
Log logging `yaml:"log" json:"log" mapstructure:"log"` // all logging-related options
Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"`
Package pkg `yaml:"package" json:"package" mapstructure:"package"`
Golang golang `yaml:"golang" json:"golang" mapstructure:"golang"`
LinuxKernel linuxKernel `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"`
Python python `yaml:"python" json:"python" mapstructure:"python"`
Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"`
FileMetadata FileMetadata `yaml:"file-metadata" json:"file-metadata" mapstructure:"file-metadata"`
FileClassification fileClassification `yaml:"file-classification" json:"file-classification" mapstructure:"file-classification"`
FileContents fileContents `yaml:"file-contents" json:"file-contents" mapstructure:"file-contents"`
Secrets secrets `yaml:"secrets" json:"secrets" mapstructure:"secrets"`
Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"`
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
Name string `yaml:"name" json:"name" mapstructure:"name"`
Source sourceCfg `yaml:"source" json:"source" mapstructure:"source"`
Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel
DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` // specify default image pull source
BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths
Quiet bool `yaml:"quiet" json:"quiet" mapstructure:"quiet"`
Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, the format to use for output
OutputTemplatePath string `yaml:"output-template-path" json:"output-template-path" mapstructure:"output-template-path"` // -t template file to use for output
File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to
CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not
Dev development `yaml:"dev" json:"dev" mapstructure:"dev"`
Log logging `yaml:"log" json:"log" mapstructure:"log"` // all logging-related options
Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"`
Package pkg `yaml:"package" json:"package" mapstructure:"package"`
Golang golang `yaml:"golang" json:"golang" mapstructure:"golang"`
LinuxKernel linuxKernel `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"`
Python python `yaml:"python" json:"python" mapstructure:"python"`
Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"`
FileMetadata FileMetadata `yaml:"file-metadata" json:"file-metadata" mapstructure:"file-metadata"`
FileClassification fileClassification `yaml:"file-classification" json:"file-classification" mapstructure:"file-classification"`
FileContents fileContents `yaml:"file-contents" json:"file-contents" mapstructure:"file-contents"`
Secrets secrets `yaml:"secrets" json:"secrets" mapstructure:"secrets"`
Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"`
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
Name string `yaml:"name" json:"name" mapstructure:"name"`
Source sourceCfg `yaml:"source" json:"source" mapstructure:"source"`
Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel
DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` // specify default image pull source
BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths
ExcludeBinaryOverlapByOwnership bool `yaml:"exclude-binary-overlap-by-ownership" json:"exclude-binary-overlap-by-ownership" mapstructure:"exclude-binary-overlap-by-ownership"` // exclude synthetic binary packages owned by os package files
}
func (cfg Application) ToCatalogerConfig() cataloger.Config {
@@ -76,8 +77,9 @@ func (cfg Application) ToCatalogerConfig() cataloger.Config {
IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives,
Scope: cfg.Package.Cataloger.ScopeOpt,
},
Catalogers: cfg.Catalogers,
Parallelism: cfg.Parallelism,
Catalogers: cfg.Catalogers,
Parallelism: cfg.Parallelism,
ExcludeBinaryOverlapByOwnership: cfg.ExcludeBinaryOverlapByOwnership,
Golang: golangCataloger.NewGoCatalogerOpts().
WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses).
WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
@@ -221,6 +223,7 @@ func loadDefaultValues(v *viper.Viper) {
v.SetDefault("catalogers", nil)
v.SetDefault("parallelism", 1)
v.SetDefault("default-image-pull-source", "")
v.SetDefault("exclude-binary-overlap-by-ownership", true)
// for each field in the configuration struct, see if the field implements the defaultValueLoader interface and invoke it if it does
value := reflect.ValueOf(Application{})
@@ -210,24 +210,33 @@ func collectRelationships(bom *cyclonedx.BOM, s *sbom.SBOM, idMap map[string]int
return
}
for _, d := range *bom.Dependencies {
to, fromExists := idMap[d.Ref].(artifact.Identifiable)
if !fromExists {
continue
}
if d.Dependencies == nil {
continue
}
toPtr, toExists := idMap[d.Ref]
if !toExists {
continue
}
to, ok := common.PtrToStruct(toPtr).(artifact.Identifiable)
if !ok {
continue
}
for _, t := range *d.Dependencies {
from, toExists := idMap[t].(artifact.Identifiable)
if !toExists {
fromPtr, fromExists := idMap[t]
if !fromExists {
continue
}
from, ok := common.PtrToStruct(fromPtr).(artifact.Identifiable)
if !ok {
continue
}
s.Relationships = append(s.Relationships, artifact.Relationship{
From: from,
To: to,
Type: artifact.DependencyOfRelationship, // FIXME this information is lost
// match assumptions in encoding, that this is the only type of relationship captured:
Type: artifact.DependencyOfRelationship,
})
}
}
@@ -8,8 +8,10 @@ import (
"github.com/CycloneDX/cyclonedx-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
)
@@ -336,3 +338,101 @@ func Test_missingComponentsDecode(t *testing.T) {
assert.NoError(t, err)
}
func Test_decodeDependencies(t *testing.T) {
c1 := cyclonedx.Component{
Name: "c1",
}
c2 := cyclonedx.Component{
Name: "c2",
}
c3 := cyclonedx.Component{
Name: "c3",
}
for _, c := range []*cyclonedx.Component{&c1, &c2, &c3} {
c.BOMRef = c.Name
}
setTypes := func(typ cyclonedx.ComponentType, components ...cyclonedx.Component) *[]cyclonedx.Component {
var out []cyclonedx.Component
for _, c := range components {
c.Type = typ
out = append(out, c)
}
return &out
}
tests := []struct {
name string
sbom cyclonedx.BOM
expected []string
}{
{
name: "dependencies decoded as dependencyOf relationships",
sbom: cyclonedx.BOM{
Components: setTypes(cyclonedx.ComponentTypeLibrary,
c1,
c2,
c3,
),
Dependencies: &[]cyclonedx.Dependency{
{
Ref: c1.BOMRef,
Dependencies: &[]string{
c2.BOMRef,
c3.BOMRef,
},
},
},
},
expected: []string{c2.Name, c3.Name},
},
{
name: "dependencies skipped with unhandled components",
sbom: cyclonedx.BOM{
Components: setTypes("",
c1,
c2,
c3,
),
Dependencies: &[]cyclonedx.Dependency{
{
Ref: c1.BOMRef,
Dependencies: &[]string{
c2.BOMRef,
c3.BOMRef,
},
},
},
},
expected: nil,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
s, err := ToSyftModel(&test.sbom)
require.NoError(t, err)
require.NotNil(t, s)
var deps []string
if s != nil {
for _, r := range s.Relationships {
if r.Type != artifact.DependencyOfRelationship {
continue
}
if p, ok := r.To.(pkg.Package); !ok || p.Name != c1.Name {
continue
}
if p, ok := r.From.(pkg.Package); ok {
deps = append(deps, p.Name)
}
}
}
require.Equal(t, test.expected, deps)
})
}
}
@@ -143,27 +143,29 @@ func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependenc
for _, r := range relationships {
exists := isExpressiblePackageRelationship(r.Type)
if !exists {
log.Debugf("unable to convert relationship from CycloneDX 1.4 JSON, dropping: %+v", r)
log.Debugf("unable to convert relationship type to CycloneDX JSON, dropping: %#v", r)
continue
}
// we only capture package-to-package relationships for now
fromPkg, ok := r.From.(*pkg.Package)
fromPkg, ok := r.From.(pkg.Package)
if !ok {
log.Tracef("unable to convert relationship fromPkg to CycloneDX JSON, dropping: %#v", r)
continue
}
toPkg, ok := r.To.(*pkg.Package)
toPkg, ok := r.To.(pkg.Package)
if !ok {
log.Tracef("unable to convert relationship toPkg to CycloneDX JSON, dropping: %#v", r)
continue
}
// ind dep
innerDeps := []string{}
innerDeps = append(innerDeps, deriveBomRef(*fromPkg))
innerDeps = append(innerDeps, deriveBomRef(fromPkg))
result = append(result, cyclonedx.Dependency{
Ref: deriveBomRef(*toPkg),
Ref: deriveBomRef(toPkg),
Dependencies: &innerDeps,
})
}
@@ -3,7 +3,13 @@ package cyclonedxhelpers
import (
"testing"
"github.com/CycloneDX/cyclonedx-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
)
func Test_formatCPE(t *testing.T) {
@@ -32,3 +38,98 @@ func Test_formatCPE(t *testing.T) {
})
}
}
func Test_relationships(t *testing.T) {
p1 := pkg.Package{
Name: "p1",
}
p1.SetID()
p2 := pkg.Package{
Name: "p2",
}
p2.SetID()
p3 := pkg.Package{
Name: "p3",
}
p3.SetID()
tests := []struct {
name string
sbom sbom.SBOM
expected []string
}{
{
name: "package dependencyOf relationships output as dependencies",
sbom: sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(p1, p2, p3),
},
Relationships: []artifact.Relationship{
{
From: p2,
To: p1,
Type: artifact.DependencyOfRelationship,
},
{
From: p3,
To: p1,
Type: artifact.DependencyOfRelationship,
},
},
},
expected: []string{p2.Name, p3.Name},
},
{
name: "package contains relationships not output",
sbom: sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(p1, p2, p3),
},
Relationships: []artifact.Relationship{
{
From: p2,
To: p1,
Type: artifact.ContainsRelationship,
},
{
From: p3,
To: p1,
Type: artifact.ContainsRelationship,
},
},
},
expected: nil,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cdx := ToFormatModel(test.sbom)
got := cdx.Dependencies
var deps []string
if got != nil {
for _, r := range *got {
for _, d := range *r.Dependencies {
c := findComponent(cdx, d)
require.NotNil(t, c)
deps = append(deps, c.Name)
}
}
}
require.Equal(t, test.expected, deps)
})
}
}
func findComponent(cdx *cyclonedx.BOM, bomRef string) *cyclonedx.Component {
for _, c := range *cdx.Components {
if c.BOMRef == bomRef {
return &c
}
}
return nil
}
+27 -18
View File
@@ -1,6 +1,8 @@
package spdxhelpers
import (
"crypto/sha256"
"fmt"
"strings"
"github.com/anchore/syft/internal/spdxlicense"
@@ -27,29 +29,18 @@ func License(p pkg.Package) (concluded, declared string) {
// https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/
pc, pd := parseLicenses(p.Licenses.ToSlice())
for i, v := range pc {
if strings.HasPrefix(v, spdxlicense.LicenseRefPrefix) {
pc[i] = SanitizeElementID(v)
}
}
for i, v := range pd {
if strings.HasPrefix(v, spdxlicense.LicenseRefPrefix) {
pd[i] = SanitizeElementID(v)
}
}
return joinLicenses(pc), joinLicenses(pd)
}
func joinLicenses(licenses []string) string {
func joinLicenses(licenses []spdxLicense) string {
if len(licenses) == 0 {
return NOASSERTION
}
var newLicenses []string
for _, v := range licenses {
for _, l := range licenses {
v := l.id
// check if license does not start or end with parens
if !strings.HasPrefix(v, "(") && !strings.HasSuffix(v, ")") {
// if license contains AND, OR, or WITH, then wrap in parens
@@ -66,14 +57,31 @@ func joinLicenses(licenses []string) string {
return strings.Join(newLicenses, " AND ")
}
func parseLicenses(raw []pkg.License) (concluded, declared []string) {
type spdxLicense struct {
id string
value string
}
func parseLicenses(raw []pkg.License) (concluded, declared []spdxLicense) {
for _, l := range raw {
var candidate string
if l.Value == "" {
continue
}
candidate := spdxLicense{}
if l.SPDXExpression != "" {
candidate = l.SPDXExpression
candidate.id = l.SPDXExpression
} else {
// we did not find a valid SPDX license ID so treat as separate license
candidate = spdxlicense.LicenseRefPrefix + l.Value
if len(l.Value) <= 64 {
// if the license text is less than the size of the hash,
// just use it directly so the id is more readable
candidate.id = spdxlicense.LicenseRefPrefix + SanitizeElementID(l.Value)
} else {
hash := sha256.Sum256([]byte(l.Value))
candidate.id = fmt.Sprintf("%s%x", spdxlicense.LicenseRefPrefix, hash)
}
candidate.value = l.Value
}
switch l.Type {
@@ -83,5 +91,6 @@ func parseLicenses(raw []pkg.License) (concluded, declared []string) {
declared = append(declared, candidate)
}
}
return concluded, declared
}
@@ -1,11 +1,16 @@
package spdxhelpers
import (
"strings"
"testing"
"github.com/spdx/tools-golang/spdx"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal/spdxlicense"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
)
func Test_License(t *testing.T) {
@@ -103,6 +108,77 @@ func Test_License(t *testing.T) {
}
}
func Test_otherLicenses(t *testing.T) {
pkg1 := pkg.Package{
Name: "first-pkg",
Version: "1.1",
Licenses: pkg.NewLicenseSet(
pkg.NewLicense("MIT"),
),
}
pkg2 := pkg.Package{
Name: "second-pkg",
Version: "2.2",
Licenses: pkg.NewLicenseSet(
pkg.NewLicense("non spdx license"),
),
}
bigText := `
Apache License
Version 2.0, January 2004`
pkg3 := pkg.Package{
Name: "third-pkg",
Version: "3.3",
Licenses: pkg.NewLicenseSet(
pkg.NewLicense(bigText),
),
}
tests := []struct {
name string
packages []pkg.Package
expected []*spdx.OtherLicense
}{
{
name: "no other licenses when all valid spdx expressions",
packages: []pkg.Package{pkg1},
expected: nil,
},
{
name: "other licenses includes original text",
packages: []pkg.Package{pkg2},
expected: []*spdx.OtherLicense{
{
LicenseIdentifier: "LicenseRef-non-spdx-license",
ExtractedText: "non spdx license",
},
},
},
{
name: "big licenses get hashed",
packages: []pkg.Package{pkg3},
expected: []*spdx.OtherLicense{
{
LicenseIdentifier: "LicenseRef-e9a1e42833d3e456f147052f4d312101bd171a0798893169fe596ca6b55c049e",
ExtractedText: bigText,
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
s := sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(test.packages...),
},
}
got := ToFormatModel(s)
require.Equal(t, test.expected, got.OtherLicenses)
})
}
}
func Test_joinLicenses(t *testing.T) {
tests := []struct {
name string
@@ -122,7 +198,18 @@ func Test_joinLicenses(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, joinLicenses(tt.args), "joinLicenses(%v)", tt.args)
assert.Equalf(t, tt.want, joinLicenses(toSpdxLicenses(tt.args)), "joinLicenses(%v)", tt.args)
})
}
}
func toSpdxLicenses(ids []string) (licenses []spdxLicense) {
for _, l := range ids {
license := spdxLicense{id: l}
if strings.HasPrefix(l, spdxlicense.LicenseRefPrefix) {
license.value = l
}
licenses = append(licenses, license)
}
return licenses
}
@@ -240,9 +240,12 @@ func toRootPackage(s source.Description) *spdx.Package {
PackageSPDXIdentifier: spdx.ElementID(SanitizeElementID(fmt.Sprintf("DocumentRoot-%s-%s", prefix, name))),
PackageVersion: version,
PackageChecksums: checksums,
PackageSupplier: nil,
PackageExternalReferences: nil,
PrimaryPackagePurpose: purpose,
PackageSupplier: &spdx.Supplier{
Supplier: NOASSERTION,
},
PackageDownloadLocation: NOASSERTION,
}
if purl != nil {
@@ -357,7 +360,7 @@ func toPackages(catalog *pkg.Collection, sbom sbom.SBOM) (results []*spdx.Packag
// 7.6: Package Originator: may have single result for either Person or Organization,
// or NOASSERTION
// Cardinality: optional, one
PackageSupplier: nil,
PackageSupplier: toPackageSupplier(p),
PackageOriginator: toPackageOriginator(p),
@@ -514,6 +517,21 @@ func toPackageOriginator(p pkg.Package) *spdx.Originator {
}
}
func toPackageSupplier(p pkg.Package) *spdx.Supplier {
// this uses the Originator function for now until
// a better distinction can be made for supplier
kind, supplier := Originator(p)
if kind == "" || supplier == "" {
return &spdx.Supplier{
Supplier: NOASSERTION,
}
}
return &spdx.Supplier{
Supplier: supplier,
SupplierType: kind,
}
}
func formatSPDXExternalRefs(p pkg.Package) (refs []*spdx.PackageExternalReference) {
for _, ref := range ExternalRefs(p) {
refs = append(refs, &spdx.PackageExternalReference{
@@ -686,32 +704,31 @@ func toFileTypes(metadata *file.Metadata) (ty []string) {
// other licenses are for licenses from the pkg.Package that do not have an SPDXExpression
// field. The spdxexpression field is only filled given a validated Value field.
func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense {
licenses := map[string]bool{}
for _, p := range catalog.Sorted() {
licenses := map[string]spdxLicense{}
for p := range catalog.Enumerate() {
declaredLicenses, concludedLicenses := parseLicenses(p.Licenses.ToSlice())
for _, license := range declaredLicenses {
if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) {
licenses[license] = true
for _, l := range declaredLicenses {
if l.value != "" {
licenses[l.id] = l
}
}
for _, license := range concludedLicenses {
if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) {
licenses[license] = true
for _, l := range concludedLicenses {
if l.value != "" {
licenses[l.id] = l
}
}
}
var result []*spdx.OtherLicense
sorted := maps.Keys(licenses)
slices.Sort(sorted)
for _, license := range sorted {
// separate the found value from the prefix
// this only contains licenses that are not found on the SPDX License List
name := strings.TrimPrefix(license, spdxlicense.LicenseRefPrefix)
ids := maps.Keys(licenses)
slices.Sort(ids)
for _, id := range ids {
license := licenses[id]
result = append(result, &spdx.OtherLicense{
LicenseIdentifier: SanitizeElementID(license),
ExtractedText: name,
LicenseIdentifier: license.id,
ExtractedText: license.value,
})
}
return result
@@ -51,12 +51,14 @@ func Test_toFormatModel(t *testing.T) {
SPDXVersion: spdx.Version,
DataLicense: spdx.DataLicense,
DocumentName: "alpine",
Packages: []*spdx.Package{
{
PackageSPDXIdentifier: "Package-pkg-1-pkg-1",
PackageName: "pkg-1",
PackageVersion: "version-1",
PackageSupplier: &spdx.Supplier{
Supplier: "NOASSERTION",
},
},
{
PackageSPDXIdentifier: "DocumentRoot-Image-alpine",
@@ -71,6 +73,9 @@ func Test_toFormatModel(t *testing.T) {
Locator: "pkg:oci/alpine@sha256:d34db33f?arch=&tag=latest",
},
},
PackageSupplier: &spdx.Supplier{
Supplier: "NOASSERTION",
},
},
},
Relationships: []*spdx.Relationship{
@@ -122,12 +127,18 @@ func Test_toFormatModel(t *testing.T) {
PackageSPDXIdentifier: "Package-pkg-1-pkg-1",
PackageName: "pkg-1",
PackageVersion: "version-1",
PackageSupplier: &spdx.Supplier{
Supplier: "NOASSERTION",
},
},
{
PackageSPDXIdentifier: "DocumentRoot-Directory-some-directory",
PackageName: "some/directory",
PackageVersion: "",
PrimaryPackagePurpose: "FILE",
PackageSupplier: &spdx.Supplier{
Supplier: "NOASSERTION",
},
},
},
Relationships: []*spdx.Relationship{
@@ -180,12 +191,14 @@ func Test_toFormatModel(t *testing.T) {
SPDXVersion: spdx.Version,
DataLicense: spdx.DataLicense,
DocumentName: "path/to/some.file",
Packages: []*spdx.Package{
{
PackageSPDXIdentifier: "Package-pkg-1-pkg-1",
PackageName: "pkg-1",
PackageVersion: "version-1",
PackageSupplier: &spdx.Supplier{
Supplier: "NOASSERTION",
},
},
{
PackageSPDXIdentifier: "DocumentRoot-File-path-to-some.file",
@@ -193,6 +206,9 @@ func Test_toFormatModel(t *testing.T) {
PackageVersion: "sha256:d34db33f",
PrimaryPackagePurpose: "FILE",
PackageChecksums: []spdx.Checksum{{Algorithm: "SHA256", Value: "d34db33f"}},
PackageSupplier: &spdx.Supplier{
Supplier: "NOASSERTION",
},
},
},
Relationships: []*spdx.Relationship{
@@ -14,6 +14,7 @@ import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/spdxlicense"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
@@ -285,6 +286,16 @@ func collectSyftPackages(s *sbom.SBOM, spdxIDMap map[string]any, packages []*spd
}
func collectSyftFiles(s *sbom.SBOM, spdxIDMap map[string]any, doc *spdx.Document) {
for _, p := range doc.Packages {
for _, f := range p.Files {
l := toSyftLocation(f)
spdxIDMap[string(f.FileSPDXIdentifier)] = l
s.Artifacts.FileMetadata[l.Coordinates] = toFileMetadata(f)
s.Artifacts.FileDigests[l.Coordinates] = toFileDigests(f)
}
}
for _, f := range doc.Files {
l := toSyftLocation(f)
spdxIDMap[string(f.FileSPDXIdentifier)] = l
@@ -331,7 +342,14 @@ func toFileMetadata(f *spdx.File) (meta file.Metadata) {
}
func toSyftRelationships(spdxIDMap map[string]any, doc *spdx.Document) []artifact.Relationship {
var out []artifact.Relationship
out := collectDocRelationships(spdxIDMap, doc)
out = append(out, collectPackageFileRelationships(spdxIDMap, doc)...)
return out
}
func collectDocRelationships(spdxIDMap map[string]any, doc *spdx.Document) (out []artifact.Relationship) {
for _, r := range doc.Relationships {
// FIXME what to do with r.RefA.DocumentRefID and r.RefA.SpecialID
if r.RefA.DocumentRefID != "" && requireAndTrimPrefix(r.RefA.DocumentRefID, "DocumentRef-") != string(doc.SPDXIdentifier) {
@@ -385,6 +403,30 @@ func toSyftRelationships(spdxIDMap map[string]any, doc *spdx.Document) []artifac
return out
}
// collectPackageFileRelationships add relationships for direct files
func collectPackageFileRelationships(spdxIDMap map[string]any, doc *spdx.Document) (out []artifact.Relationship) {
for _, p := range doc.Packages {
a := spdxIDMap[string(p.PackageSPDXIdentifier)]
from, fromOk := a.(pkg.Package)
if !fromOk {
continue
}
for _, f := range p.Files {
b := spdxIDMap[string(f.FileSPDXIdentifier)]
to, toLocationOk := b.(file.Location)
if !toLocationOk {
continue
}
out = append(out, artifact.Relationship{
From: from,
To: to,
Type: artifact.ContainsRelationship,
})
}
}
return out
}
func toSyftCoordinates(f *spdx.File) file.Coordinates {
const layerIDPrefix = "layerID: "
var fileSystemID string
@@ -467,11 +509,11 @@ func toSyftPackage(p *spdx.Package) pkg.Package {
}
func purlValue(purl packageurl.PackageURL) string {
p := purl.String()
if p == "pkg:/" {
val := purl.String()
if _, err := packageurl.FromString(val); err != nil {
return ""
}
return p
return val
}
func parseSPDXLicenses(p *spdx.Package) []pkg.License {
@@ -495,10 +537,7 @@ func parseSPDXLicenses(p *spdx.Package) []pkg.License {
}
func cleanSPDXID(id string) string {
if strings.HasPrefix(id, "LicenseRef-") {
return strings.TrimPrefix(id, "LicenseRef-")
}
return id
return strings.TrimPrefix(id, spdxlicense.LicenseRefPrefix)
}
//nolint:funlen
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -552,3 +553,132 @@ func Test_convertToAndFromFormat(t *testing.T) {
})
}
}
func Test_purlValue(t *testing.T) {
tests := []struct {
purl packageurl.PackageURL
expected string
}{
{
purl: packageurl.PackageURL{},
expected: "",
},
{
purl: packageurl.PackageURL{
Name: "name",
Version: "version",
},
expected: "",
},
{
purl: packageurl.PackageURL{
Type: "typ",
Version: "version",
},
expected: "",
},
{
purl: packageurl.PackageURL{
Type: "typ",
Name: "name",
Version: "version",
},
expected: "pkg:typ/name@version",
},
{
purl: packageurl.PackageURL{
Type: "typ",
Name: "name",
Version: "version",
Qualifiers: packageurl.Qualifiers{
{
Key: "q",
Value: "v",
},
},
},
expected: "pkg:typ/name@version?q=v",
},
}
for _, test := range tests {
t.Run(test.purl.String(), func(t *testing.T) {
got := purlValue(test.purl)
require.Equal(t, test.expected, got)
})
}
}
func Test_directPackageFiles(t *testing.T) {
doc := &spdx.Document{
SPDXVersion: "SPDX-2.3",
Packages: []*spdx.Package{
{
PackageName: "some-package",
PackageSPDXIdentifier: "1",
PackageVersion: "1.0.5",
Files: []*spdx.File{
{
FileName: "some-file",
FileSPDXIdentifier: "2",
Checksums: []spdx.Checksum{
{
Algorithm: "SHA1",
Value: "a8d733c64f9123",
},
},
},
},
},
},
}
got, err := ToSyftModel(doc)
require.NoError(t, err)
p := pkg.Package{
Name: "some-package",
Version: "1.0.5",
MetadataType: pkg.UnknownMetadataType,
}
p.SetID()
f := file.Location{
LocationData: file.LocationData{
Coordinates: file.Coordinates{
RealPath: "some-file",
FileSystemID: "",
},
VirtualPath: "some-file",
},
LocationMetadata: file.LocationMetadata{
Annotations: map[string]string{},
},
}
s := &sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(p),
FileMetadata: map[file.Coordinates]file.Metadata{
f.Coordinates: {},
},
FileDigests: map[file.Coordinates][]file.Digest{
f.Coordinates: {
{
Algorithm: "sha1",
Value: "a8d733c64f9123",
},
},
},
},
Relationships: []artifact.Relationship{
{
From: p,
To: f,
Type: artifact.ContainsRelationship,
},
},
Source: source.Description{},
Descriptor: sbom.Descriptor{},
}
require.Equal(t, s, got)
}
@@ -17,6 +17,7 @@
"name": "package-1",
"SPDXID": "SPDXRef-Package-python-package-1-9265397e5e15168a",
"versionInfo": "1.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"sourceInfo": "acquired package info from installed python package manifest file: /some/path/pkg1",
@@ -40,6 +41,7 @@
"name": "package-2",
"SPDXID": "SPDXRef-Package-deb-package-2-db4abfe497c180d3",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"sourceInfo": "acquired package info from DPKG DB: /some/path/pkg1",
@@ -62,7 +64,8 @@
{
"name": "some/path",
"SPDXID": "SPDXRef-DocumentRoot-Directory-some-path",
"downloadLocation": "",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"primaryPackagePurpose": "FILE"
}
@@ -17,6 +17,7 @@
"name": "package-1",
"SPDXID": "SPDXRef-Package-python-package-1-125840abc1c66dd7",
"versionInfo": "1.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt",
@@ -40,6 +41,7 @@
"name": "package-2",
"SPDXID": "SPDXRef-Package-deb-package-2-958443e2d9304af4",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"sourceInfo": "acquired package info from DPKG DB: /somefile-2.txt",
@@ -63,7 +65,8 @@
"name": "user-image-input",
"SPDXID": "SPDXRef-DocumentRoot-Image-user-image-input",
"versionInfo": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
"downloadLocation": "",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"checksums": [
{
@@ -17,6 +17,7 @@
"name": "package-1",
"SPDXID": "SPDXRef-Package-python-package-1-125840abc1c66dd7",
"versionInfo": "1.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt",
@@ -40,6 +41,7 @@
"name": "package-2",
"SPDXID": "SPDXRef-Package-deb-package-2-958443e2d9304af4",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"sourceInfo": "acquired package info from DPKG DB: /somefile-2.txt",
@@ -63,7 +65,8 @@
"name": "user-image-input",
"SPDXID": "SPDXRef-DocumentRoot-Image-user-image-input",
"versionInfo": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
"downloadLocation": "",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"checksums": [
{
+60
View File
@@ -2,9 +2,13 @@ package spdxtagvalue
import (
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
)
// TODO: this is a temporary coverage see below
@@ -34,3 +38,59 @@ func TestSPDXTagValueDecoder(t *testing.T) {
})
}
}
func Test_packageDirectFiles(t *testing.T) {
contents := `
SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: Some-SBOM
DocumentNamespace: https://example.org/some/namespace
Creator: Organization: Some-organization
Creator: Tool: Some-tool Version: 1.0
Created: 2021-12-29T17:02:21Z
PackageName: Some-package
PackageVersion: 5.1.2
SPDXID: SPDXRef-Package-43c51b08-cc7e-406d-8ad9-34aa292d1157
PackageSupplier: Organization: Some-organization
PackageDownloadLocation: https://example.org/download/location
FilesAnalyzed: true
PackageLicenseInfoFromFiles: NOASSERTION
PackageVerificationCode: 23460C5559C8D4DE3F6504E0E84E844CAC8B1D95
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
PackageCopyrightText: NOASSERTION
PackageChecksum: SHA1: 23460C5559C8D4DE3F6504E0E84E844CAC8B1D95
FileName: Some-file-name
SPDXID: SPDXRef-99545d55-933d-4e08-9eb5-9d826111cb79
FileContributor: Some-file-contributor
FileType: BINARY
FileChecksum: SHA1: 23460C5559C8D4DE3F6504E0E84E844CAC8B1D95
LicenseConcluded: NOASSERTION
LicenseInfoInFile: NOASSERTION
FileCopyrightText: NOASSERTION
`
s, err := decoder(strings.NewReader(contents))
require.NoError(t, err)
pkgs := s.Artifacts.Packages.Sorted()
assert.Len(t, pkgs, 1)
assert.Len(t, s.Artifacts.FileMetadata, 1)
assert.Len(t, s.Relationships, 1)
p := pkgs[0]
r := s.Relationships[0]
f := file.Location{}
for c := range s.Artifacts.FileMetadata {
f = file.Location{
LocationData: file.LocationData{
Coordinates: c,
VirtualPath: "",
},
LocationMetadata: file.LocationMetadata{},
}
break // there should only be 1
}
assert.Equal(t, p.ID(), r.From.ID())
assert.Equal(t, f.ID(), r.To.ID())
}
@@ -12,6 +12,8 @@ Created: redacted
PackageName: foobar/baz
SPDXID: SPDXRef-DocumentRoot-Directory-foobar-baz
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: FILE
FilesAnalyzed: false
@@ -19,6 +21,7 @@ FilesAnalyzed: false
PackageName: @at-sign
SPDXID: SPDXRef-Package--at-sign-3732f7a5679bdec4
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageSourceInfo: acquired package info from the following paths:
@@ -30,6 +33,7 @@ PackageCopyrightText: NOASSERTION
PackageName: some/slashes
SPDXID: SPDXRef-Package-some-slashes-1345166d4801153b
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageSourceInfo: acquired package info from the following paths:
@@ -41,6 +45,7 @@ PackageCopyrightText: NOASSERTION
PackageName: under_scores
SPDXID: SPDXRef-Package-under-scores-290d5c77210978c1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageSourceInfo: acquired package info from the following paths:
@@ -51,6 +51,8 @@ LicenseConcluded: NOASSERTION
PackageName: user-image-input
SPDXID: SPDXRef-DocumentRoot-Image-user-image-input
PackageVersion: sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: CONTAINER
FilesAnalyzed: false
PackageChecksum: SHA256: 2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368
@@ -61,6 +63,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-958443e2d9304af4
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageSourceInfo: acquired package info from DPKG DB: /somefile-2.txt
@@ -75,6 +78,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-125840abc1c66dd7
PackageVersion: 1.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageSourceInfo: acquired package info from installed python package manifest file: /somefile-1.txt
@@ -12,6 +12,8 @@ Created: redacted
PackageName: some/path
SPDXID: SPDXRef-DocumentRoot-Directory-some-path
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: FILE
FilesAnalyzed: false
@@ -20,6 +22,7 @@ FilesAnalyzed: false
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-db4abfe497c180d3
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageSourceInfo: acquired package info from DPKG DB: /some/path/pkg1
@@ -34,6 +37,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-9265397e5e15168a
PackageVersion: 1.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageSourceInfo: acquired package info from installed python package manifest file: /some/path/pkg1
@@ -13,6 +13,8 @@ Created: redacted
PackageName: user-image-input
SPDXID: SPDXRef-DocumentRoot-Image-user-image-input
PackageVersion: sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: CONTAINER
FilesAnalyzed: false
PackageChecksum: SHA256: 2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368
@@ -23,6 +25,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-958443e2d9304af4
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageSourceInfo: acquired package info from DPKG DB: /somefile-2.txt
@@ -37,6 +40,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-125840abc1c66dd7
PackageVersion: 1.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageSourceInfo: acquired package info from installed python package manifest file: /somefile-1.txt
+23 -1
View File
@@ -74,11 +74,33 @@ func CatalogPackages(src source.Source, cfg cataloger.Config) (*pkg.Collection,
catalog, relationships, err := cataloger.Catalog(resolver, release, cfg.Parallelism, catalogers...)
relationships = append(relationships, newSourceRelationshipsFromCatalog(src, catalog)...)
// apply exclusions to the package catalog
// default config value for this is true
// https://github.com/anchore/syft/issues/931
if cfg.ExcludeBinaryOverlapByOwnership {
for _, r := range relationships {
if cataloger.ExcludeBinaryByFileOwnershipOverlap(r, catalog) {
catalog.Delete(r.To.ID())
relationships = removeRelationshipsByID(relationships, r.To.ID())
}
}
}
// no need to consider source relationships for os -> binary exclusions
relationships = append(relationships, newSourceRelationshipsFromCatalog(src, catalog)...)
return catalog, relationships, release, err
}
func removeRelationshipsByID(relationships []artifact.Relationship, id artifact.ID) []artifact.Relationship {
var filtered []artifact.Relationship
for _, r := range relationships {
if r.To.ID() != id && r.From.ID() != id {
filtered = append(filtered, r)
}
}
return filtered
}
func newSourceRelationshipsFromCatalog(src source.Source, c *pkg.Collection) []artifact.Relationship {
relationships := make([]artifact.Relationship, 0) // Should we pre-allocate this by giving catalog a Len() method?
for p := range c.Enumerate() {
+42
View File
@@ -0,0 +1,42 @@
package syft
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
)
func Test_removeRelationshipsByID(t *testing.T) {
p1 := pkg.Package{}
p1.OverrideID("1")
p2 := pkg.Package{}
p2.OverrideID("2")
p3 := pkg.Package{}
p3.OverrideID("3")
rel := func(pkgs ...pkg.Package) (out []artifact.Relationship) {
for _, p := range pkgs {
out = append(out, artifact.Relationship{
From: p,
To: p,
Type: artifact.OwnershipByFileOverlapRelationship,
})
}
return
}
relationships := rel(p1, p2, p3)
for _, r := range relationships {
if r.From.ID() == "1" || r.From.ID() == "2" {
relationships = removeRelationshipsByID(relationships, r.From.ID())
}
}
require.Equal(t, rel(p3), relationships)
}
@@ -643,6 +643,30 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
Metadata: metadata("consul-binary"),
},
},
{
name: "positive-nginx-1.25.1",
fixtureDir: "test-fixtures/classifiers/positive/nginx-1.25.1",
expected: pkg.Package{
Name: "nginx",
Version: "1.25.1",
Type: "binary",
PURL: "pkg:generic/nginx@1.25.1",
Locations: locations("nginx"),
Metadata: metadata("nginx-binary"),
},
},
{
name: "positive-nginx-openresty-1.21.4.2",
fixtureDir: "test-fixtures/classifiers/positive/nginx-openresty-1.21.4.2",
expected: pkg.Package{
Name: "nginx",
Version: "1.21.4",
Type: "binary",
PURL: "pkg:generic/nginx@1.21.4",
Locations: locations("nginx"),
Metadata: metadata("nginx-binary"),
},
},
}
for _, test := range tests {
@@ -254,6 +254,21 @@ var defaultClassifiers = []classifier{
PURL: mustPURL("pkg:golang/github.com/hashicorp/consul@version"),
CPEs: singleCPE("cpe:2.3:a:hashicorp:consul:*:*:*:*:*:*:*:*"),
},
{
Class: "nginx-binary",
FileGlob: "**/nginx",
EvidenceMatcher: fileContentsVersionMatcher(
// [NUL]nginx version: nginx/1.25.1 - fetches '1.25.1'
// [NUL]nginx version: openresty/1.21.4.1 - fetches '1.21.4' as this is the nginx version part
`(?m)(\x00|\?)nginx version: [^\/]+\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(?:\+\d+)?(?:-\d+)?)`,
),
Package: "nginx",
PURL: mustPURL("pkg:generic/nginx@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:f5:nginx:*:*:*:*:*:*:*:*"),
cpe.Must("cpe:2.3:a:nginx:nginx:*:*:*:*:*:*:*:*"),
},
},
}
// in both binaries and shared libraries, the version pattern is [NUL]3.11.2[NUL]
@@ -807,6 +807,7 @@
"promise-probe": "cpe:2.3:a:promise-probe_project:promise-probe:*:*:*:*:*:node.js:*:*",
"promisehelpers": "cpe:2.3:a:yola:promisehelpers:*:*:*:*:*:node.js:*:*",
"property-expr": "cpe:2.3:a:property-expr_project:property-expr:*:*:*:*:*:node.js:*:*",
"proxy": "cpe:2.3:a:proxy_project:proxy:*:*:*:*:*:node.js:*:*",
"proxy.js": "cpe:2.3:a:proxy.js_project:proxy.js:*:*:*:*:*:node.js:*:*",
"ps-kill": "cpe:2.3:a:ps-kill_project:ps-kill:*:*:*:*:*:node.js:*:*",
"ps-visitor": "cpe:2.3:a:ps-visitor_project:ps-visitor:*:*:*:*:*:node.js:*:*",
@@ -1144,6 +1145,7 @@
"papercrop": "cpe:2.3:a:papercrop_project:papercrop:*:*:*:*:*:ruby:*:*",
"paranoid2": "cpe:2.3:a:anjlab:paranoid2:*:*:*:*:*:ruby:*:*",
"paratrooper-pingdom": "cpe:2.3:a:tobias_maier:paratrooper-pingdom:*:*:-:*:-:ruby:*:*",
"pdf_info": "cpe:2.3:a:newspaperclub:pdf_info:*:*:*:*:*:ruby:*:*",
"pdfkit": "cpe:2.3:a:pdfkit_project:pdfkit:*:*:*:*:*:ruby:*:*",
"point-cli": "cpe:2.3:a:point-cli_project:point-cli:*:*:*:*:*:ruby:*:*",
"private_address_check": "cpe:2.3:a:private_address_check_project:private_address_check:*:*:*:*:*:ruby:*:*",
+40 -25
View File
@@ -354,11 +354,12 @@ func TestGeneratePackageCPEs(t *testing.T) {
{
name: "cloudbees jenkins package identified via groupId",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
PomProperties: &pkg.PomProperties{
GroupID: "com.cloudbees.jenkins.plugins",
@@ -368,16 +369,18 @@ func TestGeneratePackageCPEs(t *testing.T) {
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:cloudbees:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "jenkins.io package identified via groupId prefix",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
PomProperties: &pkg.PomProperties{
GroupID: "io.jenkins.plugins.name.something",
@@ -389,16 +392,19 @@ func TestGeneratePackageCPEs(t *testing.T) {
"cpe:2.3:a:name:something:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:something:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:something:something:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:something:3.2:*:*:*:*:*:*:*",
},
},
{
name: "jenkins.io package identified via groupId",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
PomProperties: &pkg.PomProperties{
GroupID: "io.jenkins.plugins",
@@ -407,16 +413,18 @@ func TestGeneratePackageCPEs(t *testing.T) {
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "jenkins-ci.io package identified via groupId",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
PomProperties: &pkg.PomProperties{
GroupID: "io.jenkins-ci.plugins",
@@ -425,16 +433,20 @@ func TestGeneratePackageCPEs(t *testing.T) {
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins-ci:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins_ci:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "jenkins-ci.org package identified via groupId",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
PomProperties: &pkg.PomProperties{
GroupID: "org.jenkins-ci.plugins",
@@ -443,6 +455,9 @@ func TestGeneratePackageCPEs(t *testing.T) {
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins-ci:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins_ci:name:3.2:*:*:*:*:*:*:*",
},
},
{
+20 -46
View File
@@ -5,7 +5,6 @@ import (
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/pkg"
)
@@ -13,14 +12,6 @@ var (
forbiddenProductGroupIDFields = strset.New("plugin", "plugins", "client")
forbiddenVendorGroupIDFields = strset.New("plugin", "plugins")
domains = []string{
"com",
"org",
"net",
"io",
"be",
}
primaryJavaManifestGroupIDFields = []string{
"Extension-Name",
"Specification-Vendor",
@@ -66,7 +57,7 @@ func vendorsFromJavaManifestNames(p pkg.Package) fieldCandidateSet {
for _, name := range javaManifestNameFields {
if metadata.Manifest.Main != nil {
if value, exists := metadata.Manifest.Main[name]; exists {
if !startsWithTopLevelDomain(value) {
if !looksLikeGroupID(value) {
vendors.add(fieldCandidate{
value: normalizePersonName(value),
disallowSubSelections: true,
@@ -80,7 +71,7 @@ func vendorsFromJavaManifestNames(p pkg.Package) fieldCandidateSet {
continue
}
if value, exists := section[name]; exists {
if !startsWithTopLevelDomain(value) {
if !looksLikeGroupID(value) {
vendors.add(fieldCandidate{
value: normalizePersonName(value),
disallowSubSelections: true,
@@ -144,7 +135,7 @@ func productsFromArtifactAndGroupIDs(artifactID string, groupIDs []string) []str
continue
}
// umbrella projects tend to have sub components that either start or end with the project name. We expect
// umbrella projects tend to have subcomponents that either start or end with the project name. We expect
// to identify fields that may represent the umbrella project, and not fields that indicate auxiliary
// information about the package.
couldBeProjectName := strings.HasPrefix(artifactID, field) || strings.HasSuffix(artifactID, field)
@@ -168,7 +159,7 @@ func artifactIDFromJavaPackage(p pkg.Package) string {
}
artifactID := strings.TrimSpace(metadata.PomProperties.ArtifactID)
if startsWithTopLevelDomain(artifactID) && len(strings.Split(artifactID, ".")) > 1 {
if looksLikeGroupID(artifactID) {
// there is a strong indication that the artifact ID is really a group ID, don't use it
return ""
}
@@ -181,13 +172,13 @@ func GroupIDsFromJavaPackage(p pkg.Package) (groupIDs []string) {
return nil
}
return GroupIDsFromJavaMetadata(metadata)
return GroupIDsFromJavaMetadata(p.Name, metadata)
}
func GroupIDsFromJavaMetadata(metadata pkg.JavaMetadata) (groupIDs []string) {
func GroupIDsFromJavaMetadata(pkgName string, metadata pkg.JavaMetadata) (groupIDs []string) {
groupIDs = append(groupIDs, groupIDsFromPomProperties(metadata.PomProperties)...)
groupIDs = append(groupIDs, groupIDsFromPomProject(metadata.PomProject)...)
groupIDs = append(groupIDs, groupIDsFromJavaManifest(metadata.Manifest)...)
groupIDs = append(groupIDs, groupIDsFromJavaManifest(pkgName, metadata.Manifest)...)
return groupIDs
}
@@ -197,16 +188,10 @@ func groupIDsFromPomProperties(properties *pkg.PomProperties) (groupIDs []string
return nil
}
if startsWithTopLevelDomain(properties.GroupID) {
if properties.GroupID != "" {
groupIDs = append(groupIDs, cleanGroupID(properties.GroupID))
}
// sometimes the publisher puts the group ID in the artifact ID field unintentionally
if startsWithTopLevelDomain(properties.ArtifactID) && len(strings.Split(properties.ArtifactID, ".")) > 1 {
// there is a strong indication that the artifact ID is really a group ID
groupIDs = append(groupIDs, cleanGroupID(properties.ArtifactID))
}
return groupIDs
}
@@ -215,33 +200,22 @@ func groupIDsFromPomProject(project *pkg.PomProject) (groupIDs []string) {
return nil
}
// extract the project info...
groupIDs = addGroupIDsFromGroupIDsAndArtifactID(project.GroupID, project.ArtifactID)
if project.Parent == nil {
return groupIDs
if project.GroupID != "" {
groupIDs = append(groupIDs, project.GroupID)
}
// extract the parent project info...
groupIDs = append(groupIDs, addGroupIDsFromGroupIDsAndArtifactID(project.Parent.GroupID, project.Parent.ArtifactID)...)
if project.Parent != nil && project.Parent.GroupID != "" {
groupIDs = append(groupIDs, project.Parent.GroupID)
}
return groupIDs
}
func addGroupIDsFromGroupIDsAndArtifactID(groupID, artifactID string) (groupIDs []string) {
if startsWithTopLevelDomain(groupID) {
groupIDs = append(groupIDs, cleanGroupID(groupID))
func groupIDsFromJavaManifest(pkgName string, manifest *pkg.JavaManifest) []string {
if groupID, ok := defaultArtifactIDToGroupID[pkgName]; ok {
return []string{groupID}
}
// sometimes the publisher puts the group ID in the artifact ID field unintentionally
if startsWithTopLevelDomain(artifactID) && len(strings.Split(artifactID, ".")) > 1 {
// there is a strong indication that the artifact ID is really a group ID
groupIDs = append(groupIDs, cleanGroupID(artifactID))
}
return groupIDs
}
func groupIDsFromJavaManifest(manifest *pkg.JavaManifest) []string {
if manifest == nil {
return nil
}
@@ -268,13 +242,13 @@ func getManifestFieldGroupIDs(manifest *pkg.JavaManifest, fields []string) (grou
for _, name := range fields {
if value, exists := manifest.Main[name]; exists {
if startsWithTopLevelDomain(value) {
if looksLikeGroupID(value) {
groupIDs = append(groupIDs, cleanGroupID(value))
}
}
for _, section := range manifest.NamedSections {
if value, exists := section[name]; exists {
if startsWithTopLevelDomain(value) {
if looksLikeGroupID(value) {
groupIDs = append(groupIDs, cleanGroupID(value))
}
}
@@ -295,6 +269,6 @@ func removeOSCIDirectives(groupID string) string {
return strings.Split(groupID, ";")[0]
}
func startsWithTopLevelDomain(value string) bool {
return internal.HasAnyOfPrefixes(value, domains...)
func looksLikeGroupID(value string) bool {
return strings.Contains(value, ".")
}
@@ -0,0 +1,69 @@
package cpe
var defaultArtifactIDToGroupID = map[string]string{
"ant": "org.apache.ant",
"ant-antlr": "org.apache.ant",
"ant-antunit": "org.apache.ant",
"ant-apache-bcel": "org.apache.ant",
"ant-apache-bsf": "org.apache.ant",
"ant-apache-log4j": "org.apache.ant",
"ant-apache-oro": "org.apache.ant",
"ant-apache-regexp": "org.apache.ant",
"ant-apache-resolver": "org.apache.ant",
"ant-apache-xalan2": "org.apache.ant",
"ant-commons-logging": "org.apache.ant",
"ant-commons-net": "org.apache.ant",
"ant-compress": "org.apache.ant",
"ant-dotnet": "org.apache.ant",
"ant-imageio": "org.apache.ant",
"ant-jai": "org.apache.ant",
"ant-jakartamail": "org.apache.ant",
"ant-javamail": "org.apache.ant",
"ant-jdepend": "org.apache.ant",
"ant-jmf": "org.apache.ant",
"ant-jsch": "org.apache.ant",
"ant-junit": "org.apache.ant",
"ant-junit4": "org.apache.ant",
"ant-junitlauncher": "org.apache.ant",
"ant-launcher": "org.apache.ant",
"ant-netrexx": "org.apache.ant",
"ant-nodeps": "org.apache.ant",
"ant-parent": "org.apache.ant",
"ant-starteam": "org.apache.ant",
"ant-stylebook": "org.apache.ant",
"ant-swing": "org.apache.ant",
"ant-testutil": "org.apache.ant",
"ant-trax": "org.apache.ant",
"ant-weblogic": "org.apache.ant",
"ant-xz": "org.apache.ant",
"spring": "org.springframework",
"spring-amqp": "org.springframework.amqp",
"spring-batch-core": "org.springframework.batch",
"spring-beans": "org.springframework",
"spring-boot": "org.springframework.boot",
"spring-boot-starter-web": "org.springframework.boot",
"spring-boot-starter-webflux": "org.springframework.boot",
"spring-cloud-function-context": "org.springframework.cloud",
"spring-cloud-function-parent": "org.springframework.cloud",
"spring-cloud-gateway": "org.springframework.cloud",
"spring-cloud-openfeign-core": "org.springframework.cloud",
"spring-cloud-task-dependencies": "org.springframework.cloud",
"spring-core": "org.springframework",
"spring-data-jpa": "org.springframework.data",
"spring-data-mongodb": "org.springframework.data",
"spring-data-rest-core": "org.springframework.data",
"spring-expression": "org.springframework",
"spring-integration-zip": "org.springframework.integration",
"spring-oxm": "org.springframework",
"spring-security-core": "org.springframework.security",
"spring-security-config": "org.springframework.security",
"spring-security-oauth": "org.springframework.security.oauth",
"spring-security-oauth-parent": "org.springframework.security.oauth",
"spring-security-oauth2-client": "org.springframework.security",
"spring-session-core": "org.springframework.session",
"spring-vault-core": "org.springframework.vault",
"spring-web": "org.springframework",
"spring-webflow": "org.springframework.webflow",
"spring-webflux": "org.springframework",
"spring-webmvc": "org.springframework",
}
@@ -5,6 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/pkg"
)
@@ -427,3 +428,38 @@ func Test_vendorsFromJavaManifestNames(t *testing.T) {
})
}
}
func Test_groupIDsFromJavaManifest(t *testing.T) {
tests := []struct {
name string
manifest pkg.JavaManifest
expected []string
}{
{
name: "spring-security-core",
manifest: pkg.JavaManifest{},
expected: []string{"org.springframework.security"},
},
{
name: "spring-web",
manifest: pkg.JavaManifest{},
expected: []string{"org.springframework"},
},
{
name: "spring-foo",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Implementation-Vendor": "org.foo",
},
},
expected: []string{"org.foo"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := groupIDsFromJavaManifest(test.name, &test.manifest)
require.Equal(t, test.expected, got)
})
}
}
+12 -11
View File
@@ -8,22 +8,23 @@ import (
)
// TODO: these field naming vs helper function naming schemes are inconsistent.
type Config struct {
Search SearchConfig
Golang golang.GoCatalogerOpts
LinuxKernel kernel.LinuxCatalogerConfig
Python python.CatalogerConfig
Catalogers []string
Parallelism int
Search SearchConfig
Golang golang.GoCatalogerOpts
LinuxKernel kernel.LinuxCatalogerConfig
Python python.CatalogerConfig
Catalogers []string
Parallelism int
ExcludeBinaryOverlapByOwnership bool
}
func DefaultConfig() Config {
return Config{
Search: DefaultSearchConfig(),
Parallelism: 1,
LinuxKernel: kernel.DefaultLinuxCatalogerConfig(),
Python: python.DefaultCatalogerConfig(),
Search: DefaultSearchConfig(),
Parallelism: 1,
LinuxKernel: kernel.DefaultLinuxCatalogerConfig(),
Python: python.DefaultCatalogerConfig(),
ExcludeBinaryOverlapByOwnership: true,
}
}
+1 -1
View File
@@ -14,5 +14,5 @@ func NewDpkgdbCataloger() *generic.Cataloger {
return generic.NewCataloger(catalogerName).
// note: these globs have been intentionally split up in order to improve search performance,
// please do NOT combine into: "**/var/lib/dpkg/{status,status.d/*}"
WithParserByGlobs(parseDpkgDB, "**/var/lib/dpkg/status", "**/var/lib/dpkg/status.d/*")
WithParserByGlobs(parseDpkgDB, "**/var/lib/dpkg/status", "**/var/lib/dpkg/status.d/*", "**/lib/opkg/info/*.control", "**/lib/opkg/status")
}
+2
View File
@@ -168,6 +168,8 @@ func TestCataloger_Globs(t *testing.T) {
expected: []string{
"var/lib/dpkg/status",
"var/lib/dpkg/status.d/pkg-1.0",
"usr/lib/opkg/status",
"usr/lib/opkg/info/pkg-1.0.control",
},
},
}
@@ -0,0 +1 @@
bogus package
@@ -0,0 +1 @@
bogus status
+1 -1
View File
@@ -9,7 +9,7 @@ import (
// PackageURL returns the PURL for the specific java package (see https://github.com/package-url/purl-spec)
func packageURL(name, version string, metadata pkg.JavaMetadata) string {
var groupID = name
groupIDs := cpe.GroupIDsFromJavaMetadata(metadata)
groupIDs := cpe.GroupIDsFromJavaMetadata(name, metadata)
if len(groupIDs) > 0 {
groupID = groupIDs[0]
}
@@ -57,7 +57,16 @@ func parseGradleLockfile(_ file.Resolver, _ *generic.Environment, reader file.Lo
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
PomProject: &pkg.PomProject{
GroupID: dep.Group,
ArtifactID: dep.Name,
Version: dep.Version,
Name: dep.Name,
},
},
}
mappedPkg.SetID()
pkgs = append(pkgs, mappedPkg)
}
@@ -16,12 +16,25 @@ func Test_parserGradleLockfile(t *testing.T) {
{
input: "test-fixtures/gradle/gradle.lockfile",
expected: []pkg.Package{
{
Name: "commons-text",
Version: "1.8",
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
PomProject: &pkg.PomProject{GroupID: "org.apache.commons", ArtifactID: "commons-text", Version: "1.8", Name: "commons-text"},
},
},
{
Name: "hamcrest-core",
Version: "1.3",
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
PomProject: &pkg.PomProject{GroupID: "org.hamcrest", ArtifactID: "hamcrest-core", Version: "1.3", Name: "hamcrest-core"},
},
},
{
Name: "joda-time",
@@ -29,6 +42,9 @@ func Test_parserGradleLockfile(t *testing.T) {
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
PomProject: &pkg.PomProject{GroupID: "joda-time", ArtifactID: "joda-time", Version: "2.2", Name: "joda-time"},
},
},
{
Name: "junit",
@@ -36,6 +52,9 @@ func Test_parserGradleLockfile(t *testing.T) {
Language: pkg.Java,
Type: pkg.JavaPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
PomProject: &pkg.PomProject{GroupID: "junit", ArtifactID: "junit", Version: "4.12", Name: "junit"},
},
},
},
},
+67 -27
View File
@@ -28,17 +28,19 @@ func parserPomXML(_ file.Resolver, _ *generic.Environment, reader file.LocationR
}
var pkgs []pkg.Package
for _, dep := range pom.Dependencies {
p := newPackageFromPom(
pom,
dep,
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
)
if p.Name == "" {
continue
}
if pom.Dependencies != nil {
for _, dep := range *pom.Dependencies {
p := newPackageFromPom(
pom,
dep,
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
)
if p.Name == "" {
continue
}
pkgs = append(pkgs, p)
pkgs = append(pkgs, p)
}
}
return pkgs, nil, nil
@@ -53,15 +55,18 @@ func parsePomXMLProject(path string, reader io.Reader) (*pkg.PomProject, error)
}
func newPomProject(path string, p gopom.Project) *pkg.PomProject {
artifactID := safeString(p.ArtifactID)
name := safeString(p.Name)
projectURL := safeString(p.URL)
return &pkg.PomProject{
Path: path,
Parent: pomParent(p, p.Parent),
GroupID: resolveProperty(p, p.GroupID),
ArtifactID: p.ArtifactID,
ArtifactID: artifactID,
Version: resolveProperty(p, p.Version),
Name: p.Name,
Name: name,
Description: cleanDescription(p.Description),
URL: p.URL,
URL: projectURL,
}
}
@@ -74,7 +79,7 @@ func newPackageFromPom(pom gopom.Project, dep gopom.Dependency, locations ...fil
},
}
name := dep.ArtifactID
name := safeString(dep.ArtifactID)
version := resolveProperty(pom, dep.Version)
p := pkg.Package{
@@ -104,19 +109,29 @@ func decodePomXML(content io.Reader) (project gopom.Project, err error) {
return project, nil
}
func pomParent(pom gopom.Project, parent gopom.Parent) (result *pkg.PomParent) {
if parent.ArtifactID != "" || parent.GroupID != "" || parent.Version != "" {
result = &pkg.PomParent{
GroupID: resolveProperty(pom, parent.GroupID),
ArtifactID: parent.ArtifactID,
Version: resolveProperty(pom, parent.Version),
}
func pomParent(pom gopom.Project, parent *gopom.Parent) (result *pkg.PomParent) {
if parent == nil {
return nil
}
artifactID := safeString(parent.ArtifactID)
result = &pkg.PomParent{
GroupID: resolveProperty(pom, parent.GroupID),
ArtifactID: artifactID,
Version: resolveProperty(pom, parent.Version),
}
if result.GroupID == "" && result.ArtifactID == "" && result.Version == "" {
return nil
}
return result
}
func cleanDescription(original string) (cleaned string) {
descriptionLines := strings.Split(original, "\n")
func cleanDescription(original *string) (cleaned string) {
if original == nil {
return ""
}
descriptionLines := strings.Split(*original, "\n")
for _, line := range descriptionLines {
line = strings.TrimSpace(line)
if len(line) == 0 {
@@ -130,12 +145,17 @@ func cleanDescription(original string) (cleaned string) {
// resolveProperty emulates some maven property resolution logic by looking in the project's variables
// as well as supporting the project expressions like ${project.parent.groupId}.
// If no match is found, the entire expression including ${} is returned
func resolveProperty(pom gopom.Project, property string) string {
return propertyMatcher.ReplaceAllStringFunc(property, func(match string) string {
//
//nolint:gocognit
func resolveProperty(pom gopom.Project, property *string) string {
propertyCase := safeString(property)
return propertyMatcher.ReplaceAllStringFunc(propertyCase, func(match string) string {
propertyName := strings.TrimSpace(match[2 : len(match)-1])
if value, ok := pom.Properties.Entries[propertyName]; ok {
entries := pomProperties(pom)
if value, ok := entries[propertyName]; ok {
return value
}
// if we don't find anything directly in the pom properties,
// see if we have a project.x expression and process this based
// on the xml tags in gopom
@@ -151,9 +171,15 @@ func resolveProperty(pom gopom.Project, property string) string {
part := parts[partNum]
for fieldNum := 0; fieldNum < pomValueType.NumField(); fieldNum++ {
f := pomValueType.Field(fieldNum)
if part == f.Tag.Get("xml") {
tag := f.Tag.Get("xml")
tag = strings.TrimSuffix(tag, ",omitempty")
if part == tag {
pomValue = pomValue.Field(fieldNum)
pomValueType = pomValue.Type()
if pomValueType.Kind() == reflect.Ptr {
pomValue = pomValue.Elem()
pomValueType = pomValue.Type()
}
if partNum == numParts-1 {
return fmt.Sprintf("%v", pomValue.Interface())
}
@@ -165,3 +191,17 @@ func resolveProperty(pom gopom.Project, property string) string {
return match
})
}
func pomProperties(p gopom.Project) map[string]string {
if p.Properties != nil {
return p.Properties.Entries
}
return map[string]string{}
}
func safeString(s *string) string {
if s == nil {
return ""
}
return *s
}
+25 -16
View File
@@ -272,13 +272,13 @@ func Test_parsePomXMLProject(t *testing.T) {
func Test_pomParent(t *testing.T) {
tests := []struct {
name string
input gopom.Parent
input *gopom.Parent
expected *pkg.PomParent
}{
{
name: "only group ID",
input: gopom.Parent{
GroupID: "org.something",
input: &gopom.Parent{
GroupID: stringPointer("org.something"),
},
expected: &pkg.PomParent{
GroupID: "org.something",
@@ -286,8 +286,8 @@ func Test_pomParent(t *testing.T) {
},
{
name: "only artifact ID",
input: gopom.Parent{
ArtifactID: "something",
input: &gopom.Parent{
ArtifactID: stringPointer("something"),
},
expected: &pkg.PomParent{
ArtifactID: "something",
@@ -295,22 +295,27 @@ func Test_pomParent(t *testing.T) {
},
{
name: "only Version",
input: gopom.Parent{
Version: "something",
input: &gopom.Parent{
Version: stringPointer("something"),
},
expected: &pkg.PomParent{
Version: "something",
},
},
{
name: "nil",
input: nil,
expected: nil,
},
{
name: "empty",
input: gopom.Parent{},
input: &gopom.Parent{},
expected: nil,
},
{
name: "unused field",
input: gopom.Parent{
RelativePath: "something",
input: &gopom.Parent{
RelativePath: stringPointer("something"),
},
expected: nil,
},
@@ -341,7 +346,7 @@ func Test_cleanDescription(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
assert.Equal(t, test.expected, cleanDescription(test.input))
assert.Equal(t, test.expected, cleanDescription(stringPointer(test.input)))
})
}
}
@@ -357,7 +362,7 @@ func Test_resolveProperty(t *testing.T) {
name: "property",
property: "${version.number}",
pom: gopom.Project{
Properties: gopom.Properties{
Properties: &gopom.Properties{
Entries: map[string]string{
"version.number": "12.5.0",
},
@@ -369,7 +374,7 @@ func Test_resolveProperty(t *testing.T) {
name: "groupId",
property: "${project.groupId}",
pom: gopom.Project{
GroupID: "org.some.group",
GroupID: stringPointer("org.some.group"),
},
expected: "org.some.group",
},
@@ -377,8 +382,8 @@ func Test_resolveProperty(t *testing.T) {
name: "parent groupId",
property: "${project.parent.groupId}",
pom: gopom.Project{
Parent: gopom.Parent{
GroupID: "org.some.parent",
Parent: &gopom.Parent{
GroupID: stringPointer("org.some.parent"),
},
},
expected: "org.some.parent",
@@ -387,8 +392,12 @@ func Test_resolveProperty(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
resolved := resolveProperty(test.pom, test.property)
resolved := resolveProperty(test.pom, stringPointer(test.property))
assert.Equal(t, test.expected, resolved)
})
}
}
func stringPointer(s string) *string {
return &s
}
@@ -5,3 +5,4 @@ joda-time:joda-time:2.2=compileClasspath,runtimeClasspath,testCompileClasspath,t
junit:junit:4.12=testCompileClasspath,testRuntimeClasspath
org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath
empty=annotationProcessor,testAnnotationProcessor
org.apache.commons:commons-text:1.8=compileClasspath
+51
View File
@@ -0,0 +1,51 @@
package cataloger
import (
"golang.org/x/exp/slices"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
)
var (
osCatalogerTypes = []pkg.Type{
pkg.AlpmPkg,
pkg.ApkPkg,
pkg.DebPkg,
pkg.NixPkg,
pkg.PortagePkg,
pkg.RpmPkg,
}
binaryCatalogerTypes = []pkg.Type{
pkg.BinaryPkg,
}
)
// ExcludeBinaryByFileOwnershipOverlap will remove packages from a collection given the following properties are true
// 1) the relationship between packages is OwnershipByFileOverlap
// 2) the parent is an "os" package
// 3) the child is a synthetic package generated by the binary cataloger
// 4) the package names are identical
// This was implemented as a way to help resolve: https://github.com/anchore/syft/issues/931
func ExcludeBinaryByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collection) bool {
if artifact.OwnershipByFileOverlapRelationship != r.Type {
return false
}
parent := c.Package(r.From.ID())
if parent == nil {
return false
}
parentInExclusion := slices.Contains(osCatalogerTypes, parent.Type)
if !parentInExclusion {
return false
}
child := c.Package(r.To.ID())
if child == nil {
return false
}
return slices.Contains(binaryCatalogerTypes, child.Type)
}
@@ -0,0 +1,76 @@
package cataloger
import (
"testing"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
)
func TestExclude(t *testing.T) {
packageA := pkg.Package{Name: "package-a", Type: pkg.ApkPkg}
packageB := pkg.Package{Name: "package-a", Type: pkg.PythonPkg}
packageC := pkg.Package{Name: "package-a", Type: pkg.BinaryPkg}
packageD := pkg.Package{Name: "package-d", Type: pkg.BinaryPkg}
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD} {
p := p
p.SetID()
}
tests := []struct {
name string
relationship artifact.Relationship
packages *pkg.Collection
shouldExclude bool
}{
{
name: "no exclusions from os -> python",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageA,
To: packageB,
},
packages: pkg.NewCollection(packageA, packageB),
shouldExclude: false,
},
{
name: "exclusions from os -> binary",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageA,
To: packageC,
},
packages: pkg.NewCollection(packageA, packageC),
shouldExclude: true,
},
{
name: "no exclusions from python -> binary",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageB,
To: packageC,
},
packages: pkg.NewCollection(packageB, packageC),
shouldExclude: false,
},
{
name: "no exclusions for different package names",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageA,
To: packageD,
},
packages: pkg.NewCollection(packageA, packageD),
shouldExclude: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if !ExcludeBinaryByFileOwnershipOverlap(test.relationship, test.packages) && test.shouldExclude {
t.Errorf("expected to exclude relationship %+v", test.relationship)
}
})
}
}
+7 -2
View File
@@ -11,6 +11,11 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)
const (
dbCatalogerName = "rpm-db-cataloger"
fileCatalogerName = "rpm-file-cataloger"
)
// NewRpmDBCataloger returns a new RPM DB cataloger object.
func NewRpmDBCataloger() *generic.Cataloger {
// check if a sqlite driver is available
@@ -18,14 +23,14 @@ func NewRpmDBCataloger() *generic.Cataloger {
log.Warnf("sqlite driver is not available, newer RPM databases might not be cataloged")
}
return generic.NewCataloger("rpm-db-cataloger").
return generic.NewCataloger(dbCatalogerName).
WithParserByGlobs(parseRpmDB, pkg.RpmDBGlob).
WithParserByGlobs(parseRpmManifest, pkg.RpmManifestGlob)
}
// NewFileCataloger returns a new RPM file cataloger object.
func NewFileCataloger() *generic.Cataloger {
return generic.NewCataloger("rpm-file-cataloger").
return generic.NewCataloger(fileCatalogerName).
WithParserByGlobs(parseRpm, "**/*.rpm")
}
+3
View File
@@ -23,6 +23,9 @@ func Test_DBCataloger_Globs(t *testing.T) {
"var/lib/rpm/Packages.db",
"var/lib/rpm/rpmdb.sqlite",
"var/lib/rpmmanifest/container-manifest-2",
"usr/lib/sysimage/rpm/Packages",
"usr/lib/sysimage/rpm/Packages.db",
"usr/lib/sysimage/rpm/rpmdb.sqlite",
},
},
}
+1 -1
View File
@@ -13,7 +13,7 @@ import (
// Packages is the legacy Berkely db based format
// Packages.db is the "ndb" format used in SUSE
// rpmdb.sqlite is the sqlite format used in fedora + derivates
const RpmDBGlob = "**/{var/lib,usr/share}/rpm/{Packages,Packages.db,rpmdb.sqlite}"
const RpmDBGlob = "**/{var/lib,usr/share,usr/lib/sysimage}/rpm/{Packages,Packages.db,rpmdb.sqlite}"
// Used in CBL-Mariner distroless images
const RpmManifestGlob = "**/var/lib/rpmmanifest/container-manifest-2"
+1 -2
View File
@@ -109,8 +109,6 @@ func (s StereoscopeImageSource) Describe() Description {
} else {
if ref, ok := ref.(reference.Named); ok {
nameIfUnset(ref.Name())
} else {
nameIfUnset(s.metadata.UserInput)
}
if ref, ok := ref.(reference.NamedTagged); ok {
@@ -122,6 +120,7 @@ func (s StereoscopeImageSource) Describe() Description {
}
}
nameIfUnset(s.metadata.UserInput)
versionIfUnset(s.metadata.ManifestDigest)
return Description{
@@ -241,3 +241,31 @@ func Test_StereoscopeImageSource_ID(t *testing.T) {
})
}
}
func Test_Describe(t *testing.T) {
tests := []struct {
name string
source StereoscopeImageSource
expected Description
}{
{
name: "name from user input",
source: StereoscopeImageSource{
id: "some-id",
metadata: StereoscopeImageSourceMetadata{
UserInput: "user input",
},
},
expected: Description{
ID: "some-id",
Name: "user input",
},
},
}
for _, test := range tests {
got := test.source.Describe()
got.Metadata = nil // might want to test this, but do not to determine if the user input is userd
require.Equal(t, test.expected, got)
}
}
+3 -3
View File
@@ -104,7 +104,7 @@ func TestPackagesCmdFlags(t *testing.T) {
name: "squashed-scope-flag-hidden-packages",
args: []string{"packages", "-o", "json", "-s", "squashed", hiddenPackagesImage},
assertions: []traitAssertion{
assertPackageCount(163),
assertPackageCount(162),
assertNotInOutput("vsftpd"), // hidden package
assertSuccessfulReturnCode,
},
@@ -113,7 +113,7 @@ func TestPackagesCmdFlags(t *testing.T) {
name: "all-layers-scope-flag",
args: []string{"packages", "-o", "json", "-s", "all-layers", hiddenPackagesImage},
assertions: []traitAssertion{
assertPackageCount(164), // packages are now deduplicated for this case
assertPackageCount(163), // packages are now deduplicated for this case
assertInOutput("all-layers"),
assertInOutput("vsftpd"), // hidden package
assertSuccessfulReturnCode,
@@ -126,7 +126,7 @@ func TestPackagesCmdFlags(t *testing.T) {
"SYFT_PACKAGE_CATALOGER_SCOPE": "all-layers",
},
assertions: []traitAssertion{
assertPackageCount(164), // packages are now deduplicated for this case
assertPackageCount(163), // packages are now deduplicated for this case
assertInOutput("all-layers"),
assertInOutput("vsftpd"), // hidden package
assertSuccessfulReturnCode,
+5 -1
View File
@@ -85,7 +85,11 @@ func TestSpdxValidationTooling(t *testing.T) {
validateCmd := exec.Command("make", "validate", fileArg, mountArg, imageArg)
validateCmd.Dir = filepath.Join(cwd, "test-fixtures", "image-java-spdx-tools")
runAndShow(t, validateCmd)
stdout, stderr, err := runCommand(validateCmd, map[string]string{})
if err != nil {
t.Fatalf("invalid SPDX document:%v\nSTDOUT:\n%s\nSTDERR:\n%s", err, stdout, stderr)
}
})
}
}
@@ -1,6 +1,6 @@
FROM openjdk:11
RUN wget https://github.com/spdx/tools-java/releases/download/v1.1.3/tools-java-1.1.3.zip && \
RUN wget --no-verbose https://github.com/spdx/tools-java/releases/download/v1.1.3/tools-java-1.1.3.zip && \
unzip tools-java-1.1.3.zip && \
rm tools-java-1.1.3.zip
-28
View File
@@ -1,11 +1,9 @@
package cli
import (
"bufio"
"bytes"
"flag"
"fmt"
"io"
"math"
"os"
"os/exec"
@@ -17,8 +15,6 @@ import (
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/anchore/stereoscope/pkg/imagetest"
)
@@ -32,30 +28,6 @@ func logOutputOnFailure(t testing.TB, cmd *exec.Cmd, stdout, stderr string) {
}
}
func runAndShow(t *testing.T, cmd *exec.Cmd) {
t.Helper()
stderr, err := cmd.StderrPipe()
require.NoErrorf(t, err, "could not get stderr: +v", err)
stdout, err := cmd.StdoutPipe()
require.NoErrorf(t, err, "could not get stdout: +v", err)
err = cmd.Start()
require.NoErrorf(t, err, "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())
}
}
show("out", stdout)
show("err", stderr)
}
func setupPKI(t *testing.T, pw string) func() {
err := os.Setenv("COSIGN_PASSWORD", pw)
if err != nil {
@@ -56880,40 +56880,6 @@
]
}
},
{
"id": "875f4d287d1bdcfd",
"name": "python",
"version": "3.6.8",
"type": "binary",
"foundBy": "binary-cataloger",
"locations": [
{
"path": "/usr/lib64/libpython3.6m.so.1.0",
"layerID": "sha256:eb29745b8228e1e97c01b1d5c2554a319c00a94d8dd5746a3904222ad65a13f8"
}
],
"licenses": [],
"language": "",
"cpes": [
"cpe:2.3:a:python_software_foundation:python:3.6.8:*:*:*:*:*:*:*",
"cpe:2.3:a:python:python:3.6.8:*:*:*:*:*:*:*",
"cpe:2.3:a:python:python:3.6.8:*:*:*:*:*:*:*"
],
"purl": "pkg:generic/python@3.6.8",
"metadataType": "BinaryMetadata",
"metadata": {
"matches": [
{
"classifier": "python-binary-lib",
"location": {
"path": "/usr/lib64/libpython3.6m.so.1.0",
"layerID": "sha256:eb29745b8228e1e97c01b1d5c2554a319c00a94d8dd5746a3904222ad65a13f8",
"virtualPath": "/usr/lib64/libpython3.6m.so.1.0"
}
}
]
}
},
{
"id": "e57db3737a1d260f",
"name": "python3-dnf",
+7 -4
View File
@@ -26,7 +26,10 @@ import (
// encode-decode-encode loop which will detect lossy behavior in both directions.
func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
// use second image for relationships
images := []string{"image-pkg-coverage", "image-owning-package"}
images := []string{
"image-pkg-coverage",
"image-owning-package",
}
tests := []struct {
formatOption sbom.FormatID
redactor func(in []byte) []byte
@@ -44,8 +47,8 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
formatOption: cyclonedxjson.ID,
redactor: func(in []byte) []byte {
// unstable values
in = regexp.MustCompile(`"(timestamp|serialNumber|bom-ref)": "[^"]+",`).ReplaceAll(in, []byte{})
in = regexp.MustCompile(`"(timestamp|serialNumber|bom-ref|ref)":\s*"(\n|[^"])+"`).ReplaceAll(in, []byte(`"$1": "redacted"`))
in = regexp.MustCompile(`"(dependsOn)":\s*\[(?:\s|[^]])+]`).ReplaceAll(in, []byte(`"$1": []`))
return in
},
json: true,
@@ -54,7 +57,7 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
formatOption: cyclonedxxml.ID,
redactor: func(in []byte) []byte {
// unstable values
in = regexp.MustCompile(`(serialNumber|bom-ref)="[^"]+"`).ReplaceAll(in, []byte{})
in = regexp.MustCompile(`(serialNumber|bom-ref|ref)="[^"]+"`).ReplaceAll(in, []byte{})
in = regexp.MustCompile(`<timestamp>[^<]+</timestamp>`).ReplaceAll(in, []byte{})
return in
@@ -22,13 +22,13 @@ func TestPackageDeduplication(t *testing.T) {
}{
{
scope: source.AllLayersScope,
packageCount: 174, // without deduplication this would be 618
packageCount: 172, // without deduplication this would be 618
instanceCount: map[string]int{
"basesystem": 1,
"wget": 1,
"curl": 2, // upgraded in the image
"vsftpd": 1,
"httpd": 2, // rpm, binary
"httpd": 1, // rpm, - we exclude binary
},
locationCount: map[string]int{
"basesystem-10.0-7.el7.centos": 4,
@@ -37,18 +37,18 @@ func TestPackageDeduplication(t *testing.T) {
"wget-1.14-18.el7_6.1": 3,
"vsftpd-3.0.2-29.el7_9": 2,
"httpd-2.4.6-97.el7.centos.5": 1,
"httpd-2.4.6": 1, // binary
// "httpd-2.4.6": 1, // binary
},
},
{
scope: source.SquashedScope,
packageCount: 172,
packageCount: 170,
instanceCount: map[string]int{
"basesystem": 1,
"wget": 1,
"curl": 1, // upgraded, but the most recent
"vsftpd": 1,
"httpd": 2, // rpm, binary
"httpd": 1, // rpm, binary is now excluded by overlap
},
locationCount: map[string]int{
"basesystem-10.0-7.el7.centos": 1,
@@ -56,7 +56,7 @@ func TestPackageDeduplication(t *testing.T) {
"wget-1.14-18.el7_6.1": 1,
"vsftpd-3.0.2-29.el7_9": 1,
"httpd-2.4.6-97.el7.centos.5": 1,
"httpd-2.4.6": 1, // binary
// "httpd-2.4.6": 1, // binary (excluded)
},
},
}
@@ -64,7 +64,6 @@ func TestPackageDeduplication(t *testing.T) {
for _, tt := range tests {
t.Run(string(tt.scope), func(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-vertical-package-dups", tt.scope, nil)
for _, p := range sbom.Artifacts.Packages.Sorted() {
if p.Type == pkg.BinaryPkg {
assert.NotEmpty(t, p.Name)
@@ -7,11 +7,11 @@ import (
"github.com/anchore/syft/syft/formats/syftjson"
syftjsonModel "github.com/anchore/syft/syft/formats/syftjson/model"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
func TestPackageOwnershipRelationships(t *testing.T) {
// ensure that the json encoder is applying artifact ownership with an image that has expected ownership relationships
tests := []struct {
fixture string
@@ -45,3 +45,43 @@ func TestPackageOwnershipRelationships(t *testing.T) {
}
}
func TestPackageOwnershipExclusions(t *testing.T) {
// ensure that the json encoder is excluding packages by artifact ownership with an image that has expected ownership relationships
tests := []struct {
name string
fixture string
}{
{
name: "busybox binary is filtered based on ownership relationship",
fixture: "image-os-binary-overlap",
},
}
for _, test := range tests {
t.Run(test.fixture, func(t *testing.T) {
sbom, _ := catalogFixtureImage(t, test.fixture, source.SquashedScope, nil)
binaryPackages := make([]pkg.Package, 0)
apkPackages := make([]pkg.Package, 0)
for p := range sbom.Artifacts.Packages.Enumerate() {
if p.Type == pkg.BinaryPkg && p.Name == "busybox" {
binaryPackages = append(binaryPackages, p)
}
if p.Type == pkg.ApkPkg && p.Name == "busybox" {
apkPackages = append(apkPackages, p)
}
}
if len(binaryPackages) != 0 {
packageNames := make([]string, 0)
for _, p := range binaryPackages {
packageNames = append(packageNames, p.Name)
}
t.Errorf("expected to find no binary packages but found %d packages: %v", len(binaryPackages), packageNames)
}
if len(apkPackages) == 0 {
t.Errorf("expected to find apk packages but found none")
}
})
}
}
@@ -0,0 +1,20 @@
package integration
import (
"testing"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
func TestPhotonPackageRegression(t *testing.T) { // Regression: https://github.com/anchore/syft/pull/1997
sbom, _ := catalogFixtureImage(t, "image-photon-all-layers", source.AllLayersScope, nil)
var packages []pkg.Package
for p := range sbom.Artifacts.Packages.Enumerate() {
packages = append(packages, p)
}
if len(packages) < 1 {
t.Errorf("failed to find packages for photon distro; wanted > 0 got 0")
}
}
@@ -0,0 +1,7 @@
FROM alpine:latest
# syft should not longer show the binary package for this image:
# https://github.com/anchore/syft/issues/931
# busybox 1.36.1 binary <-- not reported synthetic package overlap
# busybox 1.36.1-r0 apk
RUN apk update && apk add busybox
@@ -0,0 +1 @@
FROM photon:5.0-20230729