Compare commits

...
42 Commits
Author SHA1 Message Date
Keith Zantow 5f90d03718 fix: possible race condition (#1639) 2023-03-01 15:35:01 -05:00
Weston Steimel e2ebc9769f fix: remove APK OriginPackage cpe candidates (#1637)
Adding APK OriginPackage CPE candidates to the child package
results in false positives in grype because it can't associate
CPE-based findings to the corresponding OriginPackage APK fixes.

This reverts changing the `upstream` in the PURL for APK packages
as the logic in Grype that uses it expects it to be an APK package
name.  This also allows refactoring to unexport and move the APK
CPE candidate generation logic closer to where CPE generation occurs

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-03-01 17:24:43 +00:00
Keith Zantow 2e6e3b0c74 fix: rebar lock file decoding panic (#1628) 2023-03-01 10:08:29 -05:00
Keith Zantow 24584a4d27 fix: handle individual cataloger panics (#1636) 2023-03-01 10:03:34 -05:00
Weston Steimel 8e1205f7ab fix: apk product/vendor generation for old metadata (#1635)
This fixes some instances where the improved APK CPE generation
logic caused regressions for older alpine package APK metadata.
It now generates multiple "upstream" candidates with both name
and package type which reduces the amount of duplicated code in
the apk cpe gen logic.  This also improves the handling of stream
version packages, so now we can correctly identify packages such
as ruby3.2-rexml as the rexml ruby gem.

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-03-01 14:58:35 +00:00
Weston Steimel e92b0fa629 feat: rust toolchain binary cataloger (#1601)
Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-03-01 14:53:37 +00:00
Weston Steimel bcc0751a40 feat: retain go package info when no module declared (#1632)
Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-03-01 14:26:44 +00:00
Weston Steimel f1169e56fc fix: improved CPE-generation for several more APK packages (#1631)
* fix: correct vendor for musl

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: correct vendor for firefox and thunderbird

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: correct vendor/product for chromium

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: correct product for apache http server

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: correct product for tiff

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: correct vendor for ghostscript

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: correct vendor for openjpeg

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: correct vendor/product for xorg-server

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: correct vendor for podofo

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: correct vendor for wpa_supplicant

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

---------

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-03-01 08:55:40 -05:00
Christopher Angelo Phillips 98e737fc27 chore: update deprecated release flag (#1629)
Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2023-02-27 15:57:56 -05:00
dependabot[bot] ff34594284 chore(deps): bump actions/upload-artifact from 2 to 3 (#1627)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-27 14:17:29 -05:00
Benji Visser 9e953b1da3 feat: add support for SUPPORT_END in /etc/os-release (#1612)
Signed-off-by: Benji Visser <benji@093b.org>
2023-02-27 13:43:19 -05:00
Weston Steimel fbda21f4f4 fix: further improvements to CPE generation for apk packages (#1623)
* fix: consider upstream logic during apk cpe gen
* fix: correct apk CPE for go
* fix: correct apk CPE for ruby
* fix: correct apk CPE for bazel
* fix: correct apk CPE for clang
* fix: correct apk CPE for openjdk
* fix: correct apk CPE for glibc
* fix: correct apk CPE for gli
* fix: correct apk CPE for bas
* fix: correct apk CPE for alsa-lib
* fix: correct apk CPE for alsa
* fix: determine apk cpe vendor from known URLs
* fix: add more url prefix->vendor mappings for apk
* refactor: allow reuse of vendor by url prefix logic
* feat: extract username as vendor candidate from github/gitlab

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-02-27 13:16:04 -05:00
dependabot[bot] d23b4d4cbd chore(deps): bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#1625)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-27 13:14:20 -05:00
dependabot[bot] f3acff81f3 chore(deps): bump actions/checkout from 2 to 3 (#1626)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-27 13:14:03 -05:00
Nils Hanke fa0a9fe8f9 feat: set cosign attest predicate type based on Syft output type (#1598)
Signed-off-by: Nils Hanke <nils.hanke@outlook.de>
2023-02-24 15:08:40 -05:00
dependabot[bot] 284bae9d5f chore(deps): bump github.com/spf13/afero from 1.9.3 to 1.9.4 (#1609)
Bumps [github.com/spf13/afero](https://github.com/spf13/afero) from 1.9.3 to 1.9.4.
- [Release notes](https://github.com/spf13/afero/releases)
- [Commits](https://github.com/spf13/afero/compare/v1.9.3...v1.9.4)

---
updated-dependencies:
- dependency-name: github.com/spf13/afero
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-24 15:07:52 -05:00
Weston Steimel 3ee1af0dc6 fix: correct apk purls for other distros (#1620)
The apk purl spec allows for vendor-specific namespace.  I noticed
in the embedded SBOMs from wolfi that the purls are of the form
`pkg:apk/wolfi/curl@7.83.0-r0?arch=x86`, but the current logic in
syft actually prevents purl generation entirely if the distro isn't
alpine, so this corrects that behaviour.

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-02-24 15:07:07 -05:00
Weston Steimel 0c5f03235e refactor: move apk upstream logic to apk metadata (#1619)
* refactor: move apk upstream logic to apk metadata

Export the logic for parsing upstream APK package names
so it can be accessed from apk metadata objects directly.

This also tightens the upstream regex pattern as several
edge cases were being missed.

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: ensure correct handling for apk packages beginning with digits

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: upstream generation for ruby

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

---------

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-02-24 15:59:19 +00:00
Keith Zantow 5e8aa4da5e fix: decoding null apk metadata pullDependencies (#1614)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-02-23 19:55:49 +00:00
Benji Visser abfec62219 feat: haproxy binary matcher (#1591)
Signed-off-by: Benji Visser <benji@093b.org>
2023-02-23 14:39:08 -05:00
Weston Steimel 0c05855131 fix: determine upstream for apk version streams (#1610)
Determines better upstream package name for version-stream apk packages:

Examples:

- postgresql-13 -> postgresql
- postgresql15 -> postgresql
- go-1.19 -> go
- perl100.55 -> perl

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-02-23 17:32:34 +00:00
Weston Steimel 1150772d06 fix: improve CPE generation for curl APK (#1608)
Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-02-23 17:32:12 +00:00
Alex Goodman 669fee84d5 Revert "add workaround for macos github actions cache issue (#1584)" (#1605)
This reverts commit 0076b19893.

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2023-02-22 15:03:12 -05:00
anchore-actions-token-generator[bot]andwagoodman aa151da5fe Update Stereoscope to fab1c9638abc2c21cd53dca1f205f37d71148ee0 (#1604)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: wagoodman <wagoodman@users.noreply.github.com>
2023-02-22 19:08:35 +00:00
Keith Zantow 4bf677d555 chore: fix cataloger_test (#1603)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-02-22 17:30:41 +00:00
Keith Zantow f5e20521e0 fix: merging of binary packages (#1583) 2023-02-22 12:03:15 -05:00
Keith Zantow 8f6a317fef fix: issue when matching format versions (#1585)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-02-22 10:32:05 -05:00
anchore-actions-token-generator[bot] d339ffdcb5 chore: update syft bootstrap tools to latest versions. (#1593) 2023-02-22 10:16:14 -05:00
Benji Visser e84ffc6003 feat: add perl binary classifier (#1592)
Signed-off-by: Benji Visser <benji@093b.org>
2023-02-22 09:58:05 -05:00
anchore-actions-token-generator[bot] bb52a25c8a Update Stereoscope to 529924d6d5aa6c708cceffc651883b6e1e27f5df (#1602)
Signed-off-by: GitHub <noreply@github.com>
2023-02-22 08:49:04 +00:00
Marc-Etienne Vargenau 4b7b709473 Update SPDX license list to 3.20 (#1600)
Signed-off-by: Marc-Etienne Vargenau <marc-etienne.vargenau@nokia.com>
2023-02-21 15:12:28 -05:00
Keith Zantow 2ef2eed382 chore: update SPDX license list (#1599) 2023-02-21 17:31:24 +00:00
Alex Goodman f6716092af fix cataloger selection to be more specific (#1582)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2023-02-17 15:35:54 +00:00
Alex Goodman 0076b19893 add workaround for macos github actions cache issue (#1584)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2023-02-17 10:29:33 -05:00
anchore-actions-token-generator[bot]andwestonsteimel 2642a36161 Update Stereoscope to 4b5ebf8c7f4b81ca79c4c3f0af1d0723eab87d42 (#1576)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: westonsteimel <westonsteimel@users.noreply.github.com>
2023-02-16 10:22:43 -05:00
dependabot[bot] 1981b249f1 chore(deps): bump golang.org/x/net from 0.6.0 to 0.7.0 (#1574)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-15 12:54:55 -05:00
Keith Zantow a3ebb31e3c chore: update bug issue template (#1571)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-02-14 12:58:44 -05:00
Alex Goodman 9b9a7d6c98 allow convert to take stdin (#1570)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2023-02-14 10:03:47 -05:00
Weston Steimel 57a13ae355 fix: improve CPE and upstream generation logic for Alpine packages (#1567)
* fix: improved CPE-generation logic for alpine packages

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: improved alpine upstream name generation

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: improve CPE vendor for alpine

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: python vendor CPE gen

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: alpine cpe gen logic

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: apk CPE update for nodejs-current

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: CPE update for python pip

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix: CPE update for some ruby packages

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

* fix linting

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>

---------

Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-02-13 17:23:13 +00:00
Keith Zantow 890fb3f0e8 fix: missing APK node vulnerabilities (#1565)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2023-02-10 15:52:13 -05:00
Weston Steimel e236054668 fix: python CPE generation for alpine (#1564)
Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
2023-02-10 18:04:16 +00:00
dependabot[bot] 3013c8b691 chore(deps): bump github.com/docker/docker (#1563)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 23.0.0+incompatible to 23.0.1+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v23.0.0...v23.0.1)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-10 10:43:19 -05:00
87 changed files with 11894 additions and 5911 deletions
+2 -2
View File
@@ -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**:
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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/**/*
+3 -3
View File
@@ -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
+18 -3
View File
@@ -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
}
+3 -2
View File
@@ -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
`
)
+17 -8
View File
@@ -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)
}
+1 -4
View File
@@ -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
+7 -7
View File
@@ -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
+14 -13
View File
@@ -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=
+1 -1
View File
@@ -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
+54 -2
View File
@@ -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
+3 -5
View File
@@ -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,
+3 -5
View File
@@ -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,
+4 -6
View File
@@ -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,
+1 -6
View File
@@ -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
+49
View File
@@ -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 {
+1
View File
@@ -213,5 +213,6 @@ func TestEncodeFullJSONDocument(t *testing.T) {
s,
*updateJson,
true,
schemaVersionRedactor,
)
}
+2 -1
View File
@@ -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 {
+1
View File
@@ -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 {
+1
View File
@@ -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
View File
@@ -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 {
+1
View File
@@ -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
+7 -6
View File
@@ -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",
},
},
{
+1
View File
@@ -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
+2
View File
@@ -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)
}
+9
View File
@@ -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) {
+8 -3
View File
@@ -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"`
}
+3 -4
View File
@@ -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(
+15 -2
View File
@@ -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 {
+30 -20
View File
@@ -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
+279 -151
View File
@@ -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()
+21 -3
View File
@@ -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>
@@ -0,0 +1 @@
#define PY_VERSION 3.8.16
+17 -13
View File
@@ -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) {
+67
View File
@@ -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)
+21 -1
View File
@@ -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
}
+139 -2
View File
@@ -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)
})
}
}
+131
View File
@@ -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
}
+443
View File
@@ -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 {
+34 -3
View File
@@ -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")
})
}
}
+235
View File
@@ -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)
})
}
}
+66 -55
View File
@@ -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">>}]}
].
+11 -3
View File
@@ -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,
+1 -3
View File
@@ -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,
+2 -2
View File
@@ -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.
+2 -7
View File
@@ -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,
}
+50
View File
@@ -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
View File
@@ -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 {