mirror of
https://github.com/anchore/syft.git
synced 2026-10-23 11:32:21 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f90d03718 | ||
|
|
e2ebc9769f | ||
|
|
2e6e3b0c74 | ||
|
|
24584a4d27 | ||
|
|
8e1205f7ab | ||
|
|
e92b0fa629 | ||
|
|
bcc0751a40 | ||
|
|
f1169e56fc | ||
|
|
98e737fc27 | ||
|
|
ff34594284 | ||
|
|
9e953b1da3 | ||
|
|
fbda21f4f4 | ||
|
|
d23b4d4cbd | ||
|
|
f3acff81f3 | ||
|
|
fa0a9fe8f9 | ||
|
|
284bae9d5f | ||
|
|
3ee1af0dc6 | ||
|
|
0c5f03235e | ||
|
|
5e8aa4da5e | ||
|
|
abfec62219 | ||
|
|
0c05855131 | ||
|
|
1150772d06 | ||
|
|
669fee84d5 | ||
|
|
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**:
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
@@ -156,7 +156,7 @@ jobs:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
|
||||
if: ${{ success() }}
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: dist/**/*
|
||||
|
||||
@@ -4,14 +4,14 @@ TEMP_DIR := ./.tmp
|
||||
# Command templates #################################
|
||||
LINT_CMD := $(TEMP_DIR)/golangci-lint run --tests=false
|
||||
GOIMPORTS_CMD := $(TEMP_DIR)/gosimports -local github.com/anchore
|
||||
RELEASE_CMD := $(TEMP_DIR)/goreleaser release --rm-dist
|
||||
RELEASE_CMD := $(TEMP_DIR)/goreleaser release --clean
|
||||
SNAPSHOT_CMD := $(RELEASE_CMD) --skip-publish --skip-sign --snapshot
|
||||
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
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/wagoodman/go-partybus"
|
||||
"github.com/wagoodman/go-progress"
|
||||
@@ -130,7 +131,21 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
|
||||
return
|
||||
}
|
||||
|
||||
args := []string{"attest", si.UserInput, "--predicate", f.Name()}
|
||||
// Select Cosign predicate type based on defined output type
|
||||
// As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go
|
||||
var predicateType string
|
||||
switch strings.ToLower(o) {
|
||||
case "cyclonedx-json":
|
||||
predicateType = "cyclonedx"
|
||||
case "spdx-tag-value":
|
||||
predicateType = "spdx"
|
||||
case "spdx-json":
|
||||
predicateType = "spdxjson"
|
||||
default:
|
||||
predicateType = "custom"
|
||||
}
|
||||
|
||||
args := []string{"attest", si.UserInput, "--predicate", f.Name(), "--type", predicateType}
|
||||
if app.Attest.Key != "" {
|
||||
args = append(args, "--key", app.Attest.Key)
|
||||
}
|
||||
@@ -152,7 +167,7 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
b := &busWriter{r: r, w: w, mon: &progress.Manual{N: -1}}
|
||||
b := &busWriter{r: r, w: w, mon: progress.NewManual(-1)}
|
||||
execCmd.Stdout = b
|
||||
execCmd.Stderr = b
|
||||
defer b.mon.SetCompleted()
|
||||
@@ -160,7 +175,7 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
|
||||
// attest the SBOM
|
||||
err = execCmd.Run()
|
||||
if err != nil {
|
||||
b.mon.Err = err
|
||||
b.mon.SetError(err)
|
||||
errs <- fmt.Errorf("unable to attest SBOM: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -46,14 +46,11 @@ func generateCatalogPackagesTask(app *config.Application) (Task, error) {
|
||||
|
||||
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
|
||||
packageCatalog, relationships, theDistro, err := syft.CatalogPackages(src, app.ToCatalogerConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results.PackageCatalog = packageCatalog
|
||||
results.LinuxDistribution = theDistro
|
||||
|
||||
return relationships, nil
|
||||
return relationships, err
|
||||
}
|
||||
|
||||
return task, nil
|
||||
|
||||
@@ -32,18 +32,18 @@ require (
|
||||
github.com/sergi/go-diff v1.3.1
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spdx/tools-golang v0.5.0-rc1
|
||||
github.com/spf13/afero v1.9.3
|
||||
github.com/spf13/afero v1.9.4
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.15.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/vifraa/gopom v0.2.1
|
||||
github.com/wagoodman/go-partybus v0.0.0-20210627031916-db1f5573bbc5
|
||||
github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240
|
||||
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5
|
||||
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-20230301191755-abfb374a1122
|
||||
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-20230301191755-abfb374a1122 h1:Oe2PE8zNbJH4nGZoCIC/VZBgpr62BInLnUqIMZICUOk=
|
||||
github.com/anchore/stereoscope v0.0.0-20230301191755-abfb374a1122/go.mod h1:IihP/SUVHP94PBwIP2bepOB/c0MVadcII7lxo13Ijzs=
|
||||
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=
|
||||
@@ -519,8 +519,8 @@ github.com/spdx/tools-golang v0.5.0-rc1 h1:ooCSe48QatlidqEFd+nSI308tyeNTR6NJvauU
|
||||
github.com/spdx/tools-golang v0.5.0-rc1/go.mod h1:LI6onw172PdO57Ob/hgnLDD4Y2PMnroeNT3wO/2WJJI=
|
||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
|
||||
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/afero v1.9.4 h1:Sd43wM1IWz/s1aVXdOBkjJvuP8UdyqioeE4AmM0QsBs=
|
||||
github.com/spf13/afero v1.9.4/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
@@ -550,8 +550,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
@@ -575,8 +576,8 @@ github.com/vifraa/gopom v0.2.1 h1:MYVMAMyiGzXPPy10EwojzKIL670kl5Zbae+o3fFvQEM=
|
||||
github.com/vifraa/gopom v0.2.1/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
|
||||
github.com/wagoodman/go-partybus v0.0.0-20210627031916-db1f5573bbc5 h1:phTLPgMRDYTizrBSKsNSOa2zthoC2KsJsaY/8sg3rD8=
|
||||
github.com/wagoodman/go-partybus v0.0.0-20210627031916-db1f5573bbc5/go.mod h1:JPirS5jde/CF5qIjcK4WX+eQmKXdPc6vcZkJ/P0hfPw=
|
||||
github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240 h1:r6BlIP7CVZtMlxUQhT40h1IE1TzEgKVqwmsVGuscvdk=
|
||||
github.com/wagoodman/go-progress v0.0.0-20200731105512-1020f39e6240/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
|
||||
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 h1:lwgTsTy18nYqASnH58qyfRW/ldj7Gt2zzBvgYPzdA4s=
|
||||
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
|
||||
github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb h1:Yz6VVOcLuWLAHYlJzTw7JKnWxdV/WXpug2X0quEzRnY=
|
||||
github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb/go.mod h1:nDi3BAC5nEbVbg+WSJDHLbjHv0ZToq8nMPA97XMxF3E=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -716,8 +717,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.1"
|
||||
)
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -51,10 +51,10 @@ func (i *DigestsCataloger) Catalog(resolver source.FileResolver) (map[source.Coo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prog.N++
|
||||
prog.Increment()
|
||||
results[location.Coordinates] = result
|
||||
}
|
||||
log.Debugf("file digests cataloger processed %d files", prog.N)
|
||||
log.Debugf("file digests cataloger processed %d files", prog.Current())
|
||||
prog.SetCompleted()
|
||||
return results, nil
|
||||
}
|
||||
@@ -123,9 +123,7 @@ func CleanDigestAlgorithmName(name string) string {
|
||||
|
||||
func digestsCatalogingProgress(locations int64) (*progress.Stage, *progress.Manual) {
|
||||
stage := &progress.Stage{}
|
||||
prog := &progress.Manual{
|
||||
Total: locations,
|
||||
}
|
||||
prog := progress.NewManual(locations)
|
||||
|
||||
bus.Publish(partybus.Event{
|
||||
Type: event.FileDigestsCatalogerStarted,
|
||||
|
||||
@@ -32,18 +32,16 @@ func (i *MetadataCataloger) Catalog(resolver source.FileResolver) (map[source.Co
|
||||
}
|
||||
|
||||
results[location.Coordinates] = metadata
|
||||
prog.N++
|
||||
prog.Increment()
|
||||
}
|
||||
log.Debugf("file metadata cataloger processed %d files", prog.N)
|
||||
log.Debugf("file metadata cataloger processed %d files", prog.Current())
|
||||
prog.SetCompleted()
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func metadataCatalogingProgress(locations int64) (*progress.Stage, *progress.Manual) {
|
||||
stage := &progress.Stage{}
|
||||
prog := &progress.Manual{
|
||||
Total: locations,
|
||||
}
|
||||
prog := progress.NewManual(locations)
|
||||
|
||||
bus.Publish(partybus.Event{
|
||||
Type: event.FileMetadataCatalogerStarted,
|
||||
|
||||
@@ -55,12 +55,12 @@ func (i *SecretsCataloger) Catalog(resolver source.FileResolver) (map[source.Coo
|
||||
return nil, err
|
||||
}
|
||||
if len(result) > 0 {
|
||||
secretsDiscovered.N += int64(len(result))
|
||||
secretsDiscovered.Add(int64(len(result)))
|
||||
results[location.Coordinates] = result
|
||||
}
|
||||
prog.N++
|
||||
prog.Increment()
|
||||
}
|
||||
log.Debugf("secrets cataloger discovered %d secrets", secretsDiscovered.N)
|
||||
log.Debugf("secrets cataloger discovered %d secrets", secretsDiscovered.Current())
|
||||
prog.SetCompleted()
|
||||
return results, nil
|
||||
}
|
||||
@@ -139,9 +139,7 @@ type SecretsMonitor struct {
|
||||
func secretsCatalogingProgress(locations int64) (*progress.Stage, *progress.Manual, *progress.Manual) {
|
||||
stage := &progress.Stage{}
|
||||
secretsDiscovered := &progress.Manual{}
|
||||
prog := &progress.Manual{
|
||||
Total: locations,
|
||||
}
|
||||
prog := progress.NewManual(locations)
|
||||
|
||||
bus.Publish(partybus.Event{
|
||||
Type: event.SecretsCatalogerStarted,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -24,6 +24,7 @@ type LinuxRelease struct {
|
||||
BugReportURL string `json:"bugReportURL,omitempty"`
|
||||
PrivacyPolicyURL string `json:"privacyPolicyURL,omitempty"`
|
||||
CPEName string `json:"cpeName,omitempty"`
|
||||
SupportEnd string `json:"supportEnd,omitempty"`
|
||||
}
|
||||
|
||||
func (s *IDLikes) UnmarshalJSON(data []byte) error {
|
||||
|
||||
@@ -62,6 +62,7 @@ func toLinuxReleaser(d *linux.Release) model.LinuxRelease {
|
||||
BugReportURL: d.BugReportURL,
|
||||
PrivacyPolicyURL: d.PrivacyPolicyURL,
|
||||
CPEName: d.CPEName,
|
||||
SupportEnd: d.SupportEnd,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -53,6 +53,7 @@ func toSyftLinuxRelease(d model.LinuxRelease) *linux.Release {
|
||||
BugReportURL: d.BugReportURL,
|
||||
PrivacyPolicyURL: d.PrivacyPolicyURL,
|
||||
CPEName: d.CPEName,
|
||||
SupportEnd: d.SupportEnd,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-4
@@ -70,13 +70,10 @@ func CatalogPackages(src *source.Source, cfg cataloger.Config) (*pkg.Catalog, []
|
||||
}
|
||||
|
||||
catalog, relationships, err := cataloger.Catalog(resolver, release, cfg.Parallelism, catalogers...)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
relationships = append(relationships, newSourceRelationshipsFromCatalog(src, catalog)...)
|
||||
|
||||
return catalog, relationships, release, nil
|
||||
return catalog, relationships, release, err
|
||||
}
|
||||
|
||||
func newSourceRelationshipsFromCatalog(src *source.Source, c *pkg.Catalog) []artifact.Relationship {
|
||||
|
||||
@@ -125,6 +125,7 @@ func parseOsRelease(contents string) (*Release, error) {
|
||||
BugReportURL: values["BUG_REPORT_URL"],
|
||||
PrivacyPolicyURL: values["PRIVACY_POLICY_URL"],
|
||||
CPEName: values["CPE_NAME"],
|
||||
SupportEnd: values["SUPPORT_END"],
|
||||
}
|
||||
|
||||
// don't allow for empty contents to result in a Release object being created
|
||||
|
||||
@@ -89,19 +89,20 @@ func TestIdentifyRelease(t *testing.T) {
|
||||
{
|
||||
fixture: "test-fixtures/os/fedora",
|
||||
release: &Release{
|
||||
PrettyName: "Fedora 31 (Container Image)",
|
||||
Name: "Fedora",
|
||||
PrettyName: "Fedora Linux 36 (Container Image)",
|
||||
Name: "Fedora Linux",
|
||||
ID: "fedora",
|
||||
IDLike: nil,
|
||||
Version: "31 (Container Image)",
|
||||
VersionID: "31",
|
||||
Version: "36 (Container Image)",
|
||||
VersionID: "36",
|
||||
Variant: "Container Image",
|
||||
VariantID: "container",
|
||||
HomeURL: "https://fedoraproject.org/",
|
||||
SupportURL: "https://fedoraproject.org/wiki/Communicating_and_getting_help",
|
||||
SupportURL: "https://ask.fedoraproject.org/",
|
||||
BugReportURL: "https://bugzilla.redhat.com/",
|
||||
PrivacyPolicyURL: "https://fedoraproject.org/wiki/Legal:PrivacyPolicy",
|
||||
CPEName: "cpe:/o:fedoraproject:fedora:31",
|
||||
CPEName: "cpe:/o:fedoraproject:fedora:36",
|
||||
SupportEnd: "2023-05-16",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ type Release struct {
|
||||
BugReportURL string
|
||||
PrivacyPolicyURL string
|
||||
CPEName string // A CPE name for the operating system, in URI binding syntax
|
||||
SupportEnd string // The date at which support for this version of the OS ends.
|
||||
}
|
||||
|
||||
func (r *Release) String() string {
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
NAME=Fedora
|
||||
VERSION="31 (Container Image)"
|
||||
NAME="Fedora Linux"
|
||||
VERSION="36 (Container Image)"
|
||||
ID=fedora
|
||||
VERSION_ID=31
|
||||
VERSION_ID=36
|
||||
VERSION_CODENAME=""
|
||||
PLATFORM_ID="platform:f31"
|
||||
PRETTY_NAME="Fedora 31 (Container Image)"
|
||||
ANSI_COLOR="0;34"
|
||||
PLATFORM_ID="platform:f36"
|
||||
PRETTY_NAME="Fedora Linux 36 (Container Image)"
|
||||
ANSI_COLOR="0;38;2;60;110;180"
|
||||
LOGO=fedora-logo-icon
|
||||
CPE_NAME="cpe:/o:fedoraproject:fedora:31"
|
||||
CPE_NAME="cpe:/o:fedoraproject:fedora:36"
|
||||
HOME_URL="https://fedoraproject.org/"
|
||||
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f31/system-administrators-guide/"
|
||||
SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help"
|
||||
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f36/system-administrators-guide/"
|
||||
SUPPORT_URL="https://ask.fedoraproject.org/"
|
||||
BUG_REPORT_URL="https://bugzilla.redhat.com/"
|
||||
REDHAT_BUGZILLA_PRODUCT="Fedora"
|
||||
REDHAT_BUGZILLA_PRODUCT_VERSION=31
|
||||
REDHAT_BUGZILLA_PRODUCT_VERSION=36
|
||||
REDHAT_SUPPORT_PRODUCT="Fedora"
|
||||
REDHAT_SUPPORT_PRODUCT_VERSION=31
|
||||
REDHAT_SUPPORT_PRODUCT_VERSION=36
|
||||
PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"
|
||||
SUPPORT_END=2023-05-16
|
||||
VARIANT="Container Image"
|
||||
VARIANT_ID=container
|
||||
|
||||
@@ -87,6 +87,8 @@ func (a *spaceDelimitedStringSlice) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
*a = s
|
||||
return nil
|
||||
case nil:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("invalid type for string array: %T", obj)
|
||||
}
|
||||
|
||||
@@ -98,6 +98,15 @@ func TestApkMetadata_UnmarshalJSON(t *testing.T) {
|
||||
Files: []ApkFileRecord{{Path: "/usr"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "null pullDependencies",
|
||||
input: `{
|
||||
"pullDependencies": null
|
||||
}`,
|
||||
want: ApkMetadata{
|
||||
Dependencies: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ func newPackage(d pkg.ApkMetadata, release *linux.Release, locations ...source.L
|
||||
|
||||
// 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" {
|
||||
// note: there is no namespace variation (like with debian ID_LIKE for ubuntu ID, for example)
|
||||
if distro == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -37,13 +36,13 @@ func packageURL(m pkg.ApkMetadata, distro *linux.Release) string {
|
||||
pkg.PURLQualifierArch: m.Architecture,
|
||||
}
|
||||
|
||||
if m.OriginPackage != "" {
|
||||
if m.OriginPackage != m.Package {
|
||||
qualifiers[pkg.PURLQualifierUpstream] = m.OriginPackage
|
||||
}
|
||||
|
||||
return packageurl.NewPackageURL(
|
||||
packageurl.TypeAlpine,
|
||||
"alpine",
|
||||
strings.ToLower(distro.ID),
|
||||
m.Package,
|
||||
m.Version,
|
||||
pkg.PURLQualifiers(
|
||||
|
||||
@@ -20,7 +20,7 @@ func Test_PackageURL(t *testing.T) {
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "bad distro",
|
||||
name: "non-alpine distro",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "p",
|
||||
Version: "v",
|
||||
@@ -30,7 +30,7 @@ func Test_PackageURL(t *testing.T) {
|
||||
ID: "something else",
|
||||
VersionID: "3.4.6",
|
||||
},
|
||||
expected: "",
|
||||
expected: "pkg:apk/something%20else/p@v?arch=a&distro=something%20else-3.4.6",
|
||||
},
|
||||
{
|
||||
name: "gocase",
|
||||
@@ -96,6 +96,19 @@ func Test_PackageURL(t *testing.T) {
|
||||
},
|
||||
expected: "pkg:apk/alpine/p@v?arch=a&upstream=origin&distro=alpine-3.4.6",
|
||||
},
|
||||
{
|
||||
name: "wolfi distro",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "p",
|
||||
Version: "v",
|
||||
Architecture: "a",
|
||||
},
|
||||
distro: linux.Release{
|
||||
ID: "wolfi",
|
||||
VersionID: "20221230",
|
||||
},
|
||||
expected: "pkg:apk/wolfi/p@v?arch=a&distro=wolfi-20221230",
|
||||
},
|
||||
}
|
||||
|
||||
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,80 @@ 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"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-haproxy-1.5.14",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/haproxy-1.5.14",
|
||||
expected: pkg.Package{
|
||||
Name: "haproxy",
|
||||
Version: "1.5.14",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/haproxy@1.5.14",
|
||||
Locations: locations("haproxy"),
|
||||
Metadata: metadata("haproxy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-haproxy-1.8.22",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/haproxy-1.8.22",
|
||||
expected: pkg.Package{
|
||||
Name: "haproxy",
|
||||
Version: "1.8.22",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/haproxy@1.8.22",
|
||||
Locations: locations("haproxy"),
|
||||
Metadata: metadata("haproxy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-haproxy-2.7.3",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/haproxy-2.7.3",
|
||||
expected: pkg.Package{
|
||||
Name: "haproxy",
|
||||
Version: "2.7.3",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/haproxy@2.7.3",
|
||||
Locations: locations("haproxy"),
|
||||
Metadata: metadata("haproxy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -181,10 +251,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 +263,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 +275,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 +287,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 +299,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 +387,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 +399,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 +411,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 +423,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 +435,44 @@ 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"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-rust-1.50.0-macos",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/rust-1.50.0",
|
||||
expected: pkg.Package{
|
||||
Name: "rust",
|
||||
Version: "1.50.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/rust@1.50.0",
|
||||
Locations: locations("lib/rustlib/aarch64-apple-darwin/lib/libstd-f6f9eec1635e636a.dylib"),
|
||||
Metadata: metadata("rust-standard-library-macos"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-rust-1.67.1-macos",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/rust-1.67.1/toolchains/stable-aarch64-apple-darwin",
|
||||
expected: pkg.Package{
|
||||
Name: "rust",
|
||||
Version: "1.67.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/rust@1.67.1",
|
||||
Locations: locations("lib/libstd-16f2b65e77054c42.dylib"),
|
||||
Metadata: metadata("rust-standard-library-macos"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive-rust-1.67.1-linux",
|
||||
fixtureDir: "test-fixtures/classifiers/positive/rust-1.67.1/toolchains/stable-x86_64-unknown-linux-musl",
|
||||
expected: pkg.Package{
|
||||
Name: "rust",
|
||||
Version: "1.67.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/rust@1.67.1",
|
||||
Locations: locations("lib/libstd-86aefecbddda356d.so"),
|
||||
Metadata: metadata("rust-standard-library-linux"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -424,18 +490,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 +520,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 +540,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 +572,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 +670,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 +701,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()
|
||||
|
||||
|
||||
@@ -51,6 +51,21 @@ type classifier struct {
|
||||
// evidenceMatcher is a function called to catalog Packages that match some sort of evidence
|
||||
type evidenceMatcher func(classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error)
|
||||
|
||||
func evidenceMatchers(matchers ...evidenceMatcher) evidenceMatcher {
|
||||
return func(classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) {
|
||||
for _, matcher := range matchers {
|
||||
match, err := matcher(classifier, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if match != nil {
|
||||
return match, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func fileNameTemplateVersionMatcher(fileNamePattern string, contentTemplate string) evidenceMatcher {
|
||||
pat := regexp.MustCompile(fileNamePattern)
|
||||
return func(classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) {
|
||||
@@ -131,9 +146,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,26 @@ var defaultClassifiers = []classifier{
|
||||
Package: "busybox",
|
||||
CPEs: singleCPE("cpe:2.3:a:busybox:busybox:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
Class: "haproxy-binary",
|
||||
FileGlob: "**/haproxy",
|
||||
EvidenceMatcher: evidenceMatchers(
|
||||
fileContentsVersionMatcher(`(?m)HA-Proxy version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
fileContentsVersionMatcher(`(?m)(?P<version>[0-9]+\.[0-9]+\.[0-9]+)-[0-9a-zA-Z]{7}.+HAProxy version`),
|
||||
),
|
||||
Package: "haproxy",
|
||||
PURL: mustPURL("pkg:generic/haproxy@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:haproxy:haproxy:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
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*",
|
||||
@@ -187,4 +207,24 @@ var defaultClassifiers = []classifier{
|
||||
Package: "postgresql",
|
||||
PURL: mustPURL("pkg:generic/postgresql@version"),
|
||||
},
|
||||
{
|
||||
Class: "rust-standard-library-linux",
|
||||
FileGlob: "**/libstd-????????????????.so",
|
||||
EvidenceMatcher: fileContentsVersionMatcher(
|
||||
// clang LLVM (rustc version 1.48.0 (7eac88abb 2020-11-16))
|
||||
`(?m)(\x00)clang LLVM \(rustc version (?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)) \(\w+ \d{4}\-\d{2}\-\d{2}\)`),
|
||||
Package: "rust",
|
||||
PURL: mustPURL("pkg:generic/rust@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:rust-lang:rust:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
{
|
||||
Class: "rust-standard-library-macos",
|
||||
FileGlob: "**/libstd-????????????????.dylib",
|
||||
EvidenceMatcher: fileContentsVersionMatcher(
|
||||
// c 1.48.0 (7eac88abb 2020-11-16)
|
||||
`(?m)c (?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)) \(\w+ \d{4}\-\d{2}\-\d{2}\)`),
|
||||
Package: "rust",
|
||||
PURL: mustPURL("pkg:generic/rust@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:rust-lang:rust:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
!libpython3.7.so
|
||||
!libphp.so
|
||||
!*.so
|
||||
!VERSION
|
||||
@@ -0,0 +1 @@
|
||||
HA-Proxy version 1.5.14 2015/07/02Copyright 2000-2015 Willy Tarreau <willy@haproxy.org>
|
||||
Binary file not shown.
Binary file not shown.
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
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -3,6 +3,7 @@ package cataloger
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
@@ -49,8 +50,15 @@ func newMonitor() (*progress.Manual, *progress.Manual) {
|
||||
return &filesProcessed, &packagesDiscovered
|
||||
}
|
||||
|
||||
func runCataloger(cataloger pkg.Cataloger, resolver source.FileResolver) (*catalogResult, error) {
|
||||
catalogerResult := new(catalogResult)
|
||||
func runCataloger(cataloger pkg.Cataloger, resolver source.FileResolver) (catalogerResult *catalogResult, err error) {
|
||||
// handle individual cataloger panics
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("%v at:\n%s", e, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
catalogerResult = new(catalogResult)
|
||||
|
||||
// find packages from the underlying raw data
|
||||
log.WithFields("cataloger", cataloger.Name()).Trace("cataloging started")
|
||||
@@ -88,7 +96,7 @@ func runCataloger(cataloger pkg.Cataloger, resolver source.FileResolver) (*catal
|
||||
}
|
||||
catalogerResult.Relationships = append(catalogerResult.Relationships, relationships...)
|
||||
log.WithFields("cataloger", cataloger.Name()).Trace("cataloging complete")
|
||||
return catalogerResult, nil
|
||||
return catalogerResult, err
|
||||
}
|
||||
|
||||
// Catalog a given source (container image or filesystem) with the given catalogers, returning all discovered packages.
|
||||
@@ -100,7 +108,11 @@ func runCataloger(cataloger pkg.Cataloger, resolver source.FileResolver) (*catal
|
||||
func Catalog(resolver source.FileResolver, release *linux.Release, parallelism int, catalogers ...pkg.Cataloger) (*pkg.Catalog, []artifact.Relationship, error) {
|
||||
catalog := pkg.NewCatalog()
|
||||
var allRelationships []artifact.Relationship
|
||||
|
||||
filesProcessed, packagesDiscovered := newMonitor()
|
||||
defer filesProcessed.SetCompleted()
|
||||
defer packagesDiscovered.SetCompleted()
|
||||
|
||||
// perform analysis, accumulating errors for each failed analysis
|
||||
var errs error
|
||||
|
||||
@@ -139,7 +151,7 @@ func Catalog(resolver source.FileResolver, release *linux.Release, parallelism i
|
||||
// dynamically show updated discovered package status
|
||||
go func() {
|
||||
for discovered := range discoveredPackages {
|
||||
packagesDiscovered.N += discovered
|
||||
packagesDiscovered.Add(discovered)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -158,7 +170,6 @@ func Catalog(resolver source.FileResolver, release *linux.Release, parallelism i
|
||||
for result := range results {
|
||||
if result.Error != nil {
|
||||
errs = multierror.Append(errs, result.Error)
|
||||
continue
|
||||
}
|
||||
for _, p := range result.Packages {
|
||||
catalog.Add(p)
|
||||
@@ -168,14 +179,7 @@ func Catalog(resolver source.FileResolver, release *linux.Release, parallelism i
|
||||
|
||||
allRelationships = append(allRelationships, pkg.NewRelationships(catalog)...)
|
||||
|
||||
if errs != nil {
|
||||
return nil, nil, errs
|
||||
}
|
||||
|
||||
filesProcessed.SetCompleted()
|
||||
packagesDiscovered.SetCompleted()
|
||||
|
||||
return catalog, allRelationships, nil
|
||||
return catalog, allRelationships, errs
|
||||
}
|
||||
|
||||
func packageFileOwnershipRelationships(p pkg.Package, resolver source.FilePathResolver) ([]artifact.Relationship, error) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package cataloger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func Test_CatalogPanicHandling(t *testing.T) {
|
||||
catalog, relationships, err := Catalog(
|
||||
source.NewMockResolverForPaths(),
|
||||
&linux.Release{},
|
||||
1,
|
||||
panickingCataloger{},
|
||||
returningCataloger{},
|
||||
)
|
||||
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "catalog_test.go")
|
||||
require.Len(t, catalog.Sorted(), 2)
|
||||
require.Len(t, relationships, 1)
|
||||
}
|
||||
|
||||
type panickingCataloger struct{}
|
||||
|
||||
func (p panickingCataloger) Name() string {
|
||||
return "panicking-cataloger"
|
||||
}
|
||||
|
||||
func (p panickingCataloger) Catalog(_ source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
panic("something bad happened")
|
||||
}
|
||||
|
||||
var _ pkg.Cataloger = (*panickingCataloger)(nil)
|
||||
|
||||
type returningCataloger struct{}
|
||||
|
||||
func (p returningCataloger) Name() string {
|
||||
return "returning-cataloger"
|
||||
}
|
||||
|
||||
func (p returningCataloger) Catalog(_ source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
pkg1 := pkg.Package{
|
||||
Name: "package-1",
|
||||
Version: "1.0",
|
||||
}
|
||||
pkg1.SetID()
|
||||
pkg2 := pkg.Package{
|
||||
Name: "package-2",
|
||||
Version: "2.0",
|
||||
}
|
||||
pkg2.SetID()
|
||||
return []pkg.Package{pkg1, pkg2}, []artifact.Relationship{
|
||||
{
|
||||
From: pkg1,
|
||||
To: pkg2,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ pkg.Cataloger = (*returningCataloger)(nil)
|
||||
@@ -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,131 @@
|
||||
package cpe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
var (
|
||||
prefixesToPackageType = map[string]pkg.Type{
|
||||
"py-": pkg.PythonPkg,
|
||||
"ruby-": pkg.GemPkg,
|
||||
}
|
||||
streamVersionPkgNamePattern = regexp.MustCompile(`^(?P<stream>[a-zA-Z][\w-]*?)(?P<streamVersion>\-?\d[\d\.]*?)($|-(?P<subPackage>[a-zA-Z][\w-]*?)?)$`)
|
||||
)
|
||||
|
||||
type upstreamCandidate struct {
|
||||
Name string
|
||||
Type pkg.Type
|
||||
}
|
||||
|
||||
func upstreamCandidates(m pkg.ApkMetadata) (candidates []upstreamCandidate) {
|
||||
// Do not consider OriginPackage variations when generating CPE candidates for the child package
|
||||
// because doing so will result in false positives when matching to vulnerabilities in Grype since
|
||||
// it won't know to lookup apk fix entries using the OriginPackage name.
|
||||
|
||||
name := m.Package
|
||||
groups := internal.MatchNamedCaptureGroups(streamVersionPkgNamePattern, m.Package)
|
||||
stream, ok := groups["stream"]
|
||||
|
||||
if ok && stream != "" {
|
||||
sub, ok := groups["subPackage"]
|
||||
|
||||
if ok && sub != "" {
|
||||
name = fmt.Sprintf("%s-%s", stream, sub)
|
||||
} else {
|
||||
name = stream
|
||||
}
|
||||
}
|
||||
|
||||
for prefix, typ := range prefixesToPackageType {
|
||||
if strings.HasPrefix(name, prefix) {
|
||||
t := strings.TrimPrefix(name, prefix)
|
||||
if t != "" {
|
||||
candidates = append(candidates, upstreamCandidate{Name: t, Type: typ})
|
||||
return candidates
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
candidates = append(candidates, upstreamCandidate{Name: name, Type: pkg.UnknownPkg})
|
||||
return candidates
|
||||
}
|
||||
|
||||
return candidates
|
||||
}
|
||||
|
||||
func candidateVendorsForAPK(p pkg.Package) fieldCandidateSet {
|
||||
metadata, ok := p.Metadata.(pkg.ApkMetadata)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
vendors := newFieldCandidateSet()
|
||||
candidates := upstreamCandidates(metadata)
|
||||
|
||||
for _, c := range candidates {
|
||||
switch c.Type {
|
||||
case pkg.UnknownPkg:
|
||||
vendors.addValue(c.Name)
|
||||
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.ApkPkg, c.Name, c.Name)...)
|
||||
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.ApkPkg, c.Name)...)
|
||||
case pkg.PythonPkg:
|
||||
vendors.addValue(c.Name)
|
||||
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, c.Type, c.Name, c.Name)...)
|
||||
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, c.Type, c.Name)...)
|
||||
for _, av := range additionalVendorsForPython(c.Name) {
|
||||
vendors.addValue(av)
|
||||
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.PythonPkg, av, av)...)
|
||||
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.PythonPkg, av)...)
|
||||
}
|
||||
default:
|
||||
vendors.addValue(c.Name)
|
||||
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, c.Type, c.Name, c.Name)...)
|
||||
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, c.Type, c.Name)...)
|
||||
}
|
||||
}
|
||||
|
||||
vendors.union(candidateVendorsFromURL(metadata.URL))
|
||||
|
||||
for v := range vendors {
|
||||
v.disallowDelimiterVariations = true
|
||||
v.disallowSubSelections = true
|
||||
}
|
||||
|
||||
return vendors
|
||||
}
|
||||
|
||||
func candidateProductsForAPK(p pkg.Package) fieldCandidateSet {
|
||||
metadata, ok := p.Metadata.(pkg.ApkMetadata)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
products := newFieldCandidateSet()
|
||||
candidates := upstreamCandidates(metadata)
|
||||
|
||||
for _, c := range candidates {
|
||||
switch c.Type {
|
||||
case pkg.UnknownPkg:
|
||||
products.addValue(c.Name)
|
||||
products.addValue(findAdditionalProducts(defaultCandidateAdditions, pkg.ApkPkg, c.Name)...)
|
||||
products.removeByValue(findProductsToRemove(defaultCandidateRemovals, pkg.ApkPkg, c.Name)...)
|
||||
default:
|
||||
products.addValue(c.Name)
|
||||
products.addValue(findAdditionalProducts(defaultCandidateAdditions, c.Type, c.Name)...)
|
||||
products.removeByValue(findProductsToRemove(defaultCandidateRemovals, c.Type, c.Name)...)
|
||||
}
|
||||
}
|
||||
|
||||
for p := range products {
|
||||
p.disallowDelimiterVariations = true
|
||||
p.disallowSubSelections = true
|
||||
}
|
||||
|
||||
return products
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
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 with explicit different origin",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "py2-pypdf",
|
||||
OriginPackage: "abcdefg",
|
||||
},
|
||||
},
|
||||
expected: []string{"pypdf", "pypdfproject", "pypdf_project"},
|
||||
},
|
||||
{
|
||||
name: "ruby-armadillo Package",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-armadillo",
|
||||
},
|
||||
},
|
||||
expected: []string{"armadillo"},
|
||||
},
|
||||
{
|
||||
name: "python-3.6",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "python-3.6",
|
||||
},
|
||||
},
|
||||
expected: []string{"python", "python_software_foundation"},
|
||||
},
|
||||
{
|
||||
name: "ruby-3.6",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-3.6",
|
||||
URL: "https://www.ruby-lang.org/",
|
||||
},
|
||||
},
|
||||
expected: []string{"ruby", "ruby-lang"},
|
||||
},
|
||||
{
|
||||
name: "make",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "make",
|
||||
URL: "https://www.gnu.org/software/make",
|
||||
},
|
||||
},
|
||||
expected: []string{"gnu", "make"},
|
||||
},
|
||||
{
|
||||
name: "ruby-rake with matching origin",
|
||||
pkg: pkg.Package{
|
||||
Name: "ruby-rake",
|
||||
Type: pkg.ApkPkg,
|
||||
MetadataType: pkg.ApkMetadataType,
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-rake",
|
||||
URL: "https://github.com/ruby/rake",
|
||||
OriginPackage: "ruby-rake",
|
||||
},
|
||||
},
|
||||
expected: []string{"rake", "ruby-lang", "ruby"},
|
||||
},
|
||||
{
|
||||
name: "ruby-rake with non-matching origin",
|
||||
pkg: pkg.Package{
|
||||
Name: "ruby-rake",
|
||||
Type: pkg.ApkPkg,
|
||||
MetadataType: pkg.ApkMetadataType,
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-rake",
|
||||
URL: "https://www.ruby-lang.org/",
|
||||
OriginPackage: "ruby",
|
||||
},
|
||||
},
|
||||
expected: []string{"rake", "ruby-lang"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.ElementsMatch(t, test.expected, candidateVendorsForAPK(test.pkg).uniqueValues(), "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 with explicit different origin",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "py2-pypdf",
|
||||
OriginPackage: "abcdefg",
|
||||
},
|
||||
},
|
||||
expected: []string{"pypdf"},
|
||||
},
|
||||
{
|
||||
name: "ruby-armadillo Package",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-armadillo",
|
||||
},
|
||||
},
|
||||
expected: []string{"armadillo"},
|
||||
},
|
||||
{
|
||||
name: "python-3.6",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "python-3.6",
|
||||
},
|
||||
},
|
||||
expected: []string{"python"},
|
||||
},
|
||||
{
|
||||
name: "ruby-3.6",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-3.6",
|
||||
URL: "https://www.ruby-lang.org/",
|
||||
},
|
||||
},
|
||||
expected: []string{"ruby"},
|
||||
},
|
||||
{
|
||||
name: "make",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "make",
|
||||
URL: "https://www.gnu.org/software/make",
|
||||
},
|
||||
},
|
||||
expected: []string{"make"},
|
||||
},
|
||||
{
|
||||
name: "ruby-rake with matching origin",
|
||||
pkg: pkg.Package{
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-rake",
|
||||
URL: "https://github.com/ruby/rake",
|
||||
OriginPackage: "ruby-rake",
|
||||
},
|
||||
},
|
||||
expected: []string{"rake"},
|
||||
},
|
||||
{
|
||||
name: "ruby-rake with non-matching origin",
|
||||
pkg: pkg.Package{
|
||||
Name: "ruby-rake",
|
||||
Type: pkg.ApkPkg,
|
||||
MetadataType: pkg.ApkMetadataType,
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-rake",
|
||||
URL: "https://www.ruby-lang.org/",
|
||||
OriginPackage: "ruby",
|
||||
},
|
||||
},
|
||||
expected: []string{"rake"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.ElementsMatch(t, test.expected, candidateProductsForAPK(test.pkg).uniqueValues(), "different products")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_upstreamCandidates(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
metadata pkg.ApkMetadata
|
||||
expected []upstreamCandidate
|
||||
}{
|
||||
{
|
||||
name: "gocase",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "p",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "p", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "same package and origin simple case",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "p",
|
||||
OriginPackage: "p",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "p", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "different package and origin",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "p",
|
||||
OriginPackage: "origin",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "p", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upstream python package information as qualifier py- prefix",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "py-potatoes",
|
||||
OriginPackage: "py-potatoes",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "potatoes", Type: pkg.PythonPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upstream python package information as qualifier py3- prefix",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "py3-potatoes",
|
||||
OriginPackage: "py3-potatoes",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "potatoes", Type: pkg.PythonPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "python package with distinct origin package",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "py3-non-existant",
|
||||
OriginPackage: "abcdefg",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "non-existant", Type: pkg.PythonPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upstream ruby package information as qualifier",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-something",
|
||||
OriginPackage: "ruby-something",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "something", Type: pkg.GemPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ruby package with distinct origin package",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-something",
|
||||
OriginPackage: "1234567",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "something", Type: pkg.GemPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "postgesql-15 upstream postgresql",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "postgresql-15",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "postgresql", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "postgesql15 upstream postgresql",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "postgresql15",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "postgresql", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "go-1.19 upstream go",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "go-1.19",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "go", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "go1.143 upstream go",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "go1.143",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "go", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "abc-101.191.23456 upstream abc",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "abc-101.191.23456",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "abc", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "abc101.191.23456 upstream abc",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "abc101.191.23456",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "abc", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "abc101-12345-1045 upstream abc101-12345",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "abc101-12345-1045",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "abc101-12345", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "abc101-a12345-1045 upstream abc101-a12345",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "abc101-a12345-1045",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "abc-a12345-1045", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "package starting with single digit",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "3proxy",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "3proxy", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "package starting with multiple digits",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "356proxy",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "356proxy", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "package composed of only digits",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "123456",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "123456", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ruby-3.6 upstream ruby",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-3.6",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "ruby", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ruby3.6 upstream ruby",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "ruby3.6",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "ruby", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ruby3.6-tacos upstream tacos",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "ruby3.6-tacos",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "tacos", Type: pkg.GemPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ruby-3.6-tacos upstream tacos",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-3.6-tacos",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "tacos", Type: pkg.GemPkg},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "abc1234jksajflksa",
|
||||
metadata: pkg.ApkMetadata{
|
||||
Package: "abc1234jksajflksa",
|
||||
},
|
||||
expected: []upstreamCandidate{
|
||||
{Name: "abc1234jksajflksa", Type: pkg.UnknownPkg},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := upstreamCandidates(test.metadata)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -121,12 +121,225 @@ 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: "curl"},
|
||||
candidateAddition{AdditionalVendors: []string{"haxx"}},
|
||||
},
|
||||
{
|
||||
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"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "go"},
|
||||
candidateAddition{AdditionalVendors: []string{"golang"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "ruby"},
|
||||
candidateAddition{AdditionalVendors: []string{"ruby-lang"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "bazel"},
|
||||
candidateAddition{AdditionalVendors: []string{"google"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "clang"},
|
||||
candidateAddition{AdditionalVendors: []string{"llvm"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "openjdk"},
|
||||
candidateAddition{AdditionalVendors: []string{"oracle"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "glibc"},
|
||||
candidateAddition{AdditionalVendors: []string{"gnu"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "glib"},
|
||||
candidateAddition{AdditionalVendors: []string{"gnome"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "bash"},
|
||||
candidateAddition{AdditionalVendors: []string{"gnu"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "alsa-lib"},
|
||||
candidateAddition{AdditionalVendors: []string{"alsa-project"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "alsa"},
|
||||
candidateAddition{AdditionalVendors: []string{"alsa-project"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "make"},
|
||||
candidateAddition{AdditionalVendors: []string{"gnu"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "git"},
|
||||
candidateAddition{AdditionalVendors: []string{"git-scm"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "bind"},
|
||||
candidateAddition{AdditionalVendors: []string{"isc"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "libxpm"},
|
||||
candidateAddition{AdditionalVendors: []string{"libxpm_project"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "musl"},
|
||||
candidateAddition{AdditionalVendors: []string{"musl-libc"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "firefox"},
|
||||
candidateAddition{AdditionalVendors: []string{"mozilla"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "firefox-esr"},
|
||||
candidateAddition{AdditionalVendors: []string{"mozilla"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "thunderbird"},
|
||||
candidateAddition{AdditionalVendors: []string{"mozilla"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "chromium"},
|
||||
candidateAddition{AdditionalVendors: []string{"google"}, AdditionalProducts: []string{"chrome"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "apache"},
|
||||
candidateAddition{AdditionalProducts: []string{"http_server"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "tiff"},
|
||||
candidateAddition{AdditionalProducts: []string{"libtiff"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "ghostscript"},
|
||||
candidateAddition{AdditionalVendors: []string{"artifex"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "openjpeg"},
|
||||
candidateAddition{AdditionalVendors: []string{"uclouvain"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "xorg-server"},
|
||||
candidateAddition{AdditionalVendors: []string{"x.org"}, AdditionalProducts: []string{"x_server"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "podofo"},
|
||||
candidateAddition{AdditionalVendors: []string{"podofo_project"}},
|
||||
},
|
||||
{
|
||||
pkg.ApkPkg,
|
||||
candidateKey{PkgName: "wpa_supplicant"},
|
||||
candidateAddition{AdditionalVendors: []string{"w1.fi"}},
|
||||
},
|
||||
//
|
||||
// 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:*:*:*:*:*:*:*",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -661,6 +673,42 @@ func TestGeneratePackageCPEs(t *testing.T) {
|
||||
"cpe:2.3:a:python_redis:redis:2.1.4:*:*:*:*:*:*:*",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "regression: ruby-rake apk missing expected ruby-lang:rake CPE",
|
||||
p: pkg.Package{
|
||||
Name: "ruby-rake",
|
||||
Version: "2.7.6-r0",
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-analyzer",
|
||||
Language: pkg.UnknownLanguage,
|
||||
MetadataType: pkg.ApkMetadataType,
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "ruby-rake",
|
||||
URL: "https://www.ruby-lang.org/",
|
||||
OriginPackage: "ruby",
|
||||
},
|
||||
},
|
||||
expected: []string{
|
||||
"cpe:2.3:a:ruby-lang:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby-lang:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby-lang:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby-rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby-rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby-rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby_lang:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby_lang:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby_lang:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby_rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby_rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
"cpe:2.3:a:ruby_rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package cpe
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
)
|
||||
|
||||
var (
|
||||
urlPrefixToVendors = map[string][]string{
|
||||
"https://www.gnu.org/": {"gnu"},
|
||||
"https://developer.gnome.org/": {"gnome"},
|
||||
"https://www.ruby-lang.org/": {"ruby-lang"},
|
||||
"https://llvm.org/": {"llvm"},
|
||||
"https://www.isc.org/": {"isc"},
|
||||
"https://musl.libc.org/": {"musl-libc"},
|
||||
"https://www.mozilla.org/": {"mozilla"},
|
||||
"https://www.x.org/": {"x.org"},
|
||||
"https://w1.fi/": {"w1.fi"},
|
||||
}
|
||||
|
||||
vendorExtractionPatterns = []*regexp.Regexp{
|
||||
regexp.MustCompile(`^https://(?:github|gitlab)\.com/(?P<vendor>[\w\-]*?)/.*$`),
|
||||
}
|
||||
)
|
||||
|
||||
func candidateVendorsFromURL(url string) fieldCandidateSet {
|
||||
vendors := newFieldCandidateSet()
|
||||
|
||||
for urlPrefix, additionalVendors := range urlPrefixToVendors {
|
||||
if strings.HasPrefix(url, urlPrefix) {
|
||||
for _, v := range additionalVendors {
|
||||
vendors.add(fieldCandidate{
|
||||
value: v,
|
||||
disallowSubSelections: true,
|
||||
disallowDelimiterVariations: true,
|
||||
})
|
||||
|
||||
return vendors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range vendorExtractionPatterns {
|
||||
groups := internal.MatchNamedCaptureGroups(p, url)
|
||||
|
||||
if v, ok := groups["vendor"]; ok {
|
||||
vendors.add(fieldCandidate{
|
||||
value: v,
|
||||
disallowSubSelections: true,
|
||||
disallowDelimiterVariations: true,
|
||||
})
|
||||
|
||||
return vendors
|
||||
}
|
||||
}
|
||||
|
||||
return vendors
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package cpe
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_candidateVendorsFromURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
url: "",
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
name: "no known vendors",
|
||||
url: "https://something-unknown.com/126374623876/12345",
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
name: "gnu vendor from url",
|
||||
url: "https://www.gnu.org/software/make",
|
||||
expected: []string{"gnu"},
|
||||
},
|
||||
{
|
||||
name: "github username as vendor",
|
||||
url: "https://github.com/armadillo/abcxyz-12345",
|
||||
expected: []string{"armadillo"},
|
||||
},
|
||||
{
|
||||
name: "github username with - as vendor",
|
||||
url: "https://github.com/1234-abc-xyz/hello",
|
||||
expected: []string{"1234-abc-xyz"},
|
||||
},
|
||||
{
|
||||
name: "gitlab username as vendor",
|
||||
url: "https://gitlab.com/armadillo/abcxyz-12345",
|
||||
expected: []string{"armadillo"},
|
||||
},
|
||||
{
|
||||
name: "gitlab username with - as vendor",
|
||||
url: "https://gitlab.com/1234-abc-xyz/hello",
|
||||
expected: []string{"1234-abc-xyz"},
|
||||
},
|
||||
{
|
||||
name: "github username as vendor from longer url",
|
||||
url: "https://github.com/armadillo/abcxyz-12345/a/b/c/d/e/f/g",
|
||||
expected: []string{"armadillo"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.ElementsMatch(t, test.expected, candidateVendorsFromURL(test.url).uniqueValues(), "different vendors")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package erlang
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type erlangNode struct {
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func (e erlangNode) Slice() []erlangNode {
|
||||
out, ok := e.value.([]erlangNode)
|
||||
if ok {
|
||||
return out
|
||||
}
|
||||
return []erlangNode{}
|
||||
}
|
||||
|
||||
func (e erlangNode) String() string {
|
||||
out, ok := e.value.(string)
|
||||
if ok {
|
||||
return out
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e erlangNode) Get(index int) erlangNode {
|
||||
s := e.Slice()
|
||||
if len(s) > index {
|
||||
return s[index]
|
||||
}
|
||||
return erlangNode{}
|
||||
}
|
||||
|
||||
func node(value interface{}) erlangNode {
|
||||
return erlangNode{
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// parseErlang basic parser for erlang, used by rebar.lock
|
||||
func parseErlang(reader io.Reader) (erlangNode, error) {
|
||||
data, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return node(nil), err
|
||||
}
|
||||
|
||||
out := erlangNode{
|
||||
value: []erlangNode{},
|
||||
}
|
||||
|
||||
i := 0
|
||||
for i < len(data) {
|
||||
item, err := parseErlangBlock(data, &i)
|
||||
if err != nil {
|
||||
return node(nil), fmt.Errorf("%w\n%s", err, printError(data, i))
|
||||
}
|
||||
|
||||
skipWhitespace(data, &i)
|
||||
|
||||
if i, ok := item.value.(string); ok && i == "." {
|
||||
continue
|
||||
}
|
||||
|
||||
out.value = append(out.value.([]erlangNode), item)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func printError(data []byte, i int) string {
|
||||
line := 1
|
||||
char := 1
|
||||
|
||||
prev := []string{}
|
||||
curr := bytes.Buffer{}
|
||||
|
||||
for idx, c := range data {
|
||||
if c == '\n' {
|
||||
prev = append(prev, curr.String())
|
||||
curr.Reset()
|
||||
if idx >= i {
|
||||
break
|
||||
} else {
|
||||
line++
|
||||
}
|
||||
char = 1
|
||||
continue
|
||||
}
|
||||
if idx < i {
|
||||
char++
|
||||
}
|
||||
curr.WriteByte(c)
|
||||
}
|
||||
|
||||
l1 := fmt.Sprintf("%d", line-1)
|
||||
l2 := fmt.Sprintf("%d", line)
|
||||
|
||||
if len(l1) < len(l2) {
|
||||
l1 = " " + l1
|
||||
}
|
||||
|
||||
sep := ": "
|
||||
|
||||
lines := ""
|
||||
if len(prev) > 1 {
|
||||
lines += fmt.Sprintf("%s%s%s\n", l1, sep, prev[len(prev)-2])
|
||||
}
|
||||
if len(prev) > 0 {
|
||||
lines += fmt.Sprintf("%s%s%s\n", l2, sep, prev[len(prev)-1])
|
||||
}
|
||||
|
||||
pointer := strings.Repeat(" ", len(l2)+len(sep)+char-1) + "^"
|
||||
|
||||
return fmt.Sprintf("line: %v, char: %v\n%s%s", line, char, lines, pointer)
|
||||
}
|
||||
|
||||
func skipWhitespace(data []byte, i *int) {
|
||||
for *i < len(data) && isWhitespace(data[*i]) {
|
||||
*i++
|
||||
}
|
||||
}
|
||||
|
||||
func parseErlangBlock(data []byte, i *int) (erlangNode, error) {
|
||||
block, err := parseErlangNode(data, i)
|
||||
if err != nil {
|
||||
return node(nil), err
|
||||
}
|
||||
|
||||
skipWhitespace(data, i)
|
||||
*i++ // skip the trailing .
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func parseErlangNode(data []byte, i *int) (erlangNode, error) {
|
||||
skipWhitespace(data, i)
|
||||
c := data[*i]
|
||||
switch c {
|
||||
case '[', '{':
|
||||
return parseErlangList(data, i)
|
||||
case '"':
|
||||
return parseErlangString(data, i)
|
||||
case '<':
|
||||
return parseErlangAngleString(data, i)
|
||||
}
|
||||
|
||||
if isLiteral(c) {
|
||||
return parseErlangLiteral(data, i)
|
||||
}
|
||||
|
||||
return erlangNode{}, fmt.Errorf("invalid literal character: %s", string(c))
|
||||
}
|
||||
|
||||
func isWhitespace(c byte) bool {
|
||||
return unicode.IsSpace(rune(c))
|
||||
}
|
||||
|
||||
func isLiteral(c byte) bool {
|
||||
r := rune(c)
|
||||
return unicode.IsNumber(r) || unicode.IsLetter(r) || r == '.' || r == '_'
|
||||
}
|
||||
|
||||
func parseErlangLiteral(data []byte, i *int) (erlangNode, error) {
|
||||
var buf bytes.Buffer
|
||||
for *i < len(data) {
|
||||
c := data[*i]
|
||||
if isLiteral(c) {
|
||||
buf.WriteByte(c)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
*i++
|
||||
}
|
||||
return node(buf.String()), nil
|
||||
}
|
||||
|
||||
func parseErlangAngleString(data []byte, i *int) (erlangNode, error) {
|
||||
*i += 2
|
||||
out, err := parseErlangString(data, i)
|
||||
*i += 2
|
||||
return out, err
|
||||
}
|
||||
|
||||
func parseErlangString(data []byte, i *int) (erlangNode, error) {
|
||||
delim := data[*i]
|
||||
*i++
|
||||
var buf bytes.Buffer
|
||||
for *i < len(data) {
|
||||
c := data[*i]
|
||||
if c == delim {
|
||||
*i++
|
||||
return node(buf.String()), nil
|
||||
}
|
||||
if c == '\\' {
|
||||
*i++
|
||||
if len(data) >= *i {
|
||||
return node(nil), fmt.Errorf("invalid escape without closed string at %d", *i)
|
||||
}
|
||||
c = data[*i]
|
||||
}
|
||||
buf.WriteByte(c)
|
||||
*i++
|
||||
}
|
||||
return node(buf.String()), nil
|
||||
}
|
||||
|
||||
func parseErlangList(data []byte, i *int) (erlangNode, error) {
|
||||
*i++
|
||||
out := erlangNode{
|
||||
value: []erlangNode{},
|
||||
}
|
||||
for *i < len(data) {
|
||||
item, err := parseErlangNode(data, i)
|
||||
if err != nil {
|
||||
return node(nil), err
|
||||
}
|
||||
out.value = append(out.value.([]erlangNode), item)
|
||||
skipWhitespace(data, i)
|
||||
c := data[*i]
|
||||
switch c {
|
||||
case ',':
|
||||
*i++
|
||||
continue
|
||||
case ']', '}':
|
||||
*i++
|
||||
return out, nil
|
||||
default:
|
||||
return node(nil), fmt.Errorf("unexpected character: %s", string(c))
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package erlang
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_parseErlang(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
content string
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "basic valid content",
|
||||
content: `
|
||||
{"1.2.0",
|
||||
[{<<"bcrypt">>,{pkg,<<"bcrypt">>,<<"1.1.5">>},0},
|
||||
{<<"bson">>,
|
||||
{git,"https://github.com/comtihon/bson-erlang",
|
||||
{ref,"14308ab927cfa69324742c3de720578094e0bb19"}},
|
||||
1},
|
||||
{<<"syslog">>,{pkg,<<"syslog">>,<<"1.1.0">>},0},
|
||||
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1},
|
||||
{<<"vernemq_dev">>,
|
||||
{git,"https://github.com/vernemq/vernemq_dev.git",
|
||||
{ref,"6d622aa8c901ae7777433aef2bd049e380c474a6"}},
|
||||
0}]
|
||||
}.
|
||||
[
|
||||
{pkg_hash,[
|
||||
{<<"bcrypt">>, <<"A6763BD4E1AF46D34776F85B7995E63A02978DE110C077E9570ED17006E03386">>},
|
||||
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]},
|
||||
{pkg_hash_ext,[
|
||||
{<<"bcrypt">>, <<"3418821BC17CE6E96A4A77D1A88D7485BF783E212069FACFC79510AFBFF95352">>},
|
||||
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]}
|
||||
].`,
|
||||
},
|
||||
{
|
||||
name: "invalid string content",
|
||||
wantErr: require.Error,
|
||||
content: `
|
||||
{"1.2.0
|
||||
">>},
|
||||
].`,
|
||||
},
|
||||
{
|
||||
name: "invalid content",
|
||||
wantErr: require.Error,
|
||||
content: `
|
||||
{"1.2.0"}.
|
||||
].`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
value, err := parseErlang(bytes.NewReader([]byte(test.content)))
|
||||
|
||||
if test.wantErr == nil {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
test.wantErr(t, err)
|
||||
}
|
||||
|
||||
assert.IsType(t, erlangNode{}, value)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,6 @@
|
||||
package erlang
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -14,57 +8,44 @@ import (
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// integrity check
|
||||
var _ generic.Parser = parseRebarLock
|
||||
|
||||
var rebarLockDelimiter = regexp.MustCompile(`[\[{<">},: \]\n]+`)
|
||||
|
||||
// parseMixLock parses a mix.lock and returns the discovered Elixir packages.
|
||||
// parseRebarLock parses a rebar.lock and returns the discovered Elixir packages.
|
||||
//
|
||||
//nolint:funlen
|
||||
func parseRebarLock(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
r := bufio.NewReader(reader)
|
||||
doc, err := parseErlang(reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkgMap := make(map[string]*pkg.Package)
|
||||
|
||||
var names []string
|
||||
loop:
|
||||
for {
|
||||
line, err := r.ReadString('\n')
|
||||
switch {
|
||||
case errors.Is(io.EOF, err):
|
||||
break loop
|
||||
case err != nil:
|
||||
// TODO: return partial result and warn
|
||||
return nil, nil, fmt.Errorf("failed to parse rebar.lock file: %w", err)
|
||||
}
|
||||
tokens := rebarLockDelimiter.Split(line, -1)
|
||||
if len(tokens) < 4 {
|
||||
continue
|
||||
}
|
||||
if len(tokens) < 5 {
|
||||
name, hash := tokens[1], tokens[2]
|
||||
sourcePkg := pkgMap[name]
|
||||
metadata, ok := sourcePkg.Metadata.(pkg.RebarLockMetadata)
|
||||
if !ok {
|
||||
log.WithFields("package", name).Warn("unable to extract rebar.lock metadata to add hash metadata")
|
||||
continue
|
||||
}
|
||||
// rebar.lock structure is:
|
||||
// [
|
||||
// ["version", [
|
||||
// [<<"package-name">>, ["version-type", "version"]...
|
||||
// ],
|
||||
// [
|
||||
// [pkg_hash, [
|
||||
// [<<"package-name">>, <<"package-hash">>]
|
||||
// ],
|
||||
// [pkg_hash_ext, [
|
||||
// [<<"package-name">>, <<"package-hash">>]
|
||||
// ]
|
||||
// ]
|
||||
// ]
|
||||
|
||||
if metadata.PkgHash == "" {
|
||||
metadata.PkgHash = hash
|
||||
} else {
|
||||
metadata.PkgHashExt = hash
|
||||
}
|
||||
sourcePkg.Metadata = metadata
|
||||
continue
|
||||
}
|
||||
name, version := tokens[1], tokens[4]
|
||||
versions := doc.Get(0)
|
||||
deps := versions.Get(1)
|
||||
|
||||
sourcePkg := pkg.Package{
|
||||
Name: name,
|
||||
Version: version,
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
for _, dep := range deps.Slice() {
|
||||
name := dep.Get(0).String()
|
||||
versionNode := dep.Get(1)
|
||||
versionType := versionNode.Get(0).String()
|
||||
version := versionNode.Get(2).String()
|
||||
|
||||
// capture git hashes if no version specified
|
||||
if versionType == "git" {
|
||||
version = versionNode.Get(2).Get(1).String()
|
||||
}
|
||||
|
||||
p := newPackage(pkg.RebarLockMetadata{
|
||||
@@ -72,15 +53,45 @@ loop:
|
||||
Version: version,
|
||||
})
|
||||
|
||||
names = append(names, name)
|
||||
pkgMap[sourcePkg.Name] = &p
|
||||
pkgMap[name] = &p
|
||||
}
|
||||
|
||||
hashes := doc.Get(1)
|
||||
for _, hashStruct := range hashes.Slice() {
|
||||
hashType := hashStruct.Get(0).String()
|
||||
|
||||
for _, hashValue := range hashStruct.Get(1).Slice() {
|
||||
name := hashValue.Get(0).String()
|
||||
hash := hashValue.Get(1).String()
|
||||
|
||||
sourcePkg := pkgMap[name]
|
||||
if sourcePkg == nil {
|
||||
log.WithFields("package", name).Warn("unable find source package")
|
||||
continue
|
||||
}
|
||||
metadata, ok := sourcePkg.Metadata.(pkg.RebarLockMetadata)
|
||||
if !ok {
|
||||
log.WithFields("package", name).Warn("unable to extract rebar.lock metadata to add hash metadata")
|
||||
continue
|
||||
}
|
||||
|
||||
switch hashType {
|
||||
case "pkg_hash":
|
||||
metadata.PkgHash = hash
|
||||
case "pkg_hash_ext":
|
||||
metadata.PkgHashExt = hash
|
||||
}
|
||||
sourcePkg.Metadata = metadata
|
||||
}
|
||||
}
|
||||
|
||||
var packages []pkg.Package
|
||||
for _, name := range names {
|
||||
p := pkgMap[name]
|
||||
for _, p := range pkgMap {
|
||||
p.SetID()
|
||||
packages = append(packages, *p)
|
||||
}
|
||||
return packages, nil, nil
|
||||
}
|
||||
|
||||
// integrity check
|
||||
var _ generic.Parser = parseRebarLock
|
||||
|
||||
@@ -9,111 +9,259 @@ import (
|
||||
)
|
||||
|
||||
func TestParseRebarLock(t *testing.T) {
|
||||
expected := []pkg.Package{
|
||||
tests := []struct {
|
||||
fixture string
|
||||
expected []pkg.Package
|
||||
}{
|
||||
{
|
||||
Name: "certifi",
|
||||
Version: "2.9.0",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/certifi@2.9.0",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "certifi",
|
||||
Version: "2.9.0",
|
||||
PkgHash: "6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21",
|
||||
PkgHashExt: "266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641",
|
||||
fixture: "test-fixtures/rebar.lock",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "certifi",
|
||||
Version: "2.9.0",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/certifi@2.9.0",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "certifi",
|
||||
Version: "2.9.0",
|
||||
PkgHash: "6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21",
|
||||
PkgHashExt: "266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "idna",
|
||||
Version: "6.1.1",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/idna@6.1.1",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "idna",
|
||||
Version: "6.1.1",
|
||||
PkgHash: "8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D",
|
||||
PkgHashExt: "92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "metrics",
|
||||
Version: "1.0.1",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/metrics@1.0.1",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "metrics",
|
||||
Version: "1.0.1",
|
||||
PkgHash: "25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486",
|
||||
PkgHashExt: "69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "mimerl",
|
||||
Version: "1.2.0",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/mimerl@1.2.0",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "mimerl",
|
||||
Version: "1.2.0",
|
||||
PkgHash: "67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3",
|
||||
PkgHashExt: "F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "parse_trans",
|
||||
Version: "3.3.1",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/parse_trans@3.3.1",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "parse_trans",
|
||||
Version: "3.3.1",
|
||||
PkgHash: "16328AB840CC09919BD10DAB29E431DA3AF9E9E7E7E6F0089DD5A2D2820011D8",
|
||||
PkgHashExt: "07CD9577885F56362D414E8C4C4E6BDF10D43A8767ABB92D24CBE8B24C54888B",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ssl_verify_fun",
|
||||
Version: "1.1.6",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/ssl_verify_fun@1.1.6",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "ssl_verify_fun",
|
||||
Version: "1.1.6",
|
||||
PkgHash: "CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0",
|
||||
PkgHashExt: "BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "unicode_util_compat",
|
||||
Version: "0.7.0",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/unicode_util_compat@0.7.0",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "unicode_util_compat",
|
||||
Version: "0.7.0",
|
||||
PkgHash: "BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78",
|
||||
PkgHashExt: "25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "idna",
|
||||
Version: "6.1.1",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/idna@6.1.1",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "idna",
|
||||
Version: "6.1.1",
|
||||
PkgHash: "8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D",
|
||||
PkgHashExt: "92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "metrics",
|
||||
Version: "1.0.1",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/metrics@1.0.1",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "metrics",
|
||||
Version: "1.0.1",
|
||||
PkgHash: "25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486",
|
||||
PkgHashExt: "69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "mimerl",
|
||||
Version: "1.2.0",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/mimerl@1.2.0",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "mimerl",
|
||||
Version: "1.2.0",
|
||||
PkgHash: "67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3",
|
||||
PkgHashExt: "F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "parse_trans",
|
||||
Version: "3.3.1",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/parse_trans@3.3.1",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "parse_trans",
|
||||
Version: "3.3.1",
|
||||
PkgHash: "16328AB840CC09919BD10DAB29E431DA3AF9E9E7E7E6F0089DD5A2D2820011D8",
|
||||
PkgHashExt: "07CD9577885F56362D414E8C4C4E6BDF10D43A8767ABB92D24CBE8B24C54888B",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ssl_verify_fun",
|
||||
Version: "1.1.6",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/ssl_verify_fun@1.1.6",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "ssl_verify_fun",
|
||||
Version: "1.1.6",
|
||||
PkgHash: "CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0",
|
||||
PkgHashExt: "BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "unicode_util_compat",
|
||||
Version: "0.7.0",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/unicode_util_compat@0.7.0",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "unicode_util_compat",
|
||||
Version: "0.7.0",
|
||||
PkgHash: "BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78",
|
||||
PkgHashExt: "25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521",
|
||||
fixture: "test-fixtures/rebar-2.lock",
|
||||
expected: []pkg.Package{
|
||||
//[{<<"bcrypt">>,{pkg,<<"bcrypt">>,<<"1.1.5">>},0},
|
||||
// {<<"bcrypt">>, <<"A6763BD4E1AF46D34776F85B7995E63A02978DE110C077E9570ED17006E03386">>},
|
||||
// {<<"bcrypt">>, <<"3418821BC17CE6E96A4A77D1A88D7485BF783E212069FACFC79510AFBFF95352">>},
|
||||
{
|
||||
Name: "bcrypt",
|
||||
Version: "1.1.5",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/bcrypt@1.1.5",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "bcrypt",
|
||||
Version: "1.1.5",
|
||||
PkgHash: "A6763BD4E1AF46D34776F85B7995E63A02978DE110C077E9570ED17006E03386",
|
||||
PkgHashExt: "3418821BC17CE6E96A4A77D1A88D7485BF783E212069FACFC79510AFBFF95352",
|
||||
},
|
||||
},
|
||||
// {<<"bson">>,
|
||||
// {git,"https://github.com/comtihon/bson-erlang",
|
||||
// {ref,"14308ab927cfa69324742c3de720578094e0bb19"}},
|
||||
// 1},
|
||||
{
|
||||
Name: "bson",
|
||||
Version: "14308ab927cfa69324742c3de720578094e0bb19",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/bson@14308ab927cfa69324742c3de720578094e0bb19",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "bson",
|
||||
Version: "14308ab927cfa69324742c3de720578094e0bb19",
|
||||
},
|
||||
},
|
||||
// {<<"certifi">>,{pkg,<<"certifi">>,<<"2.9.0">>},1},
|
||||
// {<<"certifi">>, <<"6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21">>}, {<<"stdout_formatter">>, <<"EC24868D8619757A68F0798357C7190807A1CFC42CE90C18C23760E59249A21A">>},
|
||||
// {<<"certifi">>, <<"266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641">>},
|
||||
{
|
||||
Name: "certifi",
|
||||
Version: "2.9.0",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/certifi@2.9.0",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "certifi",
|
||||
Version: "2.9.0",
|
||||
PkgHash: "6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21",
|
||||
PkgHashExt: "266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641",
|
||||
},
|
||||
},
|
||||
// {<<"stdout_formatter">>,{pkg,<<"stdout_formatter">>,<<"0.2.3">>},0},
|
||||
// {<<"stdout_formatter">>, <<"EC24868D8619757A68F0798357C7190807A1CFC42CE90C18C23760E59249A21A">>},
|
||||
// {<<"stdout_formatter">>, <<"6B9CAAD8930006F9BB35680C5D3311917AC67690C3AF1BA018623324C015ABE5">>},
|
||||
{
|
||||
Name: "stdout_formatter",
|
||||
Version: "0.2.3",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/stdout_formatter@0.2.3",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "stdout_formatter",
|
||||
Version: "0.2.3",
|
||||
PkgHash: "EC24868D8619757A68F0798357C7190807A1CFC42CE90C18C23760E59249A21A",
|
||||
PkgHashExt: "6B9CAAD8930006F9BB35680C5D3311917AC67690C3AF1BA018623324C015ABE5",
|
||||
},
|
||||
},
|
||||
// {<<"swc">>,
|
||||
// {git,"https://github.com/vernemq/ServerWideClocks.git",
|
||||
// {ref,"4835239dca5a5f4ac7202dd94d7effcaa617d575"}},
|
||||
// 0},
|
||||
{
|
||||
Name: "swc",
|
||||
Version: "4835239dca5a5f4ac7202dd94d7effcaa617d575",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/swc@4835239dca5a5f4ac7202dd94d7effcaa617d575",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "swc",
|
||||
Version: "4835239dca5a5f4ac7202dd94d7effcaa617d575",
|
||||
},
|
||||
},
|
||||
// {<<"syslog">>,{pkg,<<"syslog">>,<<"1.1.0">>},0},
|
||||
// {<<"syslog">>, <<"6419A232BEA84F07B56DC575225007FFE34D9FDC91ABE6F1B2F254FD71D8EFC2">>},
|
||||
// {<<"syslog">>, <<"4C6A41373C7E20587BE33EF841D3DE6F3BEBA08519809329ECC4D27B15B659E1">>},
|
||||
{
|
||||
Name: "syslog",
|
||||
Version: "1.1.0",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/syslog@1.1.0",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "syslog",
|
||||
Version: "1.1.0",
|
||||
PkgHash: "6419A232BEA84F07B56DC575225007FFE34D9FDC91ABE6F1B2F254FD71D8EFC2",
|
||||
PkgHashExt: "4C6A41373C7E20587BE33EF841D3DE6F3BEBA08519809329ECC4D27B15B659E1",
|
||||
},
|
||||
},
|
||||
// {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1},
|
||||
// {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]},
|
||||
// {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]}
|
||||
{
|
||||
Name: "unicode_util_compat",
|
||||
Version: "0.7.0",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/unicode_util_compat@0.7.0",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "unicode_util_compat",
|
||||
Version: "0.7.0",
|
||||
PkgHash: "BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78",
|
||||
PkgHashExt: "25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521",
|
||||
},
|
||||
},
|
||||
// {<<"vernemq_dev">>,
|
||||
// {git,"https://github.com/vernemq/vernemq_dev.git",
|
||||
// {ref,"6d622aa8c901ae7777433aef2bd049e380c474a6"}},
|
||||
// 0}]}.
|
||||
{
|
||||
Name: "vernemq_dev",
|
||||
Version: "6d622aa8c901ae7777433aef2bd049e380c474a6",
|
||||
Language: pkg.Erlang,
|
||||
Type: pkg.HexPkg,
|
||||
PURL: "pkg:hex/vernemq_dev@6d622aa8c901ae7777433aef2bd049e380c474a6",
|
||||
MetadataType: pkg.RebarLockMetadataType,
|
||||
Metadata: pkg.RebarLockMetadata{
|
||||
Name: "vernemq_dev",
|
||||
Version: "6d622aa8c901ae7777433aef2bd049e380c474a6",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
fixture := "test-fixtures/rebar.lock"
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixture, func(t *testing.T) {
|
||||
// TODO: relationships are not under test
|
||||
var expectedRelationships []artifact.Relationship
|
||||
|
||||
// TODO: relationships are not under test
|
||||
var expectedRelationships []artifact.Relationship
|
||||
|
||||
pkgtest.TestFileParser(t, fixture, parseRebarLock, expected, expectedRelationships)
|
||||
pkgtest.TestFileParser(t, test.fixture, parseRebarLock, test.expected, expectedRelationships)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{"1.2.0",
|
||||
[{<<"bcrypt">>,{pkg,<<"bcrypt">>,<<"1.1.5">>},0},
|
||||
{<<"bson">>,
|
||||
{git,"https://github.com/comtihon/bson-erlang",
|
||||
{ref,"14308ab927cfa69324742c3de720578094e0bb19"}},
|
||||
1},
|
||||
{<<"certifi">>,{pkg,<<"certifi">>,<<"2.9.0">>},1},
|
||||
{<<"stdout_formatter">>,{pkg,<<"stdout_formatter">>,<<"0.2.3">>},0},
|
||||
{<<"swc">>,
|
||||
{git,"https://github.com/vernemq/ServerWideClocks.git",
|
||||
{ref,"4835239dca5a5f4ac7202dd94d7effcaa617d575"}},
|
||||
0},
|
||||
{<<"syslog">>,{pkg,<<"syslog">>,<<"1.1.0">>},0},
|
||||
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1},
|
||||
{<<"vernemq_dev">>,
|
||||
{git,"https://github.com/vernemq/vernemq_dev.git",
|
||||
{ref,"6d622aa8c901ae7777433aef2bd049e380c474a6"}},
|
||||
0}]}.
|
||||
[
|
||||
{pkg_hash,[
|
||||
{<<"bcrypt">>, <<"A6763BD4E1AF46D34776F85B7995E63A02978DE110C077E9570ED17006E03386">>},
|
||||
{<<"certifi">>, <<"6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21">>},
|
||||
{<<"stdout_formatter">>, <<"EC24868D8619757A68F0798357C7190807A1CFC42CE90C18C23760E59249A21A">>},
|
||||
{<<"syslog">>, <<"6419A232BEA84F07B56DC575225007FFE34D9FDC91ABE6F1B2F254FD71D8EFC2">>},
|
||||
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]},
|
||||
{pkg_hash_ext,[
|
||||
{<<"bcrypt">>, <<"3418821BC17CE6E96A4A77D1A88D7485BF783E212069FACFC79510AFBFF95352">>},
|
||||
{<<"certifi">>, <<"266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641">>},
|
||||
{<<"stdout_formatter">>, <<"6B9CAAD8930006F9BB35680C5D3311917AC67690C3AF1BA018623324C015ABE5">>},
|
||||
{<<"syslog">>, <<"4C6A41373C7E20587BE33EF841D3DE6F3BEBA08519809329ECC4D27B15B659E1">>},
|
||||
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]}
|
||||
].
|
||||
@@ -179,12 +179,23 @@ func getBuildSettings(settings []debug.BuildSetting) map[string]string {
|
||||
return m
|
||||
}
|
||||
|
||||
func createMainModuleFromPath(path string) (mod debug.Module) {
|
||||
mod.Path = path
|
||||
mod.Version = devel
|
||||
return
|
||||
}
|
||||
|
||||
func buildGoPkgInfo(location source.Location, mod *debug.BuildInfo, arch string) []pkg.Package {
|
||||
var pkgs []pkg.Package
|
||||
if mod == nil {
|
||||
return pkgs
|
||||
}
|
||||
|
||||
var empty debug.Module
|
||||
if mod.Main == empty && mod.Path != "" {
|
||||
mod.Main = createMainModuleFromPath(mod.Path)
|
||||
}
|
||||
|
||||
for _, dep := range mod.Deps {
|
||||
if dep == nil {
|
||||
continue
|
||||
@@ -195,9 +206,6 @@ func buildGoPkgInfo(location source.Location, mod *debug.BuildInfo, arch string)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(jonasagx): this use happened originally while creating unit tests. It might never
|
||||
// happen in the wild, but I kept it as a safeguard against empty modules.
|
||||
var empty debug.Module
|
||||
if mod.Main == empty {
|
||||
return pkgs
|
||||
}
|
||||
|
||||
@@ -242,6 +242,48 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "parse a mod with path but no main module",
|
||||
arch: archDetails,
|
||||
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{
|
||||
{
|
||||
Name: "github.com/a/b/c",
|
||||
Version: "(devel)",
|
||||
PURL: "pkg:golang/github.com/a/b/c@(devel)",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
Architecture: archDetails,
|
||||
H1Digest: "",
|
||||
BuildSettings: map[string]string{
|
||||
"GOAMD64": "v1",
|
||||
"GOARCH": "amd64",
|
||||
"GOOS": "darwin",
|
||||
},
|
||||
MainModule: "github.com/a/b/c",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "parse a mod without packages",
|
||||
arch: archDetails,
|
||||
|
||||
@@ -347,9 +347,7 @@ func requireFileInfo(_ string, info os.FileInfo, _ error) error {
|
||||
|
||||
func indexingProgress(path string) (*progress.Stage, *progress.Manual) {
|
||||
stage := &progress.Stage{}
|
||||
prog := &progress.Manual{
|
||||
Total: -1,
|
||||
}
|
||||
prog := progress.NewManual(-1)
|
||||
|
||||
bus.Publish(partybus.Event{
|
||||
Type: event.FileIndexingStarted,
|
||||
|
||||
@@ -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