mirror of
https://github.com/anchore/syft.git
synced 2026-10-28 04:52:23 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa151da5fe | ||
|
|
4bf677d555 | ||
|
|
f5e20521e0 | ||
|
|
8f6a317fef | ||
|
|
d339ffdcb5 | ||
|
|
e84ffc6003 | ||
|
|
bb52a25c8a | ||
|
|
4b7b709473 | ||
|
|
2ef2eed382 | ||
|
|
f6716092af | ||
|
|
0076b19893 | ||
|
|
2642a36161 | ||
|
|
1981b249f1 | ||
|
|
a3ebb31e3c | ||
|
|
9b9a7d6c98 | ||
|
|
57a13ae355 | ||
|
|
890fb3f0e8 | ||
|
|
e236054668 | ||
|
|
3013c8b691 |
@@ -7,12 +7,12 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Please provide a set of steps on how to reproduce the issue**
|
||||
|
||||
**What happened**:
|
||||
|
||||
**What you expected to happen**:
|
||||
|
||||
**Steps to reproduce the issue**:
|
||||
|
||||
**Anything else we need to know?**:
|
||||
|
||||
**Environment**:
|
||||
|
||||
@@ -154,6 +154,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# TODO: remove this when actions/cache + mac runner is fixed
|
||||
# see https://github.com/actions/cache/issues/1110
|
||||
- name: Downgrade zstd on macOS
|
||||
run: |
|
||||
brew uninstall --ignore-dependencies zstd
|
||||
git -C "$(brew --repo homebrew/core)" checkout d3f04bd Formula/zstd.rb
|
||||
brew install zstd
|
||||
|
||||
- name: Download snapshot build
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
|
||||
@@ -10,8 +10,8 @@ CHRONICLE_CMD = $(TEMP_DIR)/chronicle
|
||||
GLOW_CMD = $(TEMP_DIR)/glow
|
||||
|
||||
# Tool versions #################################
|
||||
GOLANGCILINT_VERSION := v1.51.1
|
||||
GOSIMPORTS_VERSION := v0.3.5
|
||||
GOLANGCILINT_VERSION := v1.51.2
|
||||
GOSIMPORTS_VERSION := v0.3.7
|
||||
BOUNCER_VERSION := v0.4.0
|
||||
CHRONICLE_VERSION := v0.6.0
|
||||
GORELEASER_VERSION := v1.15.2
|
||||
|
||||
@@ -14,8 +14,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
convertExample = ` {{.appName}} {{.command}} img.syft.json -o spdx-json convert a syft SBOM to spdx-json, output goes to stdout in table format, by default
|
||||
{{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output goes to a file named img.cdx.json
|
||||
convertExample = ` {{.appName}} {{.command}} img.syft.json -o spdx-json convert a syft SBOM to spdx-json, output goes to stdout
|
||||
{{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output is written to the file "img.cdx.json""
|
||||
{{.appName}} {{.command}} - -o spdx-json convert an SBOM from STDIN to spdx-json
|
||||
`
|
||||
)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package convert
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
||||
@@ -26,15 +27,23 @@ func Run(_ context.Context, app *config.Application, args []string) error {
|
||||
|
||||
// this can only be a SBOM file
|
||||
userInput := args[0]
|
||||
f, err := os.Open(userInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open SBOM file: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
|
||||
sbom, _, err := formats.Decode(f)
|
||||
var reader io.ReadCloser
|
||||
|
||||
if userInput == "-" {
|
||||
reader = os.Stdin
|
||||
} else {
|
||||
f, err := os.Open(userInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open SBOM file: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
reader = f
|
||||
}
|
||||
|
||||
sbom, _, err := formats.Decode(reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode SBOM: %w", err)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ require (
|
||||
github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
golang.org/x/mod v0.8.0
|
||||
golang.org/x/net v0.6.0
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/term v0.5.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
@@ -52,8 +52,8 @@ require (
|
||||
github.com/CycloneDX/cyclonedx-go v0.7.1-0.20221222100750-41a1ac565cce
|
||||
github.com/Masterminds/sprig/v3 v3.2.3
|
||||
github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8
|
||||
github.com/anchore/stereoscope v0.0.0-20230208154630-5a306f07f2e7
|
||||
github.com/docker/docker v23.0.0+incompatible
|
||||
github.com/anchore/stereoscope v0.0.0-20230222185948-fab1c9638abc
|
||||
github.com/docker/docker v23.0.1+incompatible
|
||||
github.com/google/go-containerregistry v0.13.0
|
||||
github.com/invopop/jsonschema v0.7.0
|
||||
github.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce
|
||||
@@ -71,7 +71,7 @@ require (
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
|
||||
github.com/becheran/wildmatch-go v1.0.0 // indirect
|
||||
github.com/containerd/containerd v1.6.12 // indirect
|
||||
github.com/containerd/containerd v1.6.18 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.12.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/cli v20.10.20+incompatible // indirect
|
||||
|
||||
@@ -90,8 +90,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-20230208154630-5a306f07f2e7 h1:PrdFBPMyika+AM1/AwDmYqrVeUATDU90wbrd81ugicU=
|
||||
github.com/anchore/stereoscope v0.0.0-20230208154630-5a306f07f2e7/go.mod h1:TUCfo52tEz7ahTUFtKN//wcB7kJzQs0Oifmnd4NkIXw=
|
||||
github.com/anchore/stereoscope v0.0.0-20230222185948-fab1c9638abc h1:b+2KauWByrCPLNnzRHjLoUxo85tpszFtU7S1I5pAKK0=
|
||||
github.com/anchore/stereoscope v0.0.0-20230222185948-fab1c9638abc/go.mod h1:Y+jiUa5PmQh9jUvzmvus4EvLnEG87cDTOYgV3nw3wDg=
|
||||
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=
|
||||
@@ -132,8 +132,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/containerd/containerd v1.6.12 h1:kJ9b3mOFKf8yqo05Ob+tMoxvt1pbVWhnB0re9Y+k+8c=
|
||||
github.com/containerd/containerd v1.6.12/go.mod h1:K4Bw7gjgh4TnkmQY+py/PYQGp4e7xgnHAeg87VeWb3A=
|
||||
github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns=
|
||||
github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.12.1/go.mod h1:12VUuCq3qPq4y8yUW+l5w3+oXV3cx2Po3KSe/SmPGqw=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@@ -150,8 +150,8 @@ github.com/docker/cli v20.10.20+incompatible h1:lWQbHSHUFs7KraSN2jOJK7zbMS2jNCHI
|
||||
github.com/docker/cli v20.10.20+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
|
||||
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v23.0.0+incompatible h1:L6c28tNyqZ4/ub9AZC9d5QUuunoHHfEH4/Ue+h/E5nE=
|
||||
github.com/docker/docker v23.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY=
|
||||
github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
|
||||
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
@@ -716,8 +716,8 @@ golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
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=
|
||||
|
||||
@@ -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 = "6.2.0"
|
||||
JSONSchemaVersion = "7.0.0"
|
||||
)
|
||||
|
||||
@@ -60,6 +60,7 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"0bsd": "0BSD",
|
||||
"aal": "AAL",
|
||||
"abstyles": "Abstyles",
|
||||
"adacore-doc": "AdaCore-doc",
|
||||
"adobe-2006": "Adobe-2006",
|
||||
"adobe-2006.0": "Adobe-2006",
|
||||
"adobe-2006.0.0": "Adobe-2006",
|
||||
@@ -145,6 +146,7 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"bahyph": "Bahyph",
|
||||
"barr": "Barr",
|
||||
"beerware": "Beerware",
|
||||
"bitstream-charter": "Bitstream-Charter",
|
||||
"bitstream-vera": "Bitstream-Vera",
|
||||
"bittorrent-1": "BitTorrent-1.0",
|
||||
"bittorrent-1.0": "BitTorrent-1.0",
|
||||
@@ -156,6 +158,9 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"blueoak-1.0": "BlueOak-1.0.0",
|
||||
"blueoak-1.0.0": "BlueOak-1.0.0",
|
||||
"borceux": "Borceux",
|
||||
"brian-gladman-3-clause": "Brian-Gladman-3-Clause",
|
||||
"brian-gladman-3.0-clause": "Brian-Gladman-3-Clause",
|
||||
"brian-gladman-3.0.0-clause": "Brian-Gladman-3-Clause",
|
||||
"bsd-1-clause": "BSD-1-Clause",
|
||||
"bsd-1.0-clause": "BSD-1-Clause",
|
||||
"bsd-1.0.0-clause": "BSD-1-Clause",
|
||||
@@ -213,6 +218,14 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"bsd-4.0.0-clause": "BSD-4-Clause",
|
||||
"bsd-4.0.0-clause-shortened": "BSD-4-Clause-Shortened",
|
||||
"bsd-4.0.0-clause-uc": "BSD-4-Clause-UC",
|
||||
"bsd-4.3.0reno": "BSD-4.3RENO",
|
||||
"bsd-4.3.0tahoe": "BSD-4.3TAHOE",
|
||||
"bsd-4.3reno": "BSD-4.3RENO",
|
||||
"bsd-4.3tahoe": "BSD-4.3TAHOE",
|
||||
"bsd-4reno": "BSD-4.3RENO",
|
||||
"bsd-4tahoe": "BSD-4.3TAHOE",
|
||||
"bsd-advertising-acknowledgement": "BSD-Advertising-Acknowledgement",
|
||||
"bsd-attribution-hpnd-disclaimer": "BSD-Attribution-HPND-disclaimer",
|
||||
"bsd-protection": "BSD-Protection",
|
||||
"bsd-source-code": "BSD-Source-Code",
|
||||
"bsl-1": "BSL-1.0",
|
||||
@@ -311,12 +324,15 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"cc-by-nc-sa-1.0": "CC-BY-NC-SA-1.0",
|
||||
"cc-by-nc-sa-1.0.0": "CC-BY-NC-SA-1.0",
|
||||
"cc-by-nc-sa-2": "CC-BY-NC-SA-2.0",
|
||||
"cc-by-nc-sa-2-de": "CC-BY-NC-SA-2.0-DE",
|
||||
"cc-by-nc-sa-2-fr": "CC-BY-NC-SA-2.0-FR",
|
||||
"cc-by-nc-sa-2-uk": "CC-BY-NC-SA-2.0-UK",
|
||||
"cc-by-nc-sa-2.0": "CC-BY-NC-SA-2.0",
|
||||
"cc-by-nc-sa-2.0-de": "CC-BY-NC-SA-2.0-DE",
|
||||
"cc-by-nc-sa-2.0-fr": "CC-BY-NC-SA-2.0-FR",
|
||||
"cc-by-nc-sa-2.0-uk": "CC-BY-NC-SA-2.0-UK",
|
||||
"cc-by-nc-sa-2.0.0": "CC-BY-NC-SA-2.0",
|
||||
"cc-by-nc-sa-2.0.0-de": "CC-BY-NC-SA-2.0-DE",
|
||||
"cc-by-nc-sa-2.0.0-fr": "CC-BY-NC-SA-2.0-FR",
|
||||
"cc-by-nc-sa-2.0.0-uk": "CC-BY-NC-SA-2.0-UK",
|
||||
"cc-by-nc-sa-2.5": "CC-BY-NC-SA-2.5",
|
||||
@@ -423,8 +439,11 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"cern-ohl-w-2": "CERN-OHL-W-2.0",
|
||||
"cern-ohl-w-2.0": "CERN-OHL-W-2.0",
|
||||
"cern-ohl-w-2.0.0": "CERN-OHL-W-2.0",
|
||||
"cfitsio": "CFITSIO",
|
||||
"checkmk": "checkmk",
|
||||
"clartistic": "ClArtistic",
|
||||
"clips": "Clips",
|
||||
"cmu-mach": "CMU-Mach",
|
||||
"cnri-jython": "CNRI-Jython",
|
||||
"cnri-python": "CNRI-Python",
|
||||
"cnri-python-gpl-compatible": "CNRI-Python-GPL-Compatible",
|
||||
@@ -440,6 +459,7 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"copyleft-next-0.3": "copyleft-next-0.3.0",
|
||||
"copyleft-next-0.3.0": "copyleft-next-0.3.0",
|
||||
"copyleft-next-0.3.1": "copyleft-next-0.3.1",
|
||||
"cornell-lossless-jpeg": "Cornell-Lossless-JPEG",
|
||||
"cpal-1": "CPAL-1.0",
|
||||
"cpal-1.0": "CPAL-1.0",
|
||||
"cpal-1.0.0": "CPAL-1.0",
|
||||
@@ -637,6 +657,7 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"gpl-3.0.0-or-later": "GPL-3.0-or-later",
|
||||
"gpl-3.0.0-with-autoconf-exception": "GPL-3.0-with-autoconf-exception",
|
||||
"gpl-3.0.0-with-gcc-exception": "GPL-3.0-with-GCC-exception",
|
||||
"graphics-gems": "Graphics-Gems",
|
||||
"gsoap-1.3.0b": "gSOAP-1.3b",
|
||||
"gsoap-1.3b": "gSOAP-1.3b",
|
||||
"gsoap-1b": "gSOAP-1.3b",
|
||||
@@ -644,12 +665,20 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"hippocratic-2": "Hippocratic-2.1",
|
||||
"hippocratic-2.1": "Hippocratic-2.1",
|
||||
"hippocratic-2.1.0": "Hippocratic-2.1",
|
||||
"hp-1986": "HP-1986",
|
||||
"hp-1986.0": "HP-1986",
|
||||
"hp-1986.0.0": "HP-1986",
|
||||
"hpnd": "HPND",
|
||||
"hpnd-export-us": "HPND-export-US",
|
||||
"hpnd-markus-kuhn": "HPND-Markus-Kuhn",
|
||||
"hpnd-sell-variant": "HPND-sell-variant",
|
||||
"hpnd-sell-variant-mit-disclaimer": "HPND-sell-variant-MIT-disclaimer",
|
||||
"htmltidy": "HTMLTIDY",
|
||||
"ibm-pibs": "IBM-pibs",
|
||||
"icu": "ICU",
|
||||
"iec-code-components-eula": "IEC-Code-Components-EULA",
|
||||
"ijg": "IJG",
|
||||
"ijg-short": "IJG-short",
|
||||
"imagemagick": "ImageMagick",
|
||||
"imatix": "iMatix",
|
||||
"imlib2": "Imlib2",
|
||||
@@ -668,8 +697,10 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"jasper-2": "JasPer-2.0",
|
||||
"jasper-2.0": "JasPer-2.0",
|
||||
"jasper-2.0.0": "JasPer-2.0",
|
||||
"jpl-image": "JPL-image",
|
||||
"jpnic": "JPNIC",
|
||||
"json": "JSON",
|
||||
"kazlib": "Kazlib",
|
||||
"knuth-ctan": "Knuth-CTAN",
|
||||
"lal-1": "LAL-1.2",
|
||||
"lal-1.2": "LAL-1.2",
|
||||
@@ -731,6 +762,7 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"liliq-rplus-1.1.0": "LiLiQ-Rplus-1.1",
|
||||
"linux-man-pages-copyleft": "Linux-man-pages-copyleft",
|
||||
"linux-openib": "Linux-OpenIB",
|
||||
"loop": "LOOP",
|
||||
"lpl-1": "LPL-1.0",
|
||||
"lpl-1.0": "LPL-1.0",
|
||||
"lpl-1.0.0": "LPL-1.0",
|
||||
@@ -756,6 +788,7 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"lzma-sdk-9.22": "LZMA-SDK-9.22",
|
||||
"lzma-sdk-9.22.0": "LZMA-SDK-9.22",
|
||||
"makeindex": "MakeIndex",
|
||||
"martin-birgmeier": "Martin-Birgmeier",
|
||||
"minpack": "Minpack",
|
||||
"miros": "MirOS",
|
||||
"mit": "MIT",
|
||||
@@ -766,6 +799,7 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"mit-feh": "MIT-feh",
|
||||
"mit-modern-variant": "MIT-Modern-Variant",
|
||||
"mit-open-group": "MIT-open-group",
|
||||
"mit-wu": "MIT-Wu",
|
||||
"mitnfa": "MITNFA",
|
||||
"motosoto": "Motosoto",
|
||||
"mpi-permissive": "mpi-permissive",
|
||||
@@ -852,6 +886,7 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"odc-by-1": "ODC-By-1.0",
|
||||
"odc-by-1.0": "ODC-By-1.0",
|
||||
"odc-by-1.0.0": "ODC-By-1.0",
|
||||
"offis": "OFFIS",
|
||||
"ofl-1": "OFL-1.0",
|
||||
"ofl-1-no-rfn": "OFL-1.0-no-RFN",
|
||||
"ofl-1-rfn": "OFL-1.0-RFN",
|
||||
@@ -918,6 +953,9 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"oldap-2.8": "OLDAP-2.8",
|
||||
"oldap-2.8.0": "OLDAP-2.8",
|
||||
"oml": "OML",
|
||||
"openpbs-2": "OpenPBS-2.3",
|
||||
"openpbs-2.3": "OpenPBS-2.3",
|
||||
"openpbs-2.3.0": "OpenPBS-2.3",
|
||||
"openssl": "OpenSSL",
|
||||
"opl-1": "OPL-1.0",
|
||||
"opl-1.0": "OPL-1.0",
|
||||
@@ -974,8 +1012,11 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"python-2.0.1": "Python-2.0.1",
|
||||
"qhull": "Qhull",
|
||||
"qpl-1": "QPL-1.0",
|
||||
"qpl-1-inria-2004": "QPL-1.0-INRIA-2004",
|
||||
"qpl-1.0": "QPL-1.0",
|
||||
"qpl-1.0-inria-2004": "QPL-1.0-INRIA-2004",
|
||||
"qpl-1.0.0": "QPL-1.0",
|
||||
"qpl-1.0.0-inria-2004": "QPL-1.0-INRIA-2004",
|
||||
"rdisc": "Rdisc",
|
||||
"rhecos-1": "RHeCos-1.1",
|
||||
"rhecos-1.1": "RHeCos-1.1",
|
||||
@@ -1022,6 +1063,7 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"smlnj": "SMLNJ",
|
||||
"smppl": "SMPPL",
|
||||
"snia": "SNIA",
|
||||
"snprintf": "snprintf",
|
||||
"spencer-86": "Spencer-86",
|
||||
"spencer-86.0": "Spencer-86",
|
||||
"spencer-86.0.0": "Spencer-86",
|
||||
@@ -1043,7 +1085,9 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"sugarcrm-1": "SugarCRM-1.1.3",
|
||||
"sugarcrm-1.1": "SugarCRM-1.1.3",
|
||||
"sugarcrm-1.1.3": "SugarCRM-1.1.3",
|
||||
"sunpro": "SunPro",
|
||||
"swl": "SWL",
|
||||
"symlinks": "Symlinks",
|
||||
"tapr-ohl-1": "TAPR-OHL-1.0",
|
||||
"tapr-ohl-1.0": "TAPR-OHL-1.0",
|
||||
"tapr-ohl-1.0.0": "TAPR-OHL-1.0",
|
||||
@@ -1054,12 +1098,18 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"torque-1.1": "TORQUE-1.1",
|
||||
"torque-1.1.0": "TORQUE-1.1",
|
||||
"tosl": "TOSL",
|
||||
"tpdl": "TPDL",
|
||||
"tpl-1": "TPL-1.0",
|
||||
"tpl-1.0": "TPL-1.0",
|
||||
"tpl-1.0.0": "TPL-1.0",
|
||||
"ttwl": "TTWL",
|
||||
"tu-berlin-1": "TU-Berlin-1.0",
|
||||
"tu-berlin-1.0": "TU-Berlin-1.0",
|
||||
"tu-berlin-1.0.0": "TU-Berlin-1.0",
|
||||
"tu-berlin-2": "TU-Berlin-2.0",
|
||||
"tu-berlin-2.0": "TU-Berlin-2.0",
|
||||
"tu-berlin-2.0.0": "TU-Berlin-2.0",
|
||||
"ucar": "UCAR",
|
||||
"ucl-1": "UCL-1.0",
|
||||
"ucl-1.0": "UCL-1.0",
|
||||
"ucl-1.0.0": "UCL-1.0",
|
||||
@@ -1086,6 +1136,7 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"w3c-20150513": "W3C-20150513",
|
||||
"w3c-20150513.0": "W3C-20150513",
|
||||
"w3c-20150513.0.0": "W3C-20150513",
|
||||
"w3m": "w3m",
|
||||
"watcom-1": "Watcom-1.0",
|
||||
"watcom-1.0": "Watcom-1.0",
|
||||
"watcom-1.0.0": "Watcom-1.0",
|
||||
@@ -1099,6 +1150,7 @@ func Test_processSPDXLicense(t *testing.T) {
|
||||
"xfree86-1.1": "XFree86-1.1",
|
||||
"xfree86-1.1.0": "XFree86-1.1",
|
||||
"xinetd": "xinetd",
|
||||
"xlock": "xlock",
|
||||
"xnet": "Xnet",
|
||||
"xpp": "xpp",
|
||||
"xskat": "XSkat",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,15 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at 2022-12-02 12:51:33.814385 -0500 EST m=+1.688731869
|
||||
// This file was generated by robots at 2023-02-21 18:58:18.115328967 +0100 CET m=+0.216357753
|
||||
// using data from https://spdx.org/licenses/licenses.json
|
||||
package spdxlicense
|
||||
|
||||
const Version = "3.19"
|
||||
const Version = "3.20"
|
||||
|
||||
var licenseIDs = map[string]string{
|
||||
"0bsd": "0BSD",
|
||||
"aal": "AAL",
|
||||
"abstyles": "Abstyles",
|
||||
"adacore-doc": "AdaCore-doc",
|
||||
"adobe-2006": "Adobe-2006",
|
||||
"adobe-2006.0": "Adobe-2006",
|
||||
"adobe-2006.0.0": "Adobe-2006",
|
||||
@@ -94,6 +95,7 @@ var licenseIDs = map[string]string{
|
||||
"bahyph": "Bahyph",
|
||||
"barr": "Barr",
|
||||
"beerware": "Beerware",
|
||||
"bitstream-charter": "Bitstream-Charter",
|
||||
"bitstream-vera": "Bitstream-Vera",
|
||||
"bittorrent-1": "BitTorrent-1.0",
|
||||
"bittorrent-1.0": "BitTorrent-1.0",
|
||||
@@ -105,6 +107,9 @@ var licenseIDs = map[string]string{
|
||||
"blueoak-1.0": "BlueOak-1.0.0",
|
||||
"blueoak-1.0.0": "BlueOak-1.0.0",
|
||||
"borceux": "Borceux",
|
||||
"brian-gladman-3-clause": "Brian-Gladman-3-Clause",
|
||||
"brian-gladman-3.0-clause": "Brian-Gladman-3-Clause",
|
||||
"brian-gladman-3.0.0-clause": "Brian-Gladman-3-Clause",
|
||||
"bsd-1-clause": "BSD-1-Clause",
|
||||
"bsd-1.0-clause": "BSD-1-Clause",
|
||||
"bsd-1.0.0-clause": "BSD-1-Clause",
|
||||
@@ -162,6 +167,14 @@ var licenseIDs = map[string]string{
|
||||
"bsd-4.0.0-clause": "BSD-4-Clause",
|
||||
"bsd-4.0.0-clause-shortened": "BSD-4-Clause-Shortened",
|
||||
"bsd-4.0.0-clause-uc": "BSD-4-Clause-UC",
|
||||
"bsd-4.3.0reno": "BSD-4.3RENO",
|
||||
"bsd-4.3.0tahoe": "BSD-4.3TAHOE",
|
||||
"bsd-4.3reno": "BSD-4.3RENO",
|
||||
"bsd-4.3tahoe": "BSD-4.3TAHOE",
|
||||
"bsd-4reno": "BSD-4.3RENO",
|
||||
"bsd-4tahoe": "BSD-4.3TAHOE",
|
||||
"bsd-advertising-acknowledgement": "BSD-Advertising-Acknowledgement",
|
||||
"bsd-attribution-hpnd-disclaimer": "BSD-Attribution-HPND-disclaimer",
|
||||
"bsd-protection": "BSD-Protection",
|
||||
"bsd-source-code": "BSD-Source-Code",
|
||||
"bsl-1": "BSL-1.0",
|
||||
@@ -260,12 +273,15 @@ var licenseIDs = map[string]string{
|
||||
"cc-by-nc-sa-1.0": "CC-BY-NC-SA-1.0",
|
||||
"cc-by-nc-sa-1.0.0": "CC-BY-NC-SA-1.0",
|
||||
"cc-by-nc-sa-2": "CC-BY-NC-SA-2.0",
|
||||
"cc-by-nc-sa-2-de": "CC-BY-NC-SA-2.0-DE",
|
||||
"cc-by-nc-sa-2-fr": "CC-BY-NC-SA-2.0-FR",
|
||||
"cc-by-nc-sa-2-uk": "CC-BY-NC-SA-2.0-UK",
|
||||
"cc-by-nc-sa-2.0": "CC-BY-NC-SA-2.0",
|
||||
"cc-by-nc-sa-2.0-de": "CC-BY-NC-SA-2.0-DE",
|
||||
"cc-by-nc-sa-2.0-fr": "CC-BY-NC-SA-2.0-FR",
|
||||
"cc-by-nc-sa-2.0-uk": "CC-BY-NC-SA-2.0-UK",
|
||||
"cc-by-nc-sa-2.0.0": "CC-BY-NC-SA-2.0",
|
||||
"cc-by-nc-sa-2.0.0-de": "CC-BY-NC-SA-2.0-DE",
|
||||
"cc-by-nc-sa-2.0.0-fr": "CC-BY-NC-SA-2.0-FR",
|
||||
"cc-by-nc-sa-2.0.0-uk": "CC-BY-NC-SA-2.0-UK",
|
||||
"cc-by-nc-sa-2.5": "CC-BY-NC-SA-2.5",
|
||||
@@ -372,8 +388,11 @@ var licenseIDs = map[string]string{
|
||||
"cern-ohl-w-2": "CERN-OHL-W-2.0",
|
||||
"cern-ohl-w-2.0": "CERN-OHL-W-2.0",
|
||||
"cern-ohl-w-2.0.0": "CERN-OHL-W-2.0",
|
||||
"cfitsio": "CFITSIO",
|
||||
"checkmk": "checkmk",
|
||||
"clartistic": "ClArtistic",
|
||||
"clips": "Clips",
|
||||
"cmu-mach": "CMU-Mach",
|
||||
"cnri-jython": "CNRI-Jython",
|
||||
"cnri-python": "CNRI-Python",
|
||||
"cnri-python-gpl-compatible": "CNRI-Python-GPL-Compatible",
|
||||
@@ -389,6 +408,7 @@ var licenseIDs = map[string]string{
|
||||
"copyleft-next-0.3": "copyleft-next-0.3.0",
|
||||
"copyleft-next-0.3.0": "copyleft-next-0.3.0",
|
||||
"copyleft-next-0.3.1": "copyleft-next-0.3.1",
|
||||
"cornell-lossless-jpeg": "Cornell-Lossless-JPEG",
|
||||
"cpal-1": "CPAL-1.0",
|
||||
"cpal-1.0": "CPAL-1.0",
|
||||
"cpal-1.0.0": "CPAL-1.0",
|
||||
@@ -586,6 +606,7 @@ var licenseIDs = map[string]string{
|
||||
"gpl-3.0.0-or-later": "GPL-3.0-or-later",
|
||||
"gpl-3.0.0-with-autoconf-exception": "GPL-3.0-with-autoconf-exception",
|
||||
"gpl-3.0.0-with-gcc-exception": "GPL-3.0-with-GCC-exception",
|
||||
"graphics-gems": "Graphics-Gems",
|
||||
"gsoap-1.3.0b": "gSOAP-1.3b",
|
||||
"gsoap-1.3b": "gSOAP-1.3b",
|
||||
"gsoap-1b": "gSOAP-1.3b",
|
||||
@@ -593,12 +614,20 @@ var licenseIDs = map[string]string{
|
||||
"hippocratic-2": "Hippocratic-2.1",
|
||||
"hippocratic-2.1": "Hippocratic-2.1",
|
||||
"hippocratic-2.1.0": "Hippocratic-2.1",
|
||||
"hp-1986": "HP-1986",
|
||||
"hp-1986.0": "HP-1986",
|
||||
"hp-1986.0.0": "HP-1986",
|
||||
"hpnd": "HPND",
|
||||
"hpnd-export-us": "HPND-export-US",
|
||||
"hpnd-markus-kuhn": "HPND-Markus-Kuhn",
|
||||
"hpnd-sell-variant": "HPND-sell-variant",
|
||||
"hpnd-sell-variant-mit-disclaimer": "HPND-sell-variant-MIT-disclaimer",
|
||||
"htmltidy": "HTMLTIDY",
|
||||
"ibm-pibs": "IBM-pibs",
|
||||
"icu": "ICU",
|
||||
"iec-code-components-eula": "IEC-Code-Components-EULA",
|
||||
"ijg": "IJG",
|
||||
"ijg-short": "IJG-short",
|
||||
"imagemagick": "ImageMagick",
|
||||
"imatix": "iMatix",
|
||||
"imlib2": "Imlib2",
|
||||
@@ -617,8 +646,10 @@ var licenseIDs = map[string]string{
|
||||
"jasper-2": "JasPer-2.0",
|
||||
"jasper-2.0": "JasPer-2.0",
|
||||
"jasper-2.0.0": "JasPer-2.0",
|
||||
"jpl-image": "JPL-image",
|
||||
"jpnic": "JPNIC",
|
||||
"json": "JSON",
|
||||
"kazlib": "Kazlib",
|
||||
"knuth-ctan": "Knuth-CTAN",
|
||||
"lal-1": "LAL-1.2",
|
||||
"lal-1.2": "LAL-1.2",
|
||||
@@ -680,6 +711,7 @@ var licenseIDs = map[string]string{
|
||||
"liliq-rplus-1.1.0": "LiLiQ-Rplus-1.1",
|
||||
"linux-man-pages-copyleft": "Linux-man-pages-copyleft",
|
||||
"linux-openib": "Linux-OpenIB",
|
||||
"loop": "LOOP",
|
||||
"lpl-1": "LPL-1.0",
|
||||
"lpl-1.0": "LPL-1.0",
|
||||
"lpl-1.0.0": "LPL-1.0",
|
||||
@@ -705,6 +737,7 @@ var licenseIDs = map[string]string{
|
||||
"lzma-sdk-9.22": "LZMA-SDK-9.22",
|
||||
"lzma-sdk-9.22.0": "LZMA-SDK-9.22",
|
||||
"makeindex": "MakeIndex",
|
||||
"martin-birgmeier": "Martin-Birgmeier",
|
||||
"minpack": "Minpack",
|
||||
"miros": "MirOS",
|
||||
"mit": "MIT",
|
||||
@@ -715,6 +748,7 @@ var licenseIDs = map[string]string{
|
||||
"mit-feh": "MIT-feh",
|
||||
"mit-modern-variant": "MIT-Modern-Variant",
|
||||
"mit-open-group": "MIT-open-group",
|
||||
"mit-wu": "MIT-Wu",
|
||||
"mitnfa": "MITNFA",
|
||||
"motosoto": "Motosoto",
|
||||
"mpi-permissive": "mpi-permissive",
|
||||
@@ -801,6 +835,7 @@ var licenseIDs = map[string]string{
|
||||
"odc-by-1": "ODC-By-1.0",
|
||||
"odc-by-1.0": "ODC-By-1.0",
|
||||
"odc-by-1.0.0": "ODC-By-1.0",
|
||||
"offis": "OFFIS",
|
||||
"ofl-1": "OFL-1.0",
|
||||
"ofl-1-no-rfn": "OFL-1.0-no-RFN",
|
||||
"ofl-1-rfn": "OFL-1.0-RFN",
|
||||
@@ -867,6 +902,9 @@ var licenseIDs = map[string]string{
|
||||
"oldap-2.8": "OLDAP-2.8",
|
||||
"oldap-2.8.0": "OLDAP-2.8",
|
||||
"oml": "OML",
|
||||
"openpbs-2": "OpenPBS-2.3",
|
||||
"openpbs-2.3": "OpenPBS-2.3",
|
||||
"openpbs-2.3.0": "OpenPBS-2.3",
|
||||
"openssl": "OpenSSL",
|
||||
"opl-1": "OPL-1.0",
|
||||
"opl-1.0": "OPL-1.0",
|
||||
@@ -923,8 +961,11 @@ var licenseIDs = map[string]string{
|
||||
"python-2.0.1": "Python-2.0.1",
|
||||
"qhull": "Qhull",
|
||||
"qpl-1": "QPL-1.0",
|
||||
"qpl-1-inria-2004": "QPL-1.0-INRIA-2004",
|
||||
"qpl-1.0": "QPL-1.0",
|
||||
"qpl-1.0-inria-2004": "QPL-1.0-INRIA-2004",
|
||||
"qpl-1.0.0": "QPL-1.0",
|
||||
"qpl-1.0.0-inria-2004": "QPL-1.0-INRIA-2004",
|
||||
"rdisc": "Rdisc",
|
||||
"rhecos-1": "RHeCos-1.1",
|
||||
"rhecos-1.1": "RHeCos-1.1",
|
||||
@@ -971,6 +1012,7 @@ var licenseIDs = map[string]string{
|
||||
"smlnj": "SMLNJ",
|
||||
"smppl": "SMPPL",
|
||||
"snia": "SNIA",
|
||||
"snprintf": "snprintf",
|
||||
"spencer-86": "Spencer-86",
|
||||
"spencer-86.0": "Spencer-86",
|
||||
"spencer-86.0.0": "Spencer-86",
|
||||
@@ -992,7 +1034,9 @@ var licenseIDs = map[string]string{
|
||||
"sugarcrm-1": "SugarCRM-1.1.3",
|
||||
"sugarcrm-1.1": "SugarCRM-1.1.3",
|
||||
"sugarcrm-1.1.3": "SugarCRM-1.1.3",
|
||||
"sunpro": "SunPro",
|
||||
"swl": "SWL",
|
||||
"symlinks": "Symlinks",
|
||||
"tapr-ohl-1": "TAPR-OHL-1.0",
|
||||
"tapr-ohl-1.0": "TAPR-OHL-1.0",
|
||||
"tapr-ohl-1.0.0": "TAPR-OHL-1.0",
|
||||
@@ -1003,12 +1047,18 @@ var licenseIDs = map[string]string{
|
||||
"torque-1.1": "TORQUE-1.1",
|
||||
"torque-1.1.0": "TORQUE-1.1",
|
||||
"tosl": "TOSL",
|
||||
"tpdl": "TPDL",
|
||||
"tpl-1": "TPL-1.0",
|
||||
"tpl-1.0": "TPL-1.0",
|
||||
"tpl-1.0.0": "TPL-1.0",
|
||||
"ttwl": "TTWL",
|
||||
"tu-berlin-1": "TU-Berlin-1.0",
|
||||
"tu-berlin-1.0": "TU-Berlin-1.0",
|
||||
"tu-berlin-1.0.0": "TU-Berlin-1.0",
|
||||
"tu-berlin-2": "TU-Berlin-2.0",
|
||||
"tu-berlin-2.0": "TU-Berlin-2.0",
|
||||
"tu-berlin-2.0.0": "TU-Berlin-2.0",
|
||||
"ucar": "UCAR",
|
||||
"ucl-1": "UCL-1.0",
|
||||
"ucl-1.0": "UCL-1.0",
|
||||
"ucl-1.0.0": "UCL-1.0",
|
||||
@@ -1035,6 +1085,7 @@ var licenseIDs = map[string]string{
|
||||
"w3c-20150513": "W3C-20150513",
|
||||
"w3c-20150513.0": "W3C-20150513",
|
||||
"w3c-20150513.0.0": "W3C-20150513",
|
||||
"w3m": "w3m",
|
||||
"watcom-1": "Watcom-1.0",
|
||||
"watcom-1.0": "Watcom-1.0",
|
||||
"watcom-1.0.0": "Watcom-1.0",
|
||||
@@ -1048,6 +1099,7 @@ var licenseIDs = map[string]string{
|
||||
"xfree86-1.1": "XFree86-1.1",
|
||||
"xfree86-1.1.0": "XFree86-1.1",
|
||||
"xinetd": "xinetd",
|
||||
"xlock": "xlock",
|
||||
"xnet": "Xnet",
|
||||
"xpp": "xpp",
|
||||
"xskat": "XSkat",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -85,14 +85,9 @@ func versionMatches(version string, match string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
dots := strings.Count(match, ".")
|
||||
if dots == 0 {
|
||||
match += ".*"
|
||||
}
|
||||
match = strings.ReplaceAll(match, ".", "\\.")
|
||||
match = strings.ReplaceAll(match, "*", ".*")
|
||||
match = strings.ReplaceAll(match, "\\..*", "(\\..*)*")
|
||||
match = fmt.Sprintf("^%s$", match)
|
||||
match = fmt.Sprintf("^%s(\\..*)*$", match)
|
||||
matcher, err := regexp.Compile(match)
|
||||
if err != nil {
|
||||
return false
|
||||
|
||||
@@ -227,12 +227,61 @@ func Test_versionMatches(t *testing.T) {
|
||||
match: "3.1",
|
||||
matches: true,
|
||||
},
|
||||
{
|
||||
name: "wildcard-version matches minor",
|
||||
version: "7.1.3",
|
||||
match: "7.*",
|
||||
matches: true,
|
||||
},
|
||||
{
|
||||
name: "wildcard-version matches patch",
|
||||
version: "7.4.8",
|
||||
match: "7.4.*",
|
||||
matches: true,
|
||||
},
|
||||
{
|
||||
name: "sub-version matches major",
|
||||
version: "7.19.11",
|
||||
match: "7",
|
||||
matches: true,
|
||||
},
|
||||
{
|
||||
name: "sub-version matches minor",
|
||||
version: "7.55.2",
|
||||
match: "7.55",
|
||||
matches: true,
|
||||
},
|
||||
{
|
||||
name: "sub-version matches patch",
|
||||
version: "7.32.6",
|
||||
match: "7.32.6",
|
||||
matches: true,
|
||||
},
|
||||
// negative tests
|
||||
{
|
||||
name: "different number does not match",
|
||||
version: "3",
|
||||
match: "4",
|
||||
matches: false,
|
||||
},
|
||||
{
|
||||
name: "sub-version doesn't match major",
|
||||
version: "7.2.5",
|
||||
match: "8.2.5",
|
||||
matches: false,
|
||||
},
|
||||
{
|
||||
name: "sub-version doesn't match minor",
|
||||
version: "7.2.9",
|
||||
match: "7.1",
|
||||
matches: false,
|
||||
},
|
||||
{
|
||||
name: "sub-version doesn't match patch",
|
||||
version: "7.32.6",
|
||||
match: "7.32.5",
|
||||
matches: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -213,5 +213,6 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
s,
|
||||
*updateJson,
|
||||
true,
|
||||
schemaVersionRedactor,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package syftjson
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
@@ -8,7 +9,7 @@ const ID sbom.FormatID = "syft-json"
|
||||
|
||||
func Format() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
"6",
|
||||
internal.JSONSchemaVersion,
|
||||
encoder,
|
||||
decoder,
|
||||
validator,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package syftjson
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
@@ -9,16 +8,10 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/formats/syftjson/model"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func Test_SyftJsonID_Compatibility(t *testing.T) {
|
||||
jsonMajorVersion := strings.Split(internal.JSONSchemaVersion, ".")[0]
|
||||
assert.Equal(t, jsonMajorVersion, string(Format().Version()))
|
||||
}
|
||||
|
||||
func Test_toSourceModel(t *testing.T) {
|
||||
allSchemes := strset.New()
|
||||
for _, s := range source.AllSchemes {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package pkg
|
||||
|
||||
import "github.com/anchore/syft/syft/source"
|
||||
|
||||
type BinaryMetadata struct {
|
||||
Classifier string `mapstructure:"Classifier" json:"classifier"`
|
||||
RealPath string `mapstructure:"RealPath" json:"realPath"`
|
||||
VirtualPath string `mapstructure:"VirtualPath" json:"virtualPath"`
|
||||
Matches []ClassifierMatch `mapstructure:"Matches" json:"matches"`
|
||||
}
|
||||
|
||||
type ClassifierMatch struct {
|
||||
Classifier string `mapstructure:"Classifier" json:"classifier"`
|
||||
Location source.Location `mapstructure:"Location" json:"location"`
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ import (
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
var (
|
||||
prefixes = []string{"py-", "py2-", "py3-", "ruby-"}
|
||||
)
|
||||
|
||||
func newPackage(d pkg.ApkMetadata, release *linux.Release, locations ...source.Location) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: d.Package,
|
||||
@@ -26,6 +30,20 @@ func newPackage(d pkg.ApkMetadata, release *linux.Release, locations ...source.L
|
||||
return p
|
||||
}
|
||||
|
||||
func generateUpstream(m pkg.ApkMetadata) string {
|
||||
if m.OriginPackage != "" && m.OriginPackage != m.Package {
|
||||
return m.OriginPackage
|
||||
}
|
||||
|
||||
for _, p := range prefixes {
|
||||
if strings.HasPrefix(m.Package, p) {
|
||||
return strings.TrimPrefix(m.Package, p)
|
||||
}
|
||||
}
|
||||
|
||||
return m.Package
|
||||
}
|
||||
|
||||
// packageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec)
|
||||
func packageURL(m pkg.ApkMetadata, distro *linux.Release) string {
|
||||
if distro == nil || distro.ID != "alpine" {
|
||||
@@ -38,7 +56,7 @@ func packageURL(m pkg.ApkMetadata, distro *linux.Release) string {
|
||||
}
|
||||
|
||||
if m.OriginPackage != "" {
|
||||
qualifiers[pkg.PURLQualifierUpstream] = m.OriginPackage
|
||||
qualifiers[pkg.PURLQualifierUpstream] = generateUpstream(m)
|
||||
}
|
||||
|
||||
return packageurl.NewPackageURL(
|
||||
|
||||
@@ -96,6 +96,34 @@ func Test_PackageURL(t *testing.T) {
|
||||
},
|
||||
expected: "pkg:apk/alpine/p@v?arch=a&upstream=origin&distro=alpine-3.4.6",
|
||||
},
|
||||
{
|
||||
name: "upstream python package information as qualifier",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "py3-potatoes",
|
||||
Version: "v",
|
||||
Architecture: "a",
|
||||
OriginPackage: "py3-potatoes",
|
||||
},
|
||||
distro: linux.Release{
|
||||
ID: "alpine",
|
||||
VersionID: "3.4.6",
|
||||
},
|
||||
expected: "pkg:apk/alpine/py3-potatoes@v?arch=a&upstream=potatoes&distro=alpine-3.4.6",
|
||||
},
|
||||
{
|
||||
name: "python package with origin package as upstream",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "py3-non-existant",
|
||||
Version: "v",
|
||||
Architecture: "a",
|
||||
OriginPackage: "abcdefg",
|
||||
},
|
||||
distro: linux.Release{
|
||||
ID: "alpine",
|
||||
VersionID: "3.4.6",
|
||||
},
|
||||
expected: "pkg:apk/alpine/py3-non-existant@v?arch=a&upstream=abcdefg&distro=alpine-3.4.6",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -33,19 +33,42 @@ func (c Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artif
|
||||
|
||||
for _, cls := range defaultClassifiers {
|
||||
log.WithFields("classifier", cls.Class).Trace("cataloging binaries")
|
||||
pkgs, err := catalog(resolver, cls)
|
||||
newPkgs, err := catalog(resolver, cls)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "classifier", cls.Class).Warn("unable to catalog binary package: %w", err)
|
||||
continue
|
||||
}
|
||||
packages = append(packages, pkgs...)
|
||||
newPackages:
|
||||
for i := range newPkgs {
|
||||
newPkg := &newPkgs[i]
|
||||
for j := range packages {
|
||||
p := &packages[j]
|
||||
// consolidate identical packages found in different locations or by different classifiers
|
||||
if packagesMatch(p, newPkg) {
|
||||
mergePackages(p, newPkg)
|
||||
continue newPackages
|
||||
}
|
||||
}
|
||||
packages = append(packages, *newPkg)
|
||||
}
|
||||
}
|
||||
|
||||
return packages, relationships, nil
|
||||
}
|
||||
|
||||
func catalog(resolver source.FileResolver, cls classifier) ([]pkg.Package, error) {
|
||||
var pkgs []pkg.Package
|
||||
// mergePackages merges information from the extra package into the target package
|
||||
func mergePackages(target *pkg.Package, extra *pkg.Package) {
|
||||
// add the locations
|
||||
target.Locations.Add(extra.Locations.ToSlice()...)
|
||||
// update the metadata to indicate which classifiers were used
|
||||
meta, _ := target.Metadata.(pkg.BinaryMetadata)
|
||||
if m, ok := extra.Metadata.(pkg.BinaryMetadata); ok {
|
||||
meta.Matches = append(meta.Matches, m.Matches...)
|
||||
}
|
||||
target.Metadata = meta
|
||||
}
|
||||
|
||||
func catalog(resolver source.FileResolver, cls classifier) (packages []pkg.Package, err error) {
|
||||
locations, err := resolver.FilesByGlob(cls.FileGlob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -56,26 +79,13 @@ func catalog(resolver source.FileResolver, cls classifier) ([]pkg.Package, error
|
||||
return nil, err
|
||||
}
|
||||
locationReader := source.NewLocationReadCloser(location, reader)
|
||||
newPkgs, err := cls.EvidenceMatcher(cls, locationReader)
|
||||
pkgs, err := cls.EvidenceMatcher(cls, locationReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newPackages:
|
||||
for i := range newPkgs {
|
||||
newPkg := &newPkgs[i]
|
||||
for j := range pkgs {
|
||||
p := &pkgs[j]
|
||||
// consolidate identical packages found in different locations,
|
||||
// but continue to track each location
|
||||
if packagesMatch(p, newPkg) {
|
||||
p.Locations.Add(newPkg.Locations.ToSlice()...)
|
||||
continue newPackages
|
||||
}
|
||||
}
|
||||
pkgs = append(pkgs, *newPkg)
|
||||
}
|
||||
packages = append(packages, pkgs...)
|
||||
}
|
||||
return pkgs, nil
|
||||
return packages, nil
|
||||
}
|
||||
|
||||
// packagesMatch returns true if the binary packages "match" based on basic criteria
|
||||
|
||||
@@ -2,6 +2,7 @@ package binary
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
@@ -27,10 +28,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "15beta4",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/postgresql@15beta4",
|
||||
Locations: singleLocation("postgres"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "postgresql-binary",
|
||||
},
|
||||
Locations: locations("postgres"),
|
||||
Metadata: metadata("postgresql-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -41,10 +40,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "15.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/postgresql@15.1",
|
||||
Locations: singleLocation("postgres"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "postgresql-binary",
|
||||
},
|
||||
Locations: locations("postgres"),
|
||||
Metadata: metadata("postgresql-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -55,10 +52,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "9.6.24",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/postgresql@9.6.24",
|
||||
Locations: singleLocation("postgres"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "postgresql-binary",
|
||||
},
|
||||
Locations: locations("postgres"),
|
||||
Metadata: metadata("postgresql-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -69,9 +64,26 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "9.5alpha1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/postgresql@9.5alpha1",
|
||||
Locations: singleLocation("postgres"),
|
||||
Locations: locations("postgres"),
|
||||
Metadata: metadata("postgresql-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-python-duplicates",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/python-duplicates",
|
||||
expected: pkg.Package{
|
||||
Name: "python",
|
||||
Version: "3.8.16",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/python@3.8.16",
|
||||
Locations: locations("dir/python3.8", "python3.8", "libpython3.8.so", "patchlevel.h"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "postgresql-binary",
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
match("python-binary", "dir/python3.8"),
|
||||
match("python-binary", "python3.8"),
|
||||
match("python-binary-lib", "libpython3.8.so"),
|
||||
match("cpython-source", "patchlevel.h"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -83,10 +95,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "2.9.6",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/traefik@2.9.6",
|
||||
Locations: singleLocation("traefik"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "traefik-binary",
|
||||
},
|
||||
Locations: locations("traefik"),
|
||||
Metadata: metadata("traefik-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -97,10 +107,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "1.7.34",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/traefik@1.7.34",
|
||||
Locations: singleLocation("traefik"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "traefik-binary",
|
||||
},
|
||||
Locations: locations("traefik"),
|
||||
Metadata: metadata("traefik-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -111,10 +119,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "1.6.18",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/memcached@1.6.18",
|
||||
Locations: singleLocation("memcached"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "memcached-binary",
|
||||
},
|
||||
Locations: locations("memcached"),
|
||||
Metadata: metadata("memcached-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -125,10 +131,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "2.4.54",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/httpd@2.4.54",
|
||||
Locations: singleLocation("httpd"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "httpd-binary",
|
||||
},
|
||||
Locations: locations("httpd"),
|
||||
Metadata: metadata("httpd-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -139,10 +143,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "8.2.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/php-cli@8.2.1",
|
||||
Locations: singleLocation("php"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "php-cli-binary",
|
||||
},
|
||||
Locations: locations("php"),
|
||||
Metadata: metadata("php-cli-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -153,10 +155,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "8.2.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/php-fpm@8.2.1",
|
||||
Locations: singleLocation("php-fpm"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "php-fpm-binary",
|
||||
},
|
||||
Locations: locations("php-fpm"),
|
||||
Metadata: metadata("php-fpm-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -167,10 +167,44 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "8.2.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/php@8.2.1",
|
||||
Locations: singleLocation("libphp.so"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "php-apache-binary",
|
||||
},
|
||||
Locations: locations("libphp.so"),
|
||||
Metadata: metadata("php-apache-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-perl-5.12.5",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/perl-5.12.5",
|
||||
expected: pkg.Package{
|
||||
Name: "perl",
|
||||
Version: "5.12.5",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/perl@5.12.5",
|
||||
Locations: locations("perl"),
|
||||
Metadata: metadata("perl-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-perl-5.20.0",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/perl-5.20.0",
|
||||
expected: pkg.Package{
|
||||
Name: "perl",
|
||||
Version: "5.20.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/perl@5.20.0",
|
||||
Locations: locations("perl"),
|
||||
Metadata: metadata("perl-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-perl-5.37.8",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/perl-5.37.8",
|
||||
expected: pkg.Package{
|
||||
Name: "perl",
|
||||
Version: "5.37.8",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/perl@5.37.8",
|
||||
Locations: locations("perl"),
|
||||
Metadata: metadata("perl-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -181,10 +215,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "2.8.23",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/redis@2.8.23",
|
||||
Locations: singleLocation("redis-server"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "redis-binary",
|
||||
},
|
||||
Locations: locations("redis-server"),
|
||||
Metadata: metadata("redis-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -195,10 +227,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "4.0.11",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/redis@4.0.11",
|
||||
Locations: singleLocation("redis-server"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "redis-binary",
|
||||
},
|
||||
Locations: locations("redis-server"),
|
||||
Metadata: metadata("redis-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -209,10 +239,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "5.0.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/redis@5.0.0",
|
||||
Locations: singleLocation("redis-server"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "redis-binary",
|
||||
},
|
||||
Locations: locations("redis-server"),
|
||||
Metadata: metadata("redis-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -223,10 +251,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "6.0.16",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/redis@6.0.16",
|
||||
Locations: singleLocation("redis-server"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "redis-binary",
|
||||
},
|
||||
Locations: locations("redis-server"),
|
||||
Metadata: metadata("redis-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -237,101 +263,84 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "7.0.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/redis@7.0.0",
|
||||
Locations: singleLocation("redis-server"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "redis-binary",
|
||||
},
|
||||
Locations: locations("redis-server"),
|
||||
Metadata: metadata("redis-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-libpython3.7.so",
|
||||
fixtureDir: "test-fixtures/classifiers/positive",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/python-binary-lib-3.7",
|
||||
expected: pkg.Package{
|
||||
Name: "python",
|
||||
Version: "3.7.4a-vZ9",
|
||||
PURL: "pkg:generic/python@3.7.4a-vZ9",
|
||||
Locations: singleLocation("libpython3.7.so"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "python-binary-lib",
|
||||
},
|
||||
Locations: locations("libpython3.7.so"),
|
||||
Metadata: metadata("python-binary-lib"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-python3.6",
|
||||
fixtureDir: "test-fixtures/classifiers/positive",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/python-binary-3.6",
|
||||
expected: pkg.Package{
|
||||
Name: "python",
|
||||
Version: "3.6.3a-vZ9",
|
||||
PURL: "pkg:generic/python@3.6.3a-vZ9",
|
||||
Locations: singleLocation("python3.6"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "python-binary",
|
||||
},
|
||||
Locations: locations("python3.6"),
|
||||
Metadata: metadata("python-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-patchlevel.h",
|
||||
fixtureDir: "test-fixtures/classifiers/positive",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/python-source-3.9",
|
||||
expected: pkg.Package{
|
||||
Name: "python",
|
||||
Version: "3.9-aZ5",
|
||||
PURL: "pkg:generic/python@3.9-aZ5",
|
||||
Locations: singleLocation("patchlevel.h"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "cpython-source",
|
||||
},
|
||||
Locations: locations("patchlevel.h"),
|
||||
Metadata: metadata("cpython-source"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-go",
|
||||
fixtureDir: "test-fixtures/classifiers/positive",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/go-1.14",
|
||||
expected: pkg.Package{
|
||||
Name: "go",
|
||||
Version: "1.14",
|
||||
PURL: "pkg:generic/go@1.14",
|
||||
Locations: singleLocation("go"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "go-binary",
|
||||
},
|
||||
Locations: locations("go"),
|
||||
Metadata: metadata("go-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-node",
|
||||
fixtureDir: "test-fixtures/classifiers/positive",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/node-19.2.1",
|
||||
expected: pkg.Package{
|
||||
Name: "node",
|
||||
Version: "19.2.1",
|
||||
PURL: "pkg:generic/node@19.2.1",
|
||||
Locations: singleLocation("node"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "nodejs-binary",
|
||||
},
|
||||
Locations: locations("node"),
|
||||
Metadata: metadata("nodejs-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-go-hint",
|
||||
fixtureDir: "test-fixtures/classifiers/positive",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/go-hint-1.15",
|
||||
expected: pkg.Package{
|
||||
Name: "go",
|
||||
Version: "1.15",
|
||||
PURL: "pkg:generic/go@1.15",
|
||||
Locations: singleLocation("VERSION"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "go-binary-hint",
|
||||
},
|
||||
Locations: locations("VERSION"),
|
||||
Metadata: metadata("go-binary-hint"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-busybox",
|
||||
fixtureDir: "test-fixtures/classifiers/positive",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/busybox-3.33.3",
|
||||
expected: pkg.Package{
|
||||
Name: "busybox",
|
||||
Version: "3.33.3",
|
||||
Locations: singleLocation("["), // note: busybox is a link to [
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "busybox-binary",
|
||||
VirtualPath: "busybox",
|
||||
},
|
||||
Locations: locations("["), // note: busybox is a link to [
|
||||
Metadata: metadata("busybox-binary", "[", "busybox"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -342,11 +351,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "1.8.0_352-b08",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java@1.8.0_352-b08",
|
||||
Locations: singleLocation("java"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "java-binary-openjdk",
|
||||
VirtualPath: "java",
|
||||
},
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-openjdk", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -357,11 +363,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "11.0.17+8-LTS",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java@11.0.17+8-LTS",
|
||||
Locations: singleLocation("java"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "java-binary-openjdk",
|
||||
VirtualPath: "java",
|
||||
},
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-openjdk", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -372,11 +375,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "19.0.1+10-21",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java@19.0.1+10-21",
|
||||
Locations: singleLocation("java"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "java-binary-oracle",
|
||||
VirtualPath: "java",
|
||||
},
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-oracle", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -387,11 +387,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "19.0.1+10-21",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java@19.0.1+10-21",
|
||||
Locations: singleLocation("java"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "java-binary-oracle",
|
||||
VirtualPath: "java",
|
||||
},
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-oracle", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -402,11 +399,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
Version: "1.8.0-foreman_2022_09_22_15_30-b00",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java@1.8.0-foreman_2022_09_22_15_30-b00",
|
||||
Locations: singleLocation("java"),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "java-binary-ibm",
|
||||
VirtualPath: "java",
|
||||
},
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-ibm", "java"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -424,18 +418,20 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
|
||||
packages, _, err := c.Catalog(resolver)
|
||||
require.NoError(t, err)
|
||||
|
||||
ok := false
|
||||
for _, p := range packages {
|
||||
if test.expected.Locations.ToSlice()[0].RealPath == p.Locations.ToSlice()[0].RealPath {
|
||||
ok = true
|
||||
assertPackagesAreEqual(t, test.expected, p)
|
||||
expectedLocations := test.expected.Locations.ToSlice()
|
||||
gotLocations := p.Locations.ToSlice()
|
||||
require.Len(t, gotLocations, len(expectedLocations))
|
||||
|
||||
for i, expectedLocation := range expectedLocations {
|
||||
gotLocation := gotLocations[i]
|
||||
if expectedLocation.RealPath != gotLocation.RealPath {
|
||||
t.Fatalf("locations do not match; expected: %v got: %v", expectedLocations, gotLocations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
t.Fatalf("could not find test location=%q", test.expected.Locations.ToSlice()[0].RealPath)
|
||||
assertPackagesAreEqual(t, test.expected, p)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -452,11 +448,8 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
|
||||
expected: pkg.Package{
|
||||
Name: "busybox",
|
||||
Version: "1.35.0",
|
||||
Locations: singleLocation("/bin/["),
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: "busybox-binary",
|
||||
VirtualPath: "/bin/busybox",
|
||||
},
|
||||
Locations: locations("/bin/["),
|
||||
Metadata: metadata("busybox-binary", "/bin/[", "/bin/busybox"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -475,18 +468,20 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
|
||||
packages, _, err := c.Catalog(resolver)
|
||||
require.NoError(t, err)
|
||||
|
||||
ok := false
|
||||
for _, p := range packages {
|
||||
if test.expected.Locations.ToSlice()[0].RealPath == p.Locations.ToSlice()[0].RealPath {
|
||||
ok = true
|
||||
assertPackagesAreEqual(t, test.expected, p)
|
||||
expectedLocations := test.expected.Locations.ToSlice()
|
||||
gotLocations := p.Locations.ToSlice()
|
||||
require.Len(t, gotLocations, len(expectedLocations))
|
||||
|
||||
for i, expectedLocation := range expectedLocations {
|
||||
gotLocation := gotLocations[i]
|
||||
if expectedLocation.RealPath != gotLocation.RealPath {
|
||||
t.Fatalf("locations do not match; expected: %v got: %v", expectedLocations, gotLocations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
t.Fatalf("could not find test location=%q", test.expected.Locations.ToSlice()[0].RealPath)
|
||||
assertPackagesAreEqual(t, test.expected, p)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -505,21 +500,80 @@ func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) {
|
||||
assert.Equal(t, 0, len(actualResults))
|
||||
}
|
||||
|
||||
func singleLocation(s string) source.LocationSet {
|
||||
return source.NewLocationSet(source.NewLocation(s))
|
||||
func locations(locations ...string) source.LocationSet {
|
||||
var locs []source.Location
|
||||
for _, s := range locations {
|
||||
locs = append(locs, source.NewLocation(s))
|
||||
}
|
||||
return source.NewLocationSet(locs...)
|
||||
}
|
||||
|
||||
// metadata paths are: realPath, virtualPath
|
||||
func metadata(classifier string, paths ...string) pkg.BinaryMetadata {
|
||||
return pkg.BinaryMetadata{
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
match(classifier, paths...),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// match paths are: realPath, virtualPath
|
||||
func match(classifier string, paths ...string) pkg.ClassifierMatch {
|
||||
realPath := ""
|
||||
if len(paths) > 0 {
|
||||
realPath = paths[0]
|
||||
}
|
||||
virtualPath := ""
|
||||
if len(paths) > 1 {
|
||||
virtualPath = paths[1]
|
||||
}
|
||||
return pkg.ClassifierMatch{
|
||||
Classifier: classifier,
|
||||
Location: source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
RealPath: realPath,
|
||||
},
|
||||
VirtualPath: virtualPath,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func assertPackagesAreEqual(t *testing.T, expected pkg.Package, p pkg.Package) {
|
||||
meta1 := expected.Metadata.(pkg.BinaryMetadata)
|
||||
meta2 := p.Metadata.(pkg.BinaryMetadata)
|
||||
m1 := expected.Metadata.(pkg.BinaryMetadata).Matches
|
||||
m2 := p.Metadata.(pkg.BinaryMetadata).Matches
|
||||
matches := true
|
||||
if len(m1) == len(m2) {
|
||||
for i, m1 := range m1 {
|
||||
m2 := m2[i]
|
||||
if m1.Classifier != m2.Classifier {
|
||||
matches = false
|
||||
break
|
||||
}
|
||||
if m1.Location.RealPath != "" && m1.Location.RealPath != m2.Location.RealPath {
|
||||
matches = false
|
||||
break
|
||||
}
|
||||
if m1.Location.VirtualPath != "" && m1.Location.VirtualPath != m2.Location.VirtualPath {
|
||||
matches = false
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
matches = false
|
||||
}
|
||||
if expected.Name != p.Name ||
|
||||
expected.Version != p.Version ||
|
||||
expected.PURL != p.PURL ||
|
||||
meta1.Classifier != meta2.Classifier {
|
||||
assert.Failf(t, "packages not equal", "%v != %v", expected, p)
|
||||
!matches {
|
||||
assert.Failf(t, "packages not equal", "%v != %v", stringifyPkg(expected), stringifyPkg(p))
|
||||
}
|
||||
}
|
||||
|
||||
func stringifyPkg(p pkg.Package) string {
|
||||
matches := p.Metadata.(pkg.BinaryMetadata).Matches
|
||||
return fmt.Sprintf("(name=%s, version=%s, purl=%s, matches=%+v)", p.Name, p.Version, p.PURL, matches)
|
||||
}
|
||||
|
||||
type panicyResolver struct {
|
||||
searchCalled bool
|
||||
}
|
||||
@@ -544,7 +598,7 @@ func (p *panicyResolver) FileContentsByLocation(_ source.Location) (io.ReadClose
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (p *panicyResolver) HasPath(s string) bool {
|
||||
func (p *panicyResolver) HasPath(_ string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -575,6 +629,8 @@ func (p *panicyResolver) FileMetadataByLocation(_ source.Location) (source.FileM
|
||||
return source.FileMetadata{}, errors.New("not implemented")
|
||||
}
|
||||
|
||||
var _ source.FileResolver = (*panicyResolver)(nil)
|
||||
|
||||
func Test_Cataloger_ResilientToErrors(t *testing.T) {
|
||||
c := NewCataloger()
|
||||
|
||||
|
||||
@@ -131,9 +131,12 @@ func singlePackage(classifier classifier, reader source.LocationReadCloser, matc
|
||||
FoundBy: catalogerName,
|
||||
MetadataType: pkg.BinaryMetadataType,
|
||||
Metadata: pkg.BinaryMetadata{
|
||||
Classifier: classifier.Class,
|
||||
RealPath: reader.RealPath,
|
||||
VirtualPath: reader.VirtualPath,
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
{
|
||||
Classifier: classifier.Class,
|
||||
Location: reader.Location,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,15 @@ var defaultClassifiers = []classifier{
|
||||
Package: "busybox",
|
||||
CPEs: singleCPE("cpe:2.3:a:busybox:busybox:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
Class: "perl-binary",
|
||||
FileGlob: "**/perl",
|
||||
EvidenceMatcher: fileContentsVersionMatcher(
|
||||
`(?m)\/usr\/local\/lib\/perl\d\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
Package: "perl",
|
||||
PURL: mustPURL("pkg:generic/perl@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
Class: "php-cli-binary",
|
||||
FileGlob: "**/php*",
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
!libpython3.7.so
|
||||
!libphp.so
|
||||
!*.so
|
||||
!VERSION
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
@@ -0,0 +1 @@
|
||||
3.8.16
|
||||
+1
@@ -0,0 +1 @@
|
||||
3.8.16
|
||||
+1
@@ -0,0 +1 @@
|
||||
#define PY_VERSION 3.8.16
|
||||
+1
@@ -0,0 +1 @@
|
||||
3.8.16
|
||||
@@ -161,9 +161,29 @@ func contains(enabledPartial []string, catalogerName string) bool {
|
||||
if partial == "" {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(catalogerName, partial) {
|
||||
if hasFullWord(partial, catalogerName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasFullWord(targetPhrase, candidate string) bool {
|
||||
if targetPhrase == "cataloger" || targetPhrase == "" {
|
||||
return false
|
||||
}
|
||||
start := strings.Index(candidate, targetPhrase)
|
||||
if start == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
if start > 0 && candidate[start-1] != '-' {
|
||||
return false
|
||||
}
|
||||
|
||||
end := start + len(targetPhrase)
|
||||
if end < len(candidate) && candidate[end] != '-' {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -25,6 +25,38 @@ func (d dummy) Catalog(_ source.FileResolver) ([]pkg.Package, []artifact.Relatio
|
||||
}
|
||||
|
||||
func Test_filterCatalogers(t *testing.T) {
|
||||
largeCatalogerList := []string{
|
||||
"alpmdb-cataloger",
|
||||
"apkdb-cataloger",
|
||||
"binary-cataloger",
|
||||
"conan-cataloger",
|
||||
"dartlang-lock-cataloger",
|
||||
"dpkgdb-cataloger",
|
||||
"dotnet-deps-cataloger",
|
||||
"elixir-mix-lock-cataloger",
|
||||
"erlang-rebar-lock-cataloger",
|
||||
"go-mod-file-cataloger",
|
||||
"go-module-binary-cataloger",
|
||||
"haskell-cataloger",
|
||||
"graalvm-native-image-cataloger",
|
||||
"java-cataloger",
|
||||
"java-pom-cataloger",
|
||||
"javascript-package-cataloger",
|
||||
"javascript-lock-cataloger",
|
||||
"php-composer-installed-cataloger",
|
||||
"php-composer-lock-cataloger",
|
||||
"portage-cataloger",
|
||||
"python-index-cataloger",
|
||||
"python-package-cataloger",
|
||||
"rpm-db-cataloger",
|
||||
"rpm-file-cataloger",
|
||||
"ruby-gemfile-cataloger",
|
||||
"ruby-gemspec-cataloger",
|
||||
"rust-cargo-lock-cataloger",
|
||||
"cargo-auditable-binary-cataloger",
|
||||
"sbom-cataloger",
|
||||
"cocoapods-cataloger",
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
patterns []string
|
||||
@@ -144,6 +176,43 @@ func Test_filterCatalogers(t *testing.T) {
|
||||
"go-module-binary-cataloger",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "don't cross match ecosystems with matching prefix",
|
||||
patterns: []string{
|
||||
"java-cataloger",
|
||||
},
|
||||
catalogers: []string{
|
||||
"javascript-package-cataloger",
|
||||
"java-cataloger",
|
||||
},
|
||||
want: []string{
|
||||
"java-cataloger",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "don't cross match ecosystems with short, common name",
|
||||
patterns: []string{
|
||||
"go",
|
||||
},
|
||||
catalogers: largeCatalogerList,
|
||||
want: []string{
|
||||
"go-mod-file-cataloger",
|
||||
"go-module-binary-cataloger",
|
||||
//"rust-cargo-lock-cataloger", // with naive "contains" matching
|
||||
//"cargo-auditable-binary-cataloger", // with naive "contains" matching
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignore partial matches",
|
||||
patterns: []string{
|
||||
"mod",
|
||||
},
|
||||
catalogers: largeCatalogerList,
|
||||
want: []string{
|
||||
"go-mod-file-cataloger",
|
||||
//"go-module-binary-cataloger", // unfortunately not a full word (this should probably be renamed)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -162,8 +231,6 @@ func Test_filterCatalogers(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_contains(t *testing.T) {
|
||||
type args struct {
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
enabledCatalogers []string
|
||||
@@ -201,3 +268,73 @@ func Test_contains(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_hasFullWord(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
targetPhrase string
|
||||
candidate string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "exact match",
|
||||
targetPhrase: "php-composer-installed-cataloger",
|
||||
candidate: "php-composer-installed-cataloger",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "partial, full word match",
|
||||
targetPhrase: "composer",
|
||||
candidate: "php-composer-installed-cataloger",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "partial, full, multi-word match",
|
||||
targetPhrase: "php-composer",
|
||||
candidate: "php-composer-installed-cataloger",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "prefix match",
|
||||
targetPhrase: "php",
|
||||
candidate: "php-composer-installed-cataloger",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "postfix match with -cataloger suffix",
|
||||
targetPhrase: "installed",
|
||||
candidate: "php-composer-installed-cataloger",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "postfix match",
|
||||
targetPhrase: "installed",
|
||||
candidate: "php-composer-installed",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "ignore cataloger keyword",
|
||||
targetPhrase: "cataloger",
|
||||
candidate: "php-composer-installed-cataloger",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "ignore partial match",
|
||||
targetPhrase: "hp",
|
||||
candidate: "php-composer-installed-cataloger",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "ignore empty string",
|
||||
targetPhrase: "",
|
||||
candidate: "php-composer-installed-cataloger",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, hasFullWord(tt.targetPhrase, tt.candidate), "hasFullWord(%v, %v)", tt.targetPhrase, tt.candidate)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
package cpe
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
var (
|
||||
pythonPrefixes = []string{"py-", "py2-", "py3-"}
|
||||
rubyPrefixes = []string{"ruby-"}
|
||||
)
|
||||
|
||||
func pythonCandidateVendorsFromName(v string) fieldCandidateSet {
|
||||
vendors := newFieldCandidateSet()
|
||||
vendors.add(fieldCandidate{
|
||||
value: v,
|
||||
disallowSubSelections: true,
|
||||
disallowDelimiterVariations: true,
|
||||
})
|
||||
|
||||
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.PythonPkg, v, v)...)
|
||||
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.PythonPkg, v)...)
|
||||
|
||||
for _, av := range additionalVendorsForPython(v) {
|
||||
vendors.add(fieldCandidate{
|
||||
value: av,
|
||||
disallowSubSelections: true,
|
||||
disallowDelimiterVariations: true,
|
||||
})
|
||||
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.PythonPkg, av, av)...)
|
||||
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.PythonPkg, av)...)
|
||||
}
|
||||
|
||||
return vendors
|
||||
}
|
||||
|
||||
func pythonCandidateVendorsFromAPK(m pkg.ApkMetadata) fieldCandidateSet {
|
||||
vendors := newFieldCandidateSet()
|
||||
|
||||
for _, p := range pythonPrefixes {
|
||||
if strings.HasPrefix(m.Package, p) {
|
||||
t := strings.ToLower(strings.TrimPrefix(m.Package, p))
|
||||
vendors.union(pythonCandidateVendorsFromName(t))
|
||||
}
|
||||
|
||||
if m.OriginPackage != m.Package && strings.HasPrefix(m.OriginPackage, p) {
|
||||
t := strings.ToLower(strings.TrimPrefix(m.OriginPackage, p))
|
||||
vendors.union(pythonCandidateVendorsFromName(t))
|
||||
}
|
||||
}
|
||||
|
||||
return vendors
|
||||
}
|
||||
|
||||
func pythonCandidateProductsFromName(p string) fieldCandidateSet {
|
||||
products := newFieldCandidateSet()
|
||||
products.add(fieldCandidate{
|
||||
value: p,
|
||||
disallowSubSelections: true,
|
||||
disallowDelimiterVariations: true,
|
||||
})
|
||||
|
||||
products.addValue(findAdditionalProducts(defaultCandidateAdditions, pkg.PythonPkg, p)...)
|
||||
products.removeByValue(findProductsToRemove(defaultCandidateRemovals, pkg.PythonPkg, p)...)
|
||||
return products
|
||||
}
|
||||
|
||||
func pythonCandidateProductsFromAPK(m pkg.ApkMetadata) fieldCandidateSet {
|
||||
products := newFieldCandidateSet()
|
||||
|
||||
for _, p := range pythonPrefixes {
|
||||
if strings.HasPrefix(m.Package, p) {
|
||||
t := strings.ToLower(strings.TrimPrefix(m.Package, p))
|
||||
products.union(pythonCandidateProductsFromName(t))
|
||||
}
|
||||
|
||||
if m.OriginPackage != m.Package && strings.HasPrefix(m.OriginPackage, p) {
|
||||
t := strings.ToLower(strings.TrimPrefix(m.OriginPackage, p))
|
||||
products.union(pythonCandidateProductsFromName(t))
|
||||
}
|
||||
}
|
||||
|
||||
return products
|
||||
}
|
||||
|
||||
func rubyCandidateVendorsFromName(v string) fieldCandidateSet {
|
||||
vendors := newFieldCandidateSet()
|
||||
vendors.add(fieldCandidate{
|
||||
value: v,
|
||||
disallowSubSelections: true,
|
||||
disallowDelimiterVariations: true,
|
||||
})
|
||||
|
||||
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.GemPkg, v, v)...)
|
||||
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.GemPkg, v)...)
|
||||
return vendors
|
||||
}
|
||||
|
||||
func rubyCandidateVendorsFromAPK(m pkg.ApkMetadata) fieldCandidateSet {
|
||||
vendors := newFieldCandidateSet()
|
||||
|
||||
for _, p := range rubyPrefixes {
|
||||
if strings.HasPrefix(m.Package, p) {
|
||||
t := strings.ToLower(strings.TrimPrefix(m.Package, p))
|
||||
vendors.union(rubyCandidateVendorsFromName(t))
|
||||
}
|
||||
|
||||
if m.OriginPackage != m.Package && strings.HasPrefix(m.OriginPackage, p) {
|
||||
t := strings.ToLower(strings.TrimPrefix(m.OriginPackage, p))
|
||||
vendors.union(rubyCandidateVendorsFromName(t))
|
||||
}
|
||||
}
|
||||
|
||||
return vendors
|
||||
}
|
||||
|
||||
func rubyCandidateProductsFromName(p string) fieldCandidateSet {
|
||||
products := newFieldCandidateSet()
|
||||
products.add(fieldCandidate{
|
||||
value: p,
|
||||
disallowSubSelections: true,
|
||||
disallowDelimiterVariations: true,
|
||||
})
|
||||
|
||||
products.addValue(findAdditionalProducts(defaultCandidateAdditions, pkg.GemPkg, p)...)
|
||||
products.removeByValue(findProductsToRemove(defaultCandidateRemovals, pkg.GemPkg, p)...)
|
||||
return products
|
||||
}
|
||||
|
||||
func rubyCandidateProductsFromAPK(m pkg.ApkMetadata) fieldCandidateSet {
|
||||
products := newFieldCandidateSet()
|
||||
|
||||
for _, p := range rubyPrefixes {
|
||||
if strings.HasPrefix(m.Package, p) {
|
||||
t := strings.ToLower(strings.TrimPrefix(m.Package, p))
|
||||
products.union(rubyCandidateProductsFromName(t))
|
||||
}
|
||||
|
||||
if m.OriginPackage != m.Package && strings.HasPrefix(m.OriginPackage, p) {
|
||||
t := strings.ToLower(strings.TrimPrefix(m.OriginPackage, p))
|
||||
products.union(rubyCandidateProductsFromName(t))
|
||||
}
|
||||
}
|
||||
|
||||
return products
|
||||
}
|
||||
|
||||
func candidateVendorsForAPK(p pkg.Package) fieldCandidateSet {
|
||||
metadata, ok := p.Metadata.(pkg.ApkMetadata)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
vendors := newFieldCandidateSet()
|
||||
vendors.union(pythonCandidateVendorsFromAPK(metadata))
|
||||
vendors.union(rubyCandidateVendorsFromAPK(metadata))
|
||||
|
||||
return vendors
|
||||
}
|
||||
|
||||
func candidateProductsForAPK(p pkg.Package) fieldCandidateSet {
|
||||
metadata, ok := p.Metadata.(pkg.ApkMetadata)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
products := newFieldCandidateSet()
|
||||
products.union(pythonCandidateProductsFromAPK(metadata))
|
||||
products.union(rubyCandidateProductsFromAPK(metadata))
|
||||
|
||||
return products
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package cpe
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func Test_candidateVendorsForAPK(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pkg pkg.Package
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "py3-cryptography Package",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "py3-cryptography",
|
||||
},
|
||||
},
|
||||
expected: []string{"python-cryptography_project", "cryptography", "cryptographyproject", "cryptography_project"},
|
||||
},
|
||||
{
|
||||
name: "py2-pypdf OriginPackage",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
OriginPackage: "py2-pypdf",
|
||||
},
|
||||
},
|
||||
expected: []string{"pypdf", "pypdfproject", "pypdf_project"},
|
||||
},
|
||||
{
|
||||
name: "ruby-armadillo Package",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-armadillo",
|
||||
},
|
||||
},
|
||||
expected: []string{"armadillo"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.ElementsMatch(t, test.expected, candidateVendorsForAPK(test.pkg).values(), "different vendors")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_candidateProductsForAPK(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pkg pkg.Package
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "py3-cryptography Package",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "py3-cryptography",
|
||||
},
|
||||
},
|
||||
expected: []string{"cryptography", "python-cryptography"},
|
||||
},
|
||||
{
|
||||
name: "py2-pypdf OriginPackage",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
OriginPackage: "py2-pypdf",
|
||||
},
|
||||
},
|
||||
expected: []string{"pypdf"},
|
||||
},
|
||||
{
|
||||
name: "ruby-armadillo Package",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-armadillo",
|
||||
},
|
||||
},
|
||||
expected: []string{"armadillo"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.ElementsMatch(t, test.expected, candidateProductsForAPK(test.pkg).values(), "different products")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -121,12 +121,89 @@ var defaultCandidateAdditions = buildCandidateLookup(
|
||||
candidateKey{PkgName: "yajl-ruby"},
|
||||
candidateAddition{AdditionalProducts: []string{"yajl-ruby_gem"}},
|
||||
},
|
||||
{
|
||||
pkg.GemPkg,
|
||||
candidateKey{PkgName: "cgi"},
|
||||
candidateAddition{AdditionalVendors: []string{"ruby-lang"}},
|
||||
},
|
||||
{
|
||||
pkg.GemPkg,
|
||||
candidateKey{PkgName: "date"},
|
||||
candidateAddition{AdditionalVendors: []string{"ruby-lang"}},
|
||||
},
|
||||
{
|
||||
pkg.GemPkg,
|
||||
candidateKey{PkgName: "openssl"},
|
||||
candidateAddition{AdditionalVendors: []string{"ruby-lang"}},
|
||||
},
|
||||
{
|
||||
pkg.GemPkg,
|
||||
candidateKey{PkgName: "rake"},
|
||||
candidateAddition{AdditionalVendors: []string{"ruby-lang"}},
|
||||
},
|
||||
{
|
||||
pkg.GemPkg,
|
||||
candidateKey{PkgName: "rdoc"},
|
||||
candidateAddition{AdditionalVendors: []string{"ruby-lang"}},
|
||||
},
|
||||
{
|
||||
pkg.GemPkg,
|
||||
candidateKey{PkgName: "rexml"},
|
||||
candidateAddition{AdditionalVendors: []string{"ruby-lang"}},
|
||||
},
|
||||
{
|
||||
pkg.GemPkg,
|
||||
candidateKey{PkgName: "trunk"},
|
||||
candidateAddition{AdditionalVendors: []string{"ruby-lang"}},
|
||||
},
|
||||
{
|
||||
pkg.GemPkg,
|
||||
candidateKey{PkgName: "webrick"},
|
||||
candidateAddition{AdditionalVendors: []string{"ruby-lang"}},
|
||||
},
|
||||
// Python packages
|
||||
{
|
||||
pkg.PythonPkg,
|
||||
candidateKey{PkgName: "python-rrdtool"},
|
||||
candidateAddition{AdditionalProducts: []string{"rrdtool"}},
|
||||
},
|
||||
{
|
||||
pkg.PythonPkg,
|
||||
candidateKey{PkgName: "cryptography"},
|
||||
candidateAddition{AdditionalProducts: []string{"python-cryptography"}, AdditionalVendors: []string{"python-cryptography_project"}},
|
||||
},
|
||||
{
|
||||
pkg.PythonPkg,
|
||||
candidateKey{PkgName: "pip"},
|
||||
candidateAddition{AdditionalVendors: []string{"pypa"}},
|
||||
},
|
||||
// Alpine packages
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "python3"},
|
||||
candidateAddition{AdditionalProducts: []string{"python"}, AdditionalVendors: []string{"python", "python_software_foundation"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "python"},
|
||||
candidateAddition{AdditionalVendors: []string{"python_software_foundation"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "nodejs"},
|
||||
candidateAddition{AdditionalProducts: []string{"node.js"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "nodejs-current"},
|
||||
candidateAddition{AdditionalProducts: []string{"node.js"}},
|
||||
},
|
||||
// Binary packages
|
||||
{
|
||||
pkg.BinaryPkg,
|
||||
candidateKey{PkgName: "node"},
|
||||
candidateAddition{AdditionalProducts: []string{"nodejs", "node.js"}},
|
||||
},
|
||||
})
|
||||
|
||||
var defaultCandidateRemovals = buildCandidateRemovalLookup(
|
||||
|
||||
@@ -109,6 +109,8 @@ func candidateVendors(p pkg.Package) []string {
|
||||
vendors.union(candidateVendorsForPython(p))
|
||||
case pkg.JavaMetadataType:
|
||||
vendors.union(candidateVendorsForJava(p))
|
||||
case pkg.ApkMetadataType:
|
||||
vendors.union(candidateVendorsForAPK(p))
|
||||
}
|
||||
|
||||
// try swapping hyphens for underscores, vice versa, and removing separators altogether
|
||||
@@ -156,6 +158,11 @@ func candidateProducts(p pkg.Package) []string {
|
||||
products.addValue(prod)
|
||||
}
|
||||
}
|
||||
|
||||
if p.MetadataType == pkg.ApkMetadataType {
|
||||
products.union(candidateProductsForAPK(p))
|
||||
}
|
||||
|
||||
// it is never OK to have candidates with these values ["" and "*"] (since CPEs will match any other value)
|
||||
products.removeByValue("")
|
||||
products.removeByValue("*")
|
||||
|
||||
@@ -100,6 +100,18 @@ func TestGeneratePackageCPEs(t *testing.T) {
|
||||
"cpe:2.3:a:william_goodman:name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:william_goodman:python-name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:william_goodman:python_name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:alex_goodman_project:python_name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:alex_goodman_project:name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:alex_goodman_project:python-name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:alex_goodmanproject:name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:alex_goodmanproject:python-name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:alex_goodmanproject:python_name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:william_goodman_project:name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:william_goodman_project:python-name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:william_goodman_project:python_name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:william_goodmanproject:name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:william_goodmanproject:python-name:3.2:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:william_goodmanproject:python_name:3.2:*:*:*:*:*:*:*",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
package cpe
|
||||
|
||||
import "github.com/anchore/syft/syft/pkg"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func additionalVendorsForPython(v string) (vendors []string) {
|
||||
if !strings.HasSuffix(v, "project") {
|
||||
vendors = append(vendors, fmt.Sprintf("%sproject", v), fmt.Sprintf("%s_project", v))
|
||||
}
|
||||
|
||||
return vendors
|
||||
}
|
||||
|
||||
func candidateVendorsForPython(p pkg.Package) fieldCandidateSet {
|
||||
metadata, ok := p.Metadata.(pkg.PythonPackageMetadata)
|
||||
@@ -11,18 +24,36 @@ func candidateVendorsForPython(p pkg.Package) fieldCandidateSet {
|
||||
vendors := newFieldCandidateSet()
|
||||
|
||||
if metadata.Author != "" {
|
||||
name := normalizePersonName(metadata.Author)
|
||||
vendors.add(fieldCandidate{
|
||||
value: normalizePersonName(metadata.Author),
|
||||
value: name,
|
||||
disallowSubSelections: true,
|
||||
disallowDelimiterVariations: true,
|
||||
})
|
||||
|
||||
for _, v := range additionalVendorsForPython(name) {
|
||||
vendors.add(fieldCandidate{
|
||||
value: v,
|
||||
disallowSubSelections: true,
|
||||
disallowDelimiterVariations: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if metadata.AuthorEmail != "" {
|
||||
name := normalizePersonName(stripEmailSuffix(metadata.AuthorEmail))
|
||||
vendors.add(fieldCandidate{
|
||||
value: normalizePersonName(stripEmailSuffix(metadata.AuthorEmail)),
|
||||
value: name,
|
||||
disallowSubSelections: true,
|
||||
})
|
||||
|
||||
for _, v := range additionalVendorsForPython(name) {
|
||||
vendors.add(fieldCandidate{
|
||||
value: v,
|
||||
disallowSubSelections: true,
|
||||
disallowDelimiterVariations: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return vendors
|
||||
|
||||
@@ -13,8 +13,8 @@ type Location struct {
|
||||
Coordinates `cyclonedx:""` // Empty string here means there is no intermediate property name, e.g. syft:locations:0:path without "coordinates"
|
||||
// note: it is IMPORTANT to ignore anything but the coordinates for a Location when considering the ID (hash value)
|
||||
// since the coordinates are the minimally correct ID for a location (symlinks should not come into play)
|
||||
VirtualPath string `hash:"ignore"` // The path to the file which may or may not have hardlinks / symlinks
|
||||
ref file.Reference `hash:"ignore"` // The file reference relative to the stereoscope.FileCatalog that has more information about this location.
|
||||
VirtualPath string `hash:"ignore" json:"virtualPath,omitempty"` // The path to the file which may or may not have hardlinks / symlinks
|
||||
ref file.Reference `hash:"ignore"` // The file reference relative to the stereoscope.FileCatalog that has more information about this location.
|
||||
}
|
||||
|
||||
// NewLocation creates a new Location representing a path without denoting a filesystem or FileCatalog reference.
|
||||
|
||||
@@ -10,14 +10,9 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConvertCmdFlags(t *testing.T) {
|
||||
func TestAllFormatsConvertable(t *testing.T) {
|
||||
assertions := []traitAssertion{
|
||||
func(tb testing.TB, stdout, _ string, _ int) {
|
||||
tb.Helper()
|
||||
if len(stdout) < 1000 {
|
||||
tb.Errorf("there may not be any report output (len=%d)", len(stdout))
|
||||
}
|
||||
},
|
||||
assertStdoutLengthGreaterThan(1000),
|
||||
assertSuccessfulReturnCode,
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConvertCmd(t *testing.T) {
|
||||
assertions := []traitAssertion{
|
||||
assertInOutput("PackageName: musl-utils"),
|
||||
assertSuccessfulReturnCode,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
from string
|
||||
to string
|
||||
}{
|
||||
{from: "syft-json", to: "spdx-tag-value"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("from %s to %s", test.from, test.to), func(t *testing.T) {
|
||||
sbomArgs := []string{"dir:./test-fixtures/image-pkg-coverage", "-o", test.from}
|
||||
cmd, stdout, stderr := runSyft(t, nil, sbomArgs...)
|
||||
if cmd.ProcessState.ExitCode() != 0 {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
t.Fatalf("failure executing syft creating an sbom")
|
||||
return
|
||||
}
|
||||
|
||||
convertArgs := []string{"convert", "-", "-o", test.to}
|
||||
cmd = getSyftCommand(t, convertArgs...)
|
||||
|
||||
cmd.Stdin = strings.NewReader(stdout)
|
||||
stdout, stderr = runCommandObj(t, cmd, nil, false)
|
||||
|
||||
for _, traitFn := range assertions {
|
||||
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+43
-1
@@ -177,7 +177,7 @@ func runSyftCommand(t testing.TB, env map[string]string, expectError bool, args
|
||||
t.Errorf("STDOUT: %s", stdout)
|
||||
t.Errorf("STDERR: %s", stderr)
|
||||
|
||||
// this probably indicates a timeout
|
||||
// this probably indicates a timeout... lets run it again with more verbosity to help debug issues
|
||||
args = append(args, "-vv")
|
||||
cmd = getSyftCommand(t, args...)
|
||||
|
||||
@@ -194,6 +194,48 @@ func runSyftCommand(t testing.TB, env map[string]string, expectError bool, args
|
||||
return cmd, stdout, stderr
|
||||
}
|
||||
|
||||
func runCommandObj(t testing.TB, cmd *exec.Cmd, env map[string]string, expectError bool) (string, string) {
|
||||
cancel := make(chan bool, 1)
|
||||
defer func() {
|
||||
cancel <- true
|
||||
}()
|
||||
|
||||
if env == nil {
|
||||
env = make(map[string]string)
|
||||
}
|
||||
|
||||
// we should not have tests reaching out for app update checks
|
||||
env["SYFT_CHECK_FOR_APP_UPDATE"] = "false"
|
||||
|
||||
timeout := func() {
|
||||
select {
|
||||
case <-cancel:
|
||||
return
|
||||
case <-time.After(60 * time.Second):
|
||||
}
|
||||
|
||||
if cmd != nil && cmd.Process != nil {
|
||||
// get a stack trace printed
|
||||
err := cmd.Process.Signal(syscall.SIGABRT)
|
||||
if err != nil {
|
||||
t.Errorf("error aborting: %+v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
go timeout()
|
||||
|
||||
stdout, stderr, err := runCommand(cmd, env)
|
||||
|
||||
if !expectError && err != nil && stdout == "" {
|
||||
t.Errorf("error running syft: %+v", err)
|
||||
t.Errorf("STDOUT: %s", stdout)
|
||||
t.Errorf("STDERR: %s", stderr)
|
||||
}
|
||||
|
||||
return stdout, stderr
|
||||
}
|
||||
|
||||
func runCosign(t testing.TB, env map[string]string, args ...string) (*exec.Cmd, string, string) {
|
||||
cmd := getCommand(t, ".tmp/cosign", args...)
|
||||
if env == nil {
|
||||
|
||||
Reference in New Issue
Block a user