mirror of
https://github.com/anchore/syft.git
synced 2026-10-14 07:07:18 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fb9970481 |
@@ -29,7 +29,6 @@ jobs:
|
||||
GOSIMPORTS_LATEST_VERSION=$(go list -m -json github.com/rinchsan/gosimports@latest 2>/dev/null | jq -r '.Version')
|
||||
YAJSV_LATEST_VERSION=$(go list -m -json github.com/neilpa/yajsv@latest 2>/dev/null | jq -r '.Version')
|
||||
COSIGN_LATEST_VERSION=$(go list -m -json github.com/sigstore/cosign/v2@latest 2>/dev/null | jq -r '.Version')
|
||||
QUILL_LATEST_VERSION=$(go list -m -json github.com/anchore/quill@latest 2>/dev/null | jq -r '.Version')
|
||||
GLOW_LATEST_VERSION=$(go list -m -json github.com/charmbracelet/glow@latest 2>/dev/null | jq -r '.Version')
|
||||
|
||||
# update version variables in the Makefile
|
||||
@@ -40,7 +39,6 @@ jobs:
|
||||
sed -r -i -e 's/^(GOSIMPORTS_VERSION := ).*/\1'${GOSIMPORTS_LATEST_VERSION}'/' Makefile
|
||||
sed -r -i -e 's/^(YAJSV_VERSION := ).*/\1'${YAJSV_LATEST_VERSION}'/' Makefile
|
||||
sed -r -i -e 's/^(COSIGN_VERSION := ).*/\1'${COSIGN_LATEST_VERSION}'/' Makefile
|
||||
sed -r -i -e 's/^(QUILL_VERSION := ).*/\1'${QUILL_LATEST_VERSION}'/' Makefile
|
||||
sed -r -i -e 's/^(GLOW_VERSION := ).*/\1'${GLOW_LATEST_VERSION}'/' Makefile
|
||||
|
||||
# export the versions for use with create-pull-request
|
||||
@@ -51,7 +49,6 @@ jobs:
|
||||
echo "GOSIMPORTS=$GOSIMPORTS_LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "YAJSV=$YAJSV_LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "COSIGN=$COSIGN_LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "QUILL=$QUILL_LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "GLOW=GLOW_LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||
id: latest-versions
|
||||
|
||||
@@ -77,7 +74,6 @@ jobs:
|
||||
- [gosimports ${{ steps.latest-versions.outputs.GOSIMPORTS }}](https://github.com/rinchsan/gosimports/releases/tag/${{ steps.latest-versions.outputs.GOSIMPORTS }})
|
||||
- [yajsv ${{ steps.latest-versions.outputs.YAJSV }}](https://github.com/neilpa/yajsv/releases/tag/${{ steps.latest-versions.outputs.YAJSV }})
|
||||
- [cosign ${{ steps.latest-versions.outputs.COSIGN }}](https://github.com/sigstore/cosign/releases/tag/${{ steps.latest-versions.outputs.COSIGN }})
|
||||
- [quill ${{ steps.latest-versions.outputs.QUILL }}](https://github.com/anchore/quill/releases/tag/${{ steps.latest-versions.outputs.QUILL }})
|
||||
- [glow ${{ steps.latest-versions.outputs.GLOW }}](https://github.com/charmbracelet/glow/releases/tag/${{ steps.latest-versions.outputs.GLOW }})
|
||||
This is an auto-generated pull request to update all of the bootstrap tools to the latest versions.
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
@@ -10,14 +10,14 @@ CHRONICLE_CMD = $(TEMP_DIR)/chronicle
|
||||
GLOW_CMD = $(TEMP_DIR)/glow
|
||||
|
||||
# Tool versions #################################
|
||||
GOLANGCILINT_VERSION := v1.54.2
|
||||
GOLANGCILINT_VERSION := v1.53.3
|
||||
GOSIMPORTS_VERSION := v0.3.8
|
||||
BOUNCER_VERSION := v0.4.0
|
||||
CHRONICLE_VERSION := v0.7.0
|
||||
GORELEASER_VERSION := v1.20.0
|
||||
CHRONICLE_VERSION := v0.6.0
|
||||
GORELEASER_VERSION := v1.19.2
|
||||
YAJSV_VERSION := v1.4.1
|
||||
COSIGN_VERSION := v2.1.1
|
||||
QUILL_VERSION := v0.4.1
|
||||
QUILL_VERSION := v0.2.0
|
||||
GLOW_VERSION := v1.5.1
|
||||
|
||||
# Formatting variables #################################
|
||||
|
||||
@@ -482,10 +482,6 @@ 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: ""
|
||||
|
||||
@@ -27,7 +27,12 @@ report 1!!>
|
||||
|
||||
<notification 2>
|
||||
<notification 3>
|
||||
A newer version of syft is available for download: v0.33.0 (installed version is [not provided])
|
||||
|
||||
|
||||
<my app can be updated!!
|
||||
...to this version>
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/wagoodman/go-partybus"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/version"
|
||||
"github.com/anchore/syft/syft/event"
|
||||
"github.com/anchore/syft/syft/event/parsers"
|
||||
)
|
||||
@@ -119,14 +118,12 @@ func writeAppUpdate(writer io.Writer, events ...partybus.Event) error {
|
||||
style := lipgloss.NewStyle().Foreground(lipgloss.Color("13")).Italic(true)
|
||||
|
||||
for _, e := range events {
|
||||
newVersion, err := parsers.ParseCLIAppUpdateAvailable(e)
|
||||
notice, err := parsers.ParseCLIAppUpdateAvailable(e)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warn("failed to parse app update notification")
|
||||
continue
|
||||
}
|
||||
|
||||
notice := fmt.Sprintf("A newer version of syft is available for download: %s (installed version is %s)", newVersion, version.FromBuild().Version)
|
||||
|
||||
if _, err := fmt.Fprintln(writer, style.Render(notice)); err != nil {
|
||||
// don't let this be fatal
|
||||
log.WithFields("error", err).Warn("failed to write app update notification")
|
||||
|
||||
@@ -35,7 +35,7 @@ func Test_postUIEventWriter_write(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Type: event.CLIAppUpdateAvailable,
|
||||
Value: "v0.33.0",
|
||||
Value: "\n\n<my app can be updated!!\n...to this version>\n\n",
|
||||
},
|
||||
{
|
||||
Type: event.CLINotification,
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
func Select(verbose, quiet bool) (uis []clio.UI) {
|
||||
isStdoutATty := term.IsTerminal(int(os.Stdout.Fd()))
|
||||
isStderrATty := term.IsTerminal(int(os.Stderr.Fd()))
|
||||
notATerminal := !isStderrATty && !isStdoutATty
|
||||
notATerminal := !isStderrATty || !isStdoutATty
|
||||
|
||||
switch {
|
||||
case runtime.GOOS == "windows" || verbose || quiet || notATerminal || !isStderrATty:
|
||||
|
||||
@@ -15,10 +15,10 @@ require (
|
||||
github.com/facebookincubator/nvdtools v0.1.5
|
||||
github.com/go-test/deep v1.1.0
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/jinzhu/copier v0.3.5
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5
|
||||
@@ -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 v1.0.0
|
||||
github.com/vifraa/gopom v0.2.2
|
||||
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.14.0
|
||||
golang.org/x/term v0.11.0
|
||||
golang.org/x/net v0.12.0
|
||||
golang.org/x/term v0.10.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-20230801194016-acdb4981b461
|
||||
github.com/anchore/bubbly v0.0.0-20230712165553-812110ab0a10
|
||||
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/anchore/stereoscope v0.0.0-20230724160817-d515761c6ca2
|
||||
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
|
||||
github.com/charmbracelet/bubbletea v0.24.2
|
||||
github.com/charmbracelet/lipgloss v0.8.0
|
||||
github.com/dave/jennifer v1.7.0
|
||||
github.com/charmbracelet/lipgloss v0.7.1
|
||||
github.com/dave/jennifer v1.6.1
|
||||
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,20 +66,18 @@ 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.16.1
|
||||
github.com/google/go-containerregistry v0.15.2
|
||||
github.com/google/licensecheck v0.3.1
|
||||
github.com/invopop/jsonschema v0.7.0
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/saferwall/pe v1.4.4
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
|
||||
github.com/sassoftware/go-rpmutils v0.2.0
|
||||
github.com/vbatts/go-mtree v0.5.3
|
||||
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.25.0
|
||||
modernc.org/sqlite v1.24.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -103,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 v24.0.0+incompatible // indirect
|
||||
github.com/docker/cli v23.0.5+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
|
||||
@@ -147,7 +145,7 @@ require (
|
||||
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/muesli/termenv v0.15.1 // indirect
|
||||
github.com/nwaples/rardecode v1.1.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
|
||||
github.com/pborman/indent v1.2.1 // indirect
|
||||
@@ -180,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.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/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/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
|
||||
@@ -193,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.24.1 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.6.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
@@ -206,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.12.0 // indirect
|
||||
golang.org/x/crypto v0.11.0 // indirect
|
||||
)
|
||||
|
||||
retract (
|
||||
|
||||
@@ -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-20230801194016-acdb4981b461 h1:xGu4/uMWucwWV0YV3fpFIQZ6KVfS/Wfhmma8t0s0vRo=
|
||||
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461/go.mod h1:Ger02eh5NpPm2IqkPAy396HU1KlK3BhOeCljDYXySSk=
|
||||
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/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=
|
||||
@@ -106,8 +106,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
|
||||
github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e h1:S6IhYpsBCpvphlHA1tN0glSG/kjVvFzC6OJuU2qW5Pc=
|
||||
github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e/go.mod h1:0LsgHgXO4QFnk2hsYwtqd3fR18PIZXlFLIl2qb9tu3g=
|
||||
github.com/anchore/stereoscope v0.0.0-20230724160817-d515761c6ca2 h1:qIxlZFFZIl5YNHwxsVQ6UJG8QtkR3W0W9ozaI9JQiPk=
|
||||
github.com/anchore/stereoscope v0.0.0-20230724160817-d515761c6ca2/go.mod h1:0LsgHgXO4QFnk2hsYwtqd3fR18PIZXlFLIl2qb9tu3g=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
@@ -147,8 +147,8 @@ github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06
|
||||
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
|
||||
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
|
||||
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||
github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU=
|
||||
github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU=
|
||||
github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
|
||||
github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -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.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE=
|
||||
github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||
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/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 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/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE=
|
||||
github.com/docker/cli v23.0.5+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.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ=
|
||||
github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
|
||||
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/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=
|
||||
@@ -355,8 +355,8 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S3
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
@@ -419,8 +419,8 @@ github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy77
|
||||
github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
|
||||
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@@ -428,8 +428,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 h1:WdAeg/imY2JFPc/9CST4bZ80nNJbiBFCAdSZCSgrS5Y=
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953/go.mod h1:6o+UrvuZWc4UTyBhQf0LGjW9Ld7qJxLz/OqvSOWWlEc=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
@@ -531,8 +529,8 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
|
||||
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
|
||||
@@ -598,8 +596,6 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
|
||||
github.com/saferwall/pe v1.4.4 h1:Ml++7/2/Z1iKwV4zCsd1nIqTEAdUQKAetwbbcCarhOg=
|
||||
github.com/saferwall/pe v1.4.4/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s=
|
||||
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/sassoftware/go-rpmutils v0.2.0 h1:pKW0HDYMFWQ5b4JQPiI3WI12hGsVoW0V8+GMoZiI/JE=
|
||||
github.com/sassoftware/go-rpmutils v0.2.0/go.mod h1:TJJQYtLe/BeEmEjelI3b7xNZjzAukEkeWKmoakvaOoI=
|
||||
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
|
||||
@@ -691,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 v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0=
|
||||
github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
|
||||
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/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=
|
||||
@@ -754,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.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
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=
|
||||
@@ -847,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.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
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/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=
|
||||
@@ -878,9 +874,8 @@ 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=
|
||||
@@ -962,16 +957,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.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.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/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.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/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/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=
|
||||
@@ -984,8 +979,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.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
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/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=
|
||||
@@ -1048,8 +1043,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.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
|
||||
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
|
||||
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=
|
||||
@@ -1248,16 +1243,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.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
|
||||
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
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.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
|
||||
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.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.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
|
||||
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
|
||||
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/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=
|
||||
|
||||
@@ -42,32 +42,31 @@ 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
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
func (cfg Application) ToCatalogerConfig() cataloger.Config {
|
||||
@@ -77,9 +76,8 @@ func (cfg Application) ToCatalogerConfig() cataloger.Config {
|
||||
IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives,
|
||||
Scope: cfg.Package.Cataloger.ScopeOpt,
|
||||
},
|
||||
Catalogers: cfg.Catalogers,
|
||||
Parallelism: cfg.Parallelism,
|
||||
ExcludeBinaryOverlapByOwnership: cfg.ExcludeBinaryOverlapByOwnership,
|
||||
Catalogers: cfg.Catalogers,
|
||||
Parallelism: cfg.Parallelism,
|
||||
Golang: golangCataloger.NewGoCatalogerOpts().
|
||||
WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses).
|
||||
WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
|
||||
@@ -223,7 +221,6 @@ 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{})
|
||||
|
||||
@@ -6,5 +6,5 @@ const (
|
||||
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "10.0.1"
|
||||
JSONSchemaVersion = "10.0.0"
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -210,33 +210,24 @@ 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 {
|
||||
fromPtr, fromExists := idMap[t]
|
||||
if !fromExists {
|
||||
continue
|
||||
}
|
||||
from, ok := common.PtrToStruct(fromPtr).(artifact.Identifiable)
|
||||
if !ok {
|
||||
from, toExists := idMap[t].(artifact.Identifiable)
|
||||
if !toExists {
|
||||
continue
|
||||
}
|
||||
s.Relationships = append(s.Relationships, artifact.Relationship{
|
||||
From: from,
|
||||
To: to,
|
||||
// match assumptions in encoding, that this is the only type of relationship captured:
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
Type: artifact.DependencyOfRelationship, // FIXME this information is lost
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,8 @@ 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"
|
||||
)
|
||||
|
||||
@@ -338,101 +336,3 @@ 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
@@ -140,53 +139,34 @@ func isExpressiblePackageRelationship(ty artifact.RelationshipType) bool {
|
||||
}
|
||||
|
||||
func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependency {
|
||||
dependencies := map[string]*cyclonedx.Dependency{}
|
||||
result := make([]cyclonedx.Dependency, 0)
|
||||
for _, r := range relationships {
|
||||
exists := isExpressiblePackageRelationship(r.Type)
|
||||
if !exists {
|
||||
log.Debugf("unable to convert relationship type to CycloneDX JSON, dropping: %#v", r)
|
||||
log.Debugf("unable to convert relationship from CycloneDX 1.4 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
|
||||
}
|
||||
|
||||
toRef := deriveBomRef(toPkg)
|
||||
dep := dependencies[toRef]
|
||||
if dep == nil {
|
||||
dep = &cyclonedx.Dependency{
|
||||
Ref: toRef,
|
||||
Dependencies: &[]string{},
|
||||
}
|
||||
dependencies[toRef] = dep
|
||||
}
|
||||
// ind dep
|
||||
|
||||
fromRef := deriveBomRef(fromPkg)
|
||||
if !slices.Contains(*dep.Dependencies, fromRef) {
|
||||
*dep.Dependencies = append(*dep.Dependencies, fromRef)
|
||||
}
|
||||
innerDeps := []string{}
|
||||
innerDeps = append(innerDeps, deriveBomRef(*fromPkg))
|
||||
result = append(result, cyclonedx.Dependency{
|
||||
Ref: deriveBomRef(*toPkg),
|
||||
Dependencies: &innerDeps,
|
||||
})
|
||||
}
|
||||
|
||||
result := make([]cyclonedx.Dependency, 0, len(dependencies))
|
||||
for _, dep := range dependencies {
|
||||
slices.Sort(*dep.Dependencies)
|
||||
result = append(result, *dep)
|
||||
}
|
||||
|
||||
slices.SortFunc(result, func(a, b cyclonedx.Dependency) bool {
|
||||
return a.Ref < b.Ref
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
package cyclonedxhelpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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) {
|
||||
@@ -39,102 +32,3 @@ func Test_formatCPE(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_relationships(t *testing.T) {
|
||||
p1 := pkg.Package{
|
||||
Name: "p1",
|
||||
}
|
||||
|
||||
p2 := pkg.Package{
|
||||
Name: "p2",
|
||||
}
|
||||
|
||||
p3 := pkg.Package{
|
||||
Name: "p3",
|
||||
}
|
||||
|
||||
p4 := pkg.Package{
|
||||
Name: "p4",
|
||||
}
|
||||
|
||||
for _, p := range []*pkg.Package{&p1, &p2, &p3, &p4} {
|
||||
p.PURL = fmt.Sprintf("pkg:generic/%s@%s", p.Name, p.Name)
|
||||
p.SetID()
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
sbom sbom.SBOM
|
||||
expected *[]cyclonedx.Dependency
|
||||
}{
|
||||
{
|
||||
name: "package dependencyOf relationships output as dependencies",
|
||||
sbom: sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(p1, p2, p3, p4),
|
||||
},
|
||||
Relationships: []artifact.Relationship{
|
||||
{
|
||||
From: p2,
|
||||
To: p1,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
{
|
||||
From: p3,
|
||||
To: p1,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
{
|
||||
From: p4,
|
||||
To: p2,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &[]cyclonedx.Dependency{
|
||||
{
|
||||
Ref: deriveBomRef(p1),
|
||||
Dependencies: &[]string{
|
||||
deriveBomRef(p2),
|
||||
deriveBomRef(p3),
|
||||
},
|
||||
},
|
||||
{
|
||||
Ref: deriveBomRef(p2),
|
||||
Dependencies: &[]string{
|
||||
deriveBomRef(p4),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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
|
||||
require.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/spdxlicense"
|
||||
@@ -29,18 +27,29 @@ 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 []spdxLicense) string {
|
||||
func joinLicenses(licenses []string) string {
|
||||
if len(licenses) == 0 {
|
||||
return NOASSERTION
|
||||
}
|
||||
|
||||
var newLicenses []string
|
||||
|
||||
for _, l := range licenses {
|
||||
v := l.id
|
||||
for _, v := range licenses {
|
||||
// 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
|
||||
@@ -57,31 +66,14 @@ func joinLicenses(licenses []spdxLicense) string {
|
||||
return strings.Join(newLicenses, " AND ")
|
||||
}
|
||||
|
||||
type spdxLicense struct {
|
||||
id string
|
||||
value string
|
||||
}
|
||||
|
||||
func parseLicenses(raw []pkg.License) (concluded, declared []spdxLicense) {
|
||||
func parseLicenses(raw []pkg.License) (concluded, declared []string) {
|
||||
for _, l := range raw {
|
||||
if l.Value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
candidate := spdxLicense{}
|
||||
var candidate string
|
||||
if l.SPDXExpression != "" {
|
||||
candidate.id = l.SPDXExpression
|
||||
candidate = l.SPDXExpression
|
||||
} else {
|
||||
// we did not find a valid SPDX license ID so treat as separate license
|
||||
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
|
||||
candidate = spdxlicense.LicenseRefPrefix + l.Value
|
||||
}
|
||||
|
||||
switch l.Type {
|
||||
@@ -91,6 +83,5 @@ func parseLicenses(raw []pkg.License) (concluded, declared []spdxLicense) {
|
||||
declared = append(declared, candidate)
|
||||
}
|
||||
}
|
||||
|
||||
return concluded, declared
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
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) {
|
||||
@@ -108,77 +103,6 @@ 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
|
||||
@@ -198,18 +122,7 @@ func Test_joinLicenses(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, joinLicenses(toSpdxLicenses(tt.args)), "joinLicenses(%v)", tt.args)
|
||||
assert.Equalf(t, tt.want, joinLicenses(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,12 +240,9 @@ 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 {
|
||||
@@ -360,7 +357,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: toPackageSupplier(p),
|
||||
PackageSupplier: nil,
|
||||
|
||||
PackageOriginator: toPackageOriginator(p),
|
||||
|
||||
@@ -517,21 +514,6 @@ 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{
|
||||
@@ -704,31 +686,32 @@ 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]spdxLicense{}
|
||||
|
||||
for p := range catalog.Enumerate() {
|
||||
licenses := map[string]bool{}
|
||||
for _, p := range catalog.Sorted() {
|
||||
declaredLicenses, concludedLicenses := parseLicenses(p.Licenses.ToSlice())
|
||||
for _, l := range declaredLicenses {
|
||||
if l.value != "" {
|
||||
licenses[l.id] = l
|
||||
for _, license := range declaredLicenses {
|
||||
if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) {
|
||||
licenses[license] = true
|
||||
}
|
||||
}
|
||||
for _, l := range concludedLicenses {
|
||||
if l.value != "" {
|
||||
licenses[l.id] = l
|
||||
for _, license := range concludedLicenses {
|
||||
if strings.HasPrefix(license, spdxlicense.LicenseRefPrefix) {
|
||||
licenses[license] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result []*spdx.OtherLicense
|
||||
|
||||
ids := maps.Keys(licenses)
|
||||
slices.Sort(ids)
|
||||
for _, id := range ids {
|
||||
license := licenses[id]
|
||||
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)
|
||||
result = append(result, &spdx.OtherLicense{
|
||||
LicenseIdentifier: license.id,
|
||||
ExtractedText: license.value,
|
||||
LicenseIdentifier: SanitizeElementID(license),
|
||||
ExtractedText: name,
|
||||
})
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -51,14 +51,12 @@ 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",
|
||||
@@ -73,9 +71,6 @@ func Test_toFormatModel(t *testing.T) {
|
||||
Locator: "pkg:oci/alpine@sha256:d34db33f?arch=&tag=latest",
|
||||
},
|
||||
},
|
||||
PackageSupplier: &spdx.Supplier{
|
||||
Supplier: "NOASSERTION",
|
||||
},
|
||||
},
|
||||
},
|
||||
Relationships: []*spdx.Relationship{
|
||||
@@ -127,18 +122,12 @@ 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{
|
||||
@@ -191,14 +180,12 @@ 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",
|
||||
@@ -206,9 +193,6 @@ 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,7 +14,6 @@ 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"
|
||||
@@ -286,16 +285,6 @@ 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
|
||||
@@ -342,14 +331,7 @@ func toFileMetadata(f *spdx.File) (meta file.Metadata) {
|
||||
}
|
||||
|
||||
func toSyftRelationships(spdxIDMap map[string]any, doc *spdx.Document) []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) {
|
||||
var 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) {
|
||||
@@ -403,30 +385,6 @@ func collectDocRelationships(spdxIDMap map[string]any, doc *spdx.Document) (out
|
||||
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
|
||||
@@ -509,11 +467,11 @@ func toSyftPackage(p *spdx.Package) pkg.Package {
|
||||
}
|
||||
|
||||
func purlValue(purl packageurl.PackageURL) string {
|
||||
val := purl.String()
|
||||
if _, err := packageurl.FromString(val); err != nil {
|
||||
p := purl.String()
|
||||
if p == "pkg:/" {
|
||||
return ""
|
||||
}
|
||||
return val
|
||||
return p
|
||||
}
|
||||
|
||||
func parseSPDXLicenses(p *spdx.Package) []pkg.License {
|
||||
@@ -537,7 +495,10 @@ func parseSPDXLicenses(p *spdx.Package) []pkg.License {
|
||||
}
|
||||
|
||||
func cleanSPDXID(id string) string {
|
||||
return strings.TrimPrefix(id, spdxlicense.LicenseRefPrefix)
|
||||
if strings.HasPrefix(id, "LicenseRef-") {
|
||||
return strings.TrimPrefix(id, "LicenseRef-")
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
//nolint:funlen
|
||||
|
||||
@@ -11,7 +11,6 @@ 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"
|
||||
@@ -553,132 +552,3 @@ 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,7 +17,6 @@
|
||||
"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",
|
||||
@@ -41,7 +40,6 @@
|
||||
"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",
|
||||
@@ -64,8 +62,7 @@
|
||||
{
|
||||
"name": "some/path",
|
||||
"SPDXID": "SPDXRef-DocumentRoot-Directory-some-path",
|
||||
"supplier": "NOASSERTION",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
"downloadLocation": "",
|
||||
"filesAnalyzed": false,
|
||||
"primaryPackagePurpose": "FILE"
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
"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",
|
||||
@@ -41,7 +40,6 @@
|
||||
"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",
|
||||
@@ -65,8 +63,7 @@
|
||||
"name": "user-image-input",
|
||||
"SPDXID": "SPDXRef-DocumentRoot-Image-user-image-input",
|
||||
"versionInfo": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
|
||||
"supplier": "NOASSERTION",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
"downloadLocation": "",
|
||||
"filesAnalyzed": false,
|
||||
"checksums": [
|
||||
{
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
"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",
|
||||
@@ -41,7 +40,6 @@
|
||||
"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",
|
||||
@@ -65,8 +63,7 @@
|
||||
"name": "user-image-input",
|
||||
"SPDXID": "SPDXRef-DocumentRoot-Image-user-image-input",
|
||||
"versionInfo": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
|
||||
"supplier": "NOASSERTION",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
"downloadLocation": "",
|
||||
"filesAnalyzed": false,
|
||||
"checksums": [
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -2,13 +2,9 @@ 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
|
||||
@@ -38,59 +34,3 @@ 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,8 +12,6 @@ Created: redacted
|
||||
|
||||
PackageName: foobar/baz
|
||||
SPDXID: SPDXRef-DocumentRoot-Directory-foobar-baz
|
||||
PackageSupplier: NOASSERTION
|
||||
PackageDownloadLocation: NOASSERTION
|
||||
PrimaryPackagePurpose: FILE
|
||||
FilesAnalyzed: false
|
||||
|
||||
@@ -21,7 +19,6 @@ 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:
|
||||
@@ -33,7 +30,6 @@ 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:
|
||||
@@ -45,7 +41,6 @@ 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,8 +51,6 @@ 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
|
||||
@@ -63,7 +61,6 @@ 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
|
||||
@@ -78,7 +75,6 @@ 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
|
||||
|
||||
-4
@@ -12,8 +12,6 @@ Created: redacted
|
||||
|
||||
PackageName: some/path
|
||||
SPDXID: SPDXRef-DocumentRoot-Directory-some-path
|
||||
PackageSupplier: NOASSERTION
|
||||
PackageDownloadLocation: NOASSERTION
|
||||
PrimaryPackagePurpose: FILE
|
||||
FilesAnalyzed: false
|
||||
|
||||
@@ -22,7 +20,6 @@ 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
|
||||
@@ -37,7 +34,6 @@ 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,8 +13,6 @@ 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
|
||||
@@ -25,7 +23,6 @@ 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
|
||||
@@ -40,7 +37,6 @@ 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
|
||||
|
||||
BIN
Binary file not shown.
@@ -349,13 +349,9 @@ func (r directoryIndexer) addSymlinkToIndex(p string, info os.FileInfo) (string,
|
||||
}
|
||||
|
||||
if filepath.IsAbs(linkTarget) {
|
||||
linkTarget = filepath.Clean(linkTarget)
|
||||
// if the link is absolute (e.g, /bin/ls -> /bin/busybox) we need to
|
||||
// resolve relative to the root of the base directory, if it is not already
|
||||
// prefixed with a volume name
|
||||
if filepath.VolumeName(linkTarget) == "" {
|
||||
linkTarget = filepath.Join(r.base, filepath.Clean(linkTarget))
|
||||
}
|
||||
// resolve relative to the root of the base directory
|
||||
linkTarget = filepath.Join(r.base, filepath.Clean(linkTarget))
|
||||
} else {
|
||||
// if the link is not absolute (e.g, /dev/stderr -> fd/2 ) we need to
|
||||
// resolve it relative to the directory in question (e.g. resolve to
|
||||
|
||||
+1
-23
@@ -74,31 +74,9 @@ func CatalogPackages(src source.Source, cfg cataloger.Config) (*pkg.Collection,
|
||||
|
||||
catalog, relationships, err := cataloger.Catalog(resolver, release, cfg.Parallelism, catalogers...)
|
||||
|
||||
// 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
|
||||
return catalog, relationships, release, err
|
||||
}
|
||||
|
||||
func newSourceRelationshipsFromCatalog(src source.Source, c *pkg.Collection) []artifact.Relationship {
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
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,42 +643,6 @@ 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"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-bash-5.2.15",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/bash-5.2.15",
|
||||
expected: pkg.Package{
|
||||
Name: "bash",
|
||||
Version: "5.2.15",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/bash@5.2.15",
|
||||
Locations: locations("bash"),
|
||||
Metadata: metadata("bash-binary"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -254,35 +254,6 @@ 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:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Class: "bash-binary",
|
||||
FileGlob: "**/bash",
|
||||
EvidenceMatcher: fileContentsVersionMatcher(
|
||||
// @(#)Bash version 5.2.15(1) release GNU
|
||||
// @(#)Bash version 5.2.0(1) alpha GNU
|
||||
// @(#)Bash version 5.2.0(1) beta GNU
|
||||
// @(#)Bash version 5.2.0(1) rc4 GNU
|
||||
`(?m)@\(#\)Bash version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)\([0-9]\) [a-z0-9]+ GNU`,
|
||||
),
|
||||
Package: "bash",
|
||||
PURL: mustPURL("pkg:generic/bash@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:gnu:bash:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
}
|
||||
|
||||
// in both binaries and shared libraries, the version pattern is [NUL]3.11.2[NUL]
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
@(#)Bash version 5.2.15(1) release GNU
|
||||
Binary file not shown.
BIN
Binary file not shown.
@@ -283,7 +283,6 @@
|
||||
"360class.jansenhm": "cpe:2.3:a:360class.jansenhm_project:360class.jansenhm:*:*:*:*:*:node.js:*:*",
|
||||
"626": "cpe:2.3:a:626_project:626:*:*:*:*:*:node.js:*:*",
|
||||
"@actions/core": "cpe:2.3:a:toolkit_project:toolkit:*:*:*:*:*:node.js:*:*",
|
||||
"@antfu/utils": "cpe:2.3:a:antfu:utils:*:*:*:*:*:node.js:*:*",
|
||||
"@awsui/components-react": "cpe:2.3:a:amazon:awsui\\/components-react:*:*:*:*:*:node.js:*:*",
|
||||
"@azure/ms-rest-nodeauth": "cpe:2.3:a:microsoft:ms-rest-nodeauth:*:*:*:*:*:node.js:*:*",
|
||||
"@backstage/plugin-auth-backend": "cpe:2.3:a:linuxfoundation:auth_backend:*:*:*:*:*:node.js:*:*",
|
||||
@@ -808,7 +807,6 @@
|
||||
"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:*:*",
|
||||
@@ -1082,7 +1080,6 @@
|
||||
"spacy": "cpe:2.3:a:explosion:spacy:*:*:*:*:*:python:*:*",
|
||||
"sqlparse": "cpe:2.3:a:sqlparse_project:sqlparse:*:*:*:*:*:python:*:*",
|
||||
"tkvideoplayer": "cpe:2.3:a:python:tkvideoplayer:*:*:*:*:*:*:*:*",
|
||||
"togglee": "cpe:2.3:a:togglee:togglee:*:*:*:*:*:pypi:*:*",
|
||||
"urllib3": "cpe:2.3:a:python:urllib3:*:*:*:*:*:*:*:*",
|
||||
"validators": "cpe:2.3:a:validators_project:validators:*:*:*:*:*:python:*:*",
|
||||
"vault-cli": "cpe:2.3:a:vault-cli_project:vault-cli:*:*:*:*:*:python:*:*",
|
||||
@@ -1147,7 +1144,6 @@
|
||||
"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:*:*",
|
||||
|
||||
@@ -354,12 +354,11 @@ 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,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Name: "name",
|
||||
Version: "3.2",
|
||||
FoundBy: "some-analyzer",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JenkinsPluginPkg,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
PomProperties: &pkg.PomProperties{
|
||||
GroupID: "com.cloudbees.jenkins.plugins",
|
||||
@@ -369,18 +368,16 @@ 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,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Name: "name",
|
||||
Version: "3.2",
|
||||
FoundBy: "some-analyzer",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JenkinsPluginPkg,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
PomProperties: &pkg.PomProperties{
|
||||
GroupID: "io.jenkins.plugins.name.something",
|
||||
@@ -392,19 +389,16 @@ 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,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Name: "name",
|
||||
Version: "3.2",
|
||||
FoundBy: "some-analyzer",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JenkinsPluginPkg,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
PomProperties: &pkg.PomProperties{
|
||||
GroupID: "io.jenkins.plugins",
|
||||
@@ -413,18 +407,16 @@ 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,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Name: "name",
|
||||
Version: "3.2",
|
||||
FoundBy: "some-analyzer",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JenkinsPluginPkg,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
PomProperties: &pkg.PomProperties{
|
||||
GroupID: "io.jenkins-ci.plugins",
|
||||
@@ -433,20 +425,16 @@ 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,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Name: "name",
|
||||
Version: "3.2",
|
||||
FoundBy: "some-analyzer",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JenkinsPluginPkg,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
PomProperties: &pkg.PomProperties{
|
||||
GroupID: "org.jenkins-ci.plugins",
|
||||
@@ -455,9 +443,6 @@ 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:*:*:*:*:*:*:*",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -21,16 +21,16 @@ var (
|
||||
"be",
|
||||
}
|
||||
|
||||
PrimaryJavaManifestGroupIDFields = []string{
|
||||
"Bundle-SymbolicName",
|
||||
primaryJavaManifestGroupIDFields = []string{
|
||||
"Extension-Name",
|
||||
"Specification-Vendor",
|
||||
"Implementation-Vendor",
|
||||
"Bundle-SymbolicName",
|
||||
"Implementation-Vendor-Id",
|
||||
"Implementation-Title",
|
||||
"Bundle-Activator",
|
||||
}
|
||||
SecondaryJavaManifestGroupIDFields = []string{
|
||||
secondaryJavaManifestGroupIDFields = []string{
|
||||
"Automatic-Module-Name",
|
||||
"Main-Class",
|
||||
"Package",
|
||||
@@ -168,7 +168,7 @@ func artifactIDFromJavaPackage(p pkg.Package) string {
|
||||
}
|
||||
|
||||
artifactID := strings.TrimSpace(metadata.PomProperties.ArtifactID)
|
||||
if looksLikeGroupID(artifactID) && len(strings.Split(artifactID, ".")) > 1 {
|
||||
if startsWithTopLevelDomain(artifactID) && len(strings.Split(artifactID, ".")) > 1 {
|
||||
// there is a strong indication that the artifact ID is really a group ID, don't use it
|
||||
return ""
|
||||
}
|
||||
@@ -181,16 +181,13 @@ func GroupIDsFromJavaPackage(p pkg.Package) (groupIDs []string) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return GroupIDsFromJavaMetadata(p.Name, metadata)
|
||||
return GroupIDsFromJavaMetadata(metadata)
|
||||
}
|
||||
|
||||
// GroupIDsFromJavaMetadata returns the possible group IDs for a Java package
|
||||
// This function is similar to GroupIDFromJavaPackage, but returns all possible group IDs and is less strict
|
||||
// It is used as a way to generate possible candidates for CPE matching.
|
||||
func GroupIDsFromJavaMetadata(pkgName string, metadata pkg.JavaMetadata) (groupIDs []string) {
|
||||
func GroupIDsFromJavaMetadata(metadata pkg.JavaMetadata) (groupIDs []string) {
|
||||
groupIDs = append(groupIDs, groupIDsFromPomProperties(metadata.PomProperties)...)
|
||||
groupIDs = append(groupIDs, groupIDsFromPomProject(metadata.PomProject)...)
|
||||
groupIDs = append(groupIDs, groupIDsFromJavaManifest(pkgName, metadata.Manifest)...)
|
||||
groupIDs = append(groupIDs, groupIDsFromJavaManifest(metadata.Manifest)...)
|
||||
|
||||
return groupIDs
|
||||
}
|
||||
@@ -244,17 +241,13 @@ func addGroupIDsFromGroupIDsAndArtifactID(groupID, artifactID string) (groupIDs
|
||||
return groupIDs
|
||||
}
|
||||
|
||||
func groupIDsFromJavaManifest(pkgName string, manifest *pkg.JavaManifest) []string {
|
||||
if groupID, ok := DefaultArtifactIDToGroupID[pkgName]; ok {
|
||||
return []string{groupID}
|
||||
}
|
||||
|
||||
func groupIDsFromJavaManifest(manifest *pkg.JavaManifest) []string {
|
||||
if manifest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// try the common manifest fields first for a set of candidates
|
||||
groupIDs := GetManifestFieldGroupIDs(manifest, PrimaryJavaManifestGroupIDFields)
|
||||
groupIDs := getManifestFieldGroupIDs(manifest, primaryJavaManifestGroupIDFields)
|
||||
|
||||
if len(groupIDs) != 0 {
|
||||
return groupIDs
|
||||
@@ -265,10 +258,10 @@ func groupIDsFromJavaManifest(pkgName string, manifest *pkg.JavaManifest) []stri
|
||||
// for more info see pkg:maven/commons-io/commons-io@2.8.0 within cloudbees/cloudbees-core-mm:2.263.4.2
|
||||
// at /usr/share/jenkins/jenkins.war:WEB-INF/plugins/analysis-model-api.hpi:WEB-INF/lib/commons-io-2.8.0.jar
|
||||
// as well as the ant package from cloudbees/cloudbees-core-mm:2.277.2.4-ra.
|
||||
return GetManifestFieldGroupIDs(manifest, SecondaryJavaManifestGroupIDFields)
|
||||
return getManifestFieldGroupIDs(manifest, secondaryJavaManifestGroupIDFields)
|
||||
}
|
||||
|
||||
func GetManifestFieldGroupIDs(manifest *pkg.JavaManifest, fields []string) (groupIDs []string) {
|
||||
func getManifestFieldGroupIDs(manifest *pkg.JavaManifest, fields []string) (groupIDs []string) {
|
||||
if manifest == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -305,7 +298,3 @@ func removeOSCIDirectives(groupID string) string {
|
||||
func startsWithTopLevelDomain(value string) bool {
|
||||
return internal.HasAnyOfPrefixes(value, domains...)
|
||||
}
|
||||
|
||||
func looksLikeGroupID(value string) bool {
|
||||
return strings.Contains(value, ".")
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
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,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
@@ -428,38 +427,3 @@ 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,23 +8,22 @@ 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
|
||||
ExcludeBinaryOverlapByOwnership bool
|
||||
Search SearchConfig
|
||||
Golang golang.GoCatalogerOpts
|
||||
LinuxKernel kernel.LinuxCatalogerConfig
|
||||
Python python.CatalogerConfig
|
||||
Catalogers []string
|
||||
Parallelism int
|
||||
}
|
||||
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
Search: DefaultSearchConfig(),
|
||||
Parallelism: 1,
|
||||
LinuxKernel: kernel.DefaultLinuxCatalogerConfig(),
|
||||
Python: python.DefaultCatalogerConfig(),
|
||||
ExcludeBinaryOverlapByOwnership: true,
|
||||
Search: DefaultSearchConfig(),
|
||||
Parallelism: 1,
|
||||
LinuxKernel: kernel.DefaultLinuxCatalogerConfig(),
|
||||
Python: python.DefaultCatalogerConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,67 +8,23 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
type conanRef struct {
|
||||
Name string
|
||||
Version string
|
||||
User string
|
||||
Channel string
|
||||
Revision string
|
||||
Timestamp string
|
||||
}
|
||||
|
||||
func splitConanRef(ref string) *conanRef {
|
||||
// Conan ref format is:
|
||||
// pkg/0.1@user/channel#rrev%timestamp
|
||||
// This method is based on conan's ref.loads method:
|
||||
// https://github.com/conan-io/conan/blob/release/2.0/conans/model/recipe_ref.py#L93C21-L93C21
|
||||
|
||||
var cref conanRef
|
||||
|
||||
// timestamp
|
||||
tokens := strings.Split(ref, "%")
|
||||
text := tokens[0]
|
||||
if len(tokens) == 2 {
|
||||
cref.Timestamp = tokens[1]
|
||||
}
|
||||
|
||||
// revision
|
||||
tokens = strings.Split(text, "#")
|
||||
ref = tokens[0]
|
||||
if len(tokens) == 2 {
|
||||
cref.Revision = tokens[1]
|
||||
}
|
||||
|
||||
// name and version are always given
|
||||
tokens = strings.Split(ref, "@")
|
||||
nameAndVersion := strings.Split(tokens[0], "/")
|
||||
if len(nameAndVersion) < 2 || nameAndVersion[0] == "" || nameAndVersion[1] == "" {
|
||||
func newConanfilePackage(m pkg.ConanMetadata, locations ...file.Location) *pkg.Package {
|
||||
fields := strings.Split(strings.TrimSpace(m.Ref), "/")
|
||||
if len(fields) < 2 {
|
||||
return nil
|
||||
}
|
||||
cref.Name = nameAndVersion[0]
|
||||
cref.Version = nameAndVersion[1]
|
||||
// user and channel
|
||||
if len(tokens) == 2 && tokens[1] != "" {
|
||||
tokens = strings.Split(tokens[1], "/")
|
||||
if len(tokens) == 2 {
|
||||
cref.User = tokens[0]
|
||||
cref.Channel = tokens[1]
|
||||
}
|
||||
}
|
||||
return &cref
|
||||
}
|
||||
|
||||
func newConanfilePackage(m pkg.ConanMetadata, locations ...file.Location) *pkg.Package {
|
||||
ref := splitConanRef(m.Ref)
|
||||
if ref == nil {
|
||||
pkgName, pkgVersion := fields[0], fields[1]
|
||||
|
||||
if pkgName == "" || pkgVersion == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := pkg.Package{
|
||||
Name: ref.Name,
|
||||
Version: ref.Version,
|
||||
Name: pkgName,
|
||||
Version: pkgVersion,
|
||||
Locations: file.NewLocationSet(locations...),
|
||||
PURL: packageURL(ref),
|
||||
PURL: packageURL(pkgName, pkgVersion),
|
||||
Language: pkg.CPP,
|
||||
Type: pkg.ConanPkg,
|
||||
MetadataType: pkg.ConanMetadataType,
|
||||
@@ -81,16 +37,22 @@ func newConanfilePackage(m pkg.ConanMetadata, locations ...file.Location) *pkg.P
|
||||
}
|
||||
|
||||
func newConanlockPackage(m pkg.ConanLockMetadata, locations ...file.Location) *pkg.Package {
|
||||
ref := splitConanRef(m.Ref)
|
||||
if ref == nil {
|
||||
fields := strings.Split(strings.Split(m.Ref, "@")[0], "/")
|
||||
if len(fields) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
pkgName, pkgVersion := fields[0], fields[1]
|
||||
|
||||
if pkgName == "" || pkgVersion == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := pkg.Package{
|
||||
Name: ref.Name,
|
||||
Version: ref.Version,
|
||||
Name: pkgName,
|
||||
Version: pkgVersion,
|
||||
Locations: file.NewLocationSet(locations...),
|
||||
PURL: packageURL(ref),
|
||||
PURL: packageURL(pkgName, pkgVersion),
|
||||
Language: pkg.CPP,
|
||||
Type: pkg.ConanPkg,
|
||||
MetadataType: pkg.ConanLockMetadataType,
|
||||
@@ -102,20 +64,13 @@ func newConanlockPackage(m pkg.ConanLockMetadata, locations ...file.Location) *p
|
||||
return &p
|
||||
}
|
||||
|
||||
func packageURL(ref *conanRef) string {
|
||||
qualifiers := packageurl.Qualifiers{}
|
||||
if ref.Channel != "" {
|
||||
qualifiers = append(qualifiers, packageurl.Qualifier{
|
||||
Key: "channel",
|
||||
Value: ref.Channel,
|
||||
})
|
||||
}
|
||||
func packageURL(name, version string) string {
|
||||
return packageurl.NewPackageURL(
|
||||
packageurl.TypeConan,
|
||||
ref.User,
|
||||
ref.Name,
|
||||
ref.Version,
|
||||
qualifiers,
|
||||
"",
|
||||
name,
|
||||
version,
|
||||
nil, // TODO: no qualifiers (...yet)
|
||||
"",
|
||||
).ToString()
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func parseConanfile(_ file.Resolver, _ *generic.Environment, reader file.Locatio
|
||||
switch {
|
||||
case strings.Contains(line, "[requires]"):
|
||||
inRequirements = true
|
||||
case strings.ContainsAny(line, "[]") || strings.HasPrefix(strings.TrimSpace(line), "#"):
|
||||
case strings.ContainsAny(line, "[]#"):
|
||||
inRequirements = false
|
||||
}
|
||||
|
||||
|
||||
@@ -52,13 +52,13 @@ func TestParseConanfile(t *testing.T) {
|
||||
{
|
||||
Name: "spdlog",
|
||||
Version: "1.9.2",
|
||||
PURL: "pkg:conan/my_user/spdlog@1.9.2?channel=my_channel",
|
||||
PURL: "pkg:conan/spdlog@1.9.2",
|
||||
Locations: fixtureLocationSet,
|
||||
Language: pkg.CPP,
|
||||
Type: pkg.ConanPkg,
|
||||
MetadataType: pkg.ConanMetadataType,
|
||||
Metadata: pkg.ConanMetadata{
|
||||
Ref: "spdlog/1.9.2@my_user/my_channel#1234567%%987654",
|
||||
Ref: "spdlog/1.9.2",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -70,19 +70,19 @@ func TestParseConanfile(t *testing.T) {
|
||||
Type: pkg.ConanPkg,
|
||||
MetadataType: pkg.ConanMetadataType,
|
||||
Metadata: pkg.ConanMetadata{
|
||||
Ref: "sdl/2.0.20#1234567%%987654",
|
||||
Ref: "sdl/2.0.20",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "fltk",
|
||||
Version: "1.3.8",
|
||||
PURL: "pkg:conan/my_user/fltk@1.3.8?channel=my_channel",
|
||||
PURL: "pkg:conan/fltk@1.3.8",
|
||||
Locations: fixtureLocationSet,
|
||||
Language: pkg.CPP,
|
||||
Type: pkg.ConanPkg,
|
||||
MetadataType: pkg.ConanMetadataType,
|
||||
Metadata: pkg.ConanMetadata{
|
||||
Ref: "fltk/1.3.8@my_user/my_channel",
|
||||
Ref: "fltk/1.3.8",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -13,49 +13,20 @@ func TestParseConanlock(t *testing.T) {
|
||||
fixture := "test-fixtures/conan.lock"
|
||||
expected := []pkg.Package{
|
||||
{
|
||||
Name: "spdlog",
|
||||
Version: "1.11.0",
|
||||
PURL: "pkg:conan/spdlog@1.11.0",
|
||||
Name: "zlib",
|
||||
Version: "1.2.12",
|
||||
PURL: "pkg:conan/zlib@1.2.12",
|
||||
Locations: file.NewLocationSet(file.NewLocation(fixture)),
|
||||
Language: pkg.CPP,
|
||||
Type: pkg.ConanPkg,
|
||||
MetadataType: pkg.ConanLockMetadataType,
|
||||
Metadata: pkg.ConanLockMetadata{
|
||||
Ref: "spdlog/1.11.0",
|
||||
Ref: "zlib/1.2.12",
|
||||
Options: map[string]string{
|
||||
"fPIC": "True",
|
||||
"header_only": "False",
|
||||
"no_exceptions": "False",
|
||||
"shared": "False",
|
||||
"wchar_filenames": "False",
|
||||
"wchar_support": "False",
|
||||
"fmt:fPIC": "True",
|
||||
"fmt:header_only": "False",
|
||||
"fmt:shared": "False",
|
||||
"fmt:with_fmt_alias": "False",
|
||||
"fmt:with_os_api": "True",
|
||||
},
|
||||
Path: "conanfile.py",
|
||||
Context: "host",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "fmt",
|
||||
Version: "9.1.0",
|
||||
PURL: "pkg:conan/my_user/fmt@9.1.0?channel=my_channel",
|
||||
Locations: file.NewLocationSet(file.NewLocation(fixture)),
|
||||
Language: pkg.CPP,
|
||||
Type: pkg.ConanPkg,
|
||||
MetadataType: pkg.ConanLockMetadataType,
|
||||
Metadata: pkg.ConanLockMetadata{
|
||||
Ref: "fmt/9.1.0@my_user/my_channel#6708c9d84f98d56a6d9f2e6c2d5639ba",
|
||||
Options: map[string]string{
|
||||
"fPIC": "True",
|
||||
"header_only": "False",
|
||||
"shared": "False",
|
||||
"with_fmt_alias": "False",
|
||||
"with_os_api": "True",
|
||||
"fPIC": "True",
|
||||
"shared": "False",
|
||||
},
|
||||
Path: "all/conanfile.py",
|
||||
Context: "host",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2,24 +2,15 @@
|
||||
"graph_lock": {
|
||||
"nodes": {
|
||||
"0": {
|
||||
"ref": "spdlog/1.11.0",
|
||||
"options": "fPIC=True\nheader_only=False\nno_exceptions=False\nshared=False\nwchar_filenames=False\nwchar_support=False\nfmt:fPIC=True\nfmt:header_only=False\nfmt:shared=False\nfmt:with_fmt_alias=False\nfmt:with_os_api=True",
|
||||
"requires": [
|
||||
"1"
|
||||
],
|
||||
"path": "conanfile.py",
|
||||
"context": "host"
|
||||
},
|
||||
"1": {
|
||||
"ref": "fmt/9.1.0@my_user/my_channel#6708c9d84f98d56a6d9f2e6c2d5639ba",
|
||||
"options": "fPIC=True\nheader_only=False\nshared=False\nwith_fmt_alias=False\nwith_os_api=True",
|
||||
"package_id": "2c09c8f84c016041549fcee94e4caae5d89424b6",
|
||||
"prev": "9f5ab13fc7c73e4a9f87e4e213f2cfa4",
|
||||
"ref": "zlib/1.2.12",
|
||||
"options": "fPIC=True\nshared=False",
|
||||
"requires": [],
|
||||
"path": "all/conanfile.py",
|
||||
"context": "host"
|
||||
}
|
||||
},
|
||||
"revisions_enabled": true
|
||||
"revisions_enabled": false
|
||||
},
|
||||
"version": "0.4",
|
||||
"profile_host": "[settings]\narch=x86_64\narch.march=None\narch_build=x86_64\nbuild_type=Release\ncompiler=gcc\ncompiler.libcxx=libstdc++11\ncompiler.version=11\nos=Linux\nos_build=Linux\n[options]\n[build_requires]\n[env]\n"
|
||||
}
|
||||
"profile_host": "[settings]\narch=x86_64\narch_build=x86_64\nbuild_type=Release\ncompiler=gcc\ncompiler.libcxx=libstdc++\ncompiler.version=9\nos=Linux\nos_build=Linux\n[options]\n[build_requires]\n[env]\n"
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
catch2/2.13.8
|
||||
docopt.cpp/0.6.3
|
||||
fmt/8.1.1
|
||||
spdlog/1.9.2@my_user/my_channel#1234567%%987654
|
||||
sdl/2.0.20#1234567%%987654
|
||||
fltk/1.3.8@my_user/my_channel
|
||||
spdlog/1.9.2
|
||||
sdl/2.0.20
|
||||
fltk/1.3.8
|
||||
|
||||
[generators]
|
||||
cmake_find_package_multi
|
||||
|
||||
@@ -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/*", "**/lib/opkg/info/*.control", "**/lib/opkg/status")
|
||||
WithParserByGlobs(parseDpkgDB, "**/var/lib/dpkg/status", "**/var/lib/dpkg/status.d/*")
|
||||
}
|
||||
|
||||
@@ -168,8 +168,6 @@ 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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
bogus package
|
||||
@@ -1 +0,0 @@
|
||||
bogus status
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func (c *goBinaryCataloger) newGoBinaryPackage(resolver file.Resolver, dep *debug.Module, mainModule, goVersion, architecture string, buildSettings map[string]string, cryptoSettings []string, locations ...file.Location) pkg.Package {
|
||||
func (c *goBinaryCataloger) newGoBinaryPackage(resolver file.Resolver, dep *debug.Module, mainModule, goVersion, architecture string, buildSettings map[string]string, locations ...file.Location) pkg.Package {
|
||||
if dep.Replace != nil {
|
||||
dep = dep.Replace
|
||||
}
|
||||
@@ -36,7 +36,6 @@ func (c *goBinaryCataloger) newGoBinaryPackage(resolver file.Resolver, dep *debu
|
||||
Architecture: architecture,
|
||||
BuildSettings: buildSettings,
|
||||
MainModule: mainModule,
|
||||
GoCryptoSettings: cryptoSettings,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ func (c *goBinaryCataloger) parseGoBinary(resolver file.Resolver, _ *generic.Env
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *extendedBuildInfo, arch string, location file.Location) pkg.Package {
|
||||
func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *debug.BuildInfo, arch string, location file.Location) pkg.Package {
|
||||
gbs := getBuildSettings(mod.Settings)
|
||||
main := c.newGoBinaryPackage(
|
||||
resolver,
|
||||
@@ -75,7 +75,6 @@ func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *exten
|
||||
mod.GoVersion,
|
||||
arch,
|
||||
gbs,
|
||||
mod.cryptoSettings,
|
||||
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
|
||||
@@ -154,7 +153,7 @@ func extractVersionFromLDFlags(ldflags string) (majorVersion string, fullVersion
|
||||
// getArchs finds a binary architecture by two ways:
|
||||
// 1) reading build info from binaries compiled by go1.18+
|
||||
// 2) reading file headers from binaries compiled by < go1.18
|
||||
func getArchs(readers []io.ReaderAt, builds []*extendedBuildInfo) []string {
|
||||
func getArchs(readers []io.ReaderAt, builds []*debug.BuildInfo) []string {
|
||||
if len(readers) != len(builds) {
|
||||
log.Trace("golang cataloger: bin parsing: number of builds and readers doesn't match")
|
||||
return nil
|
||||
@@ -256,7 +255,7 @@ func createMainModuleFromPath(path string) (mod debug.Module) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file.Location, mod *extendedBuildInfo, arch string) []pkg.Package {
|
||||
func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file.Location, mod *debug.BuildInfo, arch string) []pkg.Package {
|
||||
var pkgs []pkg.Package
|
||||
if mod == nil {
|
||||
return pkgs
|
||||
@@ -278,7 +277,6 @@ func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file
|
||||
mod.GoVersion,
|
||||
arch,
|
||||
nil,
|
||||
mod.cryptoSettings,
|
||||
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
if pkg.IsValid(&p) {
|
||||
|
||||
@@ -155,7 +155,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
mod *extendedBuildInfo
|
||||
mod *debug.BuildInfo
|
||||
arch string
|
||||
expected []pkg.Package
|
||||
}{
|
||||
@@ -166,18 +166,16 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "package without name",
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "github.com/adrg/xdg",
|
||||
},
|
||||
{
|
||||
Path: "",
|
||||
Version: "v0.2.1",
|
||||
},
|
||||
mod: &debug.BuildInfo{
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "github.com/adrg/xdg",
|
||||
},
|
||||
}, nil,
|
||||
{
|
||||
Path: "",
|
||||
Version: "v0.2.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
@@ -200,28 +198,26 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "buildGoPkgInfo parses a blank mod and returns no packages",
|
||||
mod: &extendedBuildInfo{&debug.BuildInfo{}, nil},
|
||||
mod: &debug.BuildInfo{},
|
||||
expected: []pkg.Package(nil),
|
||||
},
|
||||
{
|
||||
name: "parse a mod without main module",
|
||||
arch: archDetails,
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "github.com/adrg/xdg",
|
||||
Version: "v0.2.1",
|
||||
Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
|
||||
},
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "github.com/adrg/xdg",
|
||||
Version: "v0.2.1",
|
||||
Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
|
||||
},
|
||||
},
|
||||
}, nil,
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
@@ -250,16 +246,14 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
{
|
||||
name: "parse a mod with path but no main module",
|
||||
arch: archDetails,
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
Path: "github.com/a/b/c",
|
||||
}, []string{"boringcrypto + fips"},
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
Path: "github.com/a/b/c",
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
@@ -286,8 +280,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
"GOARCH": "amd64",
|
||||
"GOOS": "darwin",
|
||||
},
|
||||
MainModule: "github.com/a/b/c",
|
||||
GoCryptoSettings: []string{"boringcrypto + fips"},
|
||||
MainModule: "github.com/a/b/c",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -295,35 +288,31 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
{
|
||||
name: "parse a mod without packages",
|
||||
arch: archDetails,
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
}, nil,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{unmodifiedMain},
|
||||
},
|
||||
{
|
||||
name: "parse main mod and replace devel pseudo version and ldflags exists (but contains no version)",
|
||||
arch: archDetails,
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
|
||||
{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
|
||||
{Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`},
|
||||
},
|
||||
}, nil,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
|
||||
{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
|
||||
{Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
@@ -360,19 +349,17 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
{
|
||||
name: "parse main mod and replace devel version with one from ldflags with vcs. build settings",
|
||||
arch: archDetails,
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
|
||||
{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
|
||||
{Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`},
|
||||
},
|
||||
}, nil,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
|
||||
{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
|
||||
{Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
@@ -409,17 +396,15 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
{
|
||||
name: "parse main mod and replace devel version with one from ldflags without any vcs. build settings",
|
||||
arch: archDetails,
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`},
|
||||
},
|
||||
}, nil,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
@@ -454,17 +439,15 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
{
|
||||
name: "parse main mod and replace devel version with one from ldflags main.version without any vcs. build settings",
|
||||
arch: archDetails,
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`},
|
||||
},
|
||||
}, nil,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
@@ -499,17 +482,15 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
{
|
||||
name: "parse main mod and replace devel version with one from ldflags main.Version without any vcs. build settings",
|
||||
arch: archDetails,
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`},
|
||||
},
|
||||
}, nil,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
@@ -544,18 +525,16 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
{
|
||||
name: "parse main mod and replace devel version with a pseudo version",
|
||||
arch: archDetails,
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
|
||||
{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
|
||||
},
|
||||
}, nil,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
|
||||
{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
@@ -591,28 +570,26 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
{
|
||||
name: "parse a populated mod string and returns packages but no source info",
|
||||
arch: archDetails,
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "github.com/adrg/xdg",
|
||||
Version: "v0.2.1",
|
||||
Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
|
||||
},
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "github.com/adrg/xdg",
|
||||
Version: "v0.2.1",
|
||||
Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
|
||||
},
|
||||
{
|
||||
Path: "github.com/anchore/client-go",
|
||||
Version: "v0.0.0-20210222170800-9c70f9b80bcf",
|
||||
Sum: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=",
|
||||
},
|
||||
{
|
||||
Path: "github.com/anchore/client-go",
|
||||
Version: "v0.0.0-20210222170800-9c70f9b80bcf",
|
||||
Sum: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=",
|
||||
},
|
||||
}, nil,
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
@@ -665,33 +642,31 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
{
|
||||
name: "parse a populated mod string and returns packages when a replace directive exists",
|
||||
arch: archDetails,
|
||||
mod: &extendedBuildInfo{
|
||||
&debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "golang.org/x/sys",
|
||||
Version: "v0.0.0-20211006194710-c8a6f5223071",
|
||||
Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
|
||||
},
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "golang.org/x/sys",
|
||||
Version: "v0.0.0-20211006194710-c8a6f5223071",
|
||||
Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
|
||||
},
|
||||
{
|
||||
{
|
||||
Path: "golang.org/x/term",
|
||||
Version: "v0.0.0-20210927222741-03fcf44c2211",
|
||||
Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
|
||||
Replace: &debug.Module{
|
||||
Path: "golang.org/x/term",
|
||||
Version: "v0.0.0-20210927222741-03fcf44c2211",
|
||||
Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
|
||||
Replace: &debug.Module{
|
||||
Path: "golang.org/x/term",
|
||||
Version: "v0.0.0-20210916214954-140adaaadfaf",
|
||||
Sum: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=",
|
||||
},
|
||||
Version: "v0.0.0-20210916214954-140adaaadfaf",
|
||||
Sum: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=",
|
||||
},
|
||||
},
|
||||
}, nil,
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
|
||||
@@ -6,19 +6,12 @@ import (
|
||||
"io"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/kastenhq/goversion/version"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/unionreader"
|
||||
)
|
||||
|
||||
type extendedBuildInfo struct {
|
||||
*debug.BuildInfo
|
||||
cryptoSettings []string
|
||||
}
|
||||
|
||||
// scanFile scans file to try to report the Go and module versions.
|
||||
func scanFile(reader unionreader.UnionReader, filename string) ([]*extendedBuildInfo, []string) {
|
||||
func scanFile(reader unionreader.UnionReader, filename string) ([]*debug.BuildInfo, []string) {
|
||||
// NOTE: multiple readers are returned to cover universal binaries, which are files
|
||||
// with more than one binary
|
||||
readers, err := unionreader.GetReaders(reader)
|
||||
@@ -27,7 +20,7 @@ func scanFile(reader unionreader.UnionReader, filename string) ([]*extendedBuild
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var builds []*extendedBuildInfo
|
||||
var builds []*debug.BuildInfo
|
||||
for _, r := range readers {
|
||||
bi, err := getBuildInfo(r)
|
||||
if err != nil {
|
||||
@@ -37,14 +30,7 @@ func scanFile(reader unionreader.UnionReader, filename string) ([]*extendedBuild
|
||||
if bi == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
v, err := getCryptoInformation(r)
|
||||
if err != nil {
|
||||
log.WithFields("file", filename, "error", err).Trace("unable to read golang version info")
|
||||
continue
|
||||
}
|
||||
|
||||
builds = append(builds, &extendedBuildInfo{bi, v})
|
||||
builds = append(builds, bi)
|
||||
}
|
||||
|
||||
archs := getArchs(readers, builds)
|
||||
@@ -52,29 +38,6 @@ func scanFile(reader unionreader.UnionReader, filename string) ([]*extendedBuild
|
||||
return builds, archs
|
||||
}
|
||||
|
||||
func getCryptoInformation(reader io.ReaderAt) ([]string, error) {
|
||||
v, err := version.ReadExeFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getCryptoSettingsFromVersion(v), nil
|
||||
}
|
||||
|
||||
func getCryptoSettingsFromVersion(v version.Version) []string {
|
||||
cryptoSettings := []string{}
|
||||
if v.StandardCrypto {
|
||||
cryptoSettings = append(cryptoSettings, "standard-crypto")
|
||||
}
|
||||
if v.BoringCrypto {
|
||||
cryptoSettings = append(cryptoSettings, "boring-crypto")
|
||||
}
|
||||
if v.FIPSOnly {
|
||||
cryptoSettings = append(cryptoSettings, "crypto/tls/fipsonly")
|
||||
}
|
||||
return cryptoSettings
|
||||
}
|
||||
|
||||
func getBuildInfo(r io.ReaderAt) (bi *debug.BuildInfo, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"runtime/debug"
|
||||
"testing"
|
||||
|
||||
"github.com/kastenhq/goversion/version"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -39,72 +38,3 @@ func Test_getBuildInfo(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getCryptoSettingsFromVersion(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
version version.Version
|
||||
result []string
|
||||
}{
|
||||
{
|
||||
name: "standard crypto",
|
||||
version: version.Version{
|
||||
StandardCrypto: true,
|
||||
},
|
||||
result: []string{"standard-crypto"},
|
||||
},
|
||||
{
|
||||
name: "boring crypto",
|
||||
version: version.Version{
|
||||
BoringCrypto: true,
|
||||
},
|
||||
result: []string{"boring-crypto"},
|
||||
},
|
||||
{ // Should never see this. Boring crypto is required for fipsonly
|
||||
name: "fipsonly",
|
||||
version: version.Version{
|
||||
FIPSOnly: true,
|
||||
},
|
||||
result: []string{"crypto/tls/fipsonly"},
|
||||
},
|
||||
{
|
||||
name: "boring crypto and fipsonly",
|
||||
version: version.Version{
|
||||
BoringCrypto: true,
|
||||
FIPSOnly: true,
|
||||
},
|
||||
result: []string{"boring-crypto", "crypto/tls/fipsonly"},
|
||||
},
|
||||
{ // Should never see this.
|
||||
name: "boring and standard crypto!",
|
||||
version: version.Version{
|
||||
BoringCrypto: true,
|
||||
StandardCrypto: true,
|
||||
},
|
||||
result: []string{"boring-crypto", "standard-crypto"},
|
||||
},
|
||||
{ // Should never see this. Boring crypto is required for fipsonly
|
||||
name: "fipsonly and standard crypto!",
|
||||
version: version.Version{
|
||||
FIPSOnly: true,
|
||||
StandardCrypto: true,
|
||||
},
|
||||
result: []string{"crypto/tls/fipsonly", "standard-crypto"},
|
||||
},
|
||||
|
||||
{ // Should never see this. Boring crypto is required for fipsonly
|
||||
name: "fipsonly boringcrypto and standard crypto!",
|
||||
version: version.Version{
|
||||
FIPSOnly: true,
|
||||
StandardCrypto: true,
|
||||
BoringCrypto: true,
|
||||
},
|
||||
result: []string{"crypto/tls/fipsonly", "standard-crypto", "boring-crypto"},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
res := getCryptoSettingsFromVersion(tt.version)
|
||||
assert.ElementsMatch(t, res, tt.result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,11 +62,11 @@ func parseJavaArchive(_ file.Resolver, _ *generic.Environment, reader file.Locat
|
||||
}
|
||||
|
||||
// uniquePkgKey creates a unique string to identify the given package.
|
||||
func uniquePkgKey(groupID string, p *pkg.Package) string {
|
||||
func uniquePkgKey(p *pkg.Package) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s|%s|%s", groupID, p.Name, p.Version)
|
||||
return fmt.Sprintf("%s|%s", p.Name, p.Version)
|
||||
}
|
||||
|
||||
// newJavaArchiveParser returns a new java archive parser object for the given archive. Can be configured to discover
|
||||
@@ -371,27 +371,13 @@ func pomProjectByParentPath(archivePath string, location file.Location, extractP
|
||||
return projectByParentPath, nil
|
||||
}
|
||||
|
||||
// newPackageFromMavenData processes a single Maven POM properties for a given parent package, returning all listed Java packages found and
|
||||
// packagesFromPomProperties processes a single Maven POM properties for a given parent package, returning all listed Java packages found and
|
||||
// associating each discovered package to the given parent package. Note the pom.xml is optional, the pom.properties is not.
|
||||
func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.PomProject, parentPkg *pkg.Package, location file.Location) *pkg.Package {
|
||||
// keep the artifact name within the virtual path if this package does not match the parent package
|
||||
vPathSuffix := ""
|
||||
groupID := ""
|
||||
if parentMetadata, ok := parentPkg.Metadata.(pkg.JavaMetadata); ok {
|
||||
groupID = groupIDFromJavaMetadata(parentPkg.Name, parentMetadata)
|
||||
}
|
||||
|
||||
parentKey := fmt.Sprintf("%s:%s:%s", groupID, parentPkg.Name, parentPkg.Version)
|
||||
// Since we don't have a package yet, it's important to use the same `field: value` association that we used when creating the parent package
|
||||
// See below where Name => pomProperties.ArtifactID and Version => pomProperties.Version. We want to check for potentially nested identical
|
||||
// packages and create equal virtual paths so they are de duped in the future
|
||||
pomProjectKey := fmt.Sprintf("%s:%s:%s", pomProperties.GroupID, pomProperties.ArtifactID, pomProperties.Version)
|
||||
if parentKey != pomProjectKey {
|
||||
// build a new virtual path suffix for the package that is different from the parent package
|
||||
// we want to use the GroupID and ArtifactID here to preserve uniqueness
|
||||
// Some packages have the same name but different group IDs (e.g. "org.glassfish.jaxb/jaxb-core", "com.sun.xml.bind/jaxb-core")
|
||||
// https://github.com/anchore/syft/issues/1944
|
||||
vPathSuffix += ":" + pomProperties.GroupID + ":" + pomProperties.ArtifactID
|
||||
if !strings.HasPrefix(pomProperties.ArtifactID, parentPkg.Name) {
|
||||
vPathSuffix += ":" + pomProperties.ArtifactID
|
||||
}
|
||||
virtualPath := location.AccessPath() + vPathSuffix
|
||||
|
||||
@@ -422,26 +408,21 @@ func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.Po
|
||||
}
|
||||
|
||||
func packageIdentitiesMatch(p pkg.Package, parentPkg *pkg.Package) bool {
|
||||
metadata, ok := p.Metadata.(pkg.JavaMetadata)
|
||||
parentMetadata, parentOk := parentPkg.Metadata.(pkg.JavaMetadata)
|
||||
if !ok || !parentOk {
|
||||
switch {
|
||||
case !ok:
|
||||
log.WithFields("package", p.String()).Trace("unable to extract java metadata to check for matching package identity for package: %s", p.Name)
|
||||
case !parentOk:
|
||||
log.WithFields("package", parentPkg.String()).Trace("unable to extract java metadata to check for matching package identity for package: %s", parentPkg.Name)
|
||||
}
|
||||
// if we can't extract metadata, we can check for matching identities via the package name
|
||||
// this is not ideal, but it's better than nothing - this should not be used if we have Metadata
|
||||
|
||||
return uniquePkgKey("", &p) == uniquePkgKey("", parentPkg)
|
||||
// the name/version pair matches...
|
||||
if uniquePkgKey(&p) == uniquePkgKey(parentPkg) {
|
||||
return true
|
||||
}
|
||||
|
||||
// try to determine identity with the metadata
|
||||
groupID := groupIDFromJavaMetadata(p.Name, metadata)
|
||||
parentGroupID := groupIDFromJavaMetadata(parentPkg.Name, parentMetadata)
|
||||
if uniquePkgKey(groupID, &p) == uniquePkgKey(parentGroupID, parentPkg) {
|
||||
return true
|
||||
metadata, ok := p.Metadata.(pkg.JavaMetadata)
|
||||
if !ok {
|
||||
log.WithFields("package", p.String()).Warn("unable to extract java metadata to check for matching package identity")
|
||||
return false
|
||||
}
|
||||
|
||||
parentMetadata, ok := parentPkg.Metadata.(pkg.JavaMetadata)
|
||||
if !ok {
|
||||
log.WithFields("package", p.String()).Warn("unable to extract java metadata from parent for verifying virtual path")
|
||||
return false
|
||||
}
|
||||
|
||||
// the virtual path matches...
|
||||
@@ -453,14 +434,10 @@ func packageIdentitiesMatch(p pkg.Package, parentPkg *pkg.Package) bool {
|
||||
// note: you CANNOT use name-is-subset-of-artifact-id or vice versa --this is too generic. Shaded jars are a good
|
||||
// example of this: where the package name is "cloudbees-analytics-segment-driver" and a child is "analytics", but
|
||||
// they do not indicate the same package.
|
||||
// NOTE: artifactId might not be a good indicator of uniqueness since archives can contain forks with the same name
|
||||
// from different groups (e.g. "org.glassfish.jaxb.jaxb-core" and "com.sun.xml.bind.jaxb-core")
|
||||
// we will use this check as a last resort
|
||||
if metadata.PomProperties != nil {
|
||||
if metadata.PomProperties.ArtifactID != "" && parentPkg.Name == metadata.PomProperties.ArtifactID {
|
||||
return true
|
||||
}
|
||||
if metadata.PomProperties.ArtifactID != "" && parentPkg.Name == metadata.PomProperties.ArtifactID {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -174,8 +174,8 @@ func TestParseJar(t *testing.T) {
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
// ensure that nested packages with different names than that of the parent are appended as
|
||||
// a suffix on the virtual path with a colon separator between group name and artifact name
|
||||
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar:joda-time:joda-time",
|
||||
// a suffix on the virtual path
|
||||
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar:joda-time",
|
||||
PomProperties: &pkg.PomProperties{
|
||||
Path: "META-INF/maven/joda-time/joda-time/pom.properties",
|
||||
GroupID: "joda-time",
|
||||
@@ -240,7 +240,7 @@ func TestParseJar(t *testing.T) {
|
||||
Metadata: pkg.JavaMetadata{
|
||||
// ensure that nested packages with different names than that of the parent are appended as
|
||||
// a suffix on the virtual path
|
||||
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar:joda-time:joda-time",
|
||||
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar:joda-time",
|
||||
PomProperties: &pkg.PomProperties{
|
||||
Path: "META-INF/maven/joda-time/joda-time/pom.properties",
|
||||
GroupID: "joda-time",
|
||||
@@ -659,7 +659,7 @@ func Test_newPackageFromMavenData(t *testing.T) {
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
VirtualPath: virtualPath + ":" + "some-group-id" + ":" + "some-artifact-id",
|
||||
VirtualPath: virtualPath + ":" + "some-artifact-id",
|
||||
PomProperties: &pkg.PomProperties{
|
||||
Name: "some-name",
|
||||
GroupID: "some-group-id",
|
||||
@@ -728,7 +728,7 @@ func Test_newPackageFromMavenData(t *testing.T) {
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
VirtualPath: virtualPath + ":" + "some-group-id" + ":" + "some-artifact-id",
|
||||
VirtualPath: virtualPath + ":" + "some-artifact-id",
|
||||
PomProperties: &pkg.PomProperties{
|
||||
Name: "some-name",
|
||||
GroupID: "some-group-id",
|
||||
@@ -797,7 +797,7 @@ func Test_newPackageFromMavenData(t *testing.T) {
|
||||
Type: pkg.JenkinsPluginPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
VirtualPath: virtualPath + ":" + "com.cloudbees.jenkins.plugins" + ":" + "some-artifact-id",
|
||||
VirtualPath: virtualPath + ":" + "some-artifact-id",
|
||||
PomProperties: &pkg.PomProperties{
|
||||
Name: "some-name",
|
||||
GroupID: "com.cloudbees.jenkins.plugins",
|
||||
@@ -894,6 +894,44 @@ func Test_newPackageFromMavenData(t *testing.T) {
|
||||
},
|
||||
expectedPackage: nil,
|
||||
},
|
||||
{
|
||||
name: "child matches parent by virtual path -- override name and version",
|
||||
props: pkg.PomProperties{
|
||||
Name: "some-name",
|
||||
GroupID: "some-group-id",
|
||||
ArtifactID: "some-parent-name", // note: DOES NOT match parent package
|
||||
Version: "3.0", // note: DOES NOT match parent package
|
||||
},
|
||||
parent: &pkg.Package{
|
||||
Name: "", // note: empty, so should not be matched on
|
||||
Version: "", // note: empty, so should not be matched on
|
||||
Type: pkg.JavaPkg,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
VirtualPath: virtualPath, // note: matching virtual path
|
||||
Manifest: nil,
|
||||
PomProperties: nil,
|
||||
Parent: nil,
|
||||
},
|
||||
},
|
||||
expectedParent: pkg.Package{
|
||||
Name: "some-parent-name",
|
||||
Version: "3.0",
|
||||
Type: pkg.JavaPkg,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
VirtualPath: virtualPath,
|
||||
Manifest: nil,
|
||||
// note: we attach the discovered pom properties data
|
||||
PomProperties: &pkg.PomProperties{
|
||||
Name: "some-name",
|
||||
GroupID: "some-group-id",
|
||||
ArtifactID: "some-parent-name",
|
||||
Version: "3.0",
|
||||
},
|
||||
Parent: nil,
|
||||
},
|
||||
},
|
||||
expectedPackage: nil,
|
||||
},
|
||||
{
|
||||
name: "child matches parent by artifact id",
|
||||
props: pkg.PomProperties{
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package java
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/common/cpe"
|
||||
@@ -11,9 +9,9 @@ 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
|
||||
|
||||
if gID := groupIDFromJavaMetadata(name, metadata); gID != "" {
|
||||
groupID = gID
|
||||
groupIDs := cpe.GroupIDsFromJavaMetadata(metadata)
|
||||
if len(groupIDs) > 0 {
|
||||
groupID = groupIDs[0]
|
||||
}
|
||||
|
||||
pURL := packageurl.NewPackageURL(
|
||||
@@ -25,121 +23,3 @@ func packageURL(name, version string, metadata pkg.JavaMetadata) string {
|
||||
"")
|
||||
return pURL.ToString()
|
||||
}
|
||||
|
||||
// GroupIDFromJavaPackage returns the authoritative group ID for a Java package.
|
||||
// The order of precedence is:
|
||||
// 1. The group ID from the POM properties
|
||||
// 2. The group ID from the POM project
|
||||
// 3. The group ID from a select map of known group IDs
|
||||
// 4. The group ID from the Java manifest
|
||||
func groupIDFromJavaMetadata(pkgName string, metadata pkg.JavaMetadata) (groupID string) {
|
||||
if groupID = groupIDFromPomProperties(metadata.PomProperties); groupID != "" {
|
||||
return groupID
|
||||
}
|
||||
|
||||
if groupID = groupIDFromPomProject(metadata.PomProject); groupID != "" {
|
||||
return groupID
|
||||
}
|
||||
|
||||
if groupID = groupIDFromKnownPackageList(pkgName); groupID != "" {
|
||||
return groupID
|
||||
}
|
||||
|
||||
if groupID = groupIDFromJavaManifest(metadata.Manifest); groupID != "" {
|
||||
return groupID
|
||||
}
|
||||
|
||||
return groupID
|
||||
}
|
||||
|
||||
func groupIDFromKnownPackageList(pkgName string) (groupID string) {
|
||||
if groupID, ok := cpe.DefaultArtifactIDToGroupID[pkgName]; ok {
|
||||
return groupID
|
||||
}
|
||||
return groupID
|
||||
}
|
||||
|
||||
func groupIDFromJavaManifest(manifest *pkg.JavaManifest) (groupID string) {
|
||||
if manifest == nil {
|
||||
return groupID
|
||||
}
|
||||
|
||||
groupIDS := cpe.GetManifestFieldGroupIDs(manifest, cpe.PrimaryJavaManifestGroupIDFields)
|
||||
// assumes that primaryJavaManifestNameFields are ordered by priority
|
||||
if len(groupIDS) != 0 {
|
||||
return groupIDS[0]
|
||||
}
|
||||
|
||||
groupIDS = cpe.GetManifestFieldGroupIDs(manifest, cpe.SecondaryJavaManifestGroupIDFields)
|
||||
|
||||
if len(groupIDS) != 0 {
|
||||
return groupIDS[0]
|
||||
}
|
||||
|
||||
return groupID
|
||||
}
|
||||
|
||||
func groupIDFromPomProperties(properties *pkg.PomProperties) (groupID string) {
|
||||
if properties == nil {
|
||||
return groupID
|
||||
}
|
||||
|
||||
if properties.GroupID != "" {
|
||||
return cleanGroupID(properties.GroupID)
|
||||
}
|
||||
|
||||
// sometimes the publisher puts the group ID in the artifact ID field unintentionally
|
||||
if looksLikeGroupID(properties.ArtifactID) {
|
||||
// there is a strong indication that the artifact ID is really a group ID
|
||||
return cleanGroupID(properties.ArtifactID)
|
||||
}
|
||||
|
||||
return groupID
|
||||
}
|
||||
|
||||
func groupIDFromPomProject(project *pkg.PomProject) (groupID string) {
|
||||
if project == nil {
|
||||
return groupID
|
||||
}
|
||||
|
||||
// check the project details
|
||||
if project.GroupID != "" {
|
||||
return cleanGroupID(project.GroupID)
|
||||
}
|
||||
|
||||
// sometimes the publisher puts the group ID in the artifact ID field unintentionally
|
||||
if looksLikeGroupID(project.ArtifactID) {
|
||||
// there is a strong indication that the artifact ID is really a group ID
|
||||
return cleanGroupID(project.ArtifactID)
|
||||
}
|
||||
|
||||
// let's check the parent details
|
||||
// if the current project does not have a group ID, but the parent does, we'll use the parent's group ID
|
||||
if project.Parent != nil {
|
||||
if project.Parent.GroupID != "" {
|
||||
return cleanGroupID(project.Parent.GroupID)
|
||||
}
|
||||
|
||||
// sometimes the publisher puts the group ID in the artifact ID field unintentionally
|
||||
if looksLikeGroupID(project.Parent.ArtifactID) {
|
||||
// there is a strong indication that the artifact ID is really a group ID
|
||||
return cleanGroupID(project.Parent.ArtifactID)
|
||||
}
|
||||
}
|
||||
|
||||
return groupID
|
||||
}
|
||||
func looksLikeGroupID(value string) bool {
|
||||
return strings.Contains(value, ".")
|
||||
}
|
||||
|
||||
func cleanGroupID(groupID string) string {
|
||||
return strings.TrimSpace(removeOSCIDirectives(groupID))
|
||||
}
|
||||
|
||||
func removeOSCIDirectives(groupID string) string {
|
||||
// for example:
|
||||
// org.bar;uses:=“org.foo” -> org.bar
|
||||
// more about OSGI directives see https://spring.io/blog/2008/10/20/understanding-the-osgi-uses-directive/
|
||||
return strings.Split(groupID, ";")[0]
|
||||
}
|
||||
|
||||
@@ -10,12 +10,10 @@ import (
|
||||
|
||||
func Test_packageURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pkg pkg.Package
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "maven",
|
||||
pkg: pkg.Package{
|
||||
Name: "example-java-app-maven",
|
||||
Version: "0.1.0",
|
||||
@@ -40,90 +38,6 @@ func Test_packageURL(t *testing.T) {
|
||||
},
|
||||
expect: "pkg:maven/org.anchore/example-java-app-maven@0.1.0",
|
||||
},
|
||||
{
|
||||
name: "POM properties have explicit group ID without . in it",
|
||||
pkg: pkg.Package{
|
||||
Name: "example-java-app-maven",
|
||||
Version: "0.1.0",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
|
||||
Manifest: &pkg.JavaManifest{
|
||||
Main: map[string]string{
|
||||
"Manifest-Version": "1.0",
|
||||
},
|
||||
},
|
||||
PomProperties: &pkg.PomProperties{
|
||||
Path: "META-INF/maven/org.anchore/example-java-app-maven/pom.properties",
|
||||
GroupID: "commons",
|
||||
ArtifactID: "example-java-app-maven",
|
||||
Version: "0.1.0",
|
||||
Extra: make(map[string]string),
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: "pkg:maven/commons/example-java-app-maven@0.1.0",
|
||||
},
|
||||
{
|
||||
name: "POM project has explicit group ID without . in it",
|
||||
pkg: pkg.Package{
|
||||
Name: "example-java-app-maven",
|
||||
Version: "0.1.0",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
|
||||
Manifest: &pkg.JavaManifest{
|
||||
Main: map[string]string{
|
||||
"Manifest-Version": "1.0",
|
||||
},
|
||||
},
|
||||
PomProperties: &pkg.PomProperties{
|
||||
Path: "META-INF/maven/org.anchore/example-java-app-maven/pom.properties",
|
||||
ArtifactID: "example-java-app-maven",
|
||||
Version: "0.1.0",
|
||||
Extra: make(map[string]string),
|
||||
},
|
||||
PomProject: &pkg.PomProject{
|
||||
GroupID: "commons",
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: "pkg:maven/commons/example-java-app-maven@0.1.0",
|
||||
},
|
||||
{
|
||||
name: "POM project has explicit group ID without . in it",
|
||||
pkg: pkg.Package{
|
||||
Name: "example-java-app-maven",
|
||||
Version: "0.1.0",
|
||||
Language: pkg.Java,
|
||||
Type: pkg.JavaPkg,
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
|
||||
Manifest: &pkg.JavaManifest{
|
||||
Main: map[string]string{
|
||||
"Manifest-Version": "1.0",
|
||||
},
|
||||
},
|
||||
PomProperties: &pkg.PomProperties{
|
||||
Path: "META-INF/maven/org.anchore/example-java-app-maven/pom.properties",
|
||||
ArtifactID: "example-java-app-maven",
|
||||
Version: "0.1.0",
|
||||
Extra: make(map[string]string),
|
||||
},
|
||||
PomProject: &pkg.PomProject{
|
||||
Parent: &pkg.PomParent{
|
||||
GroupID: "parent",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: "pkg:maven/parent/example-java-app-maven@0.1.0",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.expect, func(t *testing.T) {
|
||||
@@ -131,59 +45,3 @@ func Test_packageURL(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_groupIDFromJavaMetadata(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pkgName string
|
||||
metadata pkg.JavaMetadata
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "pom properties",
|
||||
metadata: pkg.JavaMetadata{
|
||||
PomProperties: &pkg.PomProperties{
|
||||
GroupID: "org.anchore",
|
||||
},
|
||||
},
|
||||
expect: "org.anchore",
|
||||
},
|
||||
{
|
||||
name: "pom project",
|
||||
metadata: pkg.JavaMetadata{
|
||||
PomProject: &pkg.PomProject{
|
||||
GroupID: "org.anchore",
|
||||
},
|
||||
},
|
||||
expect: "org.anchore",
|
||||
},
|
||||
{
|
||||
name: "known package list",
|
||||
pkgName: "ant-antlr",
|
||||
metadata: pkg.JavaMetadata{},
|
||||
expect: "org.apache.ant",
|
||||
},
|
||||
{
|
||||
name: "java manifest",
|
||||
metadata: pkg.JavaMetadata{
|
||||
Manifest: &pkg.JavaManifest{
|
||||
Main: map[string]string{
|
||||
"Implementation-Vendor": "org.anchore",
|
||||
},
|
||||
},
|
||||
},
|
||||
expect: "org.anchore",
|
||||
},
|
||||
{
|
||||
name: "no group id",
|
||||
metadata: pkg.JavaMetadata{},
|
||||
expect: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.expect, groupIDFromJavaMetadata(tt.pkgName, tt.metadata))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,16 +57,7 @@ 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,25 +16,12 @@ 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",
|
||||
@@ -42,9 +29,6 @@ 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",
|
||||
@@ -52,9 +36,6 @@ 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"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package java
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -9,11 +8,9 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/saintfish/chardet"
|
||||
"github.com/vifraa/gopom"
|
||||
"golang.org/x/net/html/charset"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -31,19 +28,17 @@ func parserPomXML(_ file.Resolver, _ *generic.Environment, reader file.LocationR
|
||||
}
|
||||
|
||||
var pkgs []pkg.Package
|
||||
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)
|
||||
for _, dep := range pom.Dependencies {
|
||||
p := newPackageFromPom(
|
||||
pom,
|
||||
dep,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
if p.Name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, p)
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
@@ -58,33 +53,29 @@ 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)
|
||||
log.WithFields("path", path, "artifactID", artifactID, "name", name, "projectURL", projectURL).Trace("parsing pom.xml")
|
||||
return &pkg.PomProject{
|
||||
Path: path,
|
||||
Parent: pomParent(p, p.Parent),
|
||||
GroupID: resolveProperty(p, p.GroupID, "groupId"),
|
||||
ArtifactID: artifactID,
|
||||
Version: resolveProperty(p, p.Version, "version"),
|
||||
Name: name,
|
||||
GroupID: resolveProperty(p, p.GroupID),
|
||||
ArtifactID: p.ArtifactID,
|
||||
Version: resolveProperty(p, p.Version),
|
||||
Name: p.Name,
|
||||
Description: cleanDescription(p.Description),
|
||||
URL: projectURL,
|
||||
URL: p.URL,
|
||||
}
|
||||
}
|
||||
|
||||
func newPackageFromPom(pom gopom.Project, dep gopom.Dependency, locations ...file.Location) pkg.Package {
|
||||
m := pkg.JavaMetadata{
|
||||
PomProperties: &pkg.PomProperties{
|
||||
GroupID: resolveProperty(pom, dep.GroupID, "groupId"),
|
||||
ArtifactID: resolveProperty(pom, dep.ArtifactID, "artifactId"),
|
||||
Scope: resolveProperty(pom, dep.Scope, "scope"),
|
||||
GroupID: resolveProperty(pom, dep.GroupID),
|
||||
ArtifactID: resolveProperty(pom, dep.ArtifactID),
|
||||
Scope: resolveProperty(pom, dep.Scope),
|
||||
},
|
||||
}
|
||||
|
||||
name := safeString(dep.ArtifactID)
|
||||
version := resolveProperty(pom, dep.Version, "version")
|
||||
name := dep.ArtifactID
|
||||
version := resolveProperty(pom, dep.Version)
|
||||
|
||||
p := pkg.Package{
|
||||
Name: name,
|
||||
@@ -103,15 +94,9 @@ func newPackageFromPom(pom gopom.Project, dep gopom.Dependency, locations ...fil
|
||||
}
|
||||
|
||||
func decodePomXML(content io.Reader) (project gopom.Project, err error) {
|
||||
inputReader, err := getUtf8Reader(content)
|
||||
if err != nil {
|
||||
return project, fmt.Errorf("unable to read pom.xml: %w", err)
|
||||
}
|
||||
|
||||
decoder := xml.NewDecoder(inputReader)
|
||||
// when an xml file has a character set declaration (e.g. '<?xml version="1.0" encoding="ISO-8859-1"?>') read that and use the correct decoder
|
||||
decoder := xml.NewDecoder(content)
|
||||
// prevent against warnings for "xml: encoding "iso-8859-1" declared but Decoder.CharsetReader is nil"
|
||||
decoder.CharsetReader = charset.NewReaderLabel
|
||||
|
||||
if err := decoder.Decode(&project); err != nil {
|
||||
return project, fmt.Errorf("unable to unmarshal pom.xml: %w", err)
|
||||
}
|
||||
@@ -119,56 +104,19 @@ func decodePomXML(content io.Reader) (project gopom.Project, err error) {
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func getUtf8Reader(content io.Reader) (io.Reader, error) {
|
||||
pomContents, err := io.ReadAll(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
detector := chardet.NewTextDetector()
|
||||
detection, err := detector.DetectBest(pomContents)
|
||||
|
||||
var inputReader io.Reader
|
||||
if err == nil && detection != nil {
|
||||
if detection.Charset == "UTF-8" {
|
||||
inputReader = bytes.NewReader(pomContents)
|
||||
} else {
|
||||
inputReader, err = charset.NewReaderLabel(detection.Charset, bytes.NewReader(pomContents))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get encoding: %w", err)
|
||||
}
|
||||
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),
|
||||
}
|
||||
} else {
|
||||
// we could not detect the encoding, but we want a valid file to read. Replace unreadable
|
||||
// characters with the UTF-8 replacement character.
|
||||
inputReader = strings.NewReader(strings.ToValidUTF8(string(pomContents), "�"))
|
||||
}
|
||||
return inputReader, nil
|
||||
}
|
||||
|
||||
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, "groupId"),
|
||||
ArtifactID: artifactID,
|
||||
Version: resolveProperty(pom, parent.Version, "version"),
|
||||
}
|
||||
|
||||
if result.GroupID == "" && result.ArtifactID == "" && result.Version == "" {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func cleanDescription(original *string) (cleaned string) {
|
||||
if original == nil {
|
||||
return ""
|
||||
}
|
||||
descriptionLines := strings.Split(*original, "\n")
|
||||
func cleanDescription(original string) (cleaned string) {
|
||||
descriptionLines := strings.Split(original, "\n")
|
||||
for _, line := range descriptionLines {
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
@@ -182,18 +130,12 @@ func cleanDescription(original *string) (cleaned string) {
|
||||
// resolveProperty emulates some maven property resolution logic by looking in the project's variables
|
||||
// as well as supporting the project expressions like ${project.parent.groupId}.
|
||||
// If no match is found, the entire expression including ${} is returned
|
||||
//
|
||||
//nolint:gocognit
|
||||
func resolveProperty(pom gopom.Project, property *string, propertyName string) string {
|
||||
propertyCase := safeString(property)
|
||||
log.WithFields("existingPropertyValue", propertyCase, "propertyName", propertyName).Trace("resolving property")
|
||||
return propertyMatcher.ReplaceAllStringFunc(propertyCase, func(match string) string {
|
||||
propertyName := strings.TrimSpace(match[2 : len(match)-1]) // remove leading ${ and trailing }
|
||||
entries := pomProperties(pom)
|
||||
if value, ok := entries[propertyName]; ok {
|
||||
func resolveProperty(pom gopom.Project, property string) string {
|
||||
return propertyMatcher.ReplaceAllStringFunc(property, func(match string) string {
|
||||
propertyName := strings.TrimSpace(match[2 : len(match)-1])
|
||||
if value, ok := pom.Properties.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
|
||||
@@ -209,27 +151,9 @@ func resolveProperty(pom gopom.Project, property *string, propertyName string) s
|
||||
part := parts[partNum]
|
||||
for fieldNum := 0; fieldNum < pomValueType.NumField(); fieldNum++ {
|
||||
f := pomValueType.Field(fieldNum)
|
||||
tag := f.Tag.Get("xml")
|
||||
tag = strings.Split(tag, ",")[0]
|
||||
// a segment of the property name matches the xml tag for the field,
|
||||
// so we need to recurse down the nested structs or return a match
|
||||
// if we're done.
|
||||
if part == tag {
|
||||
if part == f.Tag.Get("xml") {
|
||||
pomValue = pomValue.Field(fieldNum)
|
||||
pomValueType = pomValue.Type()
|
||||
if pomValueType.Kind() == reflect.Ptr {
|
||||
// we were recursing down the nested structs, but one of the steps
|
||||
// we need to take is a nil pointer, so give up and return the original match
|
||||
if pomValue.IsNil() {
|
||||
return match
|
||||
}
|
||||
pomValue = pomValue.Elem()
|
||||
if !pomValue.IsZero() {
|
||||
// we found a non-zero value whose tag matches this part of the property name
|
||||
pomValueType = pomValue.Type()
|
||||
}
|
||||
}
|
||||
// If this was the last part of the property name, return the value
|
||||
if partNum == numParts-1 {
|
||||
return fmt.Sprintf("%v", pomValue.Interface())
|
||||
}
|
||||
@@ -241,17 +165,3 @@ func resolveProperty(pom gopom.Project, property *string, propertyName string) s
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package java
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vifraa/gopom"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
@@ -67,60 +63,6 @@ func Test_parserPomXML(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_decodePomXML_surviveNonUtf8Encoding(t *testing.T) {
|
||||
// regression for https://github.com/anchore/syft/issues/2044
|
||||
|
||||
// we are storing the base64 contents of the pom.xml file. We are doing this to prevent accidental changes to the
|
||||
// file, which is extremely important for this test.
|
||||
|
||||
// for instance, even changing a single character in the file and saving in an IntelliJ IDE will automatically
|
||||
// convert the file to UTF-8, which will break this test:
|
||||
|
||||
// xxd with the original pom.xml
|
||||
// 00000780: 6964 3e0d 0a20 2020 2020 2020 2020 2020 id>..
|
||||
// 00000790: 203c 6e61 6d65 3e4a e972 f46d 6520 4d69 <name>J.r.me Mi
|
||||
// 000007a0: 7263 3c2f 6e61 6d65 3e0d 0a20 2020 2020 rc</name>..
|
||||
|
||||
// xxd with the pom.xml converted to UTF-8 (from a simple change with IntelliJ)
|
||||
// 00000780: 6964 3e0d 0a20 2020 2020 2020 2020 2020 id>..
|
||||
// 00000790: 203c 6e61 6d65 3e4a efbf bd72 efbf bd6d <name>J...r...m
|
||||
// 000007a0: 6520 4d69 7263 3c2f 6e61 6d65 3e0d 0a20 e Mirc</name>..
|
||||
|
||||
// Note that the name "Jérôme Mirc" was originally interpreted as "J.r.me Mi" and after the save
|
||||
// is now encoded as "J...r...m" which is not what we want (note the extra bytes for each non UTF-8 character.
|
||||
// The original 0xe9 byte (é) was converted to 0xefbfbd (�) which is the UTF-8 replacement character.
|
||||
// This is quite silly on the part of IntelliJ, but it is what it is.
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
fixture string
|
||||
}{
|
||||
{
|
||||
name: "undeclared encoding",
|
||||
fixture: "test-fixtures/pom/undeclared-iso-8859-encoded-pom.xml.base64",
|
||||
},
|
||||
{
|
||||
name: "declared encoding",
|
||||
fixture: "test-fixtures/pom/declared-iso-8859-encoded-pom.xml.base64",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
fh, err := os.Open(c.fixture)
|
||||
require.NoError(t, err)
|
||||
|
||||
decoder := base64.NewDecoder(base64.StdEncoding, fh)
|
||||
|
||||
proj, err := decodePomXML(decoder)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, proj.Developers)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_parseCommonsTextPomXMLProject(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
@@ -330,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: stringPointer("org.something"),
|
||||
input: gopom.Parent{
|
||||
GroupID: "org.something",
|
||||
},
|
||||
expected: &pkg.PomParent{
|
||||
GroupID: "org.something",
|
||||
@@ -344,8 +286,8 @@ func Test_pomParent(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "only artifact ID",
|
||||
input: &gopom.Parent{
|
||||
ArtifactID: stringPointer("something"),
|
||||
input: gopom.Parent{
|
||||
ArtifactID: "something",
|
||||
},
|
||||
expected: &pkg.PomParent{
|
||||
ArtifactID: "something",
|
||||
@@ -353,27 +295,22 @@ func Test_pomParent(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "only Version",
|
||||
input: &gopom.Parent{
|
||||
Version: stringPointer("something"),
|
||||
input: gopom.Parent{
|
||||
Version: "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: stringPointer("something"),
|
||||
input: gopom.Parent{
|
||||
RelativePath: "something",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
@@ -404,7 +341,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(stringPointer(test.input)))
|
||||
assert.Equal(t, test.expected, cleanDescription(test.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -420,7 +357,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",
|
||||
},
|
||||
@@ -432,7 +369,7 @@ func Test_resolveProperty(t *testing.T) {
|
||||
name: "groupId",
|
||||
property: "${project.groupId}",
|
||||
pom: gopom.Project{
|
||||
GroupID: stringPointer("org.some.group"),
|
||||
GroupID: "org.some.group",
|
||||
},
|
||||
expected: "org.some.group",
|
||||
},
|
||||
@@ -440,65 +377,18 @@ func Test_resolveProperty(t *testing.T) {
|
||||
name: "parent groupId",
|
||||
property: "${project.parent.groupId}",
|
||||
pom: gopom.Project{
|
||||
Parent: &gopom.Parent{
|
||||
GroupID: stringPointer("org.some.parent"),
|
||||
Parent: gopom.Parent{
|
||||
GroupID: "org.some.parent",
|
||||
},
|
||||
},
|
||||
expected: "org.some.parent",
|
||||
},
|
||||
{
|
||||
name: "nil pointer halts search",
|
||||
property: "${project.parent.groupId}",
|
||||
pom: gopom.Project{
|
||||
Parent: nil,
|
||||
},
|
||||
expected: "${project.parent.groupId}",
|
||||
},
|
||||
{
|
||||
name: "nil string pointer halts search",
|
||||
property: "${project.parent.groupId}",
|
||||
pom: gopom.Project{
|
||||
Parent: &gopom.Parent{
|
||||
GroupID: nil,
|
||||
},
|
||||
},
|
||||
expected: "${project.parent.groupId}",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
resolved := resolveProperty(test.pom, stringPointer(test.property), test.name)
|
||||
resolved := resolveProperty(test.pom, test.property)
|
||||
assert.Equal(t, test.expected, resolved)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func stringPointer(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func Test_getUtf8Reader(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
contents string
|
||||
}{
|
||||
{
|
||||
name: "unknown encoding",
|
||||
// random binary contents
|
||||
contents: "BkiJz02JyEWE0nXR6TH///9NicpJweEETIucJIgAAABJicxPjQwhTY1JCE05WQh0BU2J0eunTYshTIusJIAAAAAPHwBNOeV1BUUx2+tWTIlUJDhMiUwkSEyJRCQgSIl8JFBMiQ==",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(tt.contents))
|
||||
|
||||
got, err := getUtf8Reader(decoder)
|
||||
require.NoError(t, err)
|
||||
gotBytes, err := io.ReadAll(got)
|
||||
require.NoError(t, err)
|
||||
// if we couldn't decode the section as UTF-8, we should get a replacement character
|
||||
assert.Contains(t, string(gotBytes), "�")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,3 @@ 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
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,51 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,6 @@ 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
|
||||
@@ -23,14 +18,14 @@ func NewRpmDBCataloger() *generic.Cataloger {
|
||||
log.Warnf("sqlite driver is not available, newer RPM databases might not be cataloged")
|
||||
}
|
||||
|
||||
return generic.NewCataloger(dbCatalogerName).
|
||||
return generic.NewCataloger("rpm-db-cataloger").
|
||||
WithParserByGlobs(parseRpmDB, pkg.RpmDBGlob).
|
||||
WithParserByGlobs(parseRpmManifest, pkg.RpmManifestGlob)
|
||||
}
|
||||
|
||||
// NewFileCataloger returns a new RPM file cataloger object.
|
||||
func NewFileCataloger() *generic.Cataloger {
|
||||
return generic.NewCataloger(fileCatalogerName).
|
||||
return generic.NewCataloger("rpm-file-cataloger").
|
||||
WithParserByGlobs(parseRpm, "**/*.rpm")
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,6 @@ 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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ type GolangBinMetadata struct {
|
||||
Architecture string `json:"architecture" cyclonedx:"architecture"`
|
||||
H1Digest string `json:"h1Digest,omitempty" cyclonedx:"h1Digest"`
|
||||
MainModule string `json:"mainModule,omitempty" cyclonedx:"mainModule"`
|
||||
GoCryptoSettings []string `json:"goCryptoSettings,omitempty" cyclonedx:"goCryptoSettings"`
|
||||
}
|
||||
|
||||
// GolangModMetadata represents all captured data for a Golang source scan with go.mod/go.sum
|
||||
|
||||
@@ -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,usr/lib/sysimage}/rpm/{Packages,Packages.db,rpmdb.sqlite}"
|
||||
const RpmDBGlob = "**/{var/lib,usr/share}/rpm/{Packages,Packages.db,rpmdb.sqlite}"
|
||||
|
||||
// Used in CBL-Mariner distroless images
|
||||
const RpmManifestGlob = "**/var/lib/rpmmanifest/container-manifest-2"
|
||||
|
||||
@@ -109,6 +109,8 @@ 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 {
|
||||
@@ -120,7 +122,6 @@ func (s StereoscopeImageSource) Describe() Description {
|
||||
}
|
||||
}
|
||||
|
||||
nameIfUnset(s.metadata.UserInput)
|
||||
versionIfUnset(s.metadata.ManifestDigest)
|
||||
|
||||
return Description{
|
||||
|
||||
@@ -241,31 +241,3 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(162),
|
||||
assertPackageCount(163),
|
||||
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(163), // packages are now deduplicated for this case
|
||||
assertPackageCount(164), // 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(163), // packages are now deduplicated for this case
|
||||
assertPackageCount(164), // packages are now deduplicated for this case
|
||||
assertInOutput("all-layers"),
|
||||
assertInOutput("vsftpd"), // hidden package
|
||||
assertSuccessfulReturnCode,
|
||||
|
||||
@@ -85,11 +85,7 @@ func TestSpdxValidationTooling(t *testing.T) {
|
||||
|
||||
validateCmd := exec.Command("make", "validate", fileArg, mountArg, imageArg)
|
||||
validateCmd.Dir = filepath.Join(cwd, "test-fixtures", "image-java-spdx-tools")
|
||||
|
||||
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)
|
||||
}
|
||||
runAndShow(t, validateCmd)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM openjdk:11
|
||||
|
||||
RUN wget --no-verbose https://github.com/spdx/tools-java/releases/download/v1.1.3/tools-java-1.1.3.zip && \
|
||||
RUN wget 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
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -15,6 +17,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
)
|
||||
|
||||
@@ -28,6 +32,30 @@ 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,6 +56880,40 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"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",
|
||||
|
||||
@@ -26,10 +26,7 @@ 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
|
||||
@@ -47,8 +44,8 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
|
||||
formatOption: cyclonedxjson.ID,
|
||||
redactor: func(in []byte) []byte {
|
||||
// unstable values
|
||||
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": []`))
|
||||
in = regexp.MustCompile(`"(timestamp|serialNumber|bom-ref)": "[^"]+",`).ReplaceAll(in, []byte{})
|
||||
|
||||
return in
|
||||
},
|
||||
json: true,
|
||||
@@ -57,7 +54,7 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) {
|
||||
formatOption: cyclonedxxml.ID,
|
||||
redactor: func(in []byte) []byte {
|
||||
// unstable values
|
||||
in = regexp.MustCompile(`(serialNumber|bom-ref|ref)="[^"]+"`).ReplaceAll(in, []byte{})
|
||||
in = regexp.MustCompile(`(serialNumber|bom-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: 172, // without deduplication this would be 618
|
||||
packageCount: 174, // without deduplication this would be 618
|
||||
instanceCount: map[string]int{
|
||||
"basesystem": 1,
|
||||
"wget": 1,
|
||||
"curl": 2, // upgraded in the image
|
||||
"vsftpd": 1,
|
||||
"httpd": 1, // rpm, - we exclude binary
|
||||
"httpd": 2, // rpm, 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: 170,
|
||||
packageCount: 172,
|
||||
instanceCount: map[string]int{
|
||||
"basesystem": 1,
|
||||
"wget": 1,
|
||||
"curl": 1, // upgraded, but the most recent
|
||||
"vsftpd": 1,
|
||||
"httpd": 1, // rpm, binary is now excluded by overlap
|
||||
"httpd": 2, // rpm, binary
|
||||
},
|
||||
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 (excluded)
|
||||
"httpd-2.4.6": 1, // binary
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -64,6 +64,7 @@ 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,43 +45,3 @@ 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")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
FROM photon:5.0-20230729
|
||||
Reference in New Issue
Block a user