Compare commits

...
25 Commits
Author SHA1 Message Date
Alex Goodman e415bb21e7 Update write permissions and log into ghcr.io for release (#942) 2022-04-06 21:15:55 +00:00
Alex Goodman 748cfbf006 Retry auth URL lookup without docker credentialhelper workaround (#939) 2022-04-06 16:27:13 +00:00
Sambhav KothariandAlex Goodman 8bc5d84481 Ensure that all cyclonedx components have bom-refs (#914)
Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
2022-04-01 12:19:30 -04:00
Alex Goodman 68b7ad9770 Additionally publish docker images to GHCR (#934) 2022-04-01 11:30:21 -04:00
Oscar Hallgren 1aeda6bb50 use filepath.Base() instead of path.Base() for temp files (#882) 2022-04-01 10:42:22 -04:00
Alex Goodman f24bbc1838 Deduplicate packages across multiple container image layers (#930) 2022-03-31 15:45:51 -04:00
Eric LarssenandAlex Goodman cb3e73e308 Add dart support (#919)
Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
2022-03-31 15:44:55 -04:00
Alex Goodman f157d7a862 Pull from DockerHub fails for public images when using SSO (#928) 2022-03-30 17:32:49 +00:00
Dan Luhring 028cd9e27e Fix nil pointer dereference in directory resolver's indexPath method (#924)
* Add failing test for dir resolver panic

Signed-off-by: Dan Luhring <dan+github@luhrings.com>

* Fix panic

Signed-off-by: Dan Luhring <dan+github@luhrings.com>
2022-03-28 13:15:09 -04:00
Dan Luhring 5549939cc6 Fixups and clarifications in README (#920)
Signed-off-by: Dan Luhring <dan+github@luhrings.com>
2022-03-25 11:36:43 -04:00
briankoe741 47ea910868 Remove announcement for OSS Meetup (#915)
Proposing changes to remove our 3/23 meetup

Signed-off-by: Dan Luhring <dan+github@luhrings.com>
2022-03-25 00:17:14 +00:00
Dan Luhring a7db43f5ec Fix panic on empty sbom (#917)
* Implement fmt.Stringer with format.ID

Signed-off-by: Dan Luhring <dan+github@luhrings.com>

* Add failing test for formats processing empty SBOMs

Signed-off-by: Dan Luhring <dan+github@luhrings.com>

* Account for nil SPDX document during Syft model conversion

Signed-off-by: Dan Luhring <dan+github@luhrings.com>
2022-03-24 10:11:51 -04:00
Alex Goodman cc2c0e57a0 bump strset version to fix 386 builds (#911) 2022-03-23 14:34:54 -04:00
Alex Goodman 5253da4b36 Rollback referencing docker config items (#912) 2022-03-23 18:33:41 +00:00
Jonas XavierandAlex Goodman c0b547bdb2 Less verbose logging in Golang Cataloger (#904)
* Less verbose logging in Golang Cataloger

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* debug for known gray errors

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* only show warnings when a binary is not a go executable

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
2022-03-22 10:19:18 -07:00
Alex Goodman cffcaf5984 Improve docker config support (#906) 2022-03-22 11:02:54 -04:00
Alex Goodman 7f9edf346a Bump golangci-lint to 1.45.0 (#909) 2022-03-22 11:02:36 -04:00
j-k a644a45ef4 Correct go.mod to enforce go 1.18 (#897)
Since syft now depends on debug/buildinfo go 1.18 is required to build
syft and as such go.mod needs updating

Signed-off-by: 06kellyjac <jack@control-plane.io>
2022-03-21 15:38:32 -04:00
Jonas Xavier 283db88dc4 Omit H1Digest when empty (#902)
* Omit HD1Field when empty

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* update test-fixtures

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>
2022-03-21 11:59:10 -07:00
Alex Goodman 03e193e577 Add platform option to the README (#889) 2022-03-21 12:02:15 -04:00
Alex Goodman 069aa68b63 Fix image cleanup when there is an error (#905) 2022-03-21 14:48:11 +00:00
Keith Zantow 9240860f44 Correct ID handling during Syft JSON decoding (#900) 2022-03-18 17:03:26 -04:00
Christopher Angelo Phillips 4231f38fa2 add case to decode GolangBinMetadata for syftjson model (#901)
Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2022-03-18 15:15:10 -04:00
Dan Luhring 752b03b2d6 Remove commit signing requirement (#899)
Signed-off-by: Dan Luhring <dan+github@luhrings.com>
2022-03-18 12:48:00 -04:00
Keith Zantow 99c3339810 Fix CycloneDX license decoding panic (#898) 2022-03-18 09:44:51 -04:00
111 changed files with 4728 additions and 652 deletions
@@ -21,6 +21,14 @@ fi
if [ -z "$DOCKER_PASSWORD" ]; then
exit_with_error "DOCKER_PASSWORD not set"
fi
if [ -z "$GHCR_USERNAME" ]; then
exit_with_error "GHCR_USERNAME not set"
fi
if [ -z "$GHCR_PASSWORD" ]; then
exit_with_error "GHCR_PASSWORD not set"
fi
set -u
# setup_signing
@@ -41,4 +49,5 @@ setup_signing() {
commentary "log into docker -- required for publishing (since the default keychain has now been replaced)"
echo "${DOCKER_PASSWORD}" | docker login docker.io -u "${DOCKER_USERNAME}" --password-stdin
echo "${GHCR_PASSWORD}" | docker login ghcr.io -u "${GHCR_USERNAME}" --password-stdin
}
+5 -12
View File
@@ -34,16 +34,6 @@ jobs:
checkName: "Static analysis"
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Check static analysis results - golangci-lint
uses: fountainhead/action-wait-for-check@v1.0.0
id: static-analysis-golangci-lint
with:
token: ${{ secrets.GITHUB_TOKEN }}
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
checkName: "Static analysis - golangci-lint"
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Check unit test results
uses: fountainhead/action-wait-for-check@v1.0.0
id: unit
@@ -90,10 +80,9 @@ jobs:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Quality gate
if: steps.static-analysis.outputs.conclusion != 'success' || steps.static-analysis-golangci-lint.outputs.conclusion != 'success' || steps.unit.outputs.conclusion != 'success' || steps.integration.outputs.conclusion != 'success' || steps.cli-linux.outputs.conclusion != 'success' || steps.acceptance-linux.outputs.conclusion != 'success' || steps.acceptance-mac.outputs.conclusion != 'success'
if: steps.static-analysis.outputs.conclusion != 'success' || steps.unit.outputs.conclusion != 'success' || steps.integration.outputs.conclusion != 'success' || steps.cli-linux.outputs.conclusion != 'success' || steps.acceptance-linux.outputs.conclusion != 'success' || steps.acceptance-mac.outputs.conclusion != 'success'
run: |
echo "Static Analysis Status: ${{ steps.static-analysis.conclusion }}"
echo "Static Analysis Status - Golangci-Lint: ${{ steps.static-analysis-golangci-lint.conclusion }}"
echo "Unit Test Status: ${{ steps.unit.outputs.conclusion }}"
echo "Integration Test Status: ${{ steps.integration.outputs.conclusion }}"
echo "Acceptance Test (Linux) Status: ${{ steps.acceptance-linux.outputs.conclusion }}"
@@ -105,6 +94,8 @@ jobs:
needs: [quality-gate]
# due to our code signing process, it's vital that we run our release steps on macOS
runs-on: macos-latest
permissions:
packages: write
steps:
- uses: docker-practice/actions-setup-docker@v1
@@ -139,6 +130,8 @@ jobs:
- name: Build & publish release artifacts
run: make release
env:
GHCR_USERNAME: ${{ github.actor }}
GHCR_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
DOCKER_USERNAME: ${{ secrets.TOOLBOX_DOCKER_USER }}
DOCKER_PASSWORD: ${{ secrets.TOOLBOX_DOCKER_PASS }}
# we use a different token than GITHUB_SECRETS to additionally allow updating the homebrew repos
-39
View File
@@ -11,45 +11,6 @@ env:
GO_STABLE_VERSION: true
jobs:
# TODO: remove this job once golanci-lint is compatible with g01.18+
Static-Analysis-Golanci-lint:
name: "Static analysis - golangci-lint"
runs-on: ubuntu-20.04
steps:
- uses: actions/setup-go@v2
with:
go-version: "1.17" # NOTE: please use GO_VERSION once golangci supports go1.18+
stable: ${{ env.GO_STABLE_VERSION }}
- uses: actions/checkout@v2
- name: Restore tool cache
id: tool-cache
uses: actions/cache@v2.1.3
with:
path: ${{ github.workspace }}/.tmp
key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
- name: Restore go cache
id: go-cache
uses: actions/cache@v2.1.3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-${{ env.GO_VERSION }}-
- name: (cache-miss) Bootstrap all project dependencies
if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true'
run: make bootstrap
- name: Bootstrap CI environment dependencies
run: make ci-bootstrap
- name: Run static analysis
run: make static-analysis-golanci-lint
Static-Analysis:
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
+16
View File
@@ -99,6 +99,11 @@ dockers:
- "anchore/syft:{{ .Tag }}-amd64"
- "anchore/syft:v{{ .Major }}-amd64"
- "anchore/syft:v{{ .Major }}.{{ .Minor }}-amd64"
- "ghcr.io/anchore/syft:latest"
- "ghcr.io/anchore/syft:{{ .Tag }}-amd64"
- "ghcr.io/anchore/syft:v{{ .Major }}-amd64"
- "ghcr.io/anchore/syft:v{{ .Major }}.{{ .Minor }}-amd64"
goarch: amd64
dockerfile: Dockerfile
use: buildx
build_flag_templates:
@@ -112,6 +117,9 @@ dockers:
- "anchore/syft:{{ .Tag }}-arm64v8"
- "anchore/syft:v{{ .Major }}-arm64v8"
- "anchore/syft:v{{ .Major }}.{{ .Minor }}-arm64v8"
- "ghcr.io/anchore/syft:{{ .Tag }}-arm64v8"
- "ghcr.io/anchore/syft:v{{ .Major }}-arm64v8"
- "ghcr.io/anchore/syft:v{{ .Major }}.{{ .Minor }}-arm64v8"
goarch: arm64
dockerfile: Dockerfile
use: buildx
@@ -131,3 +139,11 @@ docker_manifests:
image_templates:
- anchore/syft:v{{ .Major }}.{{ .Minor }}-amd64
- anchore/syft:v{{ .Major }}.{{ .Minor }}-arm64v8
- name_template: ghcr.io/anchore/syft:{{ .Tag }}
image_templates:
- ghcr.io/anchore/syft:v{{ .Major }}.{{ .Minor }}-amd64
- ghcr.io/anchore/syft:v{{ .Major }}.{{ .Minor }}-arm64v8
- name_template: ghcr.io/anchore/syft:latest
image_templates:
- ghcr.io/anchore/syft:v{{ .Major }}.{{ .Minor }}-amd64
- ghcr.io/anchore/syft:v{{ .Major }}.{{ .Minor }}-arm64v8
-6
View File
@@ -82,12 +82,6 @@ Date: Mon Aug 1 11:27:13 2020 -0400
[//]: # (TODO: PR title and description)
## Sign your commits
To ensure the authenticity and integrity of code contributions, **we require that all commits are signed**. Signing commits proves that your commits were truly created by you, as the holder of a private key.
Configuring git to sign your commits is a straightforward process. To get set up, see [GitHub's documentation on signing commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits).
## Test your changes
This project has a `Makefile` which includes many helpers running both unit and integration tests. Although PRs will have automatic checks for these, it is useful to run them locally, ensuring they pass before submitting changes. Ensure you've bootstrapped once before running tests:
+2 -7
View File
@@ -106,7 +106,7 @@ $(TEMPDIR):
.PHONY: bootstrap-tools
bootstrap-tools: $(TEMPDIR)
GO111MODULE=off GOBIN=$(shell realpath $(TEMPDIR)) go get -u golang.org/x/perf/cmd/benchstat
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TEMPDIR)/ v1.42.1
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TEMPDIR)/ v1.45.0
curl -sSfL https://raw.githubusercontent.com/wagoodman/go-bouncer/master/bouncer.sh | sh -s -- -b $(TEMPDIR)/ v0.3.0
curl -sSfL https://raw.githubusercontent.com/anchore/chronicle/main/install.sh | sh -s -- -b $(TEMPDIR)/ v0.3.0
.github/scripts/goreleaser-install.sh -d -b $(TEMPDIR)/ v1.4.1
@@ -122,12 +122,7 @@ bootstrap: $(RESULTSDIR) bootstrap-go bootstrap-tools ## Download and install al
$(call title,Bootstrapping dependencies)
.PHONY: static-analysis
static-analysis: check-go-mod-tidy check-licenses
# NOTE: isolating golanci-lint so it runs over go1.17 in CI, since it is not compatible with
# go1.18+
.PHONY: static-analysis-golanci-lint
static-analysis-golanci-lint: lint
static-analysis: check-go-mod-tidy check-licenses lint
.PHONY: lint
lint: ## Run gofmt + golangci lint checks
+62 -68
View File
@@ -9,15 +9,7 @@
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/anchore/syft/blob/main/LICENSE)
[![Slack Invite](https://img.shields.io/badge/Slack-Join-blue?logo=slack)](https://anchore.com/slack)
A CLI tool and Go library for generating a Software Bill of Materials (SBOM) from container images and filesystems. Exceptional for vulnerability detection when used with a scanner tool like [Grype](https://github.com/anchore/grype).
### Join our Virtual OSS Meetup!
You are invited to join us on March 23rd, 11AM-Noon PT for our virtual open source meetup.
Hosts Priyanka “Pinky” Ravi of Weaveworks and Dan Luhring from Anchore OSS will explore how open source project Flux is utilizing Syft-generated SBOMs to secure the software supply chain.
[Register here!](https://get.anchore.com/anchore-oss-meetup-mar-23-2022/)
A CLI tool and Go library for generating a Software Bill of Materials (SBOM) from container images and filesystems. Exceptional for vulnerability detection when used with a scanner like [Grype](https://github.com/anchore/grype).
### Join our community meetings!
@@ -28,15 +20,26 @@ Hosts Priyanka “Pinky” Ravi of Weaveworks and Dan Luhring from Anchore OSS w
![syft-demo](https://user-images.githubusercontent.com/590471/90277200-2a253000-de33-11ea-893f-32c219eea11a.gif)
## Features
- Catalog container images and filesystems to discover packages and libraries.
- Generate in-toto attestations where an SBOM is included as the payload.
- Supports packages and libraries from various ecosystems (APK, DEB, RPM, Ruby Bundles, Python Wheel/Egg/requirements.txt, JavaScript NPM/Yarn, Java JAR/EAR/WAR/PAR/SAR, Jenkins plugins JPI/HPI, Go modules, PHP Composer)
- Linux distribution identification (supports Alpine, BusyBox, CentOS/RedHat, Debian/Ubuntu flavored distributions)
- Supports Docker and OCI image formats
- Direct support for [Grype](https://github.com/anchore/grype), a fast and powerful vulnerability matcher.
- Generates SBOMs for container images, filesystems, archives, and more to discover packages and libraries
- Supports OCI and Docker image formats
- Linux distribution identification
- Works seamlessly with [Grype](https://github.com/anchore/grype) (a fast, modern vulnerability scanner)
- Able to create signed SBOM attestations using the [in-toto specification](https://github.com/in-toto/attestation/blob/main/spec/README.md)
### Supported Ecosystems
If you encounter an issue, please [let us know using the issue tracker](https://github.com/anchore/syft/issues).
- Alpine (apk)
- Dart (pubs)
- Debian (dpkg)
- Go (go.mod, Go binaries)
- Java (jar, ear, war, par, sar)
- JavaScript (npm, yarn)
- Jenkins Plugins (jpi, hpi)
- PHP (composer)
- Python (wheel, egg, poetry, requirements.txt)
- Red Hat (rpm)
- Ruby (gem)
- Rust (cargo.lock)
## Installation
@@ -61,15 +64,13 @@ brew install syft
### Nix
**Note**: nix packaging of Syft is [community maintained](https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/admin/syft/default.nix)
Also syft is currently only in the [unstable channel](https://nixos.wiki/wiki/Nix_channels#The_official_channels) awaiting the `22.05` release
**Note**: Nix packaging of Syft is [community maintained](https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/admin/syft/default.nix). Syft is currently only in the [unstable channel](https://nixos.wiki/wiki/Nix_channels#The_official_channels) awaiting the `22.05` release
```bash
nix-env -i syft
```
... or, just try it out in an ephemeral nix shell
... or, just try it out in an ephemeral nix shell:
```bash
nix-shell -p syft
@@ -78,33 +79,28 @@ nix-shell -p syft
## Getting started
#### SBOM
To generate an SBOM for an OCI image:
To generate an SBOM for a container image:
```
syft <image>
```
**Note**: This is equivalent to specifying the `packages` subcommand:
```
syft packages <image>
```
The above output includes only software that is visible in the container (i.e., the squashed representation of the image).
To include software from all image layers in the SBOM, regardless of its presence in the final image, provide `--scope all-layers`:
The above output includes only software that is visible in the container (i.e., the squashed representation of the image). To include software from all image layers in the SBOM, regardless of its presence in the final image, provide `--scope all-layers`:
```
syft packages <image> --scope all-layers
syft <image> --scope all-layers
```
#### SBOM Attestation
To generate an attested SBOM for an OCI image as the predicate of an in-toto attestation
#### SBOM attestation
To generate an SBOM attestation for a container image:
```
syft attest --output [FORMAT] --key [KEY] [SOURCE] [flags]
```
The above output is in the form of the [DSSE envelope](https://github.com/secure-systems-lab/dsse/blob/master/envelope.md#dsse-envelope).
The payload is a base64 encoded `in-toto` statement with the SBOM as the predicate, the payload type is `application/vnd.in-toto+json`, and the signatures array is populated
with the contents needed for public key verification. For details on workflows using this command see [here](#adding-an-sbom-to-an-image-as-an-attestation-using-syft).
The payload is a base64 encoded `in-toto` statement with the generated SBOM as the predicate. For details on workflows using this command see [here](#adding-an-sbom-to-an-image-as-an-attestation-using-syft).
### Supported sources
@@ -112,10 +108,10 @@ Syft can generate a SBOM from a variety of sources:
```
# catalog a container image archive (from the result of `docker image save ...`, `podman save ...`, or `skopeo copy` commands)
syft packages path/to/image.tar
syft path/to/image.tar
# catalog a directory
syft packages path/to/dir
syft path/to/dir
```
Sources can be explicitly provided with a scheme:
@@ -154,7 +150,7 @@ The output format for Syft is configurable as well using the
`-o` (or `--output`) option:
```
syft packages <image> -o <format>
syft <image> -o <format>
```
Where the `formats` available are:
@@ -172,20 +168,16 @@ Syft can also output _multiple_ files in differing formats by appending
`=<file>` to the option, for example to output Syft JSON and SPDX JSON:
```shell
syft packages <image> -o json=sbom.syft.json -o spdx-json=sbom.spdx.json
syft <image> -o json=sbom.syft.json -o spdx-json=sbom.spdx.json
```
## Private Registry Authentication
### Local Docker Credentials
When a container runtime is not present, Syft can still utilize credentials configured in common credential sources (such as `~/.docker/config.json`).
It will pull images from private registries using these credentials. The config file is where your credentials are stored when authenticating with private registries via some command like `docker login`.
For more information see the `go-containerregistry` [documentation](https://github.com/google/go-containerregistry/tree/main/pkg/authn).
When a container runtime is not present, Syft can still utilize credentials configured in common credential sources (such as `~/.docker/config.json`). It will pull images from private registries using these credentials. The config file is where your credentials are stored when authenticating with private registries via some command like `docker login`. For more information see the `go-containerregistry` [documentation](https://github.com/google/go-containerregistry/tree/main/pkg/authn).
An example `config.json` looks something like this:
```
// config.json
```json
{
"auths": {
"registry.example.com": {
@@ -198,15 +190,17 @@ An example `config.json` looks something like this:
You can run the following command as an example. It details the mount/environment configuration a container needs to access a private registry:
`docker run -v ./config.json:/config/config.json -e "DOCKER_CONFIG=/config" anchore/syft:latest <private_image>`
```
docker run -v ./config.json:/config/config.json -e "DOCKER_CONFIG=/config" anchore/syft:latest <private_image>
```
### Docker Credentials in Kubernetes
The below section shows a simple workflow on how to mount this config file as a secret into a container on kubernetes.
1. Create a secret. The value of `config.json` is important. It refers to the specification detailed [here](https://github.com/google/go-containerregistry/tree/main/pkg/authn#the-config-file).
Below this section is the `secret.yaml` file that the pod configuration will consume as a volume.
The key `config.json` is important. It will end up being the name of the file when mounted into the pod.
```
Here's a simple workflow to mount this config file as a secret into a container on Kubernetes.
1. Create a secret. The value of `config.json` is important. It refers to the specification detailed [here](https://github.com/google/go-containerregistry/tree/main/pkg/authn#the-config-file). Below this section is the `secret.yaml` file that the pod configuration will consume as a volume. The key `config.json` is important. It will end up being the name of the file when mounted into the pod.
```yaml
# secret.yaml
apiVersion: v1
@@ -221,11 +215,9 @@ The key `config.json` is important. It will end up being the name of the file wh
`kubectl apply -f secret.yaml`
2. Create your pod running syft. The env `DOCKER_CONFIG` is important because it advertises where to look for the credential file.
In the below example, setting `DOCKER_CONFIG=/config` informs syft that credentials can be found at `/config/config.json`.
This is why we used `config.json` as the key for our secret. When mounted into containers the secrets' key is used as the filename.
The `volumeMounts` section mounts our secret to `/config`. The `volumes` section names our volume and leverages the secret we created in step one.
```
2. Create your pod running syft. The env `DOCKER_CONFIG` is important because it advertises where to look for the credential file. In the below example, setting `DOCKER_CONFIG=/config` informs syft that credentials can be found at `/config/config.json`. This is why we used `config.json` as the key for our secret. When mounted into containers the secrets' key is used as the filename. The `volumeMounts` section mounts our secret to `/config`. The `volumes` section names our volume and leverages the secret we created in step one.
```yaml
# pod.yaml
apiVersion: v1
@@ -254,10 +246,9 @@ The `volumeMounts` section mounts our secret to `/config`. The `volumes` section
`kubectl apply -f pod.yaml`
3. The user can now run `kubectl logs syft-private-registry-demo`. The logs should show the syft analysis for the `<private_image>` provided in the pod configuration.
3. The user can now run `kubectl logs syft-private-registry-demo`. The logs should show the Syft analysis for the `<private_image>` provided in the pod configuration.
Using the above information, users should be able to configure private registry access without having to do so in the `grype` or `syft` configuration files.
They will also not be dependent on a docker daemon, (or some other runtime software) for registry configuration and access.
Using the above information, users should be able to configure private registry access without having to do so in the `grype` or `syft` configuration files. They will also not be dependent on a Docker daemon, (or some other runtime software) for registry configuration and access.
## Configuration
@@ -294,7 +285,11 @@ check-for-app-update: true
# exclude:
# - "/etc/**"
# - "./out/**/*.json"
exclude:
exclude: []
# os and/or architecture to use when referencing container images (e.g. "windows/armv6" or "arm64")
# same as --platform; SYFT_PLATFORM env var
platform: ""
# cataloging packages is exposed through the packages and power-user subcommands
package:
@@ -468,26 +463,25 @@ anchore:
`syft attest --output [FORMAT] --key [KEY] [SOURCE] [flags]`
SBOMs themselves can serve as input to different analysis tools. The Anchore organization offers the vulnerability scanner
[grype](https://github.com/anchore/grype) as one such tool.
One of the foundational approaches to "trust" between tools is for producers to use the artifacts generated by syft as attestations to their images.
The DSSE output of `syft attest` can be used with the [cosign](https://github.com/sigstore/cosign) tool to attach an attestation to an image.
SBOMs themselves can serve as input to different analysis tools. [Grype](https://github.com/anchore/grype), a vulnerability scanner CLI tool from Anchore, is one such tool. Publishers of container images can use attestations to enable their consumers to trust Syft-generated SBOM descriptions of those container images. To create and provide these attestations, image publishers can run `syft attest` in conjunction with the [cosign](https://github.com/sigstore/cosign) tool to attach SBOM attestations to their images.
#### Example attest
#### Example attestation
Note for the following example replace `docker.io/image:latest` with an image you own. You should also have push access to
its remote reference. Replace $MY_PRIVATE_KEY with a private key you own or have generated with cosign.
its remote reference. Replace `$MY_PRIVATE_KEY` with a private key you own or have generated with cosign.
```bash
syft attest --key $MY_PRIVATE_KEY docker.io/image:latest > image_latest_sbom_attestation.json
cosign attach attestation --attestation image_latest_sbom_attestation.json docker.io/image:latest
```
Verify the new attestation exists on your image
Verify the new attestation exists on your image.
```bash
cosign verify-attestation -key $MY_PUBLIC_KEY docker.io/image:latest | jq '.payload | @base64d | .payload | fromjson | .predicate'
```
You should see this output along with the attached SBOM.
You should see this output along with the attached SBOM:
```
Verification for docker.io/image:latest --
The following checks were performed on each of these signatures:
+4 -3
View File
@@ -1,6 +1,6 @@
module github.com/anchore/syft
go 1.17
go 1.18
require (
github.com/CycloneDX/cyclonedx-go v0.5.0
@@ -14,7 +14,7 @@ require (
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b
github.com/anchore/stereoscope v0.0.0-20220315185520-25183ec78f40
github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270
github.com/antihax/optional v1.0.0
github.com/bmatcuk/doublestar/v4 v4.0.2
github.com/dustin/go-humanize v1.0.0
@@ -34,7 +34,8 @@ require (
github.com/olekukonko/tablewriter v0.0.5
github.com/pelletier/go-toml v1.9.4
github.com/pkg/profile v1.5.0
github.com/scylladb/go-set v1.0.2
// pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
github.com/sergi/go-diff v1.2.0
github.com/sigstore/sigstore v1.1.1-0.20220217212907-e48ca03a5ba7
github.com/sirupsen/logrus v1.8.1
+4 -69
View File
@@ -106,9 +106,7 @@ contrib.go.opencensus.io/exporter/zipkin v0.1.2/go.mod h1:mP5xM3rrgOjpn79MM8fZbj
contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
cuelang.org/go v0.4.2/go.mod h1:P09/R4UfAEzLkV9DXxwlxQnIZbkaT4uIhiEgs6Vsz2Q=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20211102141018-f7be0cbad29c/go.mod h1:WpB7kf89yJUETZxQnP1kgYPNwlT2jjdDYUCoxVggM3g=
github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo=
@@ -126,7 +124,6 @@ github.com/Azure/azure-sdk-for-go v51.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
github.com/Azure/azure-sdk-for-go v59.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v60.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v60.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v61.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v61.5.0+incompatible h1:OSHSFeNm7D1InGsQrFjyN9hpxD5Ec60PdsWWudCpah4=
github.com/Azure/azure-sdk-for-go v61.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0=
@@ -227,18 +224,15 @@ github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:m
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
github.com/PaesslerAG/gval v1.0.0 h1:GEKnRwkWDdf9dOmKcNrar9EA1bz1z9DqPIO1+iLzhd8=
github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I=
github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8=
github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk=
github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/ReneKroon/ttlcache/v2 v2.10.0/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY=
@@ -286,8 +280,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.20220314153042-1bcd40e5206b h1:YJWYt/6KQXR9JR46lLHrTTYi8rcye42tKcyjREA/hvA=
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
github.com/anchore/stereoscope v0.0.0-20220315185520-25183ec78f40 h1:OSyFMZgEwQW0wFyv10kEi9kmB52FQFRUQmc2H6d8LTY=
github.com/anchore/stereoscope v0.0.0-20220315185520-25183ec78f40/go.mod h1:OVVJ4y/L26pspeeHNvNa7GeQfoLPWxJe13iODl9x5l4=
github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270 h1:NmxPDR6vo3xjwCL6o+tpF1vUad/BVo+WaVSwueB9W9w=
github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270/go.mod h1:yoCLUZY0k/pYLNIy0L80p2Ko0PKVNXm8rHtgxp4OiSc=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
@@ -437,7 +431,6 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR
github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=
github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradleyfalzon/ghinstallation/v2 v2.0.3/go.mod h1:tlgi+JWCXnKFx/Y4WtnDbZEINo31N5bcvnCoqieefmk=
github.com/bradleyjkemp/cupaloy/v2 v2.7.0 h1:AT0vOjO68RcLyenLCHOGZzSNiuto7ziqzq6Q1/3xzMQ=
github.com/bradleyjkemp/cupaloy/v2 v2.7.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/breml/bidichk v0.1.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso=
@@ -447,8 +440,6 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8n
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc=
github.com/bytecodealliance/wasmtime-go v0.31.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI=
github.com/bytecodealliance/wasmtime-go v0.33.1/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI=
github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw=
github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo=
@@ -457,7 +448,6 @@ github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n
github.com/cavaliercoder/badio v0.0.0-20160213150051-ce5280129e9e/go.mod h1:V284PjgVwSk4ETmz84rpu9ehpGg7swlIH8npP9k2bGw=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
github.com/cavaliercoder/go-rpm v0.0.0-20200122174316-8cb9fd9c31a8/go.mod h1:AZIh1CCnMrcVm6afFf96PBvE2MRpWFco91z8ObJtgDY=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
@@ -470,7 +460,6 @@ github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
@@ -505,8 +494,6 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH
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 h1:KwaoQzs/WeUxxJqiJsZ4euOly1Az/IgZXXSxlD/UBNk=
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/apd/v2 v2.0.1/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
@@ -616,7 +603,6 @@ github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw=
github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
@@ -626,7 +612,6 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a h1:W8b4lQ4tFF21aspRGoBuCNV6V2fFJBF+pm1J6OY8Lys=
github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
@@ -634,7 +619,6 @@ github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzA
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@@ -664,12 +648,8 @@ github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCF
github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M=
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-gk v0.0.0-20140819190930-201884a44051/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E=
github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E=
github.com/dgryski/go-lttb v0.0.0-20180810165845-318fcdf10a77/go.mod h1:Va5MyIzkU0rAM92tn3hb3Anb7oz7KcnixF49+2wOMe4=
@@ -722,7 +702,6 @@ github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaB
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/proto v1.6.15/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -770,7 +749,6 @@ github.com/form3tech-oss/jwt-go v3.2.5+incompatible h1:/l4kBbb4/vGSsdtB5nUe8L7B9
github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
@@ -851,14 +829,12 @@ github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpX
github.com/go-openapi/errors v0.20.1/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8=
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
@@ -888,7 +864,6 @@ github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pt
github.com/go-openapi/runtime v0.21.0/go.mod h1:aQg+kaIQEn+A2CRSY1TxbM8+sT9g2V3aLc1FbIAnbbs=
github.com/go-openapi/runtime v0.23.1 h1:/Drg9R96eMmgKJHVWZADz78XbE39/6QiIiB45mc+epo=
github.com/go-openapi/runtime v0.23.1/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
@@ -915,7 +890,6 @@ github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+W
github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
github.com/go-openapi/strfmt v0.21.2 h1:5NDNgadiX1Vhemth/TH4gCGopWSTdDjxl60H3B7f+os=
github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
@@ -1112,7 +1086,6 @@ github.com/google/certificate-transparency-go v1.1.2-0.20210422104406-9f33727a7a
github.com/google/certificate-transparency-go v1.1.2-0.20210512142713-bed466244fa6/go.mod h1:aF2dp7Dh81mY8Y/zpzyXps4fQW5zQbDu2CxfpJB6NkI=
github.com/google/certificate-transparency-go v1.1.2 h1:4hE0GEId6NAW28dFpC+LrRGwQX5dtmXQGDbg8+/MZOM=
github.com/google/certificate-transparency-go v1.1.2/go.mod h1:3OL+HKDqHPUfdKrHVQxO6T8nDLO0HF7LRTlkIWXaWvQ=
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -1130,14 +1103,10 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-containerregistry v0.7.0/go.mod h1:2zaoelrL0d08gGbpdP3LqyUuBmhWbpD6IOe2s9nLS2k=
github.com/google/go-containerregistry v0.7.1-0.20211118220127-abdc633f8305/go.mod h1:6cMIl1RfryEiPzBE67OgtZdEiLWz4myqCQIiBMy3CsM=
github.com/google/go-containerregistry v0.8.0/go.mod h1:wW5v71NHGnQyb4k+gSshjxidrC7lN33MdWEn+Mz9TsI=
github.com/google/go-containerregistry v0.8.1-0.20220110151055-a61fd0a8e2bb/go.mod h1:wW5v71NHGnQyb4k+gSshjxidrC7lN33MdWEn+Mz9TsI=
github.com/google/go-containerregistry v0.8.1-0.20220209165246-a44adc326839 h1:7PunQZxMao2q43If8gKj1JFRzapmhgny9NWwXY4PGa4=
github.com/google/go-containerregistry v0.8.1-0.20220209165246-a44adc326839/go.mod h1:cwx3SjrH84Rh9VFJSIhPh43ovyOp3DCWgY3h8nWmdGQ=
github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20220219142810-1571d7fdc46e/go.mod h1:m6nUV1MgElByyfWJCAKsEt+5s/BtU9ZLjyHDmoeAHcM=
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20220128225446-c63684ed5f15/go.mod h1:juPcrnQFaTyH72YA4Lt2MwEFSGi3orvGXyzxzo+RLss=
github.com/google/go-github/v27 v27.0.6/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-github/v39 v39.0.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
github.com/google/go-github/v42 v42.0.0 h1:YNT0FwjPrEysRkLIiKuEfSvBPCGKphW5aS5PxwaoLec=
github.com/google/go-github/v42 v42.0.0/go.mod h1:jgg/jvyI0YlDOM1/ps6XYh04HNQ3vKf0CVko62/EhRg=
github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M=
@@ -1182,7 +1151,6 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg=
github.com/google/rpmpack v0.0.0-20210518075352-dc539ef4f2ea/go.mod h1:+y9lKiqDhR4zkLl+V9h4q0rdyrYVsWWm6LLCQP33DIk=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw=
github.com/google/trillian v1.3.14-0.20210409160123-c5ea3abd4a41/go.mod h1:1dPv0CUjNQVFEDuAUFhZql16pw/VlPgaX8qj+g5pVzQ=
@@ -1198,7 +1166,6 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s=
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww=
github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
@@ -1302,7 +1269,6 @@ github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ3
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
@@ -1459,7 +1425,6 @@ github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdY
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
@@ -1518,7 +1483,6 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -1528,7 +1492,6 @@ github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
@@ -1586,11 +1549,9 @@ github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Cl
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.17/go.mod h1:WgzbA6oji13JREwiNsRDNfl7jYdPnmz+VEuLrA+/48M=
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
@@ -1654,7 +1615,6 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8=
github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s=
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@@ -1722,7 +1682,6 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/open-policy-agent/opa v0.35.0/go.mod h1:xEmekKlk6/c+so5HF9wtPnGPXDfBuBsrMGhSHOHEF+U=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@@ -1783,7 +1742,6 @@ github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhEC
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@@ -1844,7 +1802,6 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
@@ -1866,7 +1823,6 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/statsd_exporter v0.21.0/go.mod h1:rbT83sZq2V+p73lHhPZfMc3MLCHmSHelCh9hSGYNLTQ=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/protocolbuffers/txtpbfmt v0.0.0-20201118171849-f6a6b3f636fc/go.mod h1:KbKfKPy2I6ecOIGA9apfheFv14+P3RSmmQvshofQyMY=
github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA=
github.com/pseudomuto/protoc-gen-doc v1.4.1/go.mod h1:exDTOVwqpp30eV/EDPFLZy3Pwr2sn6hBC1WIYH/UbIg=
github.com/pseudomuto/protoc-gen-doc v1.5.0/go.mod h1:exDTOVwqpp30eV/EDPFLZy3Pwr2sn6hBC1WIYH/UbIg=
@@ -1882,7 +1838,6 @@ github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:r
github.com/qur/ar v0.0.0-20130629153254-282534b91770/go.mod h1:SjlYv2m9lpV0UW6K7lDqVJwEIIvSjaHbGk7nIfY8Hxw=
github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -1900,7 +1855,6 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM=
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@@ -1923,8 +1877,8 @@ github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74 h1:sUNzanSKA9z/h
github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74/go.mod h1:YlB8wFIZmFLZ1JllNBfSURzz52fBxbliNgYALk1UDmk=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
github.com/scylladb/go-set v1.0.2 h1:SkvlMCKhP0wyyct6j+0IHJkBkSZL+TDzZ4E7f7BCcRE=
github.com/scylladb/go-set v1.0.2/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs=
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/secure-systems-lab/go-securesystemslib v0.2.0/go.mod h1:eIjBmIP8LD2MLBL/DkQWayLiz006Q4p+hCu79rvWleY=
@@ -1984,7 +1938,6 @@ github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4l
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
github.com/spdx/tools-golang v0.2.0 h1:KBNcw7xvVycRWeCWZK/5xQJA+plymW1+rTCs8ekJDro=
github.com/spdx/tools-golang v0.2.0/go.mod h1:RO4Y3IFROJnz+43JKm1YOrbtgQNljW4gAPpA/sY2eqo=
@@ -2147,7 +2100,6 @@ github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb h1:Yz6VVOcLuWLA
github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb/go.mod h1:nDi3BAC5nEbVbg+WSJDHLbjHv0ZToq8nMPA97XMxF3E=
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
github.com/withfig/autocomplete-tools/packages/cobra v0.0.0-20220122124547-31d3821a6898/go.mod h1:cKObXQ6PVFO7bHUd5jpApXvMIt55Ewz7UdMiC05ONxI=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
@@ -2174,7 +2126,6 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b/go.mod h1:HptNXiXVDcJjXe9SqMd0v2FsL9f8dz4GnXgltU6q/co=
github.com/yeya24/promlinter v0.1.0/go.mod h1:rs5vtZzeBHqqMwXqFScncpCF6u06lezhZepno9AB1Oc=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/ysmood/goob v0.3.0/go.mod h1:S3lq113Y91y1UBf1wj1pFOxeahvfKkCk6mTWTWbDdWs=
@@ -2208,7 +2159,6 @@ go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k=
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo=
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
go.etcd.io/etcd/api/v3 v3.5.0-alpha.0/go.mod h1:mPcW6aZJukV6Aa81LSKpBjQXTWlXB5r74ymPoSWa3Sw=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
@@ -2377,14 +2327,12 @@ golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506 h1:EuGTJDfeg/PGZJp3gq1K+14eSLFTsrj1eg8KQuiUyKg=
golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
@@ -2393,7 +2341,6 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20210126221216-84987778548c/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -2410,15 +2357,12 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -2503,7 +2447,6 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -2725,7 +2668,6 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -2775,7 +2717,6 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -2798,7 +2739,6 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200612220849-54c614fe050c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@@ -3159,7 +3099,6 @@ k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs=
k8s.io/api v0.23.3 h1:KNrME8KHGr12Ozjf8ytOewKzZh6hl/hHUZeHddT3a38=
k8s.io/api v0.23.3/go.mod h1:w258XdGyvCmnBj/vGzQMj6kzdufJZVUwEM1U2fRJwSQ=
k8s.io/apiextensions-apiserver v0.22.5/go.mod h1:tIXeZ0BrDxUb1PoAz+tgOz43Zi1Bp4BEEqVtUccMJbE=
k8s.io/apimachinery v0.19.7/go.mod h1:6sRbGRAVY5DOCuZwB5XkqguBqpqLU6q/kOaOdk29z6Q=
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
@@ -3196,7 +3135,6 @@ k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/klog/v2 v2.40.1 h1:P4RRucWk/lFOlDdkAr3mc7iWFkgKrZY9qZMAgek06S4=
k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
@@ -3206,12 +3144,10 @@ k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20220127004650-9b3446523e65 h1:ONWS0Wgdg5wRiQIAui7L/023aC9+IxrIrydY7l8llsE=
k8s.io/utils v0.0.0-20220127004650-9b3446523e65/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
knative.dev/hack v0.0.0-20220118141833-9b2ed8471e30/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
knative.dev/hack/schema v0.0.0-20220224013837-e1785985d364/go.mod h1:ffjwmdcrH5vN3mPhO8RrF2KfNnbHeCE2C60A+2cv3U0=
knative.dev/pkg v0.0.0-20220202132633-df430fa0dd96 h1:JU0DFa06CaUtwSkAY0b3j47ohEJLIYlpPPgNgbPHlAo=
knative.dev/pkg v0.0.0-20220202132633-df430fa0dd96/go.mod h1:etVT7Tm8pSDf4RKhGk4r7j/hj3dNBpvT7bO6a6wpahs=
mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48=
@@ -3232,7 +3168,6 @@ sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87J
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
sigs.k8s.io/release-utils v0.4.1-0.20220207182343-6dadf2228617 h1:qbKzP8fFAdlnj4WqB9KrAuW4ply+bUQkdRlVrH4Jus0=
sigs.k8s.io/release-utils v0.4.1-0.20220207182343-6dadf2228617/go.mod h1:t9pL38kZkTBVDcjL1y7ajrkNQFLiArVAjOVO0sxzFF0=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
+6 -2
View File
@@ -56,7 +56,7 @@ func (c *Client) Import(ctx context.Context, cfg ImportConfig) error {
authedCtx := c.newRequestContext(ctxWithTimeout)
stage.Current = "starting session"
startOperation, _, err := c.client.ImportsApi.CreateOperation(authedCtx)
startOperation, createResponse, err := c.client.ImportsApi.CreateOperation(authedCtx)
if err != nil {
var detail = "no details given"
var openAPIErr external.GenericOpenAPIError
@@ -65,6 +65,8 @@ func (c *Client) Import(ctx context.Context, cfg ImportConfig) error {
}
return fmt.Errorf("unable to start import session: %w: %s", err, detail)
}
defer createResponse.Body.Close()
prog.N++
sessionID := startOperation.Uuid
@@ -98,7 +100,7 @@ func (c *Client) Import(ctx context.Context, cfg ImportConfig) error {
Force: optional.NewBool(cfg.OverwriteExistingUpload),
}
_, _, err = c.client.ImagesApi.AddImage(authedCtx, imageModel, &opts)
_, addResponse, err := c.client.ImagesApi.AddImage(authedCtx, imageModel, &opts)
if err != nil {
var detail = "no details given"
var openAPIErr external.GenericOpenAPIError
@@ -107,6 +109,8 @@ func (c *Client) Import(ctx context.Context, cfg ImportConfig) error {
}
return fmt.Errorf("unable to complete import session=%q: %w: %s", sessionID, err, detail)
}
defer addResponse.Body.Close()
prog.N++
stage.Current = ""
+3 -3
View File
@@ -56,14 +56,14 @@ func sbomFixture() sbom.SBOM {
Name: "name",
Version: "version",
FoundBy: "foundBy",
Locations: []source.Location{
{
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "path",
FileSystemID: "layerID",
},
},
},
),
Licenses: []string{"license"},
Language: pkg.Python,
Type: pkg.PythonPkg,
+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 = "3.1.1"
JSONSchemaVersion = "3.2.2"
)
@@ -5,6 +5,7 @@ import (
"github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal/formats/common"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
@@ -13,8 +14,9 @@ import (
func encodeComponent(p pkg.Package) cyclonedx.Component {
props := encodeProperties(p, "syft:package")
props = append(props, encodeCPEs(p)...)
if len(p.Locations) > 0 {
props = append(props, encodeProperties(p.Locations, "syft:location")...)
locations := p.Locations.ToSlice()
if len(locations) > 0 {
props = append(props, encodeProperties(locations, "syft:location")...)
}
if hasMetadata(p) {
props = append(props, encodeProperties(p.Metadata, "syft:metadata")...)
@@ -38,9 +40,23 @@ func encodeComponent(p pkg.Package) cyclonedx.Component {
Description: encodeDescription(p),
ExternalReferences: encodeExternalReferences(p),
Properties: properties,
BOMRef: deriveBomRef(p),
}
}
func deriveBomRef(p pkg.Package) string {
// try and parse the PURL if possible and append syft id to it, to make
// the purl unique in the BOM.
// TODO: In the future we may want to dedupe by PURL and combine components with
// the same PURL while preserving their unique metadata.
if parsedPURL, err := packageurl.FromString(p.PURL); err == nil {
parsedPURL.Qualifiers = append(parsedPURL.Qualifiers, packageurl.Qualifier{Key: "syft-id", Value: string(p.ID())})
return parsedPURL.ToString()
}
// fallback is to use strictly the ID if there is no valid pURL
return string(p.ID())
}
func hasMetadata(p pkg.Package) bool {
return p.Metadata != nil
}
@@ -73,10 +89,13 @@ func decodeComponent(c *cyclonedx.Component) *pkg.Package {
return p
}
func decodeLocations(vals map[string]string) []source.Location {
func decodeLocations(vals map[string]string) source.LocationSet {
v := common.Decode(reflect.TypeOf([]source.Location{}), vals, "syft:location", CycloneDXFields)
out, _ := v.([]source.Location)
return out
out, ok := v.([]source.Location)
if !ok {
out = nil
}
return source.NewLocationSet(out...)
}
func decodePackageMetadata(vals map[string]string, c *cyclonedx.Component, typ pkg.MetadataType) interface{} {
@@ -1,6 +1,7 @@
package cyclonedxhelpers
import (
"fmt"
"testing"
"github.com/CycloneDX/cyclonedx-go"
@@ -26,9 +27,9 @@ func Test_encodeComponentProperties(t *testing.T) {
name: "from apk",
input: pkg.Package{
FoundBy: "cataloger",
Locations: []source.Location{
{Coordinates: source.Coordinates{RealPath: "test"}},
},
Locations: source.NewLocationSet(
source.Location{Coordinates: source.Coordinates{RealPath: "test"}},
),
Metadata: pkg.ApkMetadata{
Package: "libc-utils",
OriginPackage: "libc-dev",
@@ -139,3 +140,54 @@ func Test_encodeComponentProperties(t *testing.T) {
})
}
}
func Test_deriveBomRef(t *testing.T) {
pkgWithPurl := pkg.Package{
Name: "django",
Version: "1.11.1",
PURL: "pkg:pypi/django@1.11.1",
}
pkgWithPurl.SetID()
pkgWithOutPurl := pkg.Package{
Name: "django",
Version: "1.11.1",
PURL: "",
}
pkgWithOutPurl.SetID()
pkgWithBadPurl := pkg.Package{
Name: "django",
Version: "1.11.1",
PURL: "pkg:pyjango@1.11.1",
}
pkgWithBadPurl.SetID()
tests := []struct {
name string
pkg pkg.Package
want string
}{
{
name: "use pURL-id hybrid",
pkg: pkgWithPurl,
want: fmt.Sprintf("pkg:pypi/django@1.11.1?syft-id=%s", pkgWithPurl.ID()),
},
{
name: "fallback to ID when pURL is invalid",
pkg: pkgWithBadPurl,
want: string(pkgWithBadPurl.ID()),
},
{
name: "fallback to ID when pURL is missing",
pkg: pkgWithOutPurl,
want: string(pkgWithOutPurl.ID()),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.pkg.ID()
assert.Equal(t, tt.want, deriveBomRef(tt.pkg))
})
}
}
@@ -272,4 +272,14 @@ func Test_missingDataDecode(t *testing.T) {
_, err = toSyftModel(bom)
assert.NoError(t, err)
pkg := decodeComponent(&cyclonedx.Component{
Licenses: &cyclonedx.Licenses{
{
License: nil,
},
},
})
assert.Len(t, pkg.Licenses, 0)
}
@@ -26,7 +26,9 @@ func encodeLicenses(p pkg.Package) *cyclonedx.Licenses {
func decodeLicenses(c *cyclonedx.Component) (out []string) {
if c.Licenses != nil {
for _, l := range *c.Licenses {
out = append(out, l.License.ID)
if l.License != nil {
out = append(out, l.License.ID)
}
}
}
return
@@ -13,6 +13,8 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from RPM DB"
case pkg.ApkPkg:
answer = "acquired package info from APK DB"
case pkg.DartPubPkg:
answer = "acquired package info from pubspec manifest"
case pkg.DebPkg:
answer = "acquired package info from DPKG DB"
case pkg.NpmPkg:
@@ -33,7 +35,7 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from the following paths"
}
var paths []string
for _, l := range p.Locations {
for _, l := range p.Locations.ToSlice() {
paths = append(paths, l.RealPath)
}
@@ -18,10 +18,10 @@ func Test_SourceInfo(t *testing.T) {
name: "locations are captured",
input: pkg.Package{
// note: no type given
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/a-place", "/b-place"),
source.NewVirtualLocation("/c-place", "/d-place"),
},
),
},
expected: []string{
"from the following paths",
@@ -126,6 +126,14 @@ func Test_SourceInfo(t *testing.T) {
"from PHP composer manifest",
},
},
{
input: pkg.Package{
Type: pkg.DartPubPkg,
},
expected: []string{
"from pubspec manifest",
},
},
}
var pkgTypes []pkg.Type
for _, test := range tests {
@@ -1,6 +1,7 @@
package spdxhelpers
import (
"errors"
"strconv"
"strings"
@@ -17,6 +18,10 @@ import (
)
func ToSyftModel(doc *spdx.Document2_2) (*sbom.SBOM, error) {
if doc == nil {
return nil, errors.New("cannot convert SPDX document to Syft model because document is nil")
}
spdxIDMap := make(map[string]interface{})
s := &sbom.SBOM{
+12 -12
View File
@@ -157,9 +157,9 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
catalog.Add(pkg.Package{
Name: "package-1",
Version: "1.0.1",
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewLocationFromImage(string(ref1.RealPath), *ref1, img),
},
),
Type: pkg.PythonPkg,
FoundBy: "the-cataloger-1",
Language: pkg.Python,
@@ -169,7 +169,7 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
Name: "package-1",
Version: "1.0.1",
},
PURL: "a-purl-1",
PURL: "a-purl-1", // intentionally a bad pURL for test fixtures
CPEs: []pkg.CPE{
pkg.MustCPE("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*"),
},
@@ -177,9 +177,9 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
catalog.Add(pkg.Package{
Name: "package-2",
Version: "2.0.1",
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewLocationFromImage(string(ref2.RealPath), *ref2, img),
},
),
Type: pkg.DebPkg,
FoundBy: "the-cataloger-2",
MetadataType: pkg.DpkgMetadataType,
@@ -187,7 +187,7 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
Package: "package-2",
Version: "2.0.1",
},
PURL: "a-purl-2",
PURL: "pkg:deb/debian/package-2@2.0.1",
CPEs: []pkg.CPE{
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
},
@@ -234,9 +234,9 @@ func newDirectoryCatalog() *pkg.Catalog {
Version: "1.0.1",
Type: pkg.PythonPkg,
FoundBy: "the-cataloger-1",
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewLocation("/some/path/pkg1"),
},
),
Language: pkg.Python,
MetadataType: pkg.PythonPackageMetadataType,
Licenses: []string{"MIT"},
@@ -249,7 +249,7 @@ func newDirectoryCatalog() *pkg.Catalog {
},
},
},
PURL: "a-purl-2",
PURL: "a-purl-2", // intentionally a bad pURL for test fixtures
CPEs: []pkg.CPE{
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
},
@@ -259,15 +259,15 @@ func newDirectoryCatalog() *pkg.Catalog {
Version: "2.0.1",
Type: pkg.DebPkg,
FoundBy: "the-cataloger-2",
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewLocation("/some/path/pkg1"),
},
),
MetadataType: pkg.DpkgMetadataType,
Metadata: pkg.DpkgMetadata{
Package: "package-2",
Version: "2.0.1",
},
PURL: "a-purl-2",
PURL: "pkg:deb/debian/package-2@2.0.1",
CPEs: []pkg.CPE{
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
},
@@ -1,10 +1,10 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.4",
"serialNumber": "urn:uuid:498e659b-0758-4a7f-816e-91bee18df634",
"serialNumber": "urn:uuid:dec3f6b4-8458-48bb-b60d-dfd312f6ec4e",
"version": 1,
"metadata": {
"timestamp": "2022-03-08T12:30:39Z",
"timestamp": "2022-04-01T11:48:04-04:00",
"tools": [
{
"vendor": "anchore",
@@ -20,6 +20,7 @@
},
"components": [
{
"bom-ref": "b85dbb4e6ece5082",
"type": "library",
"name": "package-1",
"version": "1.0.1",
@@ -56,11 +57,12 @@
]
},
{
"bom-ref": "pkg:deb/debian/package-2@2.0.1?syft-id=ceda99598967ae8d",
"type": "library",
"name": "package-2",
"version": "2.0.1",
"cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*",
"purl": "a-purl-2",
"purl": "pkg:deb/debian/package-2@2.0.1",
"properties": [
{
"name": "syft:package:foundBy",
@@ -1,10 +1,10 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.4",
"serialNumber": "urn:uuid:342c3d2c-d26e-47b6-94d6-92fbf41da945",
"serialNumber": "urn:uuid:054d973e-fe99-4762-92e4-eaf01997ae41",
"version": 1,
"metadata": {
"timestamp": "2022-03-08T12:30:39Z",
"timestamp": "2022-04-01T11:48:04-04:00",
"tools": [
{
"vendor": "anchore",
@@ -13,7 +13,7 @@
}
],
"component": {
"bom-ref": "711095b1cdf90cce",
"bom-ref": "e777314b02b362e4",
"type": "container",
"name": "user-image-input",
"version": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
@@ -21,6 +21,7 @@
},
"components": [
{
"bom-ref": "2a46171f91c8d4bc",
"type": "library",
"name": "package-1",
"version": "1.0.1",
@@ -52,7 +53,7 @@
},
{
"name": "syft:location:0:layerID",
"value": "sha256:16e64541f2ddf59a90391ce7bb8af90313f7d373f2105d88f3d3267b72e0ebab"
"value": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59"
},
{
"name": "syft:location:0:path",
@@ -61,11 +62,12 @@
]
},
{
"bom-ref": "pkg:deb/debian/package-2@2.0.1?syft-id=ae77680e9b1d087e",
"type": "library",
"name": "package-2",
"version": "2.0.1",
"cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*",
"purl": "a-purl-2",
"purl": "pkg:deb/debian/package-2@2.0.1",
"properties": [
{
"name": "syft:package:foundBy",
@@ -81,7 +83,7 @@
},
{
"name": "syft:location:0:layerID",
"value": "sha256:de6c235f76ea24c8503ec08891445b5d6a8bdf8249117ed8d8b0b6fb3ebe4f67"
"value": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec"
},
{
"name": "syft:location:0:path",
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:892f8304-0142-45b1-b411-cade3c53057f" version="1">
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:554fd820-210b-40c8-8c0b-75690274e21c" version="1">
<metadata>
<timestamp>2022-03-08T12:30:33Z</timestamp>
<timestamp>2022-04-01T11:57:46-04:00</timestamp>
<tools>
<tool>
<vendor>anchore</vendor>
@@ -14,7 +14,7 @@
</component>
</metadata>
<components>
<component type="library">
<component bom-ref="b85dbb4e6ece5082" type="library">
<name>package-1</name>
<version>1.0.1</version>
<licenses>
@@ -32,11 +32,11 @@
<property name="syft:location:0:path">/some/path/pkg1</property>
</properties>
</component>
<component type="library">
<component bom-ref="pkg:deb/debian/package-2@2.0.1?syft-id=ceda99598967ae8d" type="library">
<name>package-2</name>
<version>2.0.1</version>
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
<purl>a-purl-2</purl>
<purl>pkg:deb/debian/package-2@2.0.1</purl>
<properties>
<property name="syft:package:foundBy">the-cataloger-2</property>
<property name="syft:package:metadataType">DpkgMetadata</property>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:5fa94827-eb85-4f32-a62d-76fb6e89a2dd" version="1">
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:1535f940-172f-4d97-8280-d5a5764d1557" version="1">
<metadata>
<timestamp>2022-03-08T12:30:33Z</timestamp>
<timestamp>2022-04-01T11:57:46-04:00</timestamp>
<tools>
<tool>
<vendor>anchore</vendor>
@@ -9,13 +9,13 @@
<version>[not provided]</version>
</tool>
</tools>
<component bom-ref="711095b1cdf90cce" type="container">
<component bom-ref="e777314b02b362e4" type="container">
<name>user-image-input</name>
<version>sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368</version>
</component>
</metadata>
<components>
<component type="library">
<component bom-ref="2a46171f91c8d4bc" type="library">
<name>package-1</name>
<version>1.0.1</version>
<licenses>
@@ -30,20 +30,20 @@
<property name="syft:package:language">python</property>
<property name="syft:package:metadataType">PythonPackageMetadata</property>
<property name="syft:package:type">python</property>
<property name="syft:location:0:layerID">sha256:16e64541f2ddf59a90391ce7bb8af90313f7d373f2105d88f3d3267b72e0ebab</property>
<property name="syft:location:0:layerID">sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59</property>
<property name="syft:location:0:path">/somefile-1.txt</property>
</properties>
</component>
<component type="library">
<component bom-ref="pkg:deb/debian/package-2@2.0.1?syft-id=ae77680e9b1d087e" type="library">
<name>package-2</name>
<version>2.0.1</version>
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
<purl>a-purl-2</purl>
<purl>pkg:deb/debian/package-2@2.0.1</purl>
<properties>
<property name="syft:package:foundBy">the-cataloger-2</property>
<property name="syft:package:metadataType">DpkgMetadata</property>
<property name="syft:package:type">deb</property>
<property name="syft:location:0:layerID">sha256:de6c235f76ea24c8503ec08891445b5d6a8bdf8249117ed8d8b0b6fb3ebe4f67</property>
<property name="syft:location:0:layerID">sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec</property>
<property name="syft:location:0:path">/somefile-2.txt</property>
<property name="syft:metadata:installedSize">0</property>
</properties>
+6 -4
View File
@@ -58,8 +58,9 @@ func toSnapshotMetadata(s *sbom.SBOM) Metadata {
}
func filesystem(p pkg.Package) string {
if len(p.Locations) > 0 {
return p.Locations[0].FileSystemID
locations := p.Locations.ToSlice()
if len(locations) > 0 {
return locations[0].FileSystemID
}
return ""
}
@@ -76,8 +77,9 @@ func toPath(s source.Metadata, p pkg.Package) string {
if inputPath == "." {
inputPath = ""
}
if len(p.Locations) > 0 {
location := p.Locations[0]
locations := p.Locations.ToSlice()
if len(locations) > 0 {
location := locations[0]
packagePath := location.RealPath
if location.VirtualPath != "" {
packagePath = location.VirtualPath
+21 -15
View File
@@ -35,32 +35,38 @@ func Test_toGithubModel(t *testing.T) {
{
Name: "pkg-1",
Version: "1.0.1",
Locations: []source.Location{{
Coordinates: source.Coordinates{
RealPath: "/usr/lib",
FileSystemID: "fsid-1",
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/usr/lib",
FileSystemID: "fsid-1",
},
},
}},
),
},
{
Name: "pkg-2",
Version: "2.0.2",
Locations: []source.Location{{
Coordinates: source.Coordinates{
RealPath: "/usr/lib",
FileSystemID: "fsid-1",
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/usr/lib",
FileSystemID: "fsid-1",
},
},
}},
),
},
{
Name: "pkg-3",
Version: "3.0.3",
Locations: []source.Location{{
Coordinates: source.Coordinates{
RealPath: "/etc",
FileSystemID: "fsid-1",
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/etc",
FileSystemID: "fsid-1",
},
},
}},
),
},
} {
p.PURL = packageurl.NewPackageURL(
@@ -3,18 +3,18 @@
"name": "/some/path",
"spdxVersion": "SPDX-2.2",
"creationInfo": {
"created": "2021-12-20T19:12:47.869816Z",
"created": "2022-04-01T15:48:39.459232Z",
"creators": [
"Organization: Anchore, Inc",
"Tool: syft-[not provided]"
],
"licenseListVersion": "3.15"
"licenseListVersion": "3.16"
},
"dataLicense": "CC0-1.0",
"documentNamespace": "https://anchore.com/syft/dir/some/path-4b896ded-7852-4e31-b764-136b53bdf346",
"documentNamespace": "https://anchore.com/syft/dir/some/path-8d335d81-29c9-4236-84f1-2292ea92aaf5",
"packages": [
{
"SPDXID": "SPDXRef-1d97af55efe9512f",
"SPDXID": "SPDXRef-b85dbb4e6ece5082",
"name": "package-1",
"licenseConcluded": "MIT",
"downloadLocation": "NOASSERTION",
@@ -36,7 +36,7 @@
"versionInfo": "1.0.1"
},
{
"SPDXID": "SPDXRef-ad3d1c4abd84bf75",
"SPDXID": "SPDXRef-ceda99598967ae8d",
"name": "package-2",
"licenseConcluded": "NONE",
"downloadLocation": "NOASSERTION",
@@ -48,7 +48,7 @@
},
{
"referenceCategory": "PACKAGE_MANAGER",
"referenceLocator": "a-purl-2",
"referenceLocator": "pkg:deb/debian/package-2@2.0.1",
"referenceType": "purl"
}
],
@@ -3,18 +3,18 @@
"name": "user-image-input",
"spdxVersion": "SPDX-2.2",
"creationInfo": {
"created": "2021-12-20T19:13:07.647486Z",
"created": "2022-04-01T15:48:39.465643Z",
"creators": [
"Organization: Anchore, Inc",
"Tool: syft-[not provided]"
],
"licenseListVersion": "3.15"
"licenseListVersion": "3.16"
},
"dataLicense": "CC0-1.0",
"documentNamespace": "https://anchore.com/syft/image/user-image-input-174da656-1824-4bd3-8604-28919f8a65bc",
"documentNamespace": "https://anchore.com/syft/image/user-image-input-e64e0be8-5031-4eec-842d-e59fb6deb518",
"packages": [
{
"SPDXID": "SPDXRef-d16127444133b5c1",
"SPDXID": "SPDXRef-2a46171f91c8d4bc",
"name": "package-1",
"licenseConcluded": "MIT",
"downloadLocation": "NOASSERTION",
@@ -36,7 +36,7 @@
"versionInfo": "1.0.1"
},
{
"SPDXID": "SPDXRef-24907357f3705420",
"SPDXID": "SPDXRef-ae77680e9b1d087e",
"name": "package-2",
"licenseConcluded": "NONE",
"downloadLocation": "NOASSERTION",
@@ -48,7 +48,7 @@
},
{
"referenceCategory": "PACKAGE_MANAGER",
"referenceLocator": "a-purl-2",
"referenceLocator": "pkg:deb/debian/package-2@2.0.1",
"referenceType": "purl"
}
],
@@ -2,16 +2,16 @@ SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: /some/path
DocumentNamespace: https://anchore.com/syft/dir/some/path-4b90f56d-d596-4ad8-b6a5-17f7d801350d
DocumentNamespace: https://anchore.com/syft/dir/some/path-d227b0f2-4ee8-4e10-ac43-019db86d16ff
LicenseListVersion: 3.16
Creator: Organization: Anchore, Inc
Creator: Tool: syft-[not provided]
Created: 2022-02-10T21:09:27Z
Created: 2022-04-01T15:48:44Z
##### Package: package-2
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-ad3d1c4abd84bf75
SPDXID: SPDXRef-Package-deb-package-2-ceda99598967ae8d
PackageVersion: 2.0.1
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
@@ -19,12 +19,12 @@ PackageLicenseConcluded: NONE
PackageLicenseDeclared: NONE
PackageCopyrightText: NOASSERTION
ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:*
ExternalRef: PACKAGE_MANAGER purl a-purl-2
ExternalRef: PACKAGE_MANAGER purl pkg:deb/debian/package-2@2.0.1
##### Package: package-1
PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-1d97af55efe9512f
SPDXID: SPDXRef-Package-python-package-1-b85dbb4e6ece5082
PackageVersion: 1.0.1
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
@@ -2,16 +2,16 @@ SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: user-image-input
DocumentNamespace: https://anchore.com/syft/image/user-image-input-26a2def6-53d0-4504-b99a-a046832508ac
DocumentNamespace: https://anchore.com/syft/image/user-image-input-49f98c61-3418-4427-9e00-8b1c735e9799
LicenseListVersion: 3.16
Creator: Organization: Anchore, Inc
Creator: Tool: syft-[not provided]
Created: 2022-02-10T21:09:27Z
Created: 2022-04-01T15:48:44Z
##### Package: package-2
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-73f796c846875b9e
SPDXID: SPDXRef-Package-deb-package-2-ae77680e9b1d087e
PackageVersion: 2.0.1
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
@@ -19,12 +19,12 @@ PackageLicenseConcluded: NONE
PackageLicenseDeclared: NONE
PackageCopyrightText: NOASSERTION
ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:*
ExternalRef: PACKAGE_MANAGER purl a-purl-2
ExternalRef: PACKAGE_MANAGER purl pkg:deb/debian/package-2@2.0.1
##### Package: package-1
PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-d9527e708c11f8b9
SPDXID: SPDXRef-Package-python-package-1-2a46171f91c8d4bc
PackageVersion: 1.0.1
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
+6 -6
View File
@@ -42,13 +42,13 @@ func TestEncodeFullJSONDocument(t *testing.T) {
p1 := pkg.Package{
Name: "package-1",
Version: "1.0.1",
Locations: []source.Location{
{
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/a/place/a",
},
},
},
),
Type: pkg.PythonPkg,
FoundBy: "the-cataloger-1",
Language: pkg.Python,
@@ -68,13 +68,13 @@ func TestEncodeFullJSONDocument(t *testing.T) {
p2 := pkg.Package{
Name: "package-2",
Version: "2.0.1",
Locations: []source.Location{
{
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/b/place/b",
},
},
},
),
Type: pkg.DebPkg,
FoundBy: "the-cataloger-2",
MetadataType: pkg.DpkgMetadataType,
@@ -124,6 +124,18 @@ func (p *Package) UnmarshalJSON(b []byte) error {
return err
}
p.Metadata = payload
case pkg.GolangBinMetadataType:
var payload pkg.GolangBinMetadata
if err := json.Unmarshal(unpacker.Metadata, &payload); err != nil {
return err
}
p.Metadata = payload
case pkg.DartPubMetadataType:
var payload pkg.DartPubMetadata
if err := json.Unmarshal(unpacker.Metadata, &payload); err != nil {
return err
}
p.Metadata = payload
default:
log.Warnf("unknown package metadata type=%q for packageID=%q", p.MetadataType, p.ID)
}
@@ -0,0 +1,57 @@
package model
import (
"testing"
"github.com/anchore/syft/syft/pkg"
"github.com/stretchr/testify/assert"
)
func TestUnmarshalPackageGolang(t *testing.T) {
tests := []struct {
name string
p *Package
packageData []byte
}{
{
name: "Package.UnmarshalJSON unmarshals PackageBasicData",
p: &Package{},
packageData: []byte(`{
"id": "8b594519bc23da50",
"name": "gopkg.in/square/go-jose.v2",
"version": "v2.6.0",
"type": "go-module",
"foundBy": "go-module-binary-cataloger",
"locations": [
{
"path": "/Users/hal/go/bin/syft"
}
],
"licenses": [],
"language": "go",
"cpes": [],
"purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0",
"metadataType": "GolangBinMetadata",
"metadata": {
"goCompiledVersion": "go1.18",
"architecture": "amd64",
"h1Digest": "h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI="
}
}`),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := test.p.UnmarshalJSON(test.packageData)
if err != nil {
t.Fatalf("could not unmarshal packageData: %v", err)
}
assert.NotNil(t, test.p.Metadata)
golangMetadata := test.p.Metadata.(pkg.GolangBinMetadata)
assert.NotEmpty(t, golangMetadata)
assert.Equal(t, "go1.18", golangMetadata.GoCompiledVersion)
})
}
}
@@ -1,7 +1,7 @@
{
"artifacts": [
{
"id": "1d97af55efe9512f",
"id": "b85dbb4e6ece5082",
"name": "package-1",
"version": "1.0.1",
"type": "python",
@@ -36,7 +36,7 @@
}
},
{
"id": "ad3d1c4abd84bf75",
"id": "ceda99598967ae8d",
"name": "package-2",
"version": "2.0.1",
"type": "deb",
@@ -51,7 +51,7 @@
"cpes": [
"cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"
],
"purl": "a-purl-2",
"purl": "pkg:deb/debian/package-2@2.0.1",
"metadataType": "DpkgMetadata",
"metadata": {
"package": "package-2",
@@ -88,7 +88,7 @@
}
},
"schema": {
"version": "3.1.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.1.1.json"
"version": "3.2.2",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.2.json"
}
}
@@ -1,7 +1,7 @@
{
"artifacts": [
{
"id": "d9a7c58726ab4bef",
"id": "b3fa3ee64756b0c6",
"name": "package-1",
"version": "1.0.1",
"type": "python",
@@ -31,7 +31,7 @@
}
},
{
"id": "ac462e450060da2c",
"id": "b324f4d9ee5413fe",
"name": "package-2",
"version": "2.0.1",
"type": "deb",
@@ -184,7 +184,7 @@
}
},
"schema": {
"version": "3.1.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.1.1.json"
"version": "3.2.2",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.2.json"
}
}
@@ -1,7 +1,7 @@
{
"artifacts": [
{
"id": "d9527e708c11f8b9",
"id": "2a46171f91c8d4bc",
"name": "package-1",
"version": "1.0.1",
"type": "python",
@@ -32,7 +32,7 @@
}
},
{
"id": "73f796c846875b9e",
"id": "ae77680e9b1d087e",
"name": "package-2",
"version": "2.0.1",
"type": "deb",
@@ -48,7 +48,7 @@
"cpes": [
"cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"
],
"purl": "a-purl-2",
"purl": "pkg:deb/debian/package-2@2.0.1",
"metadataType": "DpkgMetadata",
"metadata": {
"package": "package-2",
@@ -111,7 +111,7 @@
}
},
"schema": {
"version": "3.1.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.1.1.json"
"version": "3.2.2",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.2.json"
}
}
+3 -2
View File
@@ -175,8 +175,9 @@ func toPackageModel(p pkg.Package) model.Package {
licenses = p.Licenses
}
var coordinates = make([]source.Coordinates, len(p.Locations))
for i, l := range p.Locations {
locations := p.Locations.ToSlice()
var coordinates = make([]source.Coordinates, len(locations))
for i, l := range locations {
coordinates[i] = l.Coordinates
}
+32 -13
View File
@@ -12,7 +12,9 @@ import (
)
func toSyftModel(doc model.Document) (*sbom.SBOM, error) {
catalog := toSyftCatalog(doc.Artifacts)
idAliases := make(map[string]string)
catalog := toSyftCatalog(doc.Artifacts, idAliases)
return &sbom.SBOM{
Artifacts: sbom.Artifacts{
@@ -21,7 +23,7 @@ func toSyftModel(doc model.Document) (*sbom.SBOM, error) {
},
Source: *toSyftSourceData(doc.Source),
Descriptor: toSyftDescriptor(doc.Descriptor),
Relationships: toSyftRelationships(&doc, catalog, doc.ArtifactRelationships),
Relationships: toSyftRelationships(&doc, catalog, doc.ArtifactRelationships, idAliases),
}, nil
}
@@ -46,12 +48,13 @@ func toSyftLinuxRelease(d model.LinuxRelease) *linux.Release {
}
}
func toSyftRelationships(doc *model.Document, catalog *pkg.Catalog, relationships []model.Relationship) []artifact.Relationship {
func toSyftRelationships(doc *model.Document, catalog *pkg.Catalog, relationships []model.Relationship, idAliases map[string]string) []artifact.Relationship {
idMap := make(map[string]interface{})
for _, p := range catalog.Sorted() {
idMap[string(p.ID())] = p
for _, l := range p.Locations {
locations := p.Locations.ToSlice()
for _, l := range locations {
idMap[string(l.Coordinates.ID())] = l.Coordinates
}
}
@@ -62,7 +65,7 @@ func toSyftRelationships(doc *model.Document, catalog *pkg.Catalog, relationship
var out []artifact.Relationship
for _, r := range relationships {
syftRelationship := toSyftRelationship(idMap, r)
syftRelationship := toSyftRelationship(idMap, r, idAliases)
if syftRelationship != nil {
out = append(out, *syftRelationship)
}
@@ -70,13 +73,20 @@ func toSyftRelationships(doc *model.Document, catalog *pkg.Catalog, relationship
return out
}
func toSyftRelationship(idMap map[string]interface{}, relationship model.Relationship) *artifact.Relationship {
from, ok := idMap[relationship.Parent].(artifact.Identifiable)
func toSyftRelationship(idMap map[string]interface{}, relationship model.Relationship, idAliases map[string]string) *artifact.Relationship {
id := func(id string) string {
aliased, ok := idAliases[id]
if ok {
return aliased
}
return id
}
from, ok := idMap[id(relationship.Parent)].(artifact.Identifiable)
if !ok {
log.Warnf("relationship mapping from key %s is not a valid artifact.Identifiable type: %+v", relationship.Parent, idMap[relationship.Parent])
return nil
}
to, ok := idMap[relationship.Child].(artifact.Identifiable)
to, ok := idMap[id(relationship.Child)].(artifact.Identifiable)
if !ok {
log.Warnf("relationship mapping to key %s is not a valid artifact.Identifiable type: %+v", relationship.Child, idMap[relationship.Child])
return nil
@@ -128,15 +138,15 @@ func toSyftSourceData(s model.Source) *source.Metadata {
return nil
}
func toSyftCatalog(pkgs []model.Package) *pkg.Catalog {
func toSyftCatalog(pkgs []model.Package, idAliases map[string]string) *pkg.Catalog {
catalog := pkg.NewCatalog()
for _, p := range pkgs {
catalog.Add(toSyftPackage(p))
catalog.Add(toSyftPackage(p, idAliases))
}
return catalog
}
func toSyftPackage(p model.Package) pkg.Package {
func toSyftPackage(p model.Package, idAliases map[string]string) pkg.Package {
var cpes []pkg.CPE
for _, c := range p.CPEs {
value, err := pkg.NewCPE(c)
@@ -153,11 +163,11 @@ func toSyftPackage(p model.Package) pkg.Package {
locations[i] = source.NewLocationFromCoordinates(c)
}
return pkg.Package{
out := pkg.Package{
Name: p.Name,
Version: p.Version,
FoundBy: p.FoundBy,
Locations: locations,
Locations: source.NewLocationSet(locations...),
Licenses: p.Licenses,
Language: p.Language,
Type: p.Type,
@@ -166,4 +176,13 @@ func toSyftPackage(p model.Package) pkg.Package {
MetadataType: p.MetadataType,
Metadata: p.Metadata,
}
out.SetID()
id := string(out.ID())
if id != p.ID {
idAliases[p.ID] = id
}
return out
}
@@ -4,6 +4,7 @@ import (
"testing"
"github.com/anchore/syft/internal/formats/syftjson/model"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/source"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
@@ -79,3 +80,46 @@ func Test_toSyftSourceData(t *testing.T) {
// assert all possible schemes were under test
assert.ElementsMatch(t, allSchemes.List(), testedSchemes.List(), "not all source.Schemes are under test")
}
func Test_idsHaveChanged(t *testing.T) {
s, err := toSyftModel(model.Document{
Source: model.Source{
Type: "file",
Target: "some/path",
},
Artifacts: []model.Package{
{
PackageBasicData: model.PackageBasicData{
ID: "1",
Name: "pkg-1",
},
},
{
PackageBasicData: model.PackageBasicData{
ID: "2",
Name: "pkg-2",
},
},
},
ArtifactRelationships: []model.Relationship{
{
Parent: "1",
Child: "2",
Type: string(artifact.ContainsRelationship),
},
},
})
assert.NoError(t, err)
assert.Len(t, s.Relationships, 1)
r := s.Relationships[0]
from := s.Artifacts.PackageCatalog.Package(r.From.ID())
assert.NotNil(t, from)
assert.Equal(t, "pkg-1", from.Name)
to := s.Artifacts.PackageCatalog.Package(r.To.ID())
assert.NotNil(t, to)
assert.Equal(t, "pkg-2", to.Name)
}
+1
View File
@@ -37,6 +37,7 @@ type artifactMetadataContainer struct {
Cargo pkg.CargoPackageMetadata
Go pkg.GolangBinMetadata
Php pkg.PhpComposerJSONMetadata
Dart pkg.DartPubMetadata
}
func main() {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+26
View File
@@ -1,6 +1,7 @@
package syft
import (
"bytes"
"io"
"os"
"testing"
@@ -41,6 +42,31 @@ func TestIdentify(t *testing.T) {
}
}
func TestFormats_EmptyInput(t *testing.T) {
for _, format := range formats {
t.Run(format.ID().String(), func(t *testing.T) {
t.Run("format.Decode", func(t *testing.T) {
input := bytes.NewReader(nil)
assert.NotPanics(t, func() {
decodedSBOM, err := format.Decode(input)
assert.Error(t, err)
assert.Nil(t, decodedSBOM)
})
})
t.Run("format.Validate", func(t *testing.T) {
input := bytes.NewReader(nil)
assert.NotPanics(t, func() {
err := format.Validate(input)
assert.Error(t, err)
})
})
})
}
}
func TestFormatByName(t *testing.T) {
tests := []struct {
+73 -16
View File
@@ -10,11 +10,28 @@ import (
"github.com/jinzhu/copier"
)
type orderedIDSet struct {
slice []artifact.ID
}
func (s *orderedIDSet) add(ids ...artifact.ID) {
loopNewIDs:
for _, newID := range ids {
for _, existingID := range s.slice {
if existingID == newID {
continue loopNewIDs
}
}
s.slice = append(s.slice, newID)
}
}
// Catalog represents a collection of Packages.
type Catalog struct {
byID map[artifact.ID]Package
idsByType map[Type][]artifact.ID
idsByPath map[string][]artifact.ID // note: this is real path or virtual path
idsByName map[string]orderedIDSet
idsByType map[Type]orderedIDSet
idsByPath map[string]orderedIDSet // note: this is real path or virtual path
lock sync.RWMutex
}
@@ -22,8 +39,9 @@ type Catalog struct {
func NewCatalog(pkgs ...Package) *Catalog {
catalog := Catalog{
byID: make(map[artifact.ID]Package),
idsByType: make(map[Type][]artifact.ID),
idsByPath: make(map[string][]artifact.ID),
idsByName: make(map[string]orderedIDSet),
idsByType: make(map[Type]orderedIDSet),
idsByPath: make(map[string]orderedIDSet),
}
for _, p := range pkgs {
@@ -55,7 +73,12 @@ func (c *Catalog) Package(id artifact.ID) *Package {
// PackagesByPath returns all packages that were discovered from the given path.
func (c *Catalog) PackagesByPath(path string) []Package {
return c.Packages(c.idsByPath[path])
return c.Packages(c.idsByPath[path].slice)
}
// PackagesByName returns all packages that were discovered with a matching name.
func (c *Catalog) PackagesByName(name string) []Package {
return c.Packages(c.idsByName[name].slice)
}
// Packages returns all packages for the given ID.
@@ -81,26 +104,58 @@ func (c *Catalog) Add(p Package) {
id = p.ID()
}
// store by package ID
c.byID[id] = p
if existing, exists := c.byID[id]; exists {
// there is already a package with this fingerprint merge the existing record with the new one
if err := existing.merge(p); err != nil {
log.Warnf("failed to merge packages: %+v", err)
} else {
c.addPathsToIndex(p)
}
return
}
// store by package type
c.idsByType[p.Type] = append(c.idsByType[p.Type], id)
c.addToIndex(p)
}
// store by file location paths
func (c *Catalog) addToIndex(p Package) {
c.byID[p.id] = p
c.addNameToIndex(p)
c.addTypeToIndex(p)
c.addPathsToIndex(p)
}
func (c *Catalog) addNameToIndex(p Package) {
nameIndex := c.idsByName[p.Name]
nameIndex.add(p.id)
c.idsByName[p.Name] = nameIndex
}
func (c *Catalog) addTypeToIndex(p Package) {
typeIndex := c.idsByType[p.Type]
typeIndex.add(p.id)
c.idsByType[p.Type] = typeIndex
}
func (c *Catalog) addPathsToIndex(p Package) {
observedPaths := internal.NewStringSet()
for _, l := range p.Locations {
for _, l := range p.Locations.ToSlice() {
if l.RealPath != "" && !observedPaths.Contains(l.RealPath) {
c.idsByPath[l.RealPath] = append(c.idsByPath[l.RealPath], id)
c.addPathToIndex(p.id, l.RealPath)
observedPaths.Add(l.RealPath)
}
if l.VirtualPath != "" && l.RealPath != l.VirtualPath && !observedPaths.Contains(l.VirtualPath) {
c.idsByPath[l.VirtualPath] = append(c.idsByPath[l.VirtualPath], id)
c.addPathToIndex(p.id, l.VirtualPath)
observedPaths.Add(l.VirtualPath)
}
}
}
func (c *Catalog) addPathToIndex(id artifact.ID, path string) {
pathIndex := c.idsByPath[path]
pathIndex.add(id)
c.idsByPath[path] = pathIndex
}
// Enumerate all packages for the given type(s), enumerating all packages if no type is specified.
func (c *Catalog) Enumerate(types ...Type) <-chan Package {
channel := make(chan Package)
@@ -124,7 +179,7 @@ func (c *Catalog) Enumerate(types ...Type) <-chan Package {
continue
}
}
for _, id := range ids {
for _, id := range ids.slice {
p := c.Package(id)
if p != nil {
channel <- *p
@@ -145,8 +200,10 @@ func (c *Catalog) Sorted(types ...Type) (pkgs []Package) {
sort.SliceStable(pkgs, func(i, j int) bool {
if pkgs[i].Name == pkgs[j].Name {
if pkgs[i].Version == pkgs[j].Version {
if pkgs[i].Type == pkgs[j].Type && len(pkgs[i].Locations) > 0 && len(pkgs[j].Locations) > 0 {
return pkgs[i].Locations[0].String() < pkgs[j].Locations[0].String()
iLocations := pkgs[i].Locations.ToSlice()
jLocations := pkgs[j].Locations.ToSlice()
if pkgs[i].Type == pkgs[j].Type && len(iLocations) > 0 && len(jLocations) > 0 {
return iLocations[0].String() < jLocations[0].String()
}
return pkgs[i].Type < pkgs[j].Type
}
+141 -19
View File
@@ -3,11 +3,11 @@ package pkg
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/source"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type expectedIndexes struct {
@@ -19,17 +19,17 @@ func TestCatalogAddPopulatesIndex(t *testing.T) {
var pkgs = []Package{
{
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/a/path", "/another/path"),
source.NewVirtualLocation("/b/path", "/bee/path"),
},
),
Type: RpmPkg,
},
{
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/c/path", "/another/path"),
source.NewVirtualLocation("/d/path", "/another/path"),
},
),
Type: NpmPkg,
},
}
@@ -106,47 +106,169 @@ func assertIndexes(t *testing.T, c *Catalog, expectedIndexes expectedIndexes) {
func TestCatalog_PathIndexDeduplicatesRealVsVirtualPaths(t *testing.T) {
p1 := Package{
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/b/path", "/another/path"),
source.NewVirtualLocation("/b/path", "/b/path"),
},
),
Type: RpmPkg,
Name: "Package-1",
}
p2 := Package{
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/b/path", "/b/path"),
},
),
Type: RpmPkg,
Name: "Package-2",
}
p2Dup := Package{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/b/path", "/another/path"),
source.NewVirtualLocation("/b/path", "/c/path/b/dup"),
),
Type: RpmPkg,
Name: "Package-2",
}
tests := []struct {
name string
pkg Package
name string
pkgs []Package
paths []string
}{
{
name: "multiple locations with shared path",
pkg: p1,
pkgs: []Package{p1},
paths: []string{
"/b/path",
"/another/path",
},
},
{
name: "one location with shared path",
pkg: p2,
pkgs: []Package{p2},
paths: []string{
"/b/path",
},
},
{
name: "two instances with similar locations",
pkgs: []Package{p2, p2Dup},
paths: []string{
"/b/path",
"/another/path",
"/c/path/b/dup", // this updated the path index on merge
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := NewCatalog(test.pkg).PackagesByPath("/b/path")
if len(actual) != 1 {
t.Errorf("expected exactly one package path, got %d", len(actual))
for _, path := range test.paths {
actualPackages := NewCatalog(test.pkgs...).PackagesByPath(path)
require.Len(t, actualPackages, 1)
}
})
}
}
func TestCatalog_MergeRecords(t *testing.T) {
var tests = []struct {
name string
pkgs []Package
expectedLocations []source.Location
}{
{
name: "multiple Locations with shared path",
pkgs: []Package{
{
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/b/path",
FileSystemID: "a",
},
VirtualPath: "/another/path",
},
),
Type: RpmPkg,
},
{
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/b/path",
FileSystemID: "b",
},
VirtualPath: "/another/path",
},
),
Type: RpmPkg,
},
},
expectedLocations: []source.Location{
{
Coordinates: source.Coordinates{
RealPath: "/b/path",
FileSystemID: "a",
},
VirtualPath: "/another/path",
},
{
Coordinates: source.Coordinates{
RealPath: "/b/path",
FileSystemID: "b",
},
VirtualPath: "/another/path",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := NewCatalog(tt.pkgs...).PackagesByPath("/b/path")
require.Len(t, actual, 1)
assert.Equal(t, tt.expectedLocations, actual[0].Locations.ToSlice())
})
}
}
func TestCatalog_EnumerateNilCatalog(t *testing.T) {
var c *Catalog
assert.Empty(t, c.Enumerate())
}
func Test_idOrderedSet_add(t *testing.T) {
tests := []struct {
name string
input []artifact.ID
expected []artifact.ID
}{
{
name: "elements deduplicated when added",
input: []artifact.ID{
"1", "2", "3", "4", "1", "2", "3", "4", "1", "2", "3", "4",
},
expected: []artifact.ID{
"1", "2", "3", "4",
},
},
{
name: "elements retain ordering when added",
input: []artifact.ID{
"4", "3", "2", "1",
},
expected: []artifact.ID{
"4", "3", "2", "1",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var s orderedIDSet
s.add(tt.input...)
assert.Equal(t, tt.expected, s.slice)
})
}
}
+3
View File
@@ -9,6 +9,7 @@ import (
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/apkdb"
"github.com/anchore/syft/syft/pkg/cataloger/dart"
"github.com/anchore/syft/syft/pkg/cataloger/deb"
"github.com/anchore/syft/syft/pkg/cataloger/golang"
"github.com/anchore/syft/syft/pkg/cataloger/java"
@@ -61,6 +62,7 @@ func DirectoryCatalogers(cfg Config) []Cataloger {
golang.NewGoModuleBinaryCataloger(),
golang.NewGoModFileCataloger(),
rust.NewCargoLockCataloger(),
dart.NewPubspecLockCataloger(),
}
}
@@ -80,5 +82,6 @@ func AllCatalogers(cfg Config) []Cataloger {
golang.NewGoModuleBinaryCataloger(),
golang.NewGoModFileCataloger(),
rust.NewCargoLockCataloger(),
dart.NewPubspecLockCataloger(),
}
}
+13 -13
View File
@@ -16,7 +16,7 @@ func Test_disallowJenkinsServerCPEForPluginPackage(t *testing.T) {
}{
{
name: "go case (filter out)",
cpe: mustCPE("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Type: pkg.JenkinsPluginPkg,
},
@@ -24,7 +24,7 @@ func Test_disallowJenkinsServerCPEForPluginPackage(t *testing.T) {
},
{
name: "ignore jenkins plugins with unique name",
cpe: mustCPE("cpe:2.3:a:name:ci-jenkins:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:name:ci-jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Type: pkg.JenkinsPluginPkg,
},
@@ -32,7 +32,7 @@ func Test_disallowJenkinsServerCPEForPluginPackage(t *testing.T) {
},
{
name: "ignore java packages",
cpe: mustCPE("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:name:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Type: pkg.JavaPkg,
},
@@ -55,7 +55,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
}{
{
name: "filter out mismatched name (cloudbees vendor)",
cpe: mustCPE("cpe:2.3:a:cloudbees:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:cloudbees:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "not-j*nkins",
Type: pkg.JavaPkg,
@@ -64,7 +64,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
},
{
name: "filter out mismatched name (jenkins vendor)",
cpe: mustCPE("cpe:2.3:a:jenkins:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:jenkins:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "not-j*nkins",
Type: pkg.JavaPkg,
@@ -73,7 +73,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
},
{
name: "filter out mismatched name (any vendor)",
cpe: mustCPE("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "not-j*nkins",
Type: pkg.JavaPkg,
@@ -82,7 +82,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
},
{
name: "ignore packages with the name jenkins",
cpe: mustCPE("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:*:jenkins:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "jenkins-thing",
Type: pkg.JavaPkg,
@@ -91,7 +91,7 @@ func Test_disallowJenkinsCPEsNotAssociatedWithJenkins(t *testing.T) {
},
{
name: "ignore product names that are not exactly 'jenkins'",
cpe: mustCPE("cpe:2.3:a:*:jenkins-something-else:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:*:jenkins-something-else:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "not-j*nkins",
Type: pkg.JavaPkg,
@@ -115,7 +115,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
}{
{
name: "filter out mismatched name (atlassian vendor)",
cpe: mustCPE("cpe:2.3:a:atlassian:jira:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:atlassian:jira:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "something-client",
Type: pkg.JavaPkg,
@@ -124,7 +124,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
},
{
name: "filter out mismatched name (jira vendor)",
cpe: mustCPE("cpe:2.3:a:jira:jira:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:jira:jira:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "something-client",
Type: pkg.JavaPkg,
@@ -133,7 +133,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
},
{
name: "filter out mismatched name (any vendor)",
cpe: mustCPE("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "something-client",
Type: pkg.JavaPkg,
@@ -142,7 +142,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
},
{
name: "ignore package names that do not have 'client'",
cpe: mustCPE("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:*:jira:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "jira-thing",
Type: pkg.JavaPkg,
@@ -151,7 +151,7 @@ func Test_disallowJiraClientServerMismatch(t *testing.T) {
},
{
name: "ignore product names that are not exactly 'jira'",
cpe: mustCPE("cpe:2.3:a:*:jira-something-else:3.2:*:*:*:*:*:*:*"),
cpe: pkg.MustCPE("cpe:2.3:a:*:jira-something-else:3.2:*:*:*:*:*:*:*"),
pkg: pkg.Package{
Name: "not-j*ra",
Type: pkg.JavaPkg,
+1 -1
View File
@@ -55,7 +55,7 @@ func Generate(p pkg.Package) []pkg.CPE {
// filter out any known combinations that don't accurately represent this package
cpes = filter(cpes, p, cpeFilters...)
sort.Sort(BySpecificity(cpes))
sort.Sort(pkg.CPEBySpecificity(cpes))
return cpes
}
@@ -58,9 +58,8 @@ func (c *GenericCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package,
for _, p := range discoveredPackages {
p.FoundBy = c.upstreamCataloger
p.Locations = append(p.Locations, location)
p.Locations.Add(location)
p.SetID()
packages = append(packages, *p)
}
@@ -53,7 +53,7 @@ func TestGenericCataloger(t *testing.T) {
assert.Len(t, actualPkgs, len(expectedPkgs))
for _, p := range actualPkgs {
ref := p.Locations[0]
ref := p.Locations.ToSlice()[0]
exP, ok := expectedPkgs[ref.RealPath]
if !ok {
t.Errorf("missing expected pkg: ref=%+v", ref)
+14
View File
@@ -0,0 +1,14 @@
package dart
import (
"github.com/anchore/syft/syft/pkg/cataloger/common"
)
// NewPubspecLockCataloger returns a new Dartlang cataloger object base on pubspec lock files.
func NewPubspecLockCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/pubspec.lock": parsePubspecLock,
}
return common.NewGenericCataloger(nil, globParsers, "dartlang-lock-cataloger")
}
@@ -0,0 +1,96 @@
package dart
import (
"fmt"
"io"
"net/url"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/common"
"gopkg.in/yaml.v2"
)
// integrity check
var _ common.ParserFn = parsePubspecLock
const defaultPubRegistry string = "https://pub.dartlang.org"
type pubspecLock struct {
Packages map[string]pubspecLockPackage `yaml:"packages"`
Sdks map[string]string `yaml:"sdks"`
}
type pubspecLockPackage struct {
Dependency string `yaml:"dependency" mapstructure:"dependency"`
Description pubspecLockDescription `yaml:"description" mapstructure:"description"`
Source string `yaml:"source" mapstructure:"source"`
Version string `yaml:"version" mapstructure:"version"`
}
type pubspecLockDescription struct {
Name string `yaml:"name" mapstructure:"name"`
URL string `yaml:"url" mapstructure:"url"`
Path string `yaml:"path" mapstructure:"path"`
Ref string `yaml:"ref" mapstructure:"ref"`
ResolvedRef string `yaml:"resolved-ref" mapstructure:"resolved-ref"`
}
func parsePubspecLock(path string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
var packages []*pkg.Package
dec := yaml.NewDecoder(reader)
var p pubspecLock
if err := dec.Decode(&p); err != nil {
return nil, nil, fmt.Errorf("failed to parse pubspec.lock file: %w", err)
}
for name, pubPkg := range p.Packages {
packages = append(packages, newPubspecLockPackage(name, pubPkg))
}
return packages, nil, nil
}
func newPubspecLockPackage(name string, p pubspecLockPackage) *pkg.Package {
return &pkg.Package{
Name: name,
Version: p.Version,
Language: pkg.Dart,
Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType,
Metadata: &pkg.DartPubMetadata{
Name: name,
Version: p.Version,
HostedURL: p.getHostedURL(),
VcsURL: p.getVcsURL(),
},
}
}
func (p *pubspecLockPackage) getVcsURL() string {
if p.Source == "git" {
if p.Description.Path == "." {
return fmt.Sprintf("%s@%s", p.Description.URL, p.Description.ResolvedRef)
}
return fmt.Sprintf("%s@%s#%s", p.Description.URL, p.Description.ResolvedRef, p.Description.Path)
}
return ""
}
func (p *pubspecLockPackage) getHostedURL() string {
if p.Source == "hosted" && p.Description.URL != defaultPubRegistry {
u, err := url.Parse(p.Description.URL)
if err != nil {
log.Debugf("Unable to parse registry url %w", err)
return p.Description.URL
}
return u.Host
}
return ""
}
@@ -0,0 +1,99 @@
package dart
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/pkg"
)
func assertPackagesEqual(t *testing.T, actual []*pkg.Package, expected map[string]*pkg.Package) {
assert.Len(t, actual, len(expected))
}
func TestParsePubspecLock(t *testing.T) {
expected := map[string]*pkg.Package{
"ale": {
Name: "ale",
Version: "3.3.0",
Language: pkg.Dart,
Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType,
Metadata: pkg.DartPubMetadata{
Name: "ale",
Version: "3.3.0",
HostedURL: "pub.hosted.org",
},
},
"analyzer": {
Name: "analyzer",
Version: "0.40.7",
Language: pkg.Dart,
Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType,
Metadata: pkg.DartPubMetadata{
Name: "analyzer",
Version: "0.40.7",
},
},
"ansicolor": {
Name: "ansicolor",
Version: "1.1.1",
Language: pkg.Dart,
Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType,
Metadata: pkg.DartPubMetadata{
Name: "ansicolor",
Version: "1.1.1",
},
},
"archive": {
Name: "archive",
Version: "2.0.13",
Language: pkg.Dart,
Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType,
Metadata: pkg.DartPubMetadata{
Name: "archive",
Version: "2.0.13",
},
},
"args": {
Name: "args",
Version: "1.6.0",
Language: pkg.Dart,
Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType,
Metadata: pkg.DartPubMetadata{
Name: "args",
Version: "1.6.0",
},
},
"key_binder": {
Name: "key_binder",
Version: "1.11.20",
Language: pkg.Dart,
Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType,
Metadata: pkg.DartPubMetadata{
Name: "key_binder",
Version: "1.11.20",
VcsURL: "git@github.com:Workiva/key_binder.git#3f7b3a6350e73c7dcac45301c0e18fbd42af02f7",
},
},
}
fixture, err := os.Open("test-fixtures/pubspec.lock")
if err != nil {
t.Fatalf("failed to open fixture: %+v", err)
}
actual, _, err := parsePubspecLock(fixture.Name(), fixture)
if err != nil {
t.Fatalf("failed to parse pubspec.lock: %+v", err)
}
assertPackagesEqual(t, actual, expected)
}
@@ -0,0 +1,49 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
ale:
dependency: transitive
description:
name: ale
url: "https://pub.hosted.org"
source: hosted
version: "3.3.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "0.40.7"
ansicolor:
dependency: transitive
description:
name: ansicolor
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.13"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.0"
key_binder:
dependency: "direct main"
description:
path: "."
ref: HEAD
resolved-ref: "3f7b3a6350e73c7dcac45301c0e18fbd42af02f7"
url: "git@github.com:Workiva/key_binder.git"
source: git
version: "1.11.20"
sdks:
dart: ">=2.12.0 <3.0.0"
+3 -3
View File
@@ -60,7 +60,7 @@ func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []arti
for i := range pkgs {
p := &pkgs[i]
p.FoundBy = c.Name()
p.Locations = []source.Location{dbLocation}
p.Locations.Add(dbLocation)
// the current entry only has what may have been listed in the status file, however, there are additional
// files that are listed in multiple other locations. We should retrieve them all and merge the file lists
@@ -88,7 +88,7 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk
p.Licenses = parseLicensesFromCopyright(copyrightReader)
// keep a record of the file where this was discovered
p.Locations = append(p.Locations, *copyrightLocation)
p.Locations.Add(*copyrightLocation)
}
}
@@ -117,7 +117,7 @@ loopNewFiles:
p.Metadata = metadata
// persist location information from each new source of information
p.Locations = append(p.Locations, infoLocations...)
p.Locations.Add(infoLocations...)
}
func getAdditionalFileListing(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) ([]pkg.DpkgFileRecord, []source.Location) {
+5 -6
View File
@@ -1,6 +1,7 @@
package deb
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/anchore/syft/syft/file"
@@ -115,15 +116,13 @@ func TestDpkgCataloger(t *testing.T) {
for idx := range actual {
a := &actual[idx]
// we will test the sources separately
var sourcesList = make([]string, len(a.Locations))
for i, s := range a.Locations {
var sourcesList = make([]string, len(a.Locations.ToSlice()))
for i, s := range a.Locations.ToSlice() {
sourcesList[i] = s.RealPath
}
a.Locations = nil
a.Locations = source.NewLocationSet()
for _, d := range deep.Equal(sourcesList, test.sources[a.Name]) {
t.Errorf("diff: %+v", d)
}
assert.ElementsMatch(t, sourcesList, test.sources[a.Name])
}
// test remaining fields...
+6 -9
View File
@@ -1,4 +1,3 @@
//nolint
package golang
import (
@@ -45,14 +44,12 @@ func newGoBinaryPackage(dep *debug.Module, goVersion, architecture string, locat
}
p := pkg.Package{
FoundBy: catalogerName,
Name: dep.Path,
Version: dep.Version,
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: []source.Location{
location,
},
FoundBy: catalogerName,
Name: dep.Path,
Version: dep.Version,
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: source.NewLocationSet(location),
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goVersion,
+20 -18
View File
@@ -133,14 +133,14 @@ func TestBuildGoPkgInfo(t *testing.T) {
FoundBy: catalogerName,
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: []source.Location{
{
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/a-path",
FileSystemID: "layer-id",
},
},
},
),
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
@@ -190,14 +190,14 @@ func TestBuildGoPkgInfo(t *testing.T) {
Version: "v0.2.1",
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: []source.Location{
{
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/a-path",
FileSystemID: "layer-id",
},
},
},
),
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
@@ -252,14 +252,14 @@ func TestBuildGoPkgInfo(t *testing.T) {
Version: "v0.2.1",
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: []source.Location{
{
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/a-path",
FileSystemID: "layer-id",
},
},
},
),
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
@@ -273,14 +273,14 @@ func TestBuildGoPkgInfo(t *testing.T) {
Version: "v0.0.0-20210222170800-9c70f9b80bcf",
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: []source.Location{
{
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/a-path",
FileSystemID: "layer-id",
},
},
},
),
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
@@ -327,12 +327,14 @@ func TestBuildGoPkgInfo(t *testing.T) {
Version: "v0.0.0-20211006194710-c8a6f5223071",
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: []source.Location{
{
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/a-path",
FileSystemID: "layer-id",
}}},
},
},
),
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
@@ -344,14 +346,14 @@ func TestBuildGoPkgInfo(t *testing.T) {
Version: "v0.0.0-20210916214954-140adaaadfaf",
Language: pkg.Go,
Type: pkg.GoModulePkg,
Locations: []source.Location{
{
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
RealPath: "/a-path",
FileSystemID: "layer-id",
},
},
},
),
MetadataType: pkg.GolangBinMetadataType,
Metadata: pkg.GolangBinMetadata{
GoCompiledVersion: goCompiledVersion,
+12 -3
View File
@@ -1,4 +1,3 @@
//nolint
package golang
import (
@@ -25,17 +24,27 @@ func scanFile(reader unionReader, filename string) ([]*debug.BuildInfo, []string
// with more than one binary
readers, err := getReaders(reader)
if err != nil {
log.Warnf("golang cataloger: opening binary: %v", err)
log.Warnf("golang cataloger: failed to open a binary: %v", err)
return nil, nil
}
var builds []*debug.BuildInfo
for _, r := range readers {
bi, err := buildinfo.Read(r)
// note: the stdlib does not export the error we need to check for
if err != nil {
log.Warnf("golang cataloger: scanning file %s: %v", filename, err)
if err.Error() == "not a Go executable" {
// since the cataloger can only select executables and not distinguish if they are a go-compiled
// binary, we should not show warnings/logs in this case.
return nil, nil
}
// in this case we could not read the or parse the file, but not explicitly because it is not a
// go-compiled binary (though it still might be).
log.Warnf("golang cataloger: failed to read buildinfo (file=%q): %v", filename, err)
return nil, nil
}
builds = append(builds, bi)
}
@@ -5,14 +5,13 @@ import (
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"github.com/anchore/syft/internal/log"
)
func saveArchiveToTmp(archiveVirtualPath string, reader io.Reader) (string, string, func(), error) {
name := path.Base(archiveVirtualPath)
name := filepath.Base(archiveVirtualPath)
tempDir, err := ioutil.TempDir("", "syft-archive-contents-")
if err != nil {
return "", "", func() {}, fmt.Errorf("unable to create tempdir for archive processing: %w", err)
@@ -80,7 +80,7 @@ func (c *PackageCataloger) catalogEggOrWheel(resolver source.FileResolver, metad
Name: metadata.Name,
Version: metadata.Version,
FoundBy: c.Name(),
Locations: sources,
Locations: source.NewLocationSet(sources...),
Licenses: licenses,
Language: pkg.Python,
Type: pkg.PythonPkg,
@@ -144,7 +144,7 @@ func TestPythonPackageWheelCataloger(t *testing.T) {
t.Fatal(err)
}
test.expectedPackage.Locations = locations
test.expectedPackage.Locations = source.NewLocationSet(locations...)
actual, _, err := NewPythonPackageCataloger().Catalog(resolver)
if err != nil {
@@ -1,6 +1,7 @@
package python
import (
"github.com/anchore/syft/syft/source"
"os"
"testing"
@@ -48,7 +49,14 @@ func TestParsePipFileLock(t *testing.T) {
t.Fatalf("failed to parse requirements: %+v", err)
}
if diff := cmp.Diff(expected, actual, cmp.AllowUnexported(pkg.Package{})); diff != "" {
if diff := cmp.Diff(expected, actual,
cmp.AllowUnexported(pkg.Package{}),
cmp.Comparer(
func(x, y source.LocationSet) bool {
return cmp.Equal(x.ToSlice(), y.ToSlice())
},
),
); diff != "" {
t.Errorf("unexpected result from parsing (-expected +actual)\n%s", diff)
}
}
@@ -1,6 +1,7 @@
package python
import (
"github.com/anchore/syft/syft/source"
"os"
"testing"
@@ -42,7 +43,14 @@ func TestParseRequirementsTxt(t *testing.T) {
t.Fatalf("failed to parse requirements: %+v", err)
}
if diff := cmp.Diff(expected, actual, cmp.AllowUnexported(pkg.Package{})); diff != "" {
if diff := cmp.Diff(expected, actual,
cmp.AllowUnexported(pkg.Package{}),
cmp.Comparer(
func(x, y source.LocationSet) bool {
return cmp.Equal(x.ToSlice(), y.ToSlice())
},
),
); diff != "" {
t.Errorf("unexpected result from parsing (-expected +actual)\n%s", diff)
}
}
@@ -1,6 +1,7 @@
package python
import (
"github.com/anchore/syft/syft/source"
"os"
"testing"
@@ -54,7 +55,14 @@ func TestParseSetup(t *testing.T) {
t.Fatalf("failed to parse requirements: %+v", err)
}
if diff := cmp.Diff(expected, actual, cmp.AllowUnexported(pkg.Package{})); diff != "" {
if diff := cmp.Diff(expected, actual,
cmp.AllowUnexported(pkg.Package{}),
cmp.Comparer(
func(x, y source.LocationSet) bool {
return cmp.Equal(x.ToSlice(), y.ToSlice())
},
),
); diff != "" {
t.Errorf("unexpected result from parsing (-expected +actual)\n%s", diff)
}
}
+1 -1
View File
@@ -63,7 +63,7 @@ func parseRpmDB(resolver source.FilePathResolver, dbLocation source.Location, re
p := pkg.Package{
Name: entry.Name,
Version: toELVersion(metadata),
Locations: []source.Location{dbLocation},
Locations: source.NewLocationSet(dbLocation),
FoundBy: catalogerName,
Type: pkg.RpmPkg,
MetadataType: pkg.RpmdbMetadataType,
+2 -2
View File
@@ -71,7 +71,7 @@ func TestParseRpmDB(t *testing.T) {
"dive": {
Name: "dive",
Version: "0.9.2-1",
Locations: []source.Location{dbLocation},
Locations: source.NewLocationSet(dbLocation),
FoundBy: catalogerName,
Type: pkg.RpmPkg,
MetadataType: pkg.RpmdbMetadataType,
@@ -98,7 +98,7 @@ func TestParseRpmDB(t *testing.T) {
"dive": {
Name: "dive",
Version: "0.9.2-1",
Locations: []source.Location{dbLocation},
Locations: source.NewLocationSet(dbLocation),
FoundBy: catalogerName,
Type: pkg.RpmPkg,
MetadataType: pkg.RpmdbMetadataType,
@@ -1,4 +1,4 @@
package cpe
package pkg
import (
"sort"
@@ -6,15 +6,15 @@ import (
"github.com/facebookincubator/nvdtools/wfn"
)
var _ sort.Interface = (*BySpecificity)(nil)
var _ sort.Interface = (*CPEBySpecificity)(nil)
type BySpecificity []wfn.Attributes
type CPEBySpecificity []wfn.Attributes
func (c BySpecificity) Len() int { return len(c) }
func (c CPEBySpecificity) Len() int { return len(c) }
func (c BySpecificity) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c CPEBySpecificity) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c BySpecificity) Less(i, j int) bool {
func (c CPEBySpecificity) Less(i, j int) bool {
iScore := weightedCountForSpecifiedFields(c[i])
jScore := weightedCountForSpecifiedFields(c[j])
@@ -1,40 +1,32 @@
package cpe
package pkg
import (
"sort"
"testing"
"github.com/anchore/syft/syft/pkg"
"github.com/stretchr/testify/assert"
)
func mustCPE(c string) pkg.CPE {
return must(pkg.NewCPE(c))
}
func must(c pkg.CPE, e error) pkg.CPE {
if e != nil {
panic(e)
}
return c
func mustCPE(c string) CPE {
return must(NewCPE(c))
}
func TestCPESpecificity(t *testing.T) {
tests := []struct {
name string
input []pkg.CPE
expected []pkg.CPE
input []CPE
expected []CPE
}{
{
name: "sort strictly by wfn *",
input: []pkg.CPE{
input: []CPE{
mustCPE("cpe:2.3:a:*:package:1:*:*:*:*:*:*:*"),
mustCPE("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*"),
mustCPE("cpe:2.3:a:*:package:1:*:*:*:*:some:*:*"),
mustCPE("cpe:2.3:a:some:package:1:*:*:*:*:some:*:*"),
mustCPE("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*"),
},
expected: []pkg.CPE{
expected: []CPE{
mustCPE("cpe:2.3:a:some:package:1:*:*:*:*:some:*:*"),
mustCPE("cpe:2.3:a:some:package:1:*:*:*:*:*:*:*"),
mustCPE("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*"),
@@ -44,7 +36,7 @@ func TestCPESpecificity(t *testing.T) {
},
{
name: "sort strictly by field length",
input: []pkg.CPE{
input: []CPE{
mustCPE("cpe:2.3:a:1:22:1:*:*:*:*:1:*:*"),
mustCPE("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"),
mustCPE("cpe:2.3:a:1:1:333:*:*:*:*:1:*:*"),
@@ -52,7 +44,7 @@ func TestCPESpecificity(t *testing.T) {
mustCPE("cpe:2.3:a:1:1:1:*:*:*:*:1:*:*"),
mustCPE("cpe:2.3:a:1:1:1:*:*:*:*:4444:*:*"),
},
expected: []pkg.CPE{
expected: []CPE{
mustCPE("cpe:2.3:a:1:666666:1:*:*:*:*:1:*:*"),
mustCPE("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"),
mustCPE("cpe:2.3:a:1:1:1:*:*:*:*:4444:*:*"),
@@ -63,7 +55,7 @@ func TestCPESpecificity(t *testing.T) {
},
{
name: "sort by mix of field length and specificity",
input: []pkg.CPE{
input: []CPE{
mustCPE("cpe:2.3:a:1:666666:*:*:*:*:*:1:*:*"),
mustCPE("cpe:2.3:a:*:1:1:*:*:*:*:4444:*:*"),
mustCPE("cpe:2.3:a:1:*:333:*:*:*:*:*:*:*"),
@@ -71,7 +63,7 @@ func TestCPESpecificity(t *testing.T) {
mustCPE("cpe:2.3:a:1:22:1:*:*:*:*:1:*:*"),
mustCPE("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"),
},
expected: []pkg.CPE{
expected: []CPE{
mustCPE("cpe:2.3:a:55555:1:1:*:*:*:*:1:*:*"),
mustCPE("cpe:2.3:a:1:22:1:*:*:*:*:1:*:*"),
mustCPE("cpe:2.3:a:1:1:1:*:*:*:*:1:*:*"),
@@ -82,7 +74,7 @@ func TestCPESpecificity(t *testing.T) {
},
{
name: "sort by mix of field length, specificity, dash",
input: []pkg.CPE{
input: []CPE{
mustCPE("cpe:2.3:a:alpine:alpine_keys:2.3-r1:*:*:*:*:*:*:*"),
mustCPE("cpe:2.3:a:alpine_keys:alpine_keys:2.3-r1:*:*:*:*:*:*:*"),
mustCPE("cpe:2.3:a:alpine-keys:alpine_keys:2.3-r1:*:*:*:*:*:*:*"),
@@ -90,7 +82,7 @@ func TestCPESpecificity(t *testing.T) {
mustCPE("cpe:2.3:a:alpine-keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"),
mustCPE("cpe:2.3:a:alpine_keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"),
},
expected: []pkg.CPE{
expected: []CPE{
mustCPE("cpe:2.3:a:alpine-keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"),
mustCPE("cpe:2.3:a:alpine-keys:alpine_keys:2.3-r1:*:*:*:*:*:*:*"),
mustCPE("cpe:2.3:a:alpine_keys:alpine-keys:2.3-r1:*:*:*:*:*:*:*"),
@@ -103,7 +95,7 @@ func TestCPESpecificity(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
sort.Sort(BySpecificity(test.input))
sort.Sort(CPEBySpecificity(test.input))
assert.Equal(t, test.expected, test.input)
})
}
+38
View File
@@ -0,0 +1,38 @@
package pkg
import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/linux"
)
type DartPubMetadata struct {
Name string `mapstructure:"name" json:"name"`
Version string `mapstructure:"version" json:"version"`
HostedURL string `mapstructure:"hosted_url" json:"hosted_url,omitempty"`
VcsURL string `mapstructure:"vcs_url" json:"vcs_url,omitempty"`
}
func (m DartPubMetadata) PackageURL(_ *linux.Release) string {
var qualifiers packageurl.Qualifiers
if m.HostedURL != "" {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "hosted_url",
Value: m.HostedURL,
})
} else if m.VcsURL != "" { // Default to using Hosted if somehow both are provided
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "vcs_url",
Value: m.VcsURL,
})
}
return packageurl.NewPackageURL(
packageurl.TypePub,
"",
m.Name,
m.Version,
qualifiers,
"",
).ToString()
}
+1 -1
View File
@@ -5,5 +5,5 @@ type GolangBinMetadata struct {
BuildSettings map[string]string `json:"goBuildSettings,omitempty" cyclonedx:"goBuildSettings"`
GoCompiledVersion string `json:"goCompiledVersion" cyclonedx:"goCompiledVersion"`
Architecture string `json:"architecture" cyclonedx:"architecture"`
H1Digest string `json:"h1Digest" cyclonedx:"h1Digest"`
H1Digest string `json:"h1Digest,omitempty" cyclonedx:"h1Digest"`
}
+1 -1
View File
@@ -20,7 +20,7 @@ var jenkinsPluginPomPropertiesGroupIDs = []string{
// JavaMetadata encapsulates all Java ecosystem metadata for a package as well as an (optional) parent relationship.
type JavaMetadata struct {
VirtualPath string `json:"virtualPath"`
VirtualPath string `json:"virtualPath" cyclonedx:"virtualPath"` // we need to include the virtual path in cyclonedx documents to prevent deduplication of jars within jars
Manifest *JavaManifest `mapstructure:"Manifest" json:"manifest,omitempty"`
PomProperties *PomProperties `mapstructure:"PomProperties" json:"pomProperties,omitempty" cyclonedx:"-"`
PomProject *PomProject `mapstructure:"PomProject" json:"pomProject,omitempty"`
+4
View File
@@ -19,6 +19,7 @@ const (
Ruby Language = "ruby"
Go Language = "go"
Rust Language = "rust"
Dart Language = "dart"
)
// AllLanguages is a set of all programming languages detected by syft.
@@ -30,6 +31,7 @@ var AllLanguages = []Language{
Ruby,
Go,
Rust,
Dart,
}
// String returns the string representation of the language.
@@ -62,6 +64,8 @@ func LanguageByName(name string) Language {
return Ruby
case purlCargoPkgType:
return Rust
case packageurl.TypePub, string(Dart):
return Dart
default:
return UnknownLanguage
}
+6 -1
View File
@@ -1,9 +1,10 @@
package pkg
import (
"testing"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"testing"
)
func TestLanguageFromPURL(t *testing.T) {
@@ -29,6 +30,10 @@ func TestLanguageFromPURL(t *testing.T) {
purl: "pkg:golang/github.com/gorilla/context@234fd47e07d1004f0aed9c",
want: Go,
},
{
purl: "pkg:pub/util@1.2.34",
want: Dart,
},
{
purl: "pkg:cargo/clap@2.33.0",
want: Rust,
+25
View File
@@ -0,0 +1,25 @@
package pkg
import (
"sort"
)
func mergeCPEs(a, b []CPE) (result []CPE) {
aCPEs := make(map[string]CPE)
// keep all CPEs from a and create a quick string-based lookup
for _, aCPE := range a {
aCPEs[aCPE.BindToFmtString()] = aCPE
result = append(result, aCPE)
}
// keep all unique CPEs from b
for _, bCPE := range b {
if _, exists := aCPEs[bCPE.BindToFmtString()]; !exists {
result = append(result, bCPE)
}
}
sort.Sort(CPEBySpecificity(result))
return result
}
+3
View File
@@ -17,6 +17,7 @@ const (
JavaMetadataType MetadataType = "JavaMetadata"
NpmPackageJSONMetadataType MetadataType = "NpmPackageJsonMetadata"
RpmdbMetadataType MetadataType = "RpmdbMetadata"
DartPubMetadataType MetadataType = "DartPubMetadata"
PythonPackageMetadataType MetadataType = "PythonPackageMetadata"
RustCargoPackageMetadataType MetadataType = "RustCargoPackageMetadata"
KbPackageMetadataType MetadataType = "KbPackageMetadata"
@@ -31,6 +32,7 @@ var AllMetadataTypes = []MetadataType{
JavaMetadataType,
NpmPackageJSONMetadataType,
RpmdbMetadataType,
DartPubMetadataType,
PythonPackageMetadataType,
RustCargoPackageMetadataType,
KbPackageMetadataType,
@@ -45,6 +47,7 @@ var MetadataTypeByName = map[MetadataType]reflect.Type{
JavaMetadataType: reflect.TypeOf(JavaMetadata{}),
NpmPackageJSONMetadataType: reflect.TypeOf(NpmPackageJSONMetadata{}),
RpmdbMetadataType: reflect.TypeOf(RpmdbMetadata{}),
DartPubMetadataType: reflect.TypeOf(DartPubMetadata{}),
PythonPackageMetadataType: reflect.TypeOf(PythonPackageMetadata{}),
RustCargoPackageMetadataType: reflect.TypeOf(CargoMetadata{}),
KbPackageMetadataType: reflect.TypeOf(KbPackageMetadata{}),
+30 -12
View File
@@ -14,18 +14,18 @@ import (
// Package represents an application or library that has been bundled into a distributable format.
// TODO: if we ignore FoundBy for ID generation should we merge the field to show it was found in two places?
type Package struct {
id artifact.ID `hash:"ignore"`
Name string // the package name
Version string // the version of the package
FoundBy string `cyclonedx:"foundBy"` // the specific cataloger that discovered this package
Locations []source.Location // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
Licenses []string // licenses discovered with the package metadata
Language Language `cyclonedx:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc)
Type Type `cyclonedx:"type"` // the package type (e.g. Npm, Yarn, Python, Rpm, Deb, etc)
CPEs []CPE `hash:"ignore"` // all possible Common Platform Enumerators (note: this is NOT included in the definition of the ID since all fields on a CPE are derived from other fields)
PURL string `hash:"ignore"` // the Package URL (see https://github.com/package-url/purl-spec) (note: this is NOT included in the definition of the ID since all fields on a pURL are derived from other fields)
MetadataType MetadataType `cyclonedx:"metadataType"` // the shape of the additional data in the "metadata" field
Metadata interface{} // additional data found while parsing the package source
id artifact.ID `hash:"ignore"`
Name string // the package name
Version string // the version of the package
FoundBy string `cyclonedx:"foundBy"` // the specific cataloger that discovered this package
Locations source.LocationSet // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
Licenses []string // licenses discovered with the package metadata
Language Language `cyclonedx:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc)
Type Type `cyclonedx:"type"` // the package type (e.g. Npm, Yarn, Python, Rpm, Deb, etc)
CPEs []CPE `hash:"ignore"` // all possible Common Platform Enumerators (note: this is NOT included in the definition of the ID since all fields on a CPE are derived from other fields)
PURL string `hash:"ignore"` // the Package URL (see https://github.com/package-url/purl-spec)
MetadataType MetadataType `cyclonedx:"metadataType"` // the shape of the additional data in the "metadata" field
Metadata interface{} // additional data found while parsing the package source
}
func (p *Package) SetID() {
@@ -46,3 +46,21 @@ func (p Package) ID() artifact.ID {
func (p Package) String() string {
return fmt.Sprintf("Pkg(name=%q version=%q type=%q id=%q)", p.Name, p.Version, p.Type, p.id)
}
func (p *Package) merge(other Package) error {
if p.id != other.id {
return fmt.Errorf("cannot merge packages with different IDs: %q vs %q", p.id, other.id)
}
if p.PURL != other.PURL {
log.Warnf("merging packages have with different pURLs: %q=%q vs %q=%q", p.id, p.PURL, other.id, other.PURL)
}
p.Locations.Add(other.Locations.ToSlice()...)
p.CPEs = mergeCPEs(p.CPEs, other.CPEs)
if p.PURL == "" {
p.PURL = other.PURL
}
return nil
}
+266 -32
View File
@@ -3,24 +3,28 @@ package pkg
import (
"testing"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/source"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFingerprint(t *testing.T) {
func TestIDUniqueness(t *testing.T) {
originalLocation := source.Location{
Coordinates: source.Coordinates{
RealPath: "39.0742° N, 21.8243° E",
FileSystemID: "Earth",
},
VirtualPath: "/Ancient-Greece",
}
originalPkg := Package{
Name: "pi",
Version: "3.14",
FoundBy: "Archimedes",
Locations: []source.Location{
{
Coordinates: source.Coordinates{
RealPath: "39.0742° N, 21.8243° E",
FileSystemID: "Earth",
},
VirtualPath: "/Ancient-Greece",
},
},
Locations: source.NewLocationSet(
originalLocation,
),
Licenses: []string{
"cc0-1.0",
"MIT",
@@ -45,9 +49,9 @@ func TestFingerprint(t *testing.T) {
// this is a set of differential tests, ensuring that select mutations are reflected in the fingerprint (or not)
tests := []struct {
name string
transform func(pkg Package) Package
expectIdentical bool
name string
transform func(pkg Package) Package
expectedIDComparison assert.ComparisonAssertionFunc
}{
{
name: "go case (no transform)",
@@ -55,7 +59,7 @@ func TestFingerprint(t *testing.T) {
// do nothing!
return pkg
},
expectIdentical: true,
expectedIDComparison: assert.Equal,
},
{
name: "same metadata is ignored",
@@ -72,7 +76,7 @@ func TestFingerprint(t *testing.T) {
}
return pkg
},
expectIdentical: true,
expectedIDComparison: assert.Equal,
},
{
name: "licenses order is ignored",
@@ -84,7 +88,7 @@ func TestFingerprint(t *testing.T) {
}
return pkg
},
expectIdentical: true,
expectedIDComparison: assert.Equal,
},
{
name: "name is reflected",
@@ -92,7 +96,42 @@ func TestFingerprint(t *testing.T) {
pkg.Name = "new!"
return pkg
},
expectIdentical: false,
expectedIDComparison: assert.NotEqual,
},
{
name: "location is reflected",
transform: func(pkg Package) Package {
locations := source.NewLocationSet(pkg.Locations.ToSlice()...)
locations.Add(source.NewLocation("/somewhere/new"))
pkg.Locations = locations
return pkg
},
expectedIDComparison: assert.NotEqual,
},
{
name: "same path for different filesystem is NOT reflected",
transform: func(pkg Package) Package {
newLocation := originalLocation
newLocation.FileSystemID = "Mars"
pkg.Locations = source.NewLocationSet(newLocation)
return pkg
},
expectedIDComparison: assert.Equal,
},
{
name: "multiple equivalent paths for different filesystem is NOT reflected",
transform: func(pkg Package) Package {
newLocation := originalLocation
newLocation.FileSystemID = "Mars"
locations := source.NewLocationSet(pkg.Locations.ToSlice()...)
locations.Add(newLocation, originalLocation)
pkg.Locations = locations
return pkg
},
expectedIDComparison: assert.Equal,
},
{
name: "version is reflected",
@@ -100,7 +139,7 @@ func TestFingerprint(t *testing.T) {
pkg.Version = "new!"
return pkg
},
expectIdentical: false,
expectedIDComparison: assert.NotEqual,
},
{
name: "licenses is reflected",
@@ -108,7 +147,7 @@ func TestFingerprint(t *testing.T) {
pkg.Licenses = []string{"new!"}
return pkg
},
expectIdentical: false,
expectedIDComparison: assert.NotEqual,
},
{
name: "type is reflected",
@@ -116,7 +155,7 @@ func TestFingerprint(t *testing.T) {
pkg.Type = RustPkg
return pkg
},
expectIdentical: false,
expectedIDComparison: assert.NotEqual,
},
{
name: "metadata type is reflected",
@@ -124,7 +163,7 @@ func TestFingerprint(t *testing.T) {
pkg.MetadataType = RustCargoPackageMetadataType
return pkg
},
expectIdentical: false,
expectedIDComparison: assert.NotEqual,
},
{
name: "CPEs is ignored",
@@ -132,7 +171,7 @@ func TestFingerprint(t *testing.T) {
pkg.CPEs = []CPE{}
return pkg
},
expectIdentical: true,
expectedIDComparison: assert.Equal,
},
{
name: "pURL is ignored",
@@ -140,7 +179,7 @@ func TestFingerprint(t *testing.T) {
pkg.PURL = "new!"
return pkg
},
expectIdentical: true,
expectedIDComparison: assert.Equal,
},
{
name: "language is reflected",
@@ -148,7 +187,7 @@ func TestFingerprint(t *testing.T) {
pkg.Language = Rust
return pkg
},
expectIdentical: false,
expectedIDComparison: assert.NotEqual,
},
{
name: "foundBy is reflected",
@@ -156,7 +195,7 @@ func TestFingerprint(t *testing.T) {
pkg.FoundBy = "new!"
return pkg
},
expectIdentical: false,
expectedIDComparison: assert.NotEqual,
},
{
name: "metadata mutation is reflected",
@@ -166,7 +205,7 @@ func TestFingerprint(t *testing.T) {
pkg.Metadata = metadata
return pkg
},
expectIdentical: false,
expectedIDComparison: assert.NotEqual,
},
{
name: "new metadata is reflected",
@@ -176,7 +215,7 @@ func TestFingerprint(t *testing.T) {
}
return pkg
},
expectIdentical: false,
expectedIDComparison: assert.NotEqual,
},
{
name: "nil metadata is reflected",
@@ -184,7 +223,7 @@ func TestFingerprint(t *testing.T) {
pkg.Metadata = nil
return pkg
},
expectIdentical: false,
expectedIDComparison: assert.NotEqual,
},
}
@@ -199,12 +238,207 @@ func TestFingerprint(t *testing.T) {
transformedFingerprint := transformedPkg.ID()
assert.NotEmpty(t, transformedFingerprint)
if test.expectIdentical {
assert.Equal(t, originalFingerprint, transformedFingerprint)
} else {
assert.NotEqual(t, originalFingerprint, transformedFingerprint)
test.expectedIDComparison(t, originalFingerprint, transformedFingerprint)
})
}
}
func TestPackage_Merge(t *testing.T) {
originalLocation := source.Location{
Coordinates: source.Coordinates{
RealPath: "39.0742° N, 21.8243° E",
FileSystemID: "Earth",
},
VirtualPath: "/Ancient-Greece",
}
similarLocation := originalLocation
similarLocation.FileSystemID = "Mars"
tests := []struct {
name string
subject Package
other Package
expected *Package
}{
{
name: "merge two packages (different cpes + locations)",
subject: Package{
Name: "pi",
Version: "3.14",
FoundBy: "Archimedes",
Locations: source.NewLocationSet(
originalLocation,
),
Licenses: []string{
"cc0-1.0",
"MIT",
},
Language: "math",
Type: PythonPkg,
CPEs: []CPE{
must(NewCPE(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`)),
},
PURL: "pkg:pypi/pi@3.14",
MetadataType: PythonPackageMetadataType,
Metadata: PythonPackageMetadata{
Name: "pi",
Version: "3.14",
License: "cc0-1.0",
Author: "Archimedes",
AuthorEmail: "Archimedes@circles.io",
Platform: "universe",
SitePackagesRootPath: "Pi",
},
},
other: Package{
Name: "pi",
Version: "3.14",
FoundBy: "Archimedes",
Locations: source.NewLocationSet(
similarLocation, // NOTE: difference; we have a different layer but the same path
),
Licenses: []string{
"cc0-1.0",
"MIT",
},
Language: "math",
Type: PythonPkg,
CPEs: []CPE{
must(NewCPE(`cpe:2.3:a:DIFFERENT:pi:3.14:*:*:*:*:math:*:*`)), // NOTE: difference
},
PURL: "pkg:pypi/pi@3.14",
MetadataType: PythonPackageMetadataType,
Metadata: PythonPackageMetadata{
Name: "pi",
Version: "3.14",
License: "cc0-1.0",
Author: "Archimedes",
AuthorEmail: "Archimedes@circles.io",
Platform: "universe",
SitePackagesRootPath: "Pi",
},
},
expected: &Package{
Name: "pi",
Version: "3.14",
FoundBy: "Archimedes",
Locations: source.NewLocationSet(
originalLocation,
similarLocation, // NOTE: merge!
),
Licenses: []string{
"cc0-1.0",
"MIT",
},
Language: "math",
Type: PythonPkg,
CPEs: []CPE{
must(NewCPE(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`)),
must(NewCPE(`cpe:2.3:a:DIFFERENT:pi:3.14:*:*:*:*:math:*:*`)), // NOTE: merge!
},
PURL: "pkg:pypi/pi@3.14",
MetadataType: PythonPackageMetadataType,
Metadata: PythonPackageMetadata{
Name: "pi",
Version: "3.14",
License: "cc0-1.0",
Author: "Archimedes",
AuthorEmail: "Archimedes@circles.io",
Platform: "universe",
SitePackagesRootPath: "Pi",
},
},
},
{
name: "error when there are different IDs",
subject: Package{
Name: "pi",
Version: "3.14",
FoundBy: "Archimedes",
Locations: source.NewLocationSet(
originalLocation,
),
Licenses: []string{
"cc0-1.0",
"MIT",
},
Language: "math",
Type: PythonPkg,
CPEs: []CPE{
must(NewCPE(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`)),
},
PURL: "pkg:pypi/pi@3.14",
MetadataType: PythonPackageMetadataType,
Metadata: PythonPackageMetadata{
Name: "pi",
Version: "3.14",
License: "cc0-1.0",
Author: "Archimedes",
AuthorEmail: "Archimedes@circles.io",
Platform: "universe",
SitePackagesRootPath: "Pi",
},
},
other: Package{
Name: "pi-DIFFERENT", // difference
Version: "3.14",
FoundBy: "Archimedes",
Locations: source.NewLocationSet(
originalLocation,
),
Licenses: []string{
"cc0-1.0",
"MIT",
},
Language: "math",
Type: PythonPkg,
CPEs: []CPE{
must(NewCPE(`cpe:2.3:a:Archimedes:pi:3.14:*:*:*:*:math:*:*`)),
},
PURL: "pkg:pypi/pi@3.14",
MetadataType: PythonPackageMetadataType,
Metadata: PythonPackageMetadata{
Name: "pi",
Version: "3.14",
License: "cc0-1.0",
Author: "Archimedes",
AuthorEmail: "Archimedes@circles.io",
Platform: "universe",
SitePackagesRootPath: "Pi",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.subject.SetID()
tt.other.SetID()
err := tt.subject.merge(tt.other)
if tt.expected == nil {
require.Error(t, err)
return
}
require.NoError(t, err)
tt.expected.SetID()
require.Equal(t, tt.expected.id, tt.subject.id)
if diff := cmp.Diff(*tt.expected, tt.subject,
cmp.AllowUnexported(Package{}),
cmp.Comparer(
func(x, y source.LocationSet) bool {
return cmp.Equal(
x.ToSlice(), y.ToSlice(),
cmp.AllowUnexported(source.Location{}),
cmp.AllowUnexported(file.Reference{}),
)
},
),
); diff != "" {
t.Errorf("unexpected result from parsing (-expected +actual)\n%s", diff)
}
})
}
}
@@ -18,10 +18,10 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
name: "owns-by-real-path",
setup: func(t testing.TB) ([]Package, []artifact.Relationship) {
parent := Package{
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/a/path", "/another/path"),
source.NewVirtualLocation("/b/path", "/bee/path"),
},
),
Type: RpmPkg,
MetadataType: RpmdbMetadataType,
Metadata: RpmdbMetadata{
@@ -35,10 +35,10 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
parent.SetID()
child := Package{
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/c/path", "/another/path"),
source.NewVirtualLocation("/d/path", "/another/path"),
},
),
Type: NpmPkg,
}
child.SetID()
@@ -61,10 +61,10 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
name: "owns-by-virtual-path",
setup: func(t testing.TB) ([]Package, []artifact.Relationship) {
parent := Package{
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/a/path", "/some/other/path"),
source.NewVirtualLocation("/b/path", "/bee/path"),
},
),
Type: RpmPkg,
MetadataType: RpmdbMetadataType,
Metadata: RpmdbMetadata{
@@ -78,10 +78,10 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
parent.SetID()
child := Package{
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/c/path", "/another/path"),
source.NewLocation("/d/path"),
},
),
Type: NpmPkg,
}
child.SetID()
@@ -103,10 +103,10 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
name: "ignore-empty-path",
setup: func(t testing.TB) ([]Package, []artifact.Relationship) {
parent := Package{
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/a/path", "/some/other/path"),
source.NewVirtualLocation("/b/path", "/bee/path"),
},
),
Type: RpmPkg,
MetadataType: RpmdbMetadataType,
Metadata: RpmdbMetadata{
@@ -121,10 +121,10 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
parent.SetID()
child := Package{
Locations: []source.Location{
Locations: source.NewLocationSet(
source.NewVirtualLocation("/c/path", "/another/path"),
source.NewLocation("/d/path"),
},
),
Type: NpmPkg,
}
+6
View File
@@ -20,6 +20,7 @@ const (
GoModulePkg Type = "go-module"
RustPkg Type = "rust-crate"
KbPkg Type = "msrc-kb"
DartPubPkg Type = "dart-pub"
)
// AllPkgs represents all supported package types
@@ -36,6 +37,7 @@ var AllPkgs = []Type{
GoModulePkg,
RustPkg,
KbPkg,
DartPubPkg,
}
// PackageURLType returns the PURL package type for the current package.
@@ -61,6 +63,8 @@ func (t Type) PackageURLType() string {
return packageurl.TypeGolang
case RustPkg:
return "cargo"
case DartPubPkg:
return packageurl.TypePub
default:
// TODO: should this be a "generic" purl type instead?
return ""
@@ -98,6 +102,8 @@ func TypeByName(name string) Type {
return GemPkg
case "cargo", "crate":
return RustPkg
case packageurl.TypePub:
return DartPubPkg
default:
return UnknownPkg
}
+6 -1
View File
@@ -1,9 +1,10 @@
package pkg
import (
"github.com/scylladb/go-set/strset"
"testing"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
)
@@ -46,6 +47,10 @@ func TestTypeFromPURL(t *testing.T) {
purl: "pkg:cargo/clap@2.33.0",
expected: RustPkg,
},
{
purl: "pkg:pub/util@1.2.34?hosted_url=pub.hosted.org",
expected: DartPubPkg,
},
{
purl: "pkg:composer/laravel/laravel@5.5.0",
expected: PhpComposerPkg,
+14
View File
@@ -25,6 +25,20 @@ func TestPackageURL(t *testing.T) {
},
expected: "pkg:golang/github.com/anchore/syft@v0.1.0",
},
{
name: "pub",
pkg: Package{
Name: "bad-name",
Version: "0.1.0",
Type: DartPubPkg,
Metadata: DartPubMetadata{
Name: "name",
Version: "0.2.0",
HostedURL: "pub.hosted.org",
},
},
expected: "pkg:pub/name@0.2.0?hosted_url=pub.hosted.org",
},
{
name: "python",
pkg: Package{
+5
View File
@@ -13,6 +13,11 @@ var (
type FormatID string
// String returns a string representation of the FormatID.
func (f FormatID) String() string {
return string(f)
}
type Format interface {
ID() FormatID
Encode(io.Writer, SBOM) error
+86
View File
@@ -0,0 +1,86 @@
package source
import (
"sort"
"github.com/mitchellh/hashstructure/v2"
"github.com/scylladb/go-set/strset"
)
type CoordinateSet struct {
set map[Coordinates]struct{}
}
func NewCoordinateSet(coordinates ...Coordinates) (s CoordinateSet) {
for _, l := range coordinates {
s.Add(l)
}
return s
}
func (s *CoordinateSet) Add(coordinates ...Coordinates) {
if s.set == nil {
s.set = make(map[Coordinates]struct{})
}
for _, l := range coordinates {
s.set[l] = struct{}{}
}
}
func (s CoordinateSet) Remove(coordinates ...Coordinates) {
if s.set == nil {
return
}
for _, l := range coordinates {
delete(s.set, l)
}
}
func (s CoordinateSet) Contains(l Coordinates) bool {
if s.set == nil {
return false
}
_, ok := s.set[l]
return ok
}
func (s CoordinateSet) Paths() []string {
if s.set == nil {
return nil
}
paths := strset.New()
for _, c := range s.ToSlice() {
paths.Add(c.RealPath)
}
pathSlice := paths.List()
sort.Strings(pathSlice)
return pathSlice
}
func (s CoordinateSet) ToSlice() []Coordinates {
if s.set == nil {
return nil
}
coordinates := make([]Coordinates, len(s.set))
idx := 0
for v := range s.set {
coordinates[idx] = v
idx++
}
sort.SliceStable(coordinates, func(i, j int) bool {
if coordinates[i].RealPath == coordinates[j].RealPath {
return coordinates[i].FileSystemID < coordinates[j].FileSystemID
}
return coordinates[i].RealPath < coordinates[j].RealPath
})
return coordinates
}
func (s CoordinateSet) Hash() (uint64, error) {
return hashstructure.Hash(s.ToSlice(), hashstructure.FormatV2, &hashstructure.HashOptions{
ZeroNil: true,
SlicesAsSets: true,
})
}
+117
View File
@@ -0,0 +1,117 @@
package source
import (
"github.com/anchore/syft/syft/artifact"
"github.com/stretchr/testify/require"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCoordinatesSet(t *testing.T) {
binA := Coordinates{
RealPath: "/bin",
FileSystemID: "a",
}
binB := Coordinates{
RealPath: "/bin",
FileSystemID: "b",
}
tests := []struct {
name string
input []Coordinates
expected []Coordinates
}{
{
name: "de-dup same location",
input: []Coordinates{
binA, binA, binA,
},
expected: []Coordinates{
binA,
},
},
{
name: "dont de-dup different filesystem",
input: []Coordinates{
binB, binA,
},
expected: []Coordinates{
binA, binB,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
set := NewCoordinateSet(test.input...)
assert.Equal(t, test.expected, set.ToSlice())
})
}
}
func TestCoordinateSet_Hash(t *testing.T) {
etcA := Coordinates{
RealPath: "/etc",
FileSystemID: "a",
}
etcB := Coordinates{
RealPath: "/etc",
FileSystemID: "b",
}
binA := Coordinates{
RealPath: "/bin",
FileSystemID: "a",
}
binB := Coordinates{
RealPath: "/bin",
FileSystemID: "b",
}
tests := []struct {
name string
setA CoordinateSet
setB CoordinateSet
want assert.ComparisonAssertionFunc
}{
{
name: "empty sets have the same hash",
setA: NewCoordinateSet(),
setB: NewCoordinateSet(),
want: assert.Equal,
},
{
name: "sets with same elements have the same hash",
setA: NewCoordinateSet(binA, etcA),
setB: NewCoordinateSet(etcA, binA),
want: assert.Equal,
},
{
name: "sets with different elements have different hashes",
setA: NewCoordinateSet(binA, etcA),
setB: NewCoordinateSet(binA),
want: assert.NotEqual,
},
{
name: "sets with same paths but different FS IDs have different hashes",
setA: NewCoordinateSet(etcA, binA),
setB: NewCoordinateSet(etcB, binB),
want: assert.NotEqual,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotA, err := artifact.IDByHash(tt.setA)
require.NoError(t, err)
gotB, err := artifact.IDByHash(tt.setB)
require.NoError(t, err)
tt.want(t, gotA, gotB)
})
}
}
-47
View File
@@ -2,7 +2,6 @@ package source
import (
"fmt"
"sort"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
@@ -14,18 +13,6 @@ type Coordinates struct {
FileSystemID string `json:"layerID,omitempty" cyclonedx:"layerID"` // An ID representing the filesystem. For container images, this is a layer digest. For directories or a root filesystem, this is blank.
}
// CoordinateSet represents a set of string types.
type CoordinateSet map[Coordinates]struct{}
// NewCoordinateSet creates a CoordinateSet populated with values from the given slice.
func NewCoordinateSet(start ...Coordinates) CoordinateSet {
ret := make(CoordinateSet)
for _, s := range start {
ret.Add(s)
}
return ret
}
func (c Coordinates) ID() artifact.ID {
f, err := artifact.IDByHash(c)
if err != nil {
@@ -45,37 +32,3 @@ func (c Coordinates) String() string {
}
return fmt.Sprintf("Location<%s>", str)
}
// Add a string to the set.
func (s CoordinateSet) Add(i Coordinates) {
s[i] = struct{}{}
}
// Remove a string from the set.
func (s CoordinateSet) Remove(i Coordinates) {
delete(s, i)
}
// Contains indicates if the given string is contained within the set.
func (s CoordinateSet) Contains(i Coordinates) bool {
_, ok := s[i]
return ok
}
// ToSlice returns a sorted slice of Locations that are contained within the set.
func (s CoordinateSet) ToSlice() []Coordinates {
ret := make([]Coordinates, len(s))
idx := 0
for v := range s {
ret[idx] = v
idx++
}
sort.SliceStable(ret, func(i, j int) bool {
if ret[i].RealPath == ret[j].RealPath {
return ret[i].FileSystemID < ret[j].FileSystemID
}
return ret[i].RealPath < ret[j].RealPath
})
return ret
}
-51
View File
@@ -1,51 +0,0 @@
package source
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCoordinateSet(t *testing.T) {
binA := Coordinates{
RealPath: "/bin",
FileSystemID: "a",
}
binB := Coordinates{
RealPath: "/bin",
FileSystemID: "b",
}
tests := []struct {
name string
input []Coordinates
expected []Coordinates
}{
{
name: "de-dup same location",
input: []Coordinates{
binA, binA, binA,
},
expected: []Coordinates{
binA,
},
},
{
name: "dont de-dup different filesystem",
input: []Coordinates{
binB, binA,
},
expected: []Coordinates{
binA, binB,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
assert.Equal(t, test.expected, NewCoordinateSet(test.input...).ToSlice())
})
}
}
+6 -6
View File
@@ -132,10 +132,15 @@ func (r *directoryResolver) indexTree(root string, stager *progress.Stage) ([]st
}
func (r *directoryResolver) indexPath(path string, info os.FileInfo, err error) (string, error) {
// link cycles could cause a revisit --we should not allow this
if r.hasBeenIndexed(path) {
return "", nil
}
// ignore any path which a filter function returns true
for _, filterFn := range r.pathFilterFns {
if filterFn != nil && filterFn(path, info) {
if info.IsDir() {
if info != nil && info.IsDir() {
return "", fs.SkipDir
}
return "", nil
@@ -146,11 +151,6 @@ func (r *directoryResolver) indexPath(path string, info os.FileInfo, err error)
return "", nil
}
// link cycles could cause a revisit --we should not allow this
if r.hasBeenIndexed(path) {
return "", nil
}
if info == nil {
// walk may not be able to provide a FileInfo object, don't allow for this to stop indexing; keep track of the paths and continue.
r.errPaths[path] = fmt.Errorf("no file info observable at path=%q", path)
+21
View File
@@ -865,3 +865,24 @@ func Test_IncludeRootPathInIndex(t *testing.T) {
_, exists = resolver.metadata[ref.ID()]
require.True(t, exists)
}
func TestDirectoryResolver_indexPath(t *testing.T) {
// TODO: Ideally we can use an OS abstraction, which would obviate the need for real FS setup.
tempFile, err := os.CreateTemp("", "")
require.NoError(t, err)
resolver, err := newDirectoryResolver(tempFile.Name())
require.NoError(t, err)
t.Run("filtering path with nil os.FileInfo", func(t *testing.T) {
// We use one of these prefixes in order to trigger a pathFilterFn
filteredPath := unixSystemRuntimePrefixes[0]
var fileInfo os.FileInfo = nil
assert.NotPanics(t, func() {
_, err := resolver.indexPath(filteredPath, fileInfo, nil)
assert.NoError(t, err)
})
})
}
+6
View File
@@ -109,3 +109,9 @@ func (l Location) String() string {
}
return fmt.Sprintf("Location<%s>", str)
}
func (l Location) Equals(other Location) bool {
return l.RealPath == other.RealPath &&
l.VirtualPath == other.VirtualPath &&
l.FileSystemID == other.FileSystemID
}
+78
View File
@@ -0,0 +1,78 @@
package source
import (
"sort"
"github.com/mitchellh/hashstructure/v2"
)
type LocationSet struct {
set map[Location]struct{}
}
func NewLocationSet(locations ...Location) (s LocationSet) {
for _, l := range locations {
s.Add(l)
}
return s
}
func (s *LocationSet) Add(locations ...Location) {
if s.set == nil {
s.set = make(map[Location]struct{})
}
for _, l := range locations {
s.set[l] = struct{}{}
}
}
func (s LocationSet) Remove(locations ...Location) {
if s.set == nil {
return
}
for _, l := range locations {
delete(s.set, l)
}
}
func (s LocationSet) Contains(l Location) bool {
if s.set == nil {
return false
}
_, ok := s.set[l]
return ok
}
func (s LocationSet) ToSlice() []Location {
if s.set == nil {
return nil
}
locations := make([]Location, len(s.set))
idx := 0
for v := range s.set {
locations[idx] = v
idx++
}
sort.Sort(Locations(locations))
return locations
}
func (s *LocationSet) CoordinateSet() CoordinateSet {
if s.set == nil {
return NewCoordinateSet()
}
set := NewCoordinateSet()
for l := range s.set {
set.Add(l.Coordinates)
}
return set
}
func (s LocationSet) Hash() (uint64, error) {
// access paths and filesystem IDs are not considered when hashing a location set, only the real paths
return hashstructure.Hash(s.CoordinateSet().Paths(), hashstructure.FormatV2, &hashstructure.HashOptions{
ZeroNil: true,
SlicesAsSets: true,
})
}
+178
View File
@@ -0,0 +1,178 @@
package source
import (
"github.com/anchore/syft/syft/artifact"
"github.com/stretchr/testify/require"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLocationSet(t *testing.T) {
etcHostsLinkVar := Location{
Coordinates: Coordinates{
RealPath: "/etc/hosts",
FileSystemID: "a",
},
VirtualPath: "/var/etc/hosts",
}
etcHostsLinkHome := Location{
Coordinates: Coordinates{
RealPath: "/etc/hosts",
FileSystemID: "a",
},
VirtualPath: "/home/wagoodman/hosts",
}
binA := Location{
Coordinates: Coordinates{
RealPath: "/bin",
FileSystemID: "a",
},
VirtualPath: "/usr/bin",
}
binB := Location{
Coordinates: Coordinates{
RealPath: "/bin",
FileSystemID: "b",
},
VirtualPath: "/usr/bin",
}
tests := []struct {
name string
input []Location
expected []Location
}{
{
name: "de-dup same location",
input: []Location{
binA, binA, binA,
},
expected: []Location{
binA,
},
},
{
name: "dont de-dup different filesystem",
input: []Location{
binB, binA,
},
expected: []Location{
binA, binB,
},
},
{
name: "dont de-dup different virtual paths",
input: []Location{
etcHostsLinkVar, etcHostsLinkHome,
},
expected: []Location{
etcHostsLinkHome, etcHostsLinkVar,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
set := NewLocationSet(test.input...)
assert.Equal(t, test.expected, set.ToSlice())
})
}
}
func TestLocationSet_Hash(t *testing.T) {
etcAlink := Location{
Coordinates: Coordinates{
RealPath: "/etc/hosts",
FileSystemID: "a",
},
VirtualPath: "/var/etc/hosts",
}
etcA := Location{
Coordinates: Coordinates{
RealPath: "/etc/hosts",
FileSystemID: "a",
},
}
etcB := Location{
Coordinates: Coordinates{
RealPath: "/etc/hosts",
FileSystemID: "b",
},
}
binA := Location{
Coordinates: Coordinates{
RealPath: "/bin",
FileSystemID: "a",
},
VirtualPath: "/usr/bin",
}
binB := Location{
Coordinates: Coordinates{
RealPath: "/bin",
FileSystemID: "b",
},
VirtualPath: "/usr/bin",
}
tests := []struct {
name string
setA LocationSet
setB LocationSet
want assert.ComparisonAssertionFunc
}{
{
name: "empty sets have the same hash",
setA: NewLocationSet(),
setB: NewLocationSet(),
want: assert.Equal,
},
{
name: "sets with same elements accessed through different paths have the same hash",
setA: NewLocationSet(binA, etcA),
setB: NewLocationSet(etcAlink, binA),
want: assert.Equal,
},
{
name: "sets with same elements have the same hash",
setA: NewLocationSet(binA, etcA),
setB: NewLocationSet(etcA, binA),
want: assert.Equal,
},
{
name: "sets with different element counts have different hashes",
setA: NewLocationSet(binA, etcA),
setB: NewLocationSet(binA),
want: assert.NotEqual,
},
{
name: "sets with same path but different FS IDs have the same hash",
setA: NewLocationSet(binA),
setB: NewLocationSet(binB),
want: assert.Equal,
},
{
name: "sets with same paths but different FS IDs have the same hash",
setA: NewLocationSet(etcA, binA),
setB: NewLocationSet(binB, etcB),
want: assert.Equal,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotA, err := artifact.IDByHash(tt.setA)
require.NoError(t, err)
gotB, err := artifact.IDByHash(tt.setB)
require.NoError(t, err)
tt.want(t, gotA, gotB)
})
}
}
+21
View File
@@ -0,0 +1,21 @@
package source
type Locations []Location
func (l Locations) Len() int {
return len(l)
}
func (l Locations) Less(i, j int) bool {
if l[i].RealPath == l[j].RealPath {
if l[i].VirtualPath == l[j].VirtualPath {
return l[i].FileSystemID < l[j].FileSystemID
}
return l[i].VirtualPath < l[j].VirtualPath
}
return l[i].RealPath < l[j].RealPath
}
func (l Locations) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
+19 -5
View File
@@ -8,6 +8,7 @@ import (
)
func TestPackagesCmdFlags(t *testing.T) {
hiddenPackagesImage := "docker-archive:" + getFixtureImage(t, "image-hidden-packages")
coverageImage := "docker-archive:" + getFixtureImage(t, "image-pkg-coverage")
//badBinariesImage := "docker-archive:" + getFixtureImage(t, "image-bad-binaries")
tmp := t.TempDir() + "/"
@@ -100,21 +101,34 @@ func TestPackagesCmdFlags(t *testing.T) {
},
},
{
name: "all-layers-scope-flag",
args: []string{"packages", "-o", "json", "-s", "all-layers", coverageImage},
name: "squashed-scope-flag-hidden-packages",
args: []string{"packages", "-o", "json", "-s", "squashed", hiddenPackagesImage},
assertions: []traitAssertion{
assertPackageCount(22),
assertPackageCount(162),
assertNotInOutput("vsftpd"), // hidden package
assertSuccessfulReturnCode,
},
},
{
name: "all-layers-scope-flag",
args: []string{"packages", "-o", "json", "-s", "all-layers", hiddenPackagesImage},
assertions: []traitAssertion{
assertPackageCount(163), // packages are now deduplicated for this case
assertInOutput("all-layers"),
assertInOutput("vsftpd"), // hidden package
assertSuccessfulReturnCode,
},
},
{
name: "all-layers-scope-flag-by-env",
args: []string{"packages", "-o", "json", coverageImage},
args: []string{"packages", "-o", "json", hiddenPackagesImage},
env: map[string]string{
"SYFT_PACKAGE_CATALOGER_SCOPE": "all-layers",
},
assertions: []traitAssertion{
assertPackageCount(22),
assertPackageCount(163), // packages are now deduplicated for this case
assertInOutput("all-layers"),
assertInOutput("vsftpd"), // hidden package
assertSuccessfulReturnCode,
},
},
@@ -0,0 +1,4 @@
FROM centos:7.9.2009
# all-layers scope should pickup on vsftpd
RUN yum install -y vsftpd
RUN yum remove -y vsftpd
@@ -186,6 +186,19 @@ var dirOnlyTestCases = []testCase{
"alcaeus/mongo-php-adapter": "1.1.11",
},
},
{
name: "find pubspec lock packages",
pkgType: pkg.DartPubPkg,
pkgLanguage: pkg.Dart,
pkgInfo: map[string]string{
"ansicolor": "1.1.1",
"archive": "2.0.13",
"args": "1.6.0",
"key_binder": "1.11.20",
"ale": "3.3.0",
"analyzer": "0.40.7",
},
},
}
var commonTestCases = []testCase{
+5 -2
View File
@@ -1,9 +1,10 @@
package integration
import (
"github.com/stretchr/testify/require"
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg/cataloger"
"github.com/google/go-cmp/cmp"
@@ -53,7 +54,7 @@ func BenchmarkImagePackageCatalogers(b *testing.B) {
}
func TestPkgCoverageImage(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage")
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope)
observedLanguages := internal.NewStringSet()
definedLanguages := internal.NewStringSet()
@@ -64,6 +65,7 @@ func TestPkgCoverageImage(t *testing.T) {
// for image scans we should not expect to see any of the following package types
definedLanguages.Remove(pkg.Go.String())
definedLanguages.Remove(pkg.Rust.String())
definedLanguages.Remove(pkg.Dart.String())
observedPkgs := internal.NewStringSet()
definedPkgs := internal.NewStringSet()
@@ -75,6 +77,7 @@ func TestPkgCoverageImage(t *testing.T) {
definedPkgs.Remove(string(pkg.KbPkg))
definedPkgs.Remove(string(pkg.GoModulePkg))
definedPkgs.Remove(string(pkg.RustPkg))
definedPkgs.Remove(string(pkg.DartPubPkg))
var cases []testCase
cases = append(cases, commonTestCases...)

Some files were not shown because too many files have changed in this diff Show More