mirror of
https://github.com/anchore/syft.git
synced 2026-10-12 06:07:20 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b46d044d7e | ||
|
|
15e45a8ce1 | ||
|
|
782b2e3348 | ||
|
|
e415bb21e7 | ||
|
|
748cfbf006 | ||
|
|
8bc5d84481 | ||
|
|
68b7ad9770 | ||
|
|
1aeda6bb50 | ||
|
|
f24bbc1838 | ||
|
|
cb3e73e308 | ||
|
|
f157d7a862 | ||
|
|
028cd9e27e | ||
|
|
5549939cc6 | ||
|
|
47ea910868 | ||
|
|
a7db43f5ec | ||
|
|
cc2c0e57a0 | ||
|
|
5253da4b36 | ||
|
|
c0b547bdb2 | ||
|
|
cffcaf5984 | ||
|
|
7f9edf346a | ||
|
|
a644a45ef4 | ||
|
|
283db88dc4 | ||
|
|
03e193e577 | ||
|
|
069aa68b63 | ||
|
|
9240860f44 | ||
|
|
4231f38fa2 | ||
|
|
752b03b2d6 | ||
|
|
99c3339810 | ||
|
|
f4734d28b3 | ||
|
|
6ef3e45ffc | ||
|
|
ee0a1d172c | ||
|
|
b9b3ccecf9 | ||
|
|
95271fb10d | ||
|
|
93d2d57cd3 | ||
|
|
7cd3201fe9 | ||
|
|
c7cf8b0b26 | ||
|
|
44a6e00f7a | ||
|
|
6c8102bf28 | ||
|
|
7789506dc6 | ||
|
|
fa03723617 | ||
|
|
2946813a74 |
@@ -29,7 +29,7 @@ notarize() {
|
||||
fi
|
||||
|
||||
# install gon
|
||||
which gon || (brew tap mitchellh/gon && brew install mitchellh/gon/gon)
|
||||
which gon || (go install github.com/mitchellh/gon/cmd/gon@latest)
|
||||
|
||||
# create config (note: json via stdin with gon is broken, can only use HCL from file)
|
||||
hcl_file=$(mktemp).hcl
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
- "v*"
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.17.x"
|
||||
GO_VERSION: "1.18.x"
|
||||
|
||||
jobs:
|
||||
quality-gate:
|
||||
@@ -94,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
|
||||
|
||||
@@ -128,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
|
||||
|
||||
@@ -7,7 +7,8 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.17.x"
|
||||
GO_VERSION: "1.18.x"
|
||||
GO_STABLE_VERSION: true
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -19,6 +20,7 @@ jobs:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -56,6 +58,7 @@ jobs:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -92,6 +95,16 @@ jobs:
|
||||
path: syft/pkg/cataloger/java/test-fixtures/java-builds/packages
|
||||
key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/pkg/cataloger/java/test-fixtures/java-builds/packages.fingerprint' ) }}
|
||||
|
||||
- name: Build cache key for go binary test-fixture blobs (for unit tests)
|
||||
run: make go-binaries-fingerprint
|
||||
|
||||
- name: Restore Go binary test-fixture cache
|
||||
id: unit-go-binary-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: syft/pkg/cataloger/golang/test-fixtures/archs/binaries
|
||||
key: ${{ runner.os }}-unit-go-binaries-cache-${{ hashFiles( 'syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint' ) }}
|
||||
|
||||
- name: Run unit tests
|
||||
run: make unit
|
||||
|
||||
@@ -108,6 +121,7 @@ jobs:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -159,6 +173,7 @@ jobs:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -231,6 +246,7 @@ jobs:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -338,6 +354,7 @@ jobs:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,7 +122,7 @@ bootstrap: $(RESULTSDIR) bootstrap-go bootstrap-tools ## Download and install al
|
||||
$(call title,Bootstrapping dependencies)
|
||||
|
||||
.PHONY: static-analysis
|
||||
static-analysis: lint check-go-mod-tidy check-licenses
|
||||
static-analysis: check-go-mod-tidy check-licenses lint
|
||||
|
||||
.PHONY: lint
|
||||
lint: ## Run gofmt + golangci lint checks
|
||||
@@ -214,6 +214,12 @@ java-packages-fingerprint:
|
||||
cd syft/pkg/cataloger/java/test-fixtures/java-builds && \
|
||||
make packages.fingerprint
|
||||
|
||||
.PHONY: go-binaries-fingerprint
|
||||
go-binaries-fingerprint:
|
||||
$(call title,Go binaries test fixture fingerprint)
|
||||
cd syft/pkg/cataloger/golang/test-fixtures/archs && \
|
||||
make binaries.fingerprint
|
||||
|
||||
.PHONY: fixtures
|
||||
fixtures:
|
||||
$(call title,Generating test fixtures)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
[](https://github.com/anchore/syft/blob/main/LICENSE)
|
||||
[](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).
|
||||
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!
|
||||
|
||||
@@ -20,15 +20,26 @@ A CLI tool and Go library for generating a Software Bill of Materials (SBOM) fro
|
||||

|
||||
|
||||
## 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
|
||||
|
||||
@@ -53,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
|
||||
@@ -70,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
|
||||
|
||||
@@ -104,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:
|
||||
@@ -146,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:
|
||||
@@ -164,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": {
|
||||
@@ -190,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
|
||||
@@ -213,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
|
||||
@@ -246,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
|
||||
|
||||
@@ -286,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:
|
||||
@@ -460,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:
|
||||
|
||||
@@ -22,6 +22,8 @@ func formatAliases(ids ...sbom.FormatID) (aliases []string) {
|
||||
aliases = append(aliases, "cyclonedx-xml")
|
||||
case syft.CycloneDxJSONFormatID:
|
||||
aliases = append(aliases, "cyclonedx-json")
|
||||
case syft.GitHubID:
|
||||
aliases = append(aliases, "github", "github-json")
|
||||
default:
|
||||
aliases = append(aliases, string(id))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/anchore/syft
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/CycloneDX/cyclonedx-go v0.5.0
|
||||
@@ -9,11 +9,12 @@ require (
|
||||
github.com/adrg/xdg v0.2.1
|
||||
github.com/alecthomas/jsonschema v0.0.0-20210301060011-54c507b6f074
|
||||
github.com/anchore/client-go v0.0.0-20210222170800-9c70f9b80bcf
|
||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
|
||||
github.com/anchore/go-rpmdb v0.0.0-20210914181456-a9c52348da63
|
||||
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.0.0-20210922164639-b3fa992ebd29
|
||||
github.com/anchore/stereoscope v0.0.0-20220307154759-8a5a70c227d3
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b
|
||||
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
|
||||
@@ -33,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
|
||||
@@ -171,7 +173,7 @@ require (
|
||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||
github.com/hashicorp/go-version v1.4.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/hashicorp/vault/api v1.3.1 // indirect
|
||||
github.com/hashicorp/vault/api v1.4.1 // indirect
|
||||
github.com/hashicorp/vault/sdk v0.4.1 // indirect
|
||||
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
|
||||
@@ -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=
|
||||
@@ -276,16 +270,18 @@ github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:C
|
||||
github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE=
|
||||
github.com/anchore/client-go v0.0.0-20210222170800-9c70f9b80bcf h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=
|
||||
github.com/anchore/client-go v0.0.0-20210222170800-9c70f9b80bcf/go.mod h1:FaODhIA06mxO1E6R32JE0TL1JWZZkmjRIAd4ULvHUKk=
|
||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIjtjZ46C0akqveX83WFzhpTD3eqOthawb5vU=
|
||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb/go.mod h1:DmTY2Mfcv38hsHbG78xMiTDdxFtkHpgYNVDPsF2TgHk=
|
||||
github.com/anchore/go-rpmdb v0.0.0-20210914181456-a9c52348da63 h1:C9W/LAydEz/qdUhx1MdjO9l8NEcFKYknkxDVyo9LAoM=
|
||||
github.com/anchore/go-rpmdb v0.0.0-20210914181456-a9c52348da63/go.mod h1:6qH8c6U/3CBVvDDDBZnPSTbTINq3cIdADUYTaVf75EM=
|
||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0vW0nnNKJfJieyH/TZ9UYAnTZs5/gHTdAe8=
|
||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ=
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods=
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.0.0-20210922164639-b3fa992ebd29 h1:K9LfnxwhqvihqU0+MF325FNy7fsKV9EGaUxdfR4gnWk=
|
||||
github.com/anchore/packageurl-go v0.0.0-20210922164639-b3fa992ebd29/go.mod h1:Oc1UkGaJwY6ND6vtAqPSlYrptKRJngHwkwB6W7l1uP0=
|
||||
github.com/anchore/stereoscope v0.0.0-20220307154759-8a5a70c227d3 h1:Kx2jlMdENAf4cVjYGYLI+fiavVhzhtmU89GUYDITJ1w=
|
||||
github.com/anchore/stereoscope v0.0.0-20220307154759-8a5a70c227d3/go.mod h1:XESZQTgFETDBatmyoet6XZ0zVknoIMDSAhj2INj2a5w=
|
||||
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-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=
|
||||
@@ -435,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=
|
||||
@@ -445,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=
|
||||
@@ -455,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=
|
||||
@@ -468,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=
|
||||
@@ -503,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=
|
||||
@@ -614,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=
|
||||
@@ -624,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=
|
||||
@@ -632,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=
|
||||
@@ -662,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=
|
||||
@@ -720,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=
|
||||
@@ -768,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=
|
||||
@@ -849,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=
|
||||
@@ -886,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=
|
||||
@@ -913,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=
|
||||
@@ -1110,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=
|
||||
@@ -1128,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=
|
||||
@@ -1180,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=
|
||||
@@ -1196,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=
|
||||
@@ -1300,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=
|
||||
@@ -1349,8 +1317,9 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
|
||||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
|
||||
github.com/hashicorp/vault/api v1.3.0/go.mod h1:EabNQLI0VWbWoGlA+oBLC8PXmR9D60aUVgQGvangFWQ=
|
||||
github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM=
|
||||
github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw=
|
||||
github.com/hashicorp/vault/api v1.4.1 h1:mWLfPT0RhxBitjKr6swieCEP2v5pp/M//t70S3kMLRo=
|
||||
github.com/hashicorp/vault/api v1.4.1/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM=
|
||||
github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
|
||||
github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo=
|
||||
github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
|
||||
@@ -1456,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=
|
||||
@@ -1515,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=
|
||||
@@ -1525,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=
|
||||
@@ -1583,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=
|
||||
@@ -1651,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=
|
||||
@@ -1719,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=
|
||||
@@ -1780,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=
|
||||
@@ -1841,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=
|
||||
@@ -1863,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=
|
||||
@@ -1879,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=
|
||||
@@ -1897,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=
|
||||
@@ -1920,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=
|
||||
@@ -1981,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=
|
||||
@@ -2144,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=
|
||||
@@ -2171,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=
|
||||
@@ -2205,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=
|
||||
@@ -2374,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=
|
||||
@@ -2390,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=
|
||||
@@ -2407,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=
|
||||
@@ -2500,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=
|
||||
@@ -2722,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=
|
||||
@@ -2772,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=
|
||||
@@ -2795,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=
|
||||
@@ -3156,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=
|
||||
@@ -3193,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=
|
||||
@@ -3203,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=
|
||||
@@ -3229,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=
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.0.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,17 +40,33 @@ 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
|
||||
}
|
||||
|
||||
func decodeComponent(c *cyclonedx.Component) *pkg.Package {
|
||||
values := map[string]string{}
|
||||
for _, p := range *c.Properties {
|
||||
values[p.Name] = p.Value
|
||||
if c.Properties != nil {
|
||||
for _, p := range *c.Properties {
|
||||
values[p.Name] = p.Value
|
||||
}
|
||||
}
|
||||
|
||||
p := &pkg.Package{
|
||||
@@ -71,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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
|
||||
"github.com/anchore/syft/internal/formats/common"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -44,7 +46,7 @@ func GetDecoder(format cyclonedx.BOMFileFormat) sbom.Decoder {
|
||||
|
||||
func toSyftModel(bom *cyclonedx.BOM) (*sbom.SBOM, error) {
|
||||
meta := source.Metadata{}
|
||||
if bom.Metadata != nil {
|
||||
if bom.Metadata != nil && bom.Metadata.Component != nil {
|
||||
meta = decodeMetadata(bom.Metadata.Component)
|
||||
}
|
||||
s := &sbom.SBOM{
|
||||
@@ -157,6 +159,14 @@ func linuxReleaseFromOSComponent(component *cyclonedx.Component) *linux.Release
|
||||
}
|
||||
}
|
||||
|
||||
if component.Properties != nil {
|
||||
values := map[string]string{}
|
||||
for _, p := range *component.Properties {
|
||||
values[p.Name] = p.Value
|
||||
}
|
||||
common.DecodeInto(&rel, values, "syft:distro", CycloneDXFields)
|
||||
}
|
||||
|
||||
return rel
|
||||
}
|
||||
|
||||
|
||||
@@ -258,3 +258,28 @@ func Test_decode(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_missingDataDecode(t *testing.T) {
|
||||
bom := &cyclonedx.BOM{
|
||||
Metadata: nil,
|
||||
Components: &[]cyclonedx.Component{},
|
||||
}
|
||||
|
||||
_, err := toSyftModel(bom)
|
||||
assert.NoError(t, err)
|
||||
|
||||
bom.Metadata = &cyclonedx.Metadata{}
|
||||
|
||||
_, err = toSyftModel(bom)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pkg := decodeComponent(&cyclonedx.Component{
|
||||
Licenses: &cyclonedx.Licenses{
|
||||
{
|
||||
License: nil,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
assert.Len(t, pkg.Licenses, 0)
|
||||
}
|
||||
|
||||
@@ -4,12 +4,15 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
syftFile "github.com/anchore/syft/syft/file"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
//nolint:funlen, gocognit
|
||||
func encodeExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference {
|
||||
refs := []cyclonedx.ExternalReference{}
|
||||
var refs []cyclonedx.ExternalReference
|
||||
if hasMetadata(p) {
|
||||
switch metadata := p.Metadata.(type) {
|
||||
case pkg.ApkMetadata:
|
||||
@@ -46,6 +49,19 @@ func encodeExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference {
|
||||
Type: cyclonedx.ERTypeWebsite,
|
||||
})
|
||||
}
|
||||
case pkg.JavaMetadata:
|
||||
if len(metadata.ArchiveDigests) > 0 {
|
||||
for _, digest := range metadata.ArchiveDigests {
|
||||
refs = append(refs, cyclonedx.ExternalReference{
|
||||
URL: "",
|
||||
Type: cyclonedx.ERTypeBuildMeta,
|
||||
Hashes: &[]cyclonedx.Hash{{
|
||||
Algorithm: cyclonedx.HashAlgorithm(digest.Algorithm),
|
||||
Value: digest.Value,
|
||||
}},
|
||||
})
|
||||
}
|
||||
}
|
||||
case pkg.PythonPackageMetadata:
|
||||
if metadata.DirectURLOrigin != nil && metadata.DirectURLOrigin.URL != "" {
|
||||
ref := cyclonedx.ExternalReference{
|
||||
@@ -79,6 +95,20 @@ func decodeExternalReferences(c *cyclonedx.Component, metadata interface{}) {
|
||||
meta.Homepage = refURL(c, cyclonedx.ERTypeWebsite)
|
||||
case *pkg.GemMetadata:
|
||||
meta.Homepage = refURL(c, cyclonedx.ERTypeWebsite)
|
||||
case *pkg.JavaMetadata:
|
||||
var digests []syftFile.Digest
|
||||
if ref := findExternalRef(c, cyclonedx.ERTypeBuildMeta); ref != nil {
|
||||
if ref.Hashes != nil {
|
||||
for _, hash := range *ref.Hashes {
|
||||
digests = append(digests, syftFile.Digest{
|
||||
Algorithm: string(hash.Algorithm),
|
||||
Value: hash.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
meta.ArchiveDigests = digests
|
||||
case *pkg.PythonPackageMetadata:
|
||||
if meta.DirectURLOrigin == nil {
|
||||
meta.DirectURLOrigin = &pkg.PythonDirectURLOriginInfo{}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,5 +22,6 @@ func ExternalRefs(p pkg.Package) (externalRefs []ExternalRef) {
|
||||
ReferenceType: PurlExternalRefType,
|
||||
})
|
||||
}
|
||||
|
||||
return externalRefs
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
@@ -304,6 +309,14 @@ func extractMetadata(p *spdx.Package2_2, info pkgInfo) (pkg.MetadataType, interf
|
||||
Architecture: arch,
|
||||
Maintainer: p.PackageOriginatorPerson,
|
||||
}
|
||||
case pkg.JavaPkg:
|
||||
var digests []file.Digest
|
||||
for algorithm, value := range p.PackageChecksums {
|
||||
digests = append(digests, file.Digest{Algorithm: string(algorithm), Value: value.Value})
|
||||
}
|
||||
return pkg.JavaMetadataType, pkg.JavaMetadata{
|
||||
ArchiveDigests: digests,
|
||||
}
|
||||
}
|
||||
return pkg.UnknownMetadataType, nil
|
||||
}
|
||||
|
||||
@@ -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:*:*:*:*:*:*:*"),
|
||||
},
|
||||
|
||||
+5
-3
@@ -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",
|
||||
|
||||
+8
-6
@@ -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",
|
||||
|
||||
BIN
Binary file not shown.
+5
-5
@@ -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>
|
||||
|
||||
+8
-8
@@ -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>
|
||||
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,185 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mholt/archiver/v3"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/version"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// toGithubModel converts the provided SBOM to a GitHub dependency model
|
||||
func toGithubModel(s *sbom.SBOM) DependencySnapshot {
|
||||
scanTime := time.Now().Format(time.RFC3339) // TODO is there a record of this somewhere?
|
||||
v := version.FromBuild().Version
|
||||
if v == "[not provided]" {
|
||||
v = "0.0.0-dev"
|
||||
}
|
||||
return DependencySnapshot{
|
||||
Version: 0,
|
||||
// TODO allow property input to specify the Job, Sha, and Ref
|
||||
Detector: DetectorMetadata{
|
||||
Name: internal.ApplicationName,
|
||||
URL: "https://github.com/anchore/syft",
|
||||
Version: v,
|
||||
},
|
||||
Metadata: toSnapshotMetadata(s),
|
||||
Manifests: toGithubManifests(s),
|
||||
Scanned: scanTime,
|
||||
}
|
||||
}
|
||||
|
||||
// toSnapshotMetadata captures the linux distribution information and other metadata
|
||||
func toSnapshotMetadata(s *sbom.SBOM) Metadata {
|
||||
out := Metadata{}
|
||||
|
||||
if s.Artifacts.LinuxDistribution != nil {
|
||||
d := s.Artifacts.LinuxDistribution
|
||||
qualifiers := packageurl.Qualifiers{}
|
||||
if len(d.IDLike) > 0 {
|
||||
qualifiers = append(qualifiers, packageurl.Qualifier{
|
||||
Key: "like",
|
||||
Value: strings.Join(d.IDLike, ","),
|
||||
})
|
||||
}
|
||||
purl := packageurl.NewPackageURL("generic", "", d.ID, d.VersionID, qualifiers, "")
|
||||
out["syft:distro"] = purl.ToString()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func filesystem(p pkg.Package) string {
|
||||
locations := p.Locations.ToSlice()
|
||||
if len(locations) > 0 {
|
||||
return locations[0].FileSystemID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// isArchive returns true if the path appears to be an archive
|
||||
func isArchive(path string) bool {
|
||||
_, err := archiver.ByExtension(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// toPath Generates a string representation of the package location, optionally including the layer hash
|
||||
func toPath(s source.Metadata, p pkg.Package) string {
|
||||
inputPath := strings.TrimPrefix(s.Path, "./")
|
||||
if inputPath == "." {
|
||||
inputPath = ""
|
||||
}
|
||||
locations := p.Locations.ToSlice()
|
||||
if len(locations) > 0 {
|
||||
location := locations[0]
|
||||
packagePath := location.RealPath
|
||||
if location.VirtualPath != "" {
|
||||
packagePath = location.VirtualPath
|
||||
}
|
||||
packagePath = strings.TrimPrefix(packagePath, "/")
|
||||
switch s.Scheme {
|
||||
case source.ImageScheme:
|
||||
image := strings.ReplaceAll(s.ImageMetadata.UserInput, ":/", "//")
|
||||
return fmt.Sprintf("%s:/%s", image, packagePath)
|
||||
case source.FileScheme:
|
||||
if isArchive(inputPath) {
|
||||
return fmt.Sprintf("%s:/%s", inputPath, packagePath)
|
||||
}
|
||||
return inputPath
|
||||
case source.DirectoryScheme:
|
||||
if inputPath != "" {
|
||||
return fmt.Sprintf("%s/%s", inputPath, packagePath)
|
||||
}
|
||||
return packagePath
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s%s", inputPath, s.ImageMetadata.UserInput)
|
||||
}
|
||||
|
||||
// toGithubManifests manifests, each of which represents a specific location that has dependencies
|
||||
func toGithubManifests(s *sbom.SBOM) Manifests {
|
||||
manifests := map[string]*Manifest{}
|
||||
|
||||
for _, p := range s.Artifacts.PackageCatalog.Sorted() {
|
||||
path := toPath(s.Source, p)
|
||||
manifest, ok := manifests[path]
|
||||
if !ok {
|
||||
manifest = &Manifest{
|
||||
Name: path,
|
||||
File: FileInfo{
|
||||
SourceLocation: path,
|
||||
},
|
||||
Resolved: DependencyGraph{},
|
||||
}
|
||||
fs := filesystem(p)
|
||||
if fs != "" {
|
||||
manifest.Metadata = Metadata{
|
||||
"syft:filesystem": fs,
|
||||
}
|
||||
}
|
||||
manifests[path] = manifest
|
||||
}
|
||||
|
||||
name := dependencyName(p)
|
||||
manifest.Resolved[name] = DependencyNode{
|
||||
Purl: p.PURL,
|
||||
Metadata: toDependencyMetadata(p),
|
||||
Relationship: toDependencyRelationshipType(p),
|
||||
Scope: toDependencyScope(p),
|
||||
Dependencies: toDependencies(s, p),
|
||||
}
|
||||
}
|
||||
|
||||
out := Manifests{}
|
||||
for k, v := range manifests {
|
||||
out[k] = *v
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// dependencyName to make things a little nicer to read; this might end up being lossy
|
||||
func dependencyName(p pkg.Package) string {
|
||||
purl, err := packageurl.FromString(p.PURL)
|
||||
if err != nil {
|
||||
log.Warnf("Invalid PURL for package: '%s' PURL: '%s' (%w)", p.Name, p.PURL, err)
|
||||
return ""
|
||||
}
|
||||
// don't use qualifiers for this
|
||||
purl.Qualifiers = nil
|
||||
return purl.ToString()
|
||||
}
|
||||
|
||||
func toDependencyScope(_ pkg.Package) DependencyScope {
|
||||
return DependencyScopeRuntime
|
||||
}
|
||||
|
||||
func toDependencyRelationshipType(_ pkg.Package) DependencyRelationship {
|
||||
return DependencyRelationshipDirect
|
||||
}
|
||||
|
||||
func toDependencyMetadata(_ pkg.Package) Metadata {
|
||||
// We have limited properties: up to 8 with reasonably small values
|
||||
// For now, we are encoding the location as part of the key, we are encoding PURLs with most
|
||||
// of the other information Grype might need; and the distro information at the top level
|
||||
// so we don't need anything here yet
|
||||
return Metadata{}
|
||||
}
|
||||
|
||||
func toDependencies(s *sbom.SBOM, p pkg.Package) (out []string) {
|
||||
for _, r := range s.Relationships {
|
||||
if r.From.ID() == p.ID() {
|
||||
if p, ok := r.To.(pkg.Package); ok {
|
||||
out = append(out, dependencyName(p))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func Test_toGithubModel(t *testing.T) {
|
||||
s := sbom.SBOM{
|
||||
Source: source.Metadata{
|
||||
Scheme: source.ImageScheme,
|
||||
ImageMetadata: source.ImageMetadata{
|
||||
UserInput: "ubuntu:18.04",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
},
|
||||
Artifacts: sbom.Artifacts{
|
||||
LinuxDistribution: &linux.Release{
|
||||
ID: "ubuntu",
|
||||
VersionID: "18.04",
|
||||
IDLike: []string{"debian"},
|
||||
},
|
||||
PackageCatalog: pkg.NewCatalog(),
|
||||
},
|
||||
}
|
||||
for _, p := range []pkg.Package{
|
||||
{
|
||||
Name: "pkg-1",
|
||||
Version: "1.0.1",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
RealPath: "/usr/lib",
|
||||
FileSystemID: "fsid-1",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
Name: "pkg-2",
|
||||
Version: "2.0.2",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
RealPath: "/usr/lib",
|
||||
FileSystemID: "fsid-1",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
Name: "pkg-3",
|
||||
Version: "3.0.3",
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
RealPath: "/etc",
|
||||
FileSystemID: "fsid-1",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
} {
|
||||
p.PURL = packageurl.NewPackageURL(
|
||||
"generic",
|
||||
"",
|
||||
p.Name,
|
||||
p.Version,
|
||||
nil,
|
||||
"",
|
||||
).ToString()
|
||||
s.Artifacts.PackageCatalog.Add(p)
|
||||
}
|
||||
|
||||
actual := toGithubModel(&s)
|
||||
|
||||
expected := DependencySnapshot{
|
||||
Version: 0,
|
||||
Detector: DetectorMetadata{
|
||||
Name: "syft",
|
||||
Version: "0.0.0-dev",
|
||||
URL: "https://github.com/anchore/syft",
|
||||
},
|
||||
Metadata: Metadata{
|
||||
"syft:distro": "pkg:generic/ubuntu@18.04?like=debian",
|
||||
},
|
||||
Scanned: actual.Scanned,
|
||||
Manifests: Manifests{
|
||||
"ubuntu:18.04:/usr/lib": Manifest{
|
||||
Name: "ubuntu:18.04:/usr/lib",
|
||||
File: FileInfo{
|
||||
SourceLocation: "ubuntu:18.04:/usr/lib",
|
||||
},
|
||||
Metadata: Metadata{
|
||||
"syft:filesystem": "fsid-1",
|
||||
},
|
||||
Resolved: DependencyGraph{
|
||||
"pkg:generic/pkg-1@1.0.1": DependencyNode{
|
||||
Purl: "pkg:generic/pkg-1@1.0.1",
|
||||
Scope: DependencyScopeRuntime,
|
||||
Relationship: DependencyRelationshipDirect,
|
||||
},
|
||||
"pkg:generic/pkg-2@2.0.2": DependencyNode{
|
||||
Purl: "pkg:generic/pkg-2@2.0.2",
|
||||
Scope: DependencyScopeRuntime,
|
||||
Relationship: DependencyRelationshipDirect,
|
||||
},
|
||||
},
|
||||
},
|
||||
"ubuntu:18.04:/etc": Manifest{
|
||||
Name: "ubuntu:18.04:/etc",
|
||||
File: FileInfo{
|
||||
SourceLocation: "ubuntu:18.04:/etc",
|
||||
},
|
||||
Metadata: Metadata{
|
||||
"syft:filesystem": "fsid-1",
|
||||
},
|
||||
Resolved: DependencyGraph{
|
||||
"pkg:generic/pkg-3@3.0.3": DependencyNode{
|
||||
Purl: "pkg:generic/pkg-3@3.0.3",
|
||||
Scope: DependencyScopeRuntime,
|
||||
Relationship: DependencyRelationshipDirect,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// just using JSONEq because it gives a comprehensible diff
|
||||
s1, _ := json.Marshal(expected)
|
||||
s2, _ := json.Marshal(actual)
|
||||
assert.JSONEq(t, string(s1), string(s2))
|
||||
|
||||
// Just test the other schemes:
|
||||
s.Source.Path = "."
|
||||
s.Source.Scheme = source.DirectoryScheme
|
||||
actual = toGithubModel(&s)
|
||||
assert.Equal(t, "etc", actual.Manifests["etc"].Name)
|
||||
|
||||
s.Source.Path = "./artifacts"
|
||||
s.Source.Scheme = source.DirectoryScheme
|
||||
actual = toGithubModel(&s)
|
||||
assert.Equal(t, "artifacts/etc", actual.Manifests["artifacts/etc"].Name)
|
||||
|
||||
s.Source.Path = "/artifacts"
|
||||
s.Source.Scheme = source.DirectoryScheme
|
||||
actual = toGithubModel(&s)
|
||||
assert.Equal(t, "/artifacts/etc", actual.Manifests["/artifacts/etc"].Name)
|
||||
|
||||
s.Source.Path = "./executable"
|
||||
s.Source.Scheme = source.FileScheme
|
||||
actual = toGithubModel(&s)
|
||||
assert.Equal(t, "executable", actual.Manifests["executable"].Name)
|
||||
|
||||
s.Source.Path = "./archive.tar.gz"
|
||||
s.Source.Scheme = source.FileScheme
|
||||
actual = toGithubModel(&s)
|
||||
assert.Equal(t, "archive.tar.gz:/etc", actual.Manifests["archive.tar.gz:/etc"].Name)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
const ID sbom.FormatID = "github-0-json"
|
||||
|
||||
func Format() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
ID,
|
||||
func(writer io.Writer, sbom sbom.SBOM) error {
|
||||
bom := toGithubModel(&sbom)
|
||||
|
||||
bytes, err := json.MarshalIndent(bom, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = writer.Write(bytes)
|
||||
|
||||
return err
|
||||
},
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package github
|
||||
|
||||
// Derived from: https://gist.github.com/reiddraper/fdab2883db0f372c146d1a750fc1c43f
|
||||
|
||||
type DependencySnapshot struct {
|
||||
Version int `json:"version"`
|
||||
Job Job `json:"job,omitempty"` // !omitempty
|
||||
Sha string `json:"sha,omitempty"` // !omitempty sha of the Git commit
|
||||
Ref string `json:"ref,omitempty"` // !omitempty ref of the Git commit example "refs/heads/main"
|
||||
Detector DetectorMetadata `json:"detector,omitempty"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
Manifests Manifests `json:"manifests,omitempty"`
|
||||
Scanned ISO8601Date `json:"scanned,omitempty"`
|
||||
}
|
||||
|
||||
type Job struct {
|
||||
Name string `json:"name,omitempty"` // !omitempty
|
||||
ID string `json:"id,omitempty"` // !omitempty
|
||||
HTMLURL string `json:"html_url,omitempty"`
|
||||
}
|
||||
|
||||
type DetectorMetadata struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type Manifests map[string]Manifest
|
||||
|
||||
// Manifest A collection of related dependencies, either declared in a file,
|
||||
// or representing a logical group of dependencies.
|
||||
type Manifest struct {
|
||||
Name string `json:"name"`
|
||||
File FileInfo `json:"file"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
Resolved DependencyGraph `json:"resolved,omitempty"`
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
SourceLocation string `json:"source_location,omitempty"`
|
||||
}
|
||||
|
||||
// DependencyRelationship A notation of whether a dependency is requested directly
|
||||
// by this manifest, or is a dependency of another dependency.
|
||||
type DependencyRelationship string
|
||||
|
||||
const (
|
||||
DependencyRelationshipDirect DependencyRelationship = "direct"
|
||||
DependencyRelationshipIndirect DependencyRelationship = "indirect"
|
||||
)
|
||||
|
||||
// DependencyScope A notation of whether the dependency is required for the primary
|
||||
// build artifact (runtime), or is only used for development.
|
||||
// Future versions of this specification may allow for more granular
|
||||
// scopes, like `runtimeserver`, `runtimeshipped`,
|
||||
// `developmenttest`, `developmentbenchmark`.
|
||||
type DependencyScope string
|
||||
|
||||
const (
|
||||
DependencyScopeRuntime DependencyScope = "runtime"
|
||||
DependencyScopeDevelopment DependencyScope = "development"
|
||||
)
|
||||
|
||||
type DependencyNode struct {
|
||||
Purl string `json:"purl,omitempty"`
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
Relationship DependencyRelationship `json:"relationship,omitempty"`
|
||||
Scope DependencyScope `json:"scope,omitempty"`
|
||||
Dependencies []string `json:"dependencies,omitempty"`
|
||||
}
|
||||
|
||||
type DependencyGraph map[string]DependencyNode
|
||||
|
||||
type ISO8601Date = string
|
||||
|
||||
type Scalar interface{} // should be: null | boolean | string | number
|
||||
|
||||
type Metadata map[string]Scalar
|
||||
+6
-6
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
BIN
Binary file not shown.
@@ -55,14 +55,32 @@ func toPackages(catalog *pkg.Catalog, relationships []artifact.Relationship) []m
|
||||
for _, p := range catalog.Sorted() {
|
||||
license := spdxhelpers.License(p)
|
||||
packageSpdxID := model.ElementID(p.ID()).String()
|
||||
filesAnalyzed := false
|
||||
|
||||
// we generate digest for some Java packages
|
||||
// see page 33 of the spdx specification for 2.2
|
||||
// spdx.github.io/spdx-spec/package-information/#710-package-checksum-field
|
||||
var checksums []model.Checksum
|
||||
if p.MetadataType == pkg.JavaMetadataType {
|
||||
javaMetadata := p.Metadata.(pkg.JavaMetadata)
|
||||
if len(javaMetadata.ArchiveDigests) > 0 {
|
||||
filesAnalyzed = true
|
||||
for _, digest := range javaMetadata.ArchiveDigests {
|
||||
checksums = append(checksums, model.Checksum{
|
||||
Algorithm: strings.ToUpper(digest.Algorithm),
|
||||
ChecksumValue: digest.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// note: the license concluded and declared should be the same since we are collecting license information
|
||||
// from the project data itself (the installed package files).
|
||||
packages = append(packages, model.Package{
|
||||
Checksums: checksums,
|
||||
Description: spdxhelpers.Description(p),
|
||||
DownloadLocation: spdxhelpers.DownloadLocation(p),
|
||||
ExternalRefs: spdxhelpers.ExternalRefs(p),
|
||||
FilesAnalyzed: false,
|
||||
FilesAnalyzed: filesAnalyzed,
|
||||
HasFiles: fileIDsForPackage(packageSpdxID, relationships),
|
||||
Homepage: spdxhelpers.Homepage(p),
|
||||
// The Declared License is what the authors of a project believe govern the package
|
||||
|
||||
+5
-5
@@ -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
|
||||
|
||||
+5
-5
@@ -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
|
||||
|
||||
@@ -103,6 +103,24 @@ func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2
|
||||
// the Comments on License field (section 3.16) is preferred.
|
||||
license := spdxhelpers.License(p)
|
||||
|
||||
filesAnalyzed := false
|
||||
checksums := make(map[spdx.ChecksumAlgorithm]spdx.Checksum)
|
||||
|
||||
// If the pkg type is Java we have attempted to generated a digest
|
||||
// FilesAnalyzed should be true in this case
|
||||
if p.MetadataType == pkg.JavaMetadataType {
|
||||
javaMetadata := p.Metadata.(pkg.JavaMetadata)
|
||||
if len(javaMetadata.ArchiveDigests) > 0 {
|
||||
filesAnalyzed = true
|
||||
for _, digest := range javaMetadata.ArchiveDigests {
|
||||
checksums[spdx.ChecksumAlgorithm(digest.Algorithm)] = spdx.Checksum{
|
||||
Algorithm: spdx.ChecksumAlgorithm(digest.Algorithm),
|
||||
Value: digest.Value,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results[spdx.ElementID(id)] = &spdx.Package2_2{
|
||||
|
||||
// NOT PART OF SPEC
|
||||
@@ -159,7 +177,7 @@ func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2
|
||||
|
||||
// Intent: A package can refer to a project, product, artifact, distribution or a component that is
|
||||
// external to the SPDX document.
|
||||
FilesAnalyzed: false,
|
||||
FilesAnalyzed: filesAnalyzed,
|
||||
// NOT PART OF SPEC: did FilesAnalyzed tag appear?
|
||||
IsFilesAnalyzedTagPresent: true,
|
||||
|
||||
@@ -180,6 +198,7 @@ func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2
|
||||
// to determine if any file in the original package has been changed. If the SPDX file is to be included
|
||||
// in a package, this value should not be calculated. The SHA-1 algorithm will be used to provide the
|
||||
// checksum by default.
|
||||
PackageChecksums: checksums,
|
||||
|
||||
// note: based on the purpose above no discovered checksums should be provided, but instead, only
|
||||
// tool-derived checksums.
|
||||
|
||||
@@ -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.0.1",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.0.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.0.1",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.0.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.0.1",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.0.1.json"
|
||||
"version": "3.2.2",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.2.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -77,30 +77,39 @@ func (i *DigestsCataloger) catalogLocation(resolver source.FileResolver, locatio
|
||||
}
|
||||
defer internal.CloseAndLogError(contentReader, location.VirtualPath)
|
||||
|
||||
digests, err := DigestsFromFile(contentReader, i.hashes)
|
||||
if err != nil {
|
||||
return nil, internal.ErrPath{Context: "digests-cataloger", Path: location.RealPath, Err: err}
|
||||
}
|
||||
|
||||
return digests, nil
|
||||
}
|
||||
|
||||
func DigestsFromFile(closer io.ReadCloser, hashes []crypto.Hash) ([]Digest, error) {
|
||||
// create a set of hasher objects tied together with a single writer to feed content into
|
||||
hashers := make([]hash.Hash, len(i.hashes))
|
||||
writers := make([]io.Writer, len(i.hashes))
|
||||
for idx, hashObj := range i.hashes {
|
||||
hashers := make([]hash.Hash, len(hashes))
|
||||
writers := make([]io.Writer, len(hashes))
|
||||
for idx, hashObj := range hashes {
|
||||
hashers[idx] = hashObj.New()
|
||||
writers[idx] = hashers[idx]
|
||||
}
|
||||
|
||||
size, err := io.Copy(io.MultiWriter(writers...), contentReader)
|
||||
size, err := io.Copy(io.MultiWriter(writers...), closer)
|
||||
if err != nil {
|
||||
return nil, internal.ErrPath{Context: "digests-cataloger", Path: location.RealPath, Err: err}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if size == 0 {
|
||||
return make([]Digest, 0), nil
|
||||
}
|
||||
|
||||
result := make([]Digest, len(i.hashes))
|
||||
result := make([]Digest, len(hashes))
|
||||
// only capture digests when there is content. It is important to do this based on SIZE and not
|
||||
// FILE TYPE. The reasoning is that it is possible for a tar to be crafted with a header-only
|
||||
// file type but a body is still allowed.
|
||||
for idx, hasher := range hashers {
|
||||
result[idx] = Digest{
|
||||
Algorithm: DigestAlgorithmName(i.hashes[idx]),
|
||||
Algorithm: DigestAlgorithmName(hashes[idx]),
|
||||
Value: fmt.Sprintf("%+x", hasher.Sum(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/anchore/syft/internal/formats/cyclonedxjson"
|
||||
"github.com/anchore/syft/internal/formats/cyclonedxxml"
|
||||
"github.com/anchore/syft/internal/formats/github"
|
||||
"github.com/anchore/syft/internal/formats/spdx22json"
|
||||
"github.com/anchore/syft/internal/formats/spdx22tagvalue"
|
||||
"github.com/anchore/syft/internal/formats/syftjson"
|
||||
@@ -21,6 +22,7 @@ const (
|
||||
TableFormatID = table.ID
|
||||
CycloneDxXMLFormatID = cyclonedxxml.ID
|
||||
CycloneDxJSONFormatID = cyclonedxjson.ID
|
||||
GitHubID = github.ID
|
||||
SPDXTagValueFormatID = spdx22tagvalue.ID
|
||||
SPDXJSONFormatID = spdx22json.ID
|
||||
)
|
||||
@@ -32,6 +34,7 @@ func init() {
|
||||
syftjson.Format(),
|
||||
cyclonedxxml.Format(),
|
||||
cyclonedxjson.Format(),
|
||||
github.Format(),
|
||||
spdx22tagvalue.Format(),
|
||||
spdx22json.Format(),
|
||||
table.Format(),
|
||||
@@ -71,6 +74,8 @@ func FormatByName(name string) sbom.Format {
|
||||
return FormatByID(cyclonedxxml.ID)
|
||||
case "cyclonedxjson":
|
||||
return FormatByID(cyclonedxjson.ID)
|
||||
case "github":
|
||||
return FormatByID(github.ID)
|
||||
case "spdx", "spdxtv", "spdxtagvalue":
|
||||
return FormatByID(spdx22tagvalue.ID)
|
||||
case "spdxjson":
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ var defaultCandidateAdditions = buildCandidateLookup(
|
||||
{
|
||||
pkg.JavaPkg,
|
||||
candidateKey{PkgName: "springframework"},
|
||||
candidateAddition{AdditionalProducts: []string{"spring_framework", "springsource_spring_framework"}},
|
||||
candidateAddition{AdditionalProducts: []string{"spring_framework", "springsource_spring_framework"}, AdditionalVendors: []string{"pivotal_software", "springsource", "vmware"}},
|
||||
},
|
||||
{
|
||||
pkg.JavaPkg,
|
||||
candidateKey{PkgName: "spring-core"},
|
||||
candidateAddition{AdditionalProducts: []string{"spring_framework", "springsource_spring_framework"}},
|
||||
candidateAddition{AdditionalProducts: []string{"spring_framework", "springsource_spring_framework"}, AdditionalVendors: []string{"pivotal_software", "springsource", "vmware"}},
|
||||
},
|
||||
{
|
||||
// example image: docker.io/nuxeo:latest
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ var (
|
||||
"org",
|
||||
"net",
|
||||
"io",
|
||||
"be",
|
||||
}
|
||||
|
||||
primaryJavaManifestGroupIDFields = []string{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
@@ -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) {
|
||||
|
||||
@@ -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...
|
||||
|
||||
@@ -4,10 +4,12 @@ Package golang provides a concrete Cataloger implementation for go.mod files.
|
||||
package golang
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -38,19 +40,51 @@ func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []arti
|
||||
}
|
||||
|
||||
for _, location := range fileMatches {
|
||||
r, err := resolver.FileContentsByLocation(location)
|
||||
readerCloser, err := resolver.FileContentsByLocation(location)
|
||||
if err != nil {
|
||||
return pkgs, nil, fmt.Errorf("failed to resolve file contents by location=%q: %w", location.RealPath, err)
|
||||
log.Warnf("golang cataloger: opening file: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
goPkgs, err := parseGoBin(location, r, openExe)
|
||||
reader, err := getUnionReader(readerCloser)
|
||||
if err != nil {
|
||||
log.Warnf("could not parse possible go binary at %q: %+v", location.RealPath, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
internal.CloseAndLogError(r, location.RealPath)
|
||||
pkgs = append(pkgs, goPkgs...)
|
||||
mods, archs := scanFile(reader, location.RealPath)
|
||||
internal.CloseAndLogError(readerCloser, location.RealPath)
|
||||
|
||||
for i, mod := range mods {
|
||||
pkgs = append(pkgs, buildGoPkgInfo(location, mod, archs[i])...)
|
||||
}
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func getUnionReader(readerCloser io.ReadCloser) (unionReader, error) {
|
||||
reader, ok := readerCloser.(unionReader)
|
||||
if ok {
|
||||
return reader, nil
|
||||
}
|
||||
log.Debugf("golang cataloger: unable to use stereoscope file, reading entire contents")
|
||||
|
||||
b, err := ioutil.ReadAll(readerCloser)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read contents from go binary: %w", err)
|
||||
}
|
||||
|
||||
bytesReader := bytes.NewReader(b)
|
||||
|
||||
reader = struct {
|
||||
io.ReadCloser
|
||||
io.ReaderAt
|
||||
io.Seeker
|
||||
}{
|
||||
ReadCloser: io.NopCloser(bytesReader),
|
||||
ReaderAt: bytesReader,
|
||||
Seeker: bytesReader,
|
||||
}
|
||||
|
||||
return reader, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_getUnionReader_notUnionReader(t *testing.T) {
|
||||
expectedContents := "this is a test"
|
||||
reader := io.NopCloser(strings.NewReader(expectedContents))
|
||||
|
||||
// make certain that the test fixture does not implement the union reader
|
||||
_, ok := reader.(unionReader)
|
||||
require.False(t, ok)
|
||||
|
||||
actual, err := getUnionReader(reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, ok = actual.(unionReader)
|
||||
require.True(t, ok)
|
||||
|
||||
b, err := io.ReadAll(actual)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expectedContents, string(b))
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
// This code was copied from the Go std library.
|
||||
// https://github.com/golang/go/blob/master/src/cmd/go/internal/version/exe.go
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//nolint
|
||||
package golang
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// An exe is a generic interface to an OS executable (ELF, Mach-O, PE, XCOFF).
|
||||
type exe interface {
|
||||
// Close closes the underlying file.
|
||||
Close() error
|
||||
|
||||
// ReadData reads and returns up to size byte starting at virtual address addr.
|
||||
ReadData(addr, size uint64) ([]byte, error)
|
||||
|
||||
// ArchName returns a string that represents the CPU architecture of the executable.
|
||||
ArchName() string
|
||||
|
||||
// DataStart returns the writable data segment start address.
|
||||
DataStart() uint64
|
||||
}
|
||||
|
||||
// openExe opens file and returns it as an exe.
|
||||
// we changed this signature from accpeting a string
|
||||
// to a ReadCloser so we could adapt the code to the
|
||||
// stereoscope api. We removed the file open methods.
|
||||
func openExe(file io.ReadCloser) ([]exe, error) {
|
||||
/*
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := make([]byte, 16)
|
||||
if _, err := io.ReadFull(f, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Seek(0, 0)
|
||||
*/
|
||||
data, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := bytes.NewReader(data)
|
||||
f := io.NewSectionReader(r, 0, int64(len(data)))
|
||||
if bytes.HasPrefix(data, []byte("\x7FELF")) {
|
||||
e, err := elf.NewFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []exe{&elfExe{file, e}}, nil
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(data, []byte("MZ")) {
|
||||
e, err := pe.NewFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []exe{&peExe{file, e}}, nil
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) {
|
||||
e, err := macho.NewFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []exe{&machoExe{file, e}}, nil
|
||||
}
|
||||
|
||||
// adding macho multi-architecture support (both for 64bit and 32 bit)... this case is not in the stdlib yet
|
||||
if bytes.HasPrefix(data, []byte("\xCA\xFE\xBA\xBE")) || bytes.HasPrefix(data, []byte("\xCA\xFE\xBA\xBF")) {
|
||||
fatExe, err := macho.NewFatFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var exes []exe
|
||||
for _, arch := range fatExe.Arches {
|
||||
exes = append(exes, &machoExe{file, arch.File})
|
||||
}
|
||||
return exes, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unrecognized executable format")
|
||||
}
|
||||
|
||||
// elfExe is the ELF implementation of the exe interface.
|
||||
// updated os to be io.ReadCloser to interopt with stereoscope
|
||||
type elfExe struct {
|
||||
os io.ReadCloser
|
||||
f *elf.File
|
||||
}
|
||||
|
||||
func (x *elfExe) Close() error {
|
||||
return x.os.Close()
|
||||
}
|
||||
|
||||
func (x *elfExe) ArchName() string {
|
||||
return cleanElfArch(x.f.Machine)
|
||||
}
|
||||
|
||||
func cleanElfArch(machine elf.Machine) string {
|
||||
return strings.TrimPrefix(strings.ToLower(machine.String()), "em_")
|
||||
}
|
||||
|
||||
func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) {
|
||||
for _, prog := range x.f.Progs {
|
||||
if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 {
|
||||
n := prog.Vaddr + prog.Filesz - addr
|
||||
if n > size {
|
||||
n = size
|
||||
}
|
||||
data := make([]byte, n)
|
||||
_, err := prog.ReadAt(data, int64(addr-prog.Vaddr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("address not mapped")
|
||||
}
|
||||
|
||||
func (x *elfExe) DataStart() uint64 {
|
||||
for _, s := range x.f.Sections {
|
||||
if s.Name == ".go.buildinfo" {
|
||||
return s.Addr
|
||||
}
|
||||
}
|
||||
for _, p := range x.f.Progs {
|
||||
if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W {
|
||||
return p.Vaddr
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// peExe is the PE (Windows Portable Executable) implementation of the exe interface.
|
||||
type peExe struct {
|
||||
os io.ReadCloser
|
||||
f *pe.File
|
||||
}
|
||||
|
||||
func (x *peExe) Close() error {
|
||||
return x.os.Close()
|
||||
}
|
||||
|
||||
func (x *peExe) ArchName() string {
|
||||
// from: debug/pe/pe.go
|
||||
switch x.f.Machine {
|
||||
case pe.IMAGE_FILE_MACHINE_AM33:
|
||||
return "amd33"
|
||||
case pe.IMAGE_FILE_MACHINE_AMD64:
|
||||
return "amd64"
|
||||
case pe.IMAGE_FILE_MACHINE_ARM:
|
||||
return "arm"
|
||||
case pe.IMAGE_FILE_MACHINE_ARMNT:
|
||||
return "armnt"
|
||||
case pe.IMAGE_FILE_MACHINE_ARM64:
|
||||
return "arm64"
|
||||
case pe.IMAGE_FILE_MACHINE_EBC:
|
||||
return "ebc"
|
||||
case pe.IMAGE_FILE_MACHINE_I386:
|
||||
return "i386"
|
||||
case pe.IMAGE_FILE_MACHINE_IA64:
|
||||
return "ia64"
|
||||
case pe.IMAGE_FILE_MACHINE_M32R:
|
||||
return "m32r"
|
||||
case pe.IMAGE_FILE_MACHINE_MIPS16:
|
||||
return "mips16"
|
||||
case pe.IMAGE_FILE_MACHINE_MIPSFPU:
|
||||
return "mipsfpu"
|
||||
case pe.IMAGE_FILE_MACHINE_MIPSFPU16:
|
||||
return "mipsfpu16"
|
||||
case pe.IMAGE_FILE_MACHINE_POWERPC:
|
||||
return "ppc"
|
||||
case pe.IMAGE_FILE_MACHINE_POWERPCFP:
|
||||
return "ppcfp"
|
||||
case pe.IMAGE_FILE_MACHINE_R4000:
|
||||
return "r4000"
|
||||
case pe.IMAGE_FILE_MACHINE_SH3:
|
||||
return "sh3"
|
||||
case pe.IMAGE_FILE_MACHINE_SH3DSP:
|
||||
return "sh3dsp"
|
||||
case pe.IMAGE_FILE_MACHINE_SH4:
|
||||
return "sh4"
|
||||
case pe.IMAGE_FILE_MACHINE_SH5:
|
||||
return "sh5"
|
||||
case pe.IMAGE_FILE_MACHINE_THUMB:
|
||||
return "thumb"
|
||||
case pe.IMAGE_FILE_MACHINE_WCEMIPSV2:
|
||||
return "wcemipsv2"
|
||||
default:
|
||||
return fmt.Sprintf("unknown-pe-machine-%d", x.f.Machine)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *peExe) imageBase() uint64 {
|
||||
switch oh := x.f.OptionalHeader.(type) {
|
||||
case *pe.OptionalHeader32:
|
||||
return uint64(oh.ImageBase)
|
||||
case *pe.OptionalHeader64:
|
||||
return oh.ImageBase
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *peExe) ReadData(addr, size uint64) ([]byte, error) {
|
||||
addr -= x.imageBase()
|
||||
for _, sect := range x.f.Sections {
|
||||
if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
|
||||
n := uint64(sect.VirtualAddress+sect.Size) - addr
|
||||
if n > size {
|
||||
n = size
|
||||
}
|
||||
data := make([]byte, n)
|
||||
_, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("address not mapped")
|
||||
}
|
||||
|
||||
func (x *peExe) DataStart() uint64 {
|
||||
// Assume data is first writable section.
|
||||
const (
|
||||
IMAGE_SCN_CNT_CODE = 0x00000020
|
||||
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
|
||||
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
|
||||
IMAGE_SCN_MEM_EXECUTE = 0x20000000
|
||||
IMAGE_SCN_MEM_READ = 0x40000000
|
||||
IMAGE_SCN_MEM_WRITE = 0x80000000
|
||||
IMAGE_SCN_MEM_DISCARDABLE = 0x2000000
|
||||
IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000
|
||||
IMAGE_SCN_ALIGN_32BYTES = 0x600000
|
||||
)
|
||||
for _, sect := range x.f.Sections {
|
||||
if sect.VirtualAddress != 0 && sect.Size != 0 &&
|
||||
sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE {
|
||||
return uint64(sect.VirtualAddress) + x.imageBase()
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface.
|
||||
type machoExe struct {
|
||||
os io.ReadCloser
|
||||
f *macho.File
|
||||
}
|
||||
|
||||
func (x *machoExe) Close() error {
|
||||
return x.os.Close()
|
||||
}
|
||||
|
||||
func (x *machoExe) ArchName() string {
|
||||
return cleanMachoArch(x.f.Cpu)
|
||||
}
|
||||
|
||||
func cleanMachoArch(cpu macho.Cpu) string {
|
||||
return strings.TrimPrefix(strings.ToLower(cpu.String()), "cpu")
|
||||
}
|
||||
|
||||
func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) {
|
||||
for _, load := range x.f.Loads {
|
||||
seg, ok := load.(*macho.Segment)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 {
|
||||
if seg.Name == "__PAGEZERO" {
|
||||
continue
|
||||
}
|
||||
n := seg.Addr + seg.Filesz - addr
|
||||
if n > size {
|
||||
n = size
|
||||
}
|
||||
data := make([]byte, n)
|
||||
_, err := seg.ReadAt(data, int64(addr-seg.Addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("address not mapped")
|
||||
}
|
||||
|
||||
func (x *machoExe) DataStart() uint64 {
|
||||
// Look for section named "__go_buildinfo".
|
||||
for _, sec := range x.f.Sections {
|
||||
if sec.Name == "__go_buildinfo" {
|
||||
return sec.Addr
|
||||
}
|
||||
}
|
||||
// Try the first non-empty writable segment.
|
||||
const RW = 3
|
||||
for _, load := range x.f.Loads {
|
||||
seg, ok := load.(*macho.Segment)
|
||||
if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW {
|
||||
return seg.Addr
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/*
|
||||
// xcoffExe is the XCOFF (AIX eXtended COFF) implementation of the exe interface.
|
||||
type xcoffExe struct {
|
||||
os *os.File
|
||||
f *xcoff.File
|
||||
}
|
||||
|
||||
func (x *xcoffExe) Close() error {
|
||||
return x.os.Close()
|
||||
}
|
||||
|
||||
func (x *xcoffExe) ReadData(addr, size uint64) ([]byte, error) {
|
||||
for _, sect := range x.f.Sections {
|
||||
if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
|
||||
n := uint64(sect.VirtualAddress+sect.Size) - addr
|
||||
if n > size {
|
||||
n = size
|
||||
}
|
||||
data := make([]byte, n)
|
||||
_, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("address not mapped")
|
||||
}
|
||||
|
||||
func (x *xcoffExe) DataStart() uint64 {
|
||||
return x.f.SectionByType(xcoff.STYP_DATA).VirtualAddress
|
||||
}
|
||||
*/
|
||||
@@ -1,43 +0,0 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_cleanElfArch(t *testing.T) {
|
||||
tests := []struct {
|
||||
machine elf.Machine
|
||||
want string
|
||||
}{
|
||||
{
|
||||
machine: elf.EM_X86_64,
|
||||
want: "x86_64",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.machine.String(), func(t *testing.T) {
|
||||
assert.Equalf(t, test.want, cleanElfArch(test.machine), "cleanElfArch(%v)", test.machine)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_cleanMachoArch(t *testing.T) {
|
||||
tests := []struct {
|
||||
cpu macho.Cpu
|
||||
want string
|
||||
}{
|
||||
{
|
||||
cpu: macho.CpuAmd64,
|
||||
want: "amd64",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.cpu.String(), func(t *testing.T) {
|
||||
assert.Equalf(t, test.want, cleanMachoArch(test.cpu), "cleanMachoArch(%v)", test.cpu)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
xcoff
|
||||
-----
|
||||
|
||||
The code in this package comes from: https://github.com/golang/go/tree/master/src/internal/xcoff -- it was copied over to add support for [xcoff](https://en.wikipedia.org/wiki/XCOFF) binaries. Golang keeps this package as internal, forbidding its external use.
|
||||
@@ -0,0 +1,688 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package xcoff implements access to XCOFF (Extended Common Object File Format) files.
|
||||
|
||||
//nolint //this is an internal golang lib
|
||||
package xcoff
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SectionHeader holds information about an XCOFF section header.
|
||||
type SectionHeader struct {
|
||||
Name string
|
||||
VirtualAddress uint64
|
||||
Size uint64
|
||||
Type uint32
|
||||
Relptr uint64
|
||||
Nreloc uint32
|
||||
}
|
||||
|
||||
type Section struct {
|
||||
SectionHeader
|
||||
Relocs []Reloc
|
||||
io.ReaderAt
|
||||
sr *io.SectionReader
|
||||
}
|
||||
|
||||
// AuxiliaryCSect holds information about an XCOFF symbol in an AUX_CSECT entry.
|
||||
type AuxiliaryCSect struct {
|
||||
Length int64
|
||||
StorageMappingClass int
|
||||
SymbolType int
|
||||
}
|
||||
|
||||
// AuxiliaryFcn holds information about an XCOFF symbol in an AUX_FCN entry.
|
||||
type AuxiliaryFcn struct {
|
||||
Size int64
|
||||
}
|
||||
|
||||
type Symbol struct {
|
||||
Name string
|
||||
Value uint64
|
||||
SectionNumber int
|
||||
StorageClass int
|
||||
AuxFcn AuxiliaryFcn
|
||||
AuxCSect AuxiliaryCSect
|
||||
}
|
||||
|
||||
type Reloc struct {
|
||||
VirtualAddress uint64
|
||||
Symbol *Symbol
|
||||
Signed bool
|
||||
InstructionFixed bool
|
||||
Length uint8
|
||||
Type uint8
|
||||
}
|
||||
|
||||
// ImportedSymbol holds information about an imported XCOFF symbol.
|
||||
type ImportedSymbol struct {
|
||||
Name string
|
||||
Library string
|
||||
}
|
||||
|
||||
// FileHeader holds information about an XCOFF file header.
|
||||
type FileHeader struct {
|
||||
TargetMachine uint16
|
||||
}
|
||||
|
||||
// A File represents an open XCOFF file.
|
||||
type File struct {
|
||||
FileHeader
|
||||
Sections []*Section
|
||||
Symbols []*Symbol
|
||||
StringTable []byte
|
||||
LibraryPaths []string
|
||||
|
||||
closer io.Closer
|
||||
}
|
||||
|
||||
// Open opens the named file using os.Open and prepares it for use as an XCOFF binary.
|
||||
func Open(name string) (*File, error) {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ff, err := NewFile(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
ff.closer = f
|
||||
return ff, nil
|
||||
}
|
||||
|
||||
// Close closes the File.
|
||||
// If the File was created using NewFile directly instead of Open,
|
||||
// Close has no effect.
|
||||
func (f *File) Close() error {
|
||||
var err error
|
||||
if f.closer != nil {
|
||||
err = f.closer.Close()
|
||||
f.closer = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Section returns the first section with the given name, or nil if no such
|
||||
// section exists.
|
||||
// Xcoff have section's name limited to 8 bytes. Some sections like .gosymtab
|
||||
// can be trunked but this method will still find them.
|
||||
func (f *File) Section(name string) *Section {
|
||||
for _, s := range f.Sections {
|
||||
if s.Name == name || (len(name) > 8 && s.Name == name[:8]) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SectionByType returns the first section in f with the
|
||||
// given type, or nil if there is no such section.
|
||||
func (f *File) SectionByType(typ uint32) *Section {
|
||||
for _, s := range f.Sections {
|
||||
if s.Type == typ {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// cstring converts ASCII byte sequence b to string.
|
||||
// It stops once it finds 0 or reaches end of b.
|
||||
func cstring(b []byte) string {
|
||||
var i int
|
||||
for i = 0; i < len(b) && b[i] != 0; i++ {
|
||||
}
|
||||
return string(b[:i])
|
||||
}
|
||||
|
||||
// getString extracts a string from an XCOFF string table.
|
||||
func getString(st []byte, offset uint32) (string, bool) {
|
||||
if offset < 4 || int(offset) >= len(st) {
|
||||
return "", false
|
||||
}
|
||||
return cstring(st[offset:]), true
|
||||
}
|
||||
|
||||
// NewFile creates a new File for accessing an XCOFF binary in an underlying reader.
|
||||
func NewFile(r io.ReaderAt) (*File, error) {
|
||||
sr := io.NewSectionReader(r, 0, 1<<63-1)
|
||||
// Read XCOFF target machine
|
||||
var magic uint16
|
||||
if err := binary.Read(sr, binary.BigEndian, &magic); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if magic != U802TOCMAGIC && magic != U64_TOCMAGIC {
|
||||
return nil, fmt.Errorf("unrecognised XCOFF magic: 0x%x", magic)
|
||||
}
|
||||
|
||||
f := new(File)
|
||||
f.TargetMachine = magic
|
||||
|
||||
// Read XCOFF file header
|
||||
if _, err := sr.Seek(0, os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var nscns uint16
|
||||
var symptr uint64
|
||||
var nsyms int32
|
||||
var opthdr uint16
|
||||
var hdrsz int
|
||||
switch f.TargetMachine {
|
||||
case U802TOCMAGIC:
|
||||
fhdr := new(FileHeader32)
|
||||
if err := binary.Read(sr, binary.BigEndian, fhdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nscns = fhdr.Fnscns
|
||||
symptr = uint64(fhdr.Fsymptr)
|
||||
nsyms = fhdr.Fnsyms
|
||||
opthdr = fhdr.Fopthdr
|
||||
hdrsz = FILHSZ_32
|
||||
case U64_TOCMAGIC:
|
||||
fhdr := new(FileHeader64)
|
||||
if err := binary.Read(sr, binary.BigEndian, fhdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nscns = fhdr.Fnscns
|
||||
symptr = fhdr.Fsymptr
|
||||
nsyms = fhdr.Fnsyms
|
||||
opthdr = fhdr.Fopthdr
|
||||
hdrsz = FILHSZ_64
|
||||
}
|
||||
|
||||
if symptr == 0 || nsyms <= 0 {
|
||||
return nil, fmt.Errorf("no symbol table")
|
||||
}
|
||||
|
||||
// Read string table (located right after symbol table).
|
||||
offset := symptr + uint64(nsyms)*SYMESZ
|
||||
if _, err := sr.Seek(int64(offset), os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The first 4 bytes contain the length (in bytes).
|
||||
var l uint32
|
||||
if err := binary.Read(sr, binary.BigEndian, &l); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if l > 4 {
|
||||
if _, err := sr.Seek(int64(offset), os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.StringTable = make([]byte, l)
|
||||
if _, err := io.ReadFull(sr, f.StringTable); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Read section headers
|
||||
if _, err := sr.Seek(int64(hdrsz)+int64(opthdr), os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Sections = make([]*Section, nscns)
|
||||
for i := 0; i < int(nscns); i++ {
|
||||
var scnptr uint64
|
||||
s := new(Section)
|
||||
switch f.TargetMachine {
|
||||
case U802TOCMAGIC:
|
||||
shdr := new(SectionHeader32)
|
||||
if err := binary.Read(sr, binary.BigEndian, shdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Name = cstring(shdr.Sname[:])
|
||||
s.VirtualAddress = uint64(shdr.Svaddr)
|
||||
s.Size = uint64(shdr.Ssize)
|
||||
scnptr = uint64(shdr.Sscnptr)
|
||||
s.Type = shdr.Sflags
|
||||
s.Relptr = uint64(shdr.Srelptr)
|
||||
s.Nreloc = uint32(shdr.Snreloc)
|
||||
case U64_TOCMAGIC:
|
||||
shdr := new(SectionHeader64)
|
||||
if err := binary.Read(sr, binary.BigEndian, shdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Name = cstring(shdr.Sname[:])
|
||||
s.VirtualAddress = shdr.Svaddr
|
||||
s.Size = shdr.Ssize
|
||||
scnptr = shdr.Sscnptr
|
||||
s.Type = shdr.Sflags
|
||||
s.Relptr = shdr.Srelptr
|
||||
s.Nreloc = shdr.Snreloc
|
||||
}
|
||||
r2 := r
|
||||
if scnptr == 0 { // .bss must have all 0s
|
||||
r2 = zeroReaderAt{}
|
||||
}
|
||||
s.sr = io.NewSectionReader(r2, int64(scnptr), int64(s.Size))
|
||||
s.ReaderAt = s.sr
|
||||
f.Sections[i] = s
|
||||
}
|
||||
|
||||
// Symbol map needed by relocation
|
||||
var idxToSym = make(map[int]*Symbol)
|
||||
|
||||
// Read symbol table
|
||||
if _, err := sr.Seek(int64(symptr), os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Symbols = make([]*Symbol, 0)
|
||||
for i := 0; i < int(nsyms); i++ {
|
||||
var numaux int
|
||||
var ok, needAuxFcn bool
|
||||
sym := new(Symbol)
|
||||
switch f.TargetMachine {
|
||||
case U802TOCMAGIC:
|
||||
se := new(SymEnt32)
|
||||
if err := binary.Read(sr, binary.BigEndian, se); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
numaux = int(se.Nnumaux)
|
||||
sym.SectionNumber = int(se.Nscnum)
|
||||
sym.StorageClass = int(se.Nsclass)
|
||||
sym.Value = uint64(se.Nvalue)
|
||||
needAuxFcn = se.Ntype&SYM_TYPE_FUNC != 0 && numaux > 1
|
||||
zeroes := binary.BigEndian.Uint32(se.Nname[:4])
|
||||
if zeroes != 0 {
|
||||
sym.Name = cstring(se.Nname[:])
|
||||
} else {
|
||||
offset := binary.BigEndian.Uint32(se.Nname[4:])
|
||||
sym.Name, ok = getString(f.StringTable, offset)
|
||||
if !ok {
|
||||
goto skip
|
||||
}
|
||||
}
|
||||
case U64_TOCMAGIC:
|
||||
se := new(SymEnt64)
|
||||
if err := binary.Read(sr, binary.BigEndian, se); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
numaux = int(se.Nnumaux)
|
||||
sym.SectionNumber = int(se.Nscnum)
|
||||
sym.StorageClass = int(se.Nsclass)
|
||||
sym.Value = se.Nvalue
|
||||
needAuxFcn = se.Ntype&SYM_TYPE_FUNC != 0 && numaux > 1
|
||||
sym.Name, ok = getString(f.StringTable, se.Noffset)
|
||||
if !ok {
|
||||
goto skip
|
||||
}
|
||||
}
|
||||
if sym.StorageClass != C_EXT && sym.StorageClass != C_WEAKEXT && sym.StorageClass != C_HIDEXT {
|
||||
goto skip
|
||||
}
|
||||
// Must have at least one csect auxiliary entry.
|
||||
if numaux < 1 || i+numaux >= int(nsyms) {
|
||||
goto skip
|
||||
}
|
||||
|
||||
if sym.SectionNumber > int(nscns) {
|
||||
goto skip
|
||||
}
|
||||
if sym.SectionNumber == 0 {
|
||||
sym.Value = 0
|
||||
} else {
|
||||
sym.Value -= f.Sections[sym.SectionNumber-1].VirtualAddress
|
||||
}
|
||||
|
||||
idxToSym[i] = sym
|
||||
|
||||
// If this symbol is a function, it must retrieve its size from
|
||||
// its AUX_FCN entry.
|
||||
// It can happen that a function symbol doesn't have any AUX_FCN.
|
||||
// In this case, needAuxFcn is false and their size will be set to 0.
|
||||
if needAuxFcn {
|
||||
switch f.TargetMachine {
|
||||
case U802TOCMAGIC:
|
||||
aux := new(AuxFcn32)
|
||||
if err := binary.Read(sr, binary.BigEndian, aux); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sym.AuxFcn.Size = int64(aux.Xfsize)
|
||||
case U64_TOCMAGIC:
|
||||
aux := new(AuxFcn64)
|
||||
if err := binary.Read(sr, binary.BigEndian, aux); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sym.AuxFcn.Size = int64(aux.Xfsize)
|
||||
}
|
||||
}
|
||||
|
||||
// Read csect auxiliary entry (by convention, it is the last).
|
||||
if !needAuxFcn {
|
||||
if _, err := sr.Seek(int64(numaux-1)*SYMESZ, os.SEEK_CUR); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
i += numaux
|
||||
numaux = 0
|
||||
switch f.TargetMachine {
|
||||
case U802TOCMAGIC:
|
||||
aux := new(AuxCSect32)
|
||||
if err := binary.Read(sr, binary.BigEndian, aux); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sym.AuxCSect.SymbolType = int(aux.Xsmtyp & 0x7)
|
||||
sym.AuxCSect.StorageMappingClass = int(aux.Xsmclas)
|
||||
sym.AuxCSect.Length = int64(aux.Xscnlen)
|
||||
case U64_TOCMAGIC:
|
||||
aux := new(AuxCSect64)
|
||||
if err := binary.Read(sr, binary.BigEndian, aux); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sym.AuxCSect.SymbolType = int(aux.Xsmtyp & 0x7)
|
||||
sym.AuxCSect.StorageMappingClass = int(aux.Xsmclas)
|
||||
sym.AuxCSect.Length = int64(aux.Xscnlenhi)<<32 | int64(aux.Xscnlenlo)
|
||||
}
|
||||
f.Symbols = append(f.Symbols, sym)
|
||||
skip:
|
||||
i += numaux // Skip auxiliary entries
|
||||
if _, err := sr.Seek(int64(numaux)*SYMESZ, os.SEEK_CUR); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Read relocations
|
||||
// Only for .data or .text section
|
||||
for _, sect := range f.Sections {
|
||||
if sect.Type != STYP_TEXT && sect.Type != STYP_DATA {
|
||||
continue
|
||||
}
|
||||
sect.Relocs = make([]Reloc, sect.Nreloc)
|
||||
if sect.Relptr == 0 {
|
||||
continue
|
||||
}
|
||||
if _, err := sr.Seek(int64(sect.Relptr), os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := uint32(0); i < sect.Nreloc; i++ {
|
||||
switch f.TargetMachine {
|
||||
case U802TOCMAGIC:
|
||||
rel := new(Reloc32)
|
||||
if err := binary.Read(sr, binary.BigEndian, rel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sect.Relocs[i].VirtualAddress = uint64(rel.Rvaddr)
|
||||
sect.Relocs[i].Symbol = idxToSym[int(rel.Rsymndx)]
|
||||
sect.Relocs[i].Type = rel.Rtype
|
||||
sect.Relocs[i].Length = rel.Rsize&0x3F + 1
|
||||
|
||||
if rel.Rsize&0x80 != 0 {
|
||||
sect.Relocs[i].Signed = true
|
||||
}
|
||||
if rel.Rsize&0x40 != 0 {
|
||||
sect.Relocs[i].InstructionFixed = true
|
||||
}
|
||||
|
||||
case U64_TOCMAGIC:
|
||||
rel := new(Reloc64)
|
||||
if err := binary.Read(sr, binary.BigEndian, rel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sect.Relocs[i].VirtualAddress = rel.Rvaddr
|
||||
sect.Relocs[i].Symbol = idxToSym[int(rel.Rsymndx)]
|
||||
sect.Relocs[i].Type = rel.Rtype
|
||||
sect.Relocs[i].Length = rel.Rsize&0x3F + 1
|
||||
if rel.Rsize&0x80 != 0 {
|
||||
sect.Relocs[i].Signed = true
|
||||
}
|
||||
if rel.Rsize&0x40 != 0 {
|
||||
sect.Relocs[i].InstructionFixed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// zeroReaderAt is ReaderAt that reads 0s.
|
||||
type zeroReaderAt struct{}
|
||||
|
||||
// ReadAt writes len(p) 0s into p.
|
||||
func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
for i := range p {
|
||||
p[i] = 0
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Data reads and returns the contents of the XCOFF section s.
|
||||
func (s *Section) Data() ([]byte, error) {
|
||||
dat := make([]byte, s.sr.Size())
|
||||
n, err := s.sr.ReadAt(dat, 0)
|
||||
if n == len(dat) {
|
||||
err = nil
|
||||
}
|
||||
return dat[:n], err
|
||||
}
|
||||
|
||||
// CSect reads and returns the contents of a csect.
|
||||
// func (f *File) CSect(name string) []byte {
|
||||
// for _, sym := range f.Symbols {
|
||||
// if sym.Name == name && sym.AuxCSect.SymbolType == XTY_SD {
|
||||
// if i := sym.SectionNumber - 1; 0 <= i && i < len(f.Sections) {
|
||||
// s := f.Sections[i]
|
||||
// if sym.Value+uint64(sym.AuxCSect.Length) <= s.Size {
|
||||
// dat := make([]byte, sym.AuxCSect.Length)
|
||||
// _, err := s.sr.ReadAt(dat, int64(sym.Value))
|
||||
// if err != nil {
|
||||
// return nil
|
||||
// }
|
||||
// return dat
|
||||
// }
|
||||
// }
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func (f *File) DWARF() (*dwarf.Data, error) {
|
||||
// // There are many other DWARF sections, but these
|
||||
// // are the ones the debug/dwarf package uses.
|
||||
// // Don't bother loading others.
|
||||
// var subtypes = [...]uint32{SSUBTYP_DWABREV, SSUBTYP_DWINFO, SSUBTYP_DWLINE, SSUBTYP_DWRNGES, SSUBTYP_DWSTR}
|
||||
// var dat [len(subtypes)][]byte
|
||||
// for i, subtype := range subtypes {
|
||||
// s := f.SectionByType(STYP_DWARF | subtype)
|
||||
// if s != nil {
|
||||
// b, err := s.Data()
|
||||
// if err != nil && uint64(len(b)) < s.Size {
|
||||
// return nil, err
|
||||
// }
|
||||
// dat[i] = b
|
||||
// }
|
||||
// }
|
||||
|
||||
// abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4]
|
||||
// return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
|
||||
// }
|
||||
|
||||
// readImportID returns the import file IDs stored inside the .loader section.
|
||||
// Library name pattern is either path/base/member or base/member
|
||||
func (f *File) readImportIDs(s *Section) ([]string, error) {
|
||||
// Read loader header
|
||||
if _, err := s.sr.Seek(0, os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var istlen uint32
|
||||
var nimpid int32
|
||||
var impoff uint64
|
||||
switch f.TargetMachine {
|
||||
case U802TOCMAGIC:
|
||||
lhdr := new(LoaderHeader32)
|
||||
if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
istlen = lhdr.Listlen
|
||||
nimpid = lhdr.Lnimpid
|
||||
impoff = uint64(lhdr.Limpoff)
|
||||
case U64_TOCMAGIC:
|
||||
lhdr := new(LoaderHeader64)
|
||||
if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
istlen = lhdr.Listlen
|
||||
nimpid = lhdr.Lnimpid
|
||||
impoff = lhdr.Limpoff
|
||||
}
|
||||
|
||||
// Read loader import file ID table
|
||||
if _, err := s.sr.Seek(int64(impoff), os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
table := make([]byte, istlen)
|
||||
if _, err := io.ReadFull(s.sr, table); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
offset := 0
|
||||
// First import file ID is the default LIBPATH value
|
||||
libpath := cstring(table[offset:])
|
||||
f.LibraryPaths = strings.Split(libpath, ":")
|
||||
offset += len(libpath) + 3 // 3 null bytes
|
||||
all := make([]string, 0)
|
||||
for i := 1; i < int(nimpid); i++ {
|
||||
impidpath := cstring(table[offset:])
|
||||
offset += len(impidpath) + 1
|
||||
impidbase := cstring(table[offset:])
|
||||
offset += len(impidbase) + 1
|
||||
impidmem := cstring(table[offset:])
|
||||
offset += len(impidmem) + 1
|
||||
var path string
|
||||
if len(impidpath) > 0 {
|
||||
path = impidpath + "/" + impidbase + "/" + impidmem
|
||||
} else {
|
||||
path = impidbase + "/" + impidmem
|
||||
}
|
||||
all = append(all, path)
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// ImportedSymbols returns the names of all symbols
|
||||
// referred to by the binary f that are expected to be
|
||||
// satisfied by other libraries at dynamic load time.
|
||||
// It does not return weak symbols.
|
||||
func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
|
||||
s := f.SectionByType(STYP_LOADER)
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
// Read loader header
|
||||
if _, err := s.sr.Seek(0, os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var stlen uint32
|
||||
var stoff uint64
|
||||
var nsyms int32
|
||||
var symoff uint64
|
||||
switch f.TargetMachine {
|
||||
case U802TOCMAGIC:
|
||||
lhdr := new(LoaderHeader32)
|
||||
if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stlen = lhdr.Lstlen
|
||||
stoff = uint64(lhdr.Lstoff)
|
||||
nsyms = lhdr.Lnsyms
|
||||
symoff = LDHDRSZ_32
|
||||
case U64_TOCMAGIC:
|
||||
lhdr := new(LoaderHeader64)
|
||||
if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stlen = lhdr.Lstlen
|
||||
stoff = lhdr.Lstoff
|
||||
nsyms = lhdr.Lnsyms
|
||||
symoff = lhdr.Lsymoff
|
||||
}
|
||||
|
||||
// Read loader section string table
|
||||
if _, err := s.sr.Seek(int64(stoff), os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st := make([]byte, stlen)
|
||||
if _, err := io.ReadFull(s.sr, st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read imported libraries
|
||||
libs, err := f.readImportIDs(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read loader symbol table
|
||||
if _, err := s.sr.Seek(int64(symoff), os.SEEK_SET); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
all := make([]ImportedSymbol, 0)
|
||||
for i := 0; i < int(nsyms); i++ {
|
||||
var name string
|
||||
var ifile int32
|
||||
var ok bool
|
||||
switch f.TargetMachine {
|
||||
case U802TOCMAGIC:
|
||||
ldsym := new(LoaderSymbol32)
|
||||
if err := binary.Read(s.sr, binary.BigEndian, ldsym); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ldsym.Lsmtype&0x40 == 0 {
|
||||
continue // Imported symbols only
|
||||
}
|
||||
zeroes := binary.BigEndian.Uint32(ldsym.Lname[:4])
|
||||
if zeroes != 0 {
|
||||
name = cstring(ldsym.Lname[:])
|
||||
} else {
|
||||
offset := binary.BigEndian.Uint32(ldsym.Lname[4:])
|
||||
name, ok = getString(st, offset)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
ifile = ldsym.Lifile
|
||||
case U64_TOCMAGIC:
|
||||
ldsym := new(LoaderSymbol64)
|
||||
if err := binary.Read(s.sr, binary.BigEndian, ldsym); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ldsym.Lsmtype&0x40 == 0 {
|
||||
continue // Imported symbols only
|
||||
}
|
||||
name, ok = getString(st, ldsym.Loffset)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
ifile = ldsym.Lifile
|
||||
}
|
||||
var sym ImportedSymbol
|
||||
sym.Name = name
|
||||
if ifile >= 1 && int(ifile) <= len(libs) {
|
||||
sym.Library = libs[ifile-1]
|
||||
}
|
||||
all = append(all, sym)
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// ImportedLibraries returns the names of all libraries
|
||||
// referred to by the binary f that are expected to be
|
||||
// linked with the binary at dynamic link time.
|
||||
func (f *File) ImportedLibraries() ([]string, error) {
|
||||
s := f.SectionByType(STYP_LOADER)
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
all, err := f.readImportIDs(s)
|
||||
return all, err
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xcoff
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type fileTest struct {
|
||||
file string
|
||||
hdr FileHeader
|
||||
sections []*SectionHeader
|
||||
needed []string
|
||||
}
|
||||
|
||||
var fileTests = []fileTest{
|
||||
{
|
||||
"testdata/gcc-ppc32-aix-dwarf2-exec",
|
||||
FileHeader{U802TOCMAGIC},
|
||||
[]*SectionHeader{
|
||||
{".text", 0x10000290, 0x00000bbd, STYP_TEXT, 0x7ae6, 0x36},
|
||||
{".data", 0x20000e4d, 0x00000437, STYP_DATA, 0x7d02, 0x2b},
|
||||
{".bss", 0x20001284, 0x0000021c, STYP_BSS, 0, 0},
|
||||
{".loader", 0x00000000, 0x000004b3, STYP_LOADER, 0, 0},
|
||||
{".dwline", 0x00000000, 0x000000df, STYP_DWARF | SSUBTYP_DWLINE, 0x7eb0, 0x7},
|
||||
{".dwinfo", 0x00000000, 0x00000314, STYP_DWARF | SSUBTYP_DWINFO, 0x7ef6, 0xa},
|
||||
{".dwabrev", 0x00000000, 0x000000d6, STYP_DWARF | SSUBTYP_DWABREV, 0, 0},
|
||||
{".dwarnge", 0x00000000, 0x00000020, STYP_DWARF | SSUBTYP_DWARNGE, 0x7f5a, 0x2},
|
||||
{".dwloc", 0x00000000, 0x00000074, STYP_DWARF | SSUBTYP_DWLOC, 0, 0},
|
||||
{".debug", 0x00000000, 0x00005e4f, STYP_DEBUG, 0, 0},
|
||||
},
|
||||
[]string{"libc.a/shr.o"},
|
||||
},
|
||||
{
|
||||
"testdata/gcc-ppc64-aix-dwarf2-exec",
|
||||
FileHeader{U64_TOCMAGIC},
|
||||
[]*SectionHeader{
|
||||
{".text", 0x10000480, 0x00000afd, STYP_TEXT, 0x8322, 0x34},
|
||||
{".data", 0x20000f7d, 0x000002f3, STYP_DATA, 0x85fa, 0x25},
|
||||
{".bss", 0x20001270, 0x00000428, STYP_BSS, 0, 0},
|
||||
{".loader", 0x00000000, 0x00000535, STYP_LOADER, 0, 0},
|
||||
{".dwline", 0x00000000, 0x000000b4, STYP_DWARF | SSUBTYP_DWLINE, 0x8800, 0x4},
|
||||
{".dwinfo", 0x00000000, 0x0000036a, STYP_DWARF | SSUBTYP_DWINFO, 0x8838, 0x7},
|
||||
{".dwabrev", 0x00000000, 0x000000b5, STYP_DWARF | SSUBTYP_DWABREV, 0, 0},
|
||||
{".dwarnge", 0x00000000, 0x00000040, STYP_DWARF | SSUBTYP_DWARNGE, 0x889a, 0x2},
|
||||
{".dwloc", 0x00000000, 0x00000062, STYP_DWARF | SSUBTYP_DWLOC, 0, 0},
|
||||
{".debug", 0x00000000, 0x00006605, STYP_DEBUG, 0, 0},
|
||||
},
|
||||
[]string{"libc.a/shr_64.o"},
|
||||
},
|
||||
}
|
||||
|
||||
func TestOpen(t *testing.T) {
|
||||
for i := range fileTests {
|
||||
tt := &fileTests[i]
|
||||
|
||||
f, err := Open(tt.file)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(f.FileHeader, tt.hdr) {
|
||||
t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr)
|
||||
continue
|
||||
}
|
||||
|
||||
for i, sh := range f.Sections {
|
||||
if i >= len(tt.sections) {
|
||||
break
|
||||
}
|
||||
have := &sh.SectionHeader
|
||||
want := tt.sections[i]
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
|
||||
}
|
||||
}
|
||||
tn := len(tt.sections)
|
||||
fn := len(f.Sections)
|
||||
if tn != fn {
|
||||
t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn)
|
||||
}
|
||||
tl := tt.needed
|
||||
fl, err := f.ImportedLibraries()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !reflect.DeepEqual(tl, fl) {
|
||||
t.Errorf("open %s: loader import = %v, want %v", tt.file, tl, fl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenFailure(t *testing.T) {
|
||||
filename := "file.go" // not an XCOFF object file
|
||||
_, err := Open(filename) // don't crash
|
||||
if err == nil {
|
||||
t.Errorf("open %s: succeeded unexpectedly", filename)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,373 @@
|
||||
// The code in this package comes from:
|
||||
// https://github.com/golang/go/tree/master/src/internal/xcoff
|
||||
// it was copied over to add support for xcoff binaries.
|
||||
// Golang keeps this package as internal, forbidding its external use.
|
||||
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//nolint // this is an internal golang lib
|
||||
package xcoff
|
||||
|
||||
// File Header.
|
||||
type FileHeader32 struct {
|
||||
Fmagic uint16 // Target machine
|
||||
Fnscns uint16 // Number of sections
|
||||
Ftimedat int32 // Time and date of file creation
|
||||
Fsymptr uint32 // Byte offset to symbol table start
|
||||
Fnsyms int32 // Number of entries in symbol table
|
||||
Fopthdr uint16 // Number of bytes in optional header
|
||||
Fflags uint16 // Flags
|
||||
}
|
||||
|
||||
type FileHeader64 struct {
|
||||
Fmagic uint16 // Target machine
|
||||
Fnscns uint16 // Number of sections
|
||||
Ftimedat int32 // Time and date of file creation
|
||||
Fsymptr uint64 // Byte offset to symbol table start
|
||||
Fopthdr uint16 // Number of bytes in optional header
|
||||
Fflags uint16 // Flags
|
||||
Fnsyms int32 // Number of entries in symbol table
|
||||
}
|
||||
|
||||
const (
|
||||
FILHSZ_32 = 20
|
||||
FILHSZ_64 = 24
|
||||
)
|
||||
const (
|
||||
U802TOCMAGIC = 0737 // AIX 32-bit XCOFF
|
||||
U64_TOCMAGIC = 0767 // AIX 64-bit XCOFF
|
||||
)
|
||||
|
||||
// Flags that describe the type of the object file.
|
||||
const (
|
||||
F_RELFLG = 0x0001
|
||||
F_EXEC = 0x0002
|
||||
F_LNNO = 0x0004
|
||||
F_FDPR_PROF = 0x0010
|
||||
F_FDPR_OPTI = 0x0020
|
||||
F_DSA = 0x0040
|
||||
F_VARPG = 0x0100
|
||||
F_DYNLOAD = 0x1000
|
||||
F_SHROBJ = 0x2000
|
||||
F_LOADONLY = 0x4000
|
||||
)
|
||||
|
||||
// Section Header.
|
||||
type SectionHeader32 struct {
|
||||
Sname [8]byte // Section name
|
||||
Spaddr uint32 // Physical address
|
||||
Svaddr uint32 // Virtual address
|
||||
Ssize uint32 // Section size
|
||||
Sscnptr uint32 // Offset in file to raw data for section
|
||||
Srelptr uint32 // Offset in file to relocation entries for section
|
||||
Slnnoptr uint32 // Offset in file to line number entries for section
|
||||
Snreloc uint16 // Number of relocation entries
|
||||
Snlnno uint16 // Number of line number entries
|
||||
Sflags uint32 // Flags to define the section type
|
||||
}
|
||||
|
||||
type SectionHeader64 struct {
|
||||
Sname [8]byte // Section name
|
||||
Spaddr uint64 // Physical address
|
||||
Svaddr uint64 // Virtual address
|
||||
Ssize uint64 // Section size
|
||||
Sscnptr uint64 // Offset in file to raw data for section
|
||||
Srelptr uint64 // Offset in file to relocation entries for section
|
||||
Slnnoptr uint64 // Offset in file to line number entries for section
|
||||
Snreloc uint32 // Number of relocation entries
|
||||
Snlnno uint32 // Number of line number entries
|
||||
Sflags uint32 // Flags to define the section type
|
||||
Spad uint32 // Needs to be 72 bytes long
|
||||
}
|
||||
|
||||
// Flags defining the section type.
|
||||
const (
|
||||
STYP_DWARF = 0x0010
|
||||
STYP_TEXT = 0x0020
|
||||
STYP_DATA = 0x0040
|
||||
STYP_BSS = 0x0080
|
||||
STYP_EXCEPT = 0x0100
|
||||
STYP_INFO = 0x0200
|
||||
STYP_TDATA = 0x0400
|
||||
STYP_TBSS = 0x0800
|
||||
STYP_LOADER = 0x1000
|
||||
STYP_DEBUG = 0x2000
|
||||
STYP_TYPCHK = 0x4000
|
||||
STYP_OVRFLO = 0x8000
|
||||
)
|
||||
const (
|
||||
SSUBTYP_DWINFO = 0x10000 // DWARF info section
|
||||
SSUBTYP_DWLINE = 0x20000 // DWARF line-number section
|
||||
SSUBTYP_DWPBNMS = 0x30000 // DWARF public names section
|
||||
SSUBTYP_DWPBTYP = 0x40000 // DWARF public types section
|
||||
SSUBTYP_DWARNGE = 0x50000 // DWARF aranges section
|
||||
SSUBTYP_DWABREV = 0x60000 // DWARF abbreviation section
|
||||
SSUBTYP_DWSTR = 0x70000 // DWARF strings section
|
||||
SSUBTYP_DWRNGES = 0x80000 // DWARF ranges section
|
||||
SSUBTYP_DWLOC = 0x90000 // DWARF location lists section
|
||||
SSUBTYP_DWFRAME = 0xA0000 // DWARF frames section
|
||||
SSUBTYP_DWMAC = 0xB0000 // DWARF macros section
|
||||
)
|
||||
|
||||
// Symbol Table Entry.
|
||||
type SymEnt32 struct {
|
||||
Nname [8]byte // Symbol name
|
||||
Nvalue uint32 // Symbol value
|
||||
Nscnum int16 // Section number of symbol
|
||||
Ntype uint16 // Basic and derived type specification
|
||||
Nsclass int8 // Storage class of symbol
|
||||
Nnumaux int8 // Number of auxiliary entries
|
||||
}
|
||||
|
||||
type SymEnt64 struct {
|
||||
Nvalue uint64 // Symbol value
|
||||
Noffset uint32 // Offset of the name in string table or .debug section
|
||||
Nscnum int16 // Section number of symbol
|
||||
Ntype uint16 // Basic and derived type specification
|
||||
Nsclass int8 // Storage class of symbol
|
||||
Nnumaux int8 // Number of auxiliary entries
|
||||
}
|
||||
|
||||
const SYMESZ = 18
|
||||
|
||||
const (
|
||||
// Nscnum
|
||||
N_DEBUG = -2
|
||||
N_ABS = -1
|
||||
N_UNDEF = 0
|
||||
|
||||
//Ntype
|
||||
SYM_V_INTERNAL = 0x1000
|
||||
SYM_V_HIDDEN = 0x2000
|
||||
SYM_V_PROTECTED = 0x3000
|
||||
SYM_V_EXPORTED = 0x4000
|
||||
SYM_TYPE_FUNC = 0x0020 // is function
|
||||
)
|
||||
|
||||
// Storage Class.
|
||||
const (
|
||||
C_NULL = 0 // Symbol table entry marked for deletion
|
||||
C_EXT = 2 // External symbol
|
||||
C_STAT = 3 // Static symbol
|
||||
C_BLOCK = 100 // Beginning or end of inner block
|
||||
C_FCN = 101 // Beginning or end of function
|
||||
C_FILE = 103 // Source file name and compiler information
|
||||
C_HIDEXT = 107 // Unnamed external symbol
|
||||
C_BINCL = 108 // Beginning of include file
|
||||
C_EINCL = 109 // End of include file
|
||||
C_WEAKEXT = 111 // Weak external symbol
|
||||
C_DWARF = 112 // DWARF symbol
|
||||
C_GSYM = 128 // Global variable
|
||||
C_LSYM = 129 // Automatic variable allocated on stack
|
||||
C_PSYM = 130 // Argument to subroutine allocated on stack
|
||||
C_RSYM = 131 // Register variable
|
||||
C_RPSYM = 132 // Argument to function or procedure stored in register
|
||||
C_STSYM = 133 // Statically allocated symbol
|
||||
C_BCOMM = 135 // Beginning of common block
|
||||
C_ECOML = 136 // Local member of common block
|
||||
C_ECOMM = 137 // End of common block
|
||||
C_DECL = 140 // Declaration of object
|
||||
C_ENTRY = 141 // Alternate entry
|
||||
C_FUN = 142 // Function or procedure
|
||||
C_BSTAT = 143 // Beginning of static block
|
||||
C_ESTAT = 144 // End of static block
|
||||
C_GTLS = 145 // Global thread-local variable
|
||||
C_STTLS = 146 // Static thread-local variable
|
||||
)
|
||||
|
||||
// File Auxiliary Entry
|
||||
type AuxFile64 struct {
|
||||
Xfname [8]byte // Name or offset inside string table
|
||||
Xftype uint8 // Source file string type
|
||||
Xauxtype uint8 // Type of auxiliary entry
|
||||
}
|
||||
|
||||
// Function Auxiliary Entry
|
||||
type AuxFcn32 struct {
|
||||
Xexptr uint32 // File offset to exception table entry
|
||||
Xfsize uint32 // Size of function in bytes
|
||||
Xlnnoptr uint32 // File pointer to line number
|
||||
Xendndx uint32 // Symbol table index of next entry
|
||||
Xpad uint16 // Unused
|
||||
}
|
||||
type AuxFcn64 struct {
|
||||
Xlnnoptr uint64 // File pointer to line number
|
||||
Xfsize uint32 // Size of function in bytes
|
||||
Xendndx uint32 // Symbol table index of next entry
|
||||
Xpad uint8 // Unused
|
||||
Xauxtype uint8 // Type of auxiliary entry
|
||||
}
|
||||
|
||||
type AuxSect64 struct {
|
||||
Xscnlen uint64 // section length
|
||||
Xnreloc uint64 // Num RLDs
|
||||
pad uint8
|
||||
Xauxtype uint8 // Type of auxiliary entry
|
||||
}
|
||||
|
||||
// csect Auxiliary Entry.
|
||||
type AuxCSect32 struct {
|
||||
Xscnlen int32 // Length or symbol table index
|
||||
Xparmhash uint32 // Offset of parameter type-check string
|
||||
Xsnhash uint16 // .typchk section number
|
||||
Xsmtyp uint8 // Symbol alignment and type
|
||||
Xsmclas uint8 // Storage-mapping class
|
||||
Xstab uint32 // Reserved
|
||||
Xsnstab uint16 // Reserved
|
||||
}
|
||||
|
||||
type AuxCSect64 struct {
|
||||
Xscnlenlo uint32 // Lower 4 bytes of length or symbol table index
|
||||
Xparmhash uint32 // Offset of parameter type-check string
|
||||
Xsnhash uint16 // .typchk section number
|
||||
Xsmtyp uint8 // Symbol alignment and type
|
||||
Xsmclas uint8 // Storage-mapping class
|
||||
Xscnlenhi int32 // Upper 4 bytes of length or symbol table index
|
||||
Xpad uint8 // Unused
|
||||
Xauxtype uint8 // Type of auxiliary entry
|
||||
}
|
||||
|
||||
// Auxiliary type
|
||||
// const (
|
||||
// _AUX_EXCEPT = 255
|
||||
// _AUX_FCN = 254
|
||||
// _AUX_SYM = 253
|
||||
// _AUX_FILE = 252
|
||||
// _AUX_CSECT = 251
|
||||
// _AUX_SECT = 250
|
||||
// )
|
||||
|
||||
// Symbol type field.
|
||||
const (
|
||||
XTY_ER = 0 // External reference
|
||||
XTY_SD = 1 // Section definition
|
||||
XTY_LD = 2 // Label definition
|
||||
XTY_CM = 3 // Common csect definition
|
||||
)
|
||||
|
||||
// Defines for File auxiliary definitions: x_ftype field of x_file
|
||||
const (
|
||||
XFT_FN = 0 // Source File Name
|
||||
XFT_CT = 1 // Compile Time Stamp
|
||||
XFT_CV = 2 // Compiler Version Number
|
||||
XFT_CD = 128 // Compiler Defined Information
|
||||
)
|
||||
|
||||
// Storage-mapping class.
|
||||
const (
|
||||
XMC_PR = 0 // Program code
|
||||
XMC_RO = 1 // Read-only constant
|
||||
XMC_DB = 2 // Debug dictionary table
|
||||
XMC_TC = 3 // TOC entry
|
||||
XMC_UA = 4 // Unclassified
|
||||
XMC_RW = 5 // Read/Write data
|
||||
XMC_GL = 6 // Global linkage
|
||||
XMC_XO = 7 // Extended operation
|
||||
XMC_SV = 8 // 32-bit supervisor call descriptor
|
||||
XMC_BS = 9 // BSS class
|
||||
XMC_DS = 10 // Function descriptor
|
||||
XMC_UC = 11 // Unnamed FORTRAN common
|
||||
XMC_TC0 = 15 // TOC anchor
|
||||
XMC_TD = 16 // Scalar data entry in the TOC
|
||||
XMC_SV64 = 17 // 64-bit supervisor call descriptor
|
||||
XMC_SV3264 = 18 // Supervisor call descriptor for both 32-bit and 64-bit
|
||||
XMC_TL = 20 // Read/Write thread-local data
|
||||
XMC_UL = 21 // Read/Write thread-local data (.tbss)
|
||||
XMC_TE = 22 // TOC entry
|
||||
)
|
||||
|
||||
// Loader Header.
|
||||
type LoaderHeader32 struct {
|
||||
Lversion int32 // Loader section version number
|
||||
Lnsyms int32 // Number of symbol table entries
|
||||
Lnreloc int32 // Number of relocation table entries
|
||||
Listlen uint32 // Length of import file ID string table
|
||||
Lnimpid int32 // Number of import file IDs
|
||||
Limpoff uint32 // Offset to start of import file IDs
|
||||
Lstlen uint32 // Length of string table
|
||||
Lstoff uint32 // Offset to start of string table
|
||||
}
|
||||
|
||||
type LoaderHeader64 struct {
|
||||
Lversion int32 // Loader section version number
|
||||
Lnsyms int32 // Number of symbol table entries
|
||||
Lnreloc int32 // Number of relocation table entries
|
||||
Listlen uint32 // Length of import file ID string table
|
||||
Lnimpid int32 // Number of import file IDs
|
||||
Lstlen uint32 // Length of string table
|
||||
Limpoff uint64 // Offset to start of import file IDs
|
||||
Lstoff uint64 // Offset to start of string table
|
||||
Lsymoff uint64 // Offset to start of symbol table
|
||||
Lrldoff uint64 // Offset to start of relocation entries
|
||||
}
|
||||
|
||||
const (
|
||||
LDHDRSZ_32 = 32
|
||||
LDHDRSZ_64 = 56
|
||||
)
|
||||
|
||||
// Loader Symbol.
|
||||
type LoaderSymbol32 struct {
|
||||
Lname [8]byte // Symbol name or byte offset into string table
|
||||
Lvalue uint32 // Address field
|
||||
Lscnum int16 // Section number containing symbol
|
||||
Lsmtype int8 // Symbol type, export, import flags
|
||||
Lsmclas int8 // Symbol storage class
|
||||
Lifile int32 // Import file ID; ordinal of import file IDs
|
||||
Lparm uint32 // Parameter type-check field
|
||||
}
|
||||
|
||||
type LoaderSymbol64 struct {
|
||||
Lvalue uint64 // Address field
|
||||
Loffset uint32 // Byte offset into string table of symbol name
|
||||
Lscnum int16 // Section number containing symbol
|
||||
Lsmtype int8 // Symbol type, export, import flags
|
||||
Lsmclas int8 // Symbol storage class
|
||||
Lifile int32 // Import file ID; ordinal of import file IDs
|
||||
Lparm uint32 // Parameter type-check field
|
||||
}
|
||||
|
||||
type Reloc32 struct {
|
||||
Rvaddr uint32 // (virtual) address of reference
|
||||
Rsymndx uint32 // Index into symbol table
|
||||
Rsize uint8 // Sign and reloc bit len
|
||||
Rtype uint8 // Toc relocation type
|
||||
}
|
||||
|
||||
type Reloc64 struct {
|
||||
Rvaddr uint64 // (virtual) address of reference
|
||||
Rsymndx uint32 // Index into symbol table
|
||||
Rsize uint8 // Sign and reloc bit len
|
||||
Rtype uint8 // Toc relocation type
|
||||
}
|
||||
|
||||
const (
|
||||
R_POS = 0x00 // A(sym) Positive Relocation
|
||||
R_NEG = 0x01 // -A(sym) Negative Relocation
|
||||
R_REL = 0x02 // A(sym-*) Relative to self
|
||||
R_TOC = 0x03 // A(sym-TOC) Relative to TOC
|
||||
R_TRL = 0x12 // A(sym-TOC) TOC Relative indirect load.
|
||||
|
||||
R_TRLA = 0x13 // A(sym-TOC) TOC Rel load address. modifiable inst
|
||||
R_GL = 0x05 // A(external TOC of sym) Global Linkage
|
||||
R_TCL = 0x06 // A(local TOC of sym) Local object TOC address
|
||||
R_RL = 0x0C // A(sym) Pos indirect load. modifiable instruction
|
||||
R_RLA = 0x0D // A(sym) Pos Load Address. modifiable instruction
|
||||
R_REF = 0x0F // AL0(sym) Non relocating ref. No garbage collect
|
||||
R_BA = 0x08 // A(sym) Branch absolute. Cannot modify instruction
|
||||
R_RBA = 0x18 // A(sym) Branch absolute. modifiable instruction
|
||||
R_BR = 0x0A // A(sym-*) Branch rel to self. non modifiable
|
||||
R_RBR = 0x1A // A(sym-*) Branch rel to self. modifiable instr
|
||||
|
||||
R_TLS = 0x20 // General-dynamic reference to TLS symbol
|
||||
R_TLS_IE = 0x21 // Initial-exec reference to TLS symbol
|
||||
R_TLS_LD = 0x22 // Local-dynamic reference to TLS symbol
|
||||
R_TLS_LE = 0x23 // Local-exec reference to TLS symbol
|
||||
R_TLSM = 0x24 // Module reference to TLS symbol
|
||||
R_TLSML = 0x25 // Module reference to local (own) module
|
||||
|
||||
R_TOCU = 0x30 // Relative to TOC - high order bits
|
||||
R_TOCL = 0x31 // Relative to TOC - low order bits
|
||||
)
|
||||
@@ -1,36 +1,61 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/golang/internal/xcoff"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
const (
|
||||
packageIdentifier = "dep"
|
||||
replaceIdentifier = "=>"
|
||||
const GOARCH = "GOARCH"
|
||||
|
||||
var (
|
||||
// errUnrecognizedFormat is returned when a given executable file doesn't
|
||||
// appear to be in a known format, or it breaks the rules of that format,
|
||||
// or when there are I/O errors reading the file.
|
||||
errUnrecognizedFormat = errors.New("unrecognized file format")
|
||||
)
|
||||
|
||||
type exeOpener func(file io.ReadCloser) ([]exe, error)
|
||||
func makeGoMainPackage(mod *debug.BuildInfo, arch string, location source.Location) pkg.Package {
|
||||
gbs := getBuildSettings(mod.Settings)
|
||||
main := newGoBinaryPackage(&mod.Main, mod.GoVersion, arch, location, gbs)
|
||||
main.Version = ""
|
||||
|
||||
if v, ok := gbs["vcs.revision"]; ok {
|
||||
main.Version = v
|
||||
}
|
||||
|
||||
return main
|
||||
}
|
||||
|
||||
func newGoBinaryPackage(dep *debug.Module, goVersion, architecture string, location source.Location, buildSettings map[string]string) pkg.Package {
|
||||
if dep.Replace != nil {
|
||||
dep = dep.Replace
|
||||
}
|
||||
|
||||
func newGoBinaryPackage(name, version, h1Digest, goVersion, architecture string, location source.Location) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: name,
|
||||
Version: 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,
|
||||
H1Digest: h1Digest,
|
||||
H1Digest: dep.Sum,
|
||||
Architecture: architecture,
|
||||
BuildSettings: buildSettings,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -39,49 +64,128 @@ func newGoBinaryPackage(name, version, h1Digest, goVersion, architecture string,
|
||||
return p
|
||||
}
|
||||
|
||||
func parseGoBin(location source.Location, reader io.ReadCloser, opener exeOpener) (pkgs []pkg.Package, err error) {
|
||||
var exes []exe
|
||||
// it has been found that there are stdlib paths within openExe that can panic. We want to prevent this behavior
|
||||
// bubbling up and halting execution. For this reason we try to recover from any panic and return an error.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("recovered from panic while parse go binary at %q: %+v", location.RealPath, r)
|
||||
}
|
||||
}()
|
||||
|
||||
// Identify if bin was compiled by go
|
||||
exes, err = opener(reader)
|
||||
if err != nil {
|
||||
return pkgs, err
|
||||
// getArchs finds a binary architecture by two ways:
|
||||
// 1) reading build info from binaries compiled by go1.18+
|
||||
// 2) reading file headers from binaries compiled by < go1.18
|
||||
func getArchs(readers []io.ReaderAt, builds []*debug.BuildInfo) []string {
|
||||
if len(readers) != len(builds) {
|
||||
log.Warnf("golang cataloger: bin parsing: number of builds and readers doesn't match")
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, x := range exes {
|
||||
goVersion, mod := findVers(x)
|
||||
pkgs = append(pkgs, buildGoPkgInfo(location, mod, goVersion, x.ArchName())...)
|
||||
if len(readers) == 0 || len(builds) == 0 {
|
||||
log.Warnf("golang cataloger: bin parsing: %d readers and %d build info items", len(readers), len(builds))
|
||||
return nil
|
||||
}
|
||||
return pkgs, err
|
||||
}
|
||||
|
||||
func buildGoPkgInfo(location source.Location, mod, goVersion, arch string) []pkg.Package {
|
||||
pkgsSlice := make([]pkg.Package, 0)
|
||||
scanner := bufio.NewScanner(strings.NewReader(mod))
|
||||
archs := make([]string, len(builds))
|
||||
for i, build := range builds {
|
||||
archs[i] = getGOARCH(build.Settings)
|
||||
}
|
||||
|
||||
// filter mod dependencies: [dep, name, version, sha]
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(scanner.Text())
|
||||
// if architecture was found via build settings return
|
||||
if archs[0] != "" {
|
||||
return archs
|
||||
}
|
||||
|
||||
// must have dep, name, version, sha
|
||||
if len(fields) < 4 {
|
||||
for i, r := range readers {
|
||||
a, err := getGOARCHFromBin(r)
|
||||
if err != nil {
|
||||
log.Warnf("golang cataloger: bin parsing: getting arch from binary: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if fields[0] == packageIdentifier || fields[0] == replaceIdentifier {
|
||||
name := fields[1]
|
||||
version := fields[2]
|
||||
h1Digest := fields[3]
|
||||
pkgsSlice = append(pkgsSlice, newGoBinaryPackage(name, version, h1Digest, goVersion, arch, location))
|
||||
archs[i] = a
|
||||
}
|
||||
return archs
|
||||
}
|
||||
|
||||
func getGOARCH(settings []debug.BuildSetting) string {
|
||||
for _, s := range settings {
|
||||
if s.Key == GOARCH {
|
||||
return s.Value
|
||||
}
|
||||
}
|
||||
|
||||
return pkgsSlice
|
||||
return ""
|
||||
}
|
||||
|
||||
func getGOARCHFromBin(r io.ReaderAt) (string, error) {
|
||||
// Read the first bytes of the file to identify the format, then delegate to
|
||||
// a format-specific function to load segment and section headers.
|
||||
ident := make([]byte, 16)
|
||||
if n, err := r.ReadAt(ident, 0); n < len(ident) || err != nil {
|
||||
return "", fmt.Errorf("unrecognized file format: %w", err)
|
||||
}
|
||||
|
||||
var arch string
|
||||
switch {
|
||||
case bytes.HasPrefix(ident, []byte("\x7FELF")):
|
||||
f, err := elf.NewFile(r)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unrecognized file format: %w", err)
|
||||
}
|
||||
arch = f.Machine.String()
|
||||
case bytes.HasPrefix(ident, []byte("MZ")):
|
||||
f, err := pe.NewFile(r)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unrecognized file format: %w", err)
|
||||
}
|
||||
arch = fmt.Sprintf("%d", f.Machine)
|
||||
case bytes.HasPrefix(ident, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(ident[1:], []byte("\xFA\xED\xFE")):
|
||||
f, err := macho.NewFile(r)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unrecognized file format: %w", err)
|
||||
}
|
||||
arch = f.Cpu.String()
|
||||
case bytes.HasPrefix(ident, []byte{0x01, 0xDF}) || bytes.HasPrefix(ident, []byte{0x01, 0xF7}):
|
||||
f, err := xcoff.NewFile(r)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unrecognized file format: %w", err)
|
||||
}
|
||||
arch = fmt.Sprintf("%d", f.FileHeader.TargetMachine)
|
||||
default:
|
||||
return "", errUnrecognizedFormat
|
||||
}
|
||||
|
||||
arch = strings.Replace(arch, "EM_", "", 1)
|
||||
arch = strings.Replace(arch, "Cpu", "", 1)
|
||||
arch = strings.ToLower(arch)
|
||||
|
||||
return arch, nil
|
||||
}
|
||||
|
||||
func getBuildSettings(settings []debug.BuildSetting) map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, s := range settings {
|
||||
m[s.Key] = s.Value
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func buildGoPkgInfo(location source.Location, mod *debug.BuildInfo, arch string) []pkg.Package {
|
||||
var pkgs []pkg.Package
|
||||
if mod == nil {
|
||||
return pkgs
|
||||
}
|
||||
|
||||
for _, dep := range mod.Deps {
|
||||
if dep == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newGoBinaryPackage(dep, mod.GoVersion, arch, location, nil))
|
||||
}
|
||||
|
||||
// NOTE(jonasagx): this use happened originally while creating unit tests. It might never
|
||||
// happen in the wild, but I kept it as a safeguard against empty modules.
|
||||
var empty debug.Module
|
||||
if mod.Main == empty {
|
||||
return pkgs
|
||||
}
|
||||
|
||||
main := makeGoMainPackage(mod, arch, location)
|
||||
pkgs = append(pkgs, main)
|
||||
|
||||
return pkgs
|
||||
}
|
||||
|
||||
@@ -1,48 +1,265 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// make will run the default make target for the given test fixture path
|
||||
func runMakeTarget(t *testing.T, fixtureName string) {
|
||||
cwd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
fixtureDir := filepath.Join(cwd, "test-fixtures/", fixtureName)
|
||||
|
||||
t.Logf("Generating Fixture in %q", fixtureDir)
|
||||
|
||||
cmd := exec.Command("make")
|
||||
cmd.Dir = fixtureDir
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
require.NoError(t, err)
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = cmd.Start()
|
||||
require.NoError(t, err)
|
||||
|
||||
show := func(label string, reader io.ReadCloser) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
for scanner.Scan() {
|
||||
t.Logf("%s: %s", label, scanner.Text())
|
||||
}
|
||||
}
|
||||
go show("out", stdout)
|
||||
go show("err", stderr)
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
// The program has exited with an exit code != 0
|
||||
|
||||
// This works on both Unix and Windows. Although package
|
||||
// syscall is generally platform dependent, WaitStatus is
|
||||
// defined for both Unix and Windows and in both cases has
|
||||
// an ExitStatus() method with the same signature.
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
if status.ExitStatus() != 0 {
|
||||
t.Fatalf("failed to generate fixture: rc=%d", status.ExitStatus())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("unable to get generate fixture result: %+v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getGOARCHFromBin(t *testing.T) {
|
||||
runMakeTarget(t, "archs")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
filepath string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "pe",
|
||||
filepath: "test-fixtures/archs/binaries/hello-win-amd64",
|
||||
// see: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
|
||||
expected: strconv.Itoa(0x8664),
|
||||
},
|
||||
{
|
||||
name: "elf-ppc64",
|
||||
filepath: "test-fixtures/archs/binaries/hello-linux-ppc64le",
|
||||
expected: "ppc64",
|
||||
},
|
||||
{
|
||||
name: "mach-o-arm64",
|
||||
filepath: "test-fixtures/archs/binaries/hello-mach-o-arm64",
|
||||
expected: "arm64",
|
||||
},
|
||||
{
|
||||
name: "linux-arm",
|
||||
filepath: "test-fixtures/archs/binaries/hello-linux-arm",
|
||||
expected: "arm",
|
||||
},
|
||||
{
|
||||
name: "xcoff-32bit",
|
||||
filepath: "internal/xcoff/testdata/gcc-ppc32-aix-dwarf2-exec",
|
||||
expected: strconv.Itoa(0x1DF),
|
||||
},
|
||||
{
|
||||
name: "xcoff-64bit",
|
||||
filepath: "internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec",
|
||||
expected: strconv.Itoa(0x1F7),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
f, err := os.Open(tt.filepath)
|
||||
require.NoError(t, err)
|
||||
arch, err := getGOARCHFromBin(f)
|
||||
require.NoError(t, err, "test name: %s", tt.name)
|
||||
assert.Equal(t, tt.expected, arch)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBuildGoPkgInfo(t *testing.T) {
|
||||
const (
|
||||
goCompiledVersion = "1.17"
|
||||
goCompiledVersion = "1.18"
|
||||
archDetails = "amd64"
|
||||
)
|
||||
buildSettings := map[string]string{
|
||||
"GOARCH": "amd64",
|
||||
"GOOS": "darwin",
|
||||
"GOAMD64": "v1",
|
||||
}
|
||||
|
||||
expectedMain := pkg.Package{
|
||||
Name: "github.com/anchore/syft",
|
||||
FoundBy: catalogerName,
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
Architecture: archDetails,
|
||||
BuildSettings: buildSettings,
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
mod string
|
||||
mod *debug.BuildInfo
|
||||
arch string
|
||||
expected []pkg.Package
|
||||
}{
|
||||
{
|
||||
name: "buildGoPkgInfo parses a blank mod string and returns no packages",
|
||||
mod: "",
|
||||
expected: make([]pkg.Package, 0),
|
||||
name: "buildGoPkgInfo parses a nil mod",
|
||||
mod: nil,
|
||||
expected: []pkg.Package(nil),
|
||||
},
|
||||
{
|
||||
name: "buildGoPkgInfo parses a populated mod string and returns packages but no source info",
|
||||
mod: `path github.com/anchore/syft mod github.com/anchore/syft (devel)
|
||||
dep github.com/adrg/xdg v0.2.1 h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=
|
||||
dep github.com/anchore/client-go v0.0.0-20210222170800-9c70f9b80bcf h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=`,
|
||||
name: "buildGoPkgInfo parses a blank mod and returns no packages",
|
||||
mod: &debug.BuildInfo{},
|
||||
expected: []pkg.Package(nil),
|
||||
},
|
||||
{
|
||||
name: "buildGoPkgInfo parses a mod without main module",
|
||||
arch: archDetails,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "github.com/adrg/xdg",
|
||||
Version: "v0.2.1",
|
||||
Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/adrg/xdg",
|
||||
FoundBy: catalogerName,
|
||||
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,
|
||||
Architecture: archDetails,
|
||||
H1Digest: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "buildGoPkgInfo parses a mod without packages",
|
||||
arch: archDetails,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{expectedMain},
|
||||
},
|
||||
{
|
||||
name: "buildGoPkgInfo parses a populated mod string and returns packages but no source info",
|
||||
arch: archDetails,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "github.com/adrg/xdg",
|
||||
Version: "v0.2.1",
|
||||
Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
|
||||
},
|
||||
{
|
||||
Path: "github.com/anchore/client-go",
|
||||
Version: "v0.0.0-20210222170800-9c70f9b80bcf",
|
||||
Sum: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "github.com/adrg/xdg",
|
||||
FoundBy: catalogerName,
|
||||
Version: "v0.2.1",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
},
|
||||
),
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
@@ -52,17 +269,18 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Name: "github.com/anchore/client-go",
|
||||
FoundBy: catalogerName,
|
||||
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,
|
||||
@@ -70,77 +288,79 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
H1Digest: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=",
|
||||
},
|
||||
},
|
||||
expectedMain,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "buildGoPkgInfo parses a populated mod string and returns packages when a replace directive exists",
|
||||
mod: `path github.com/anchore/test
|
||||
mod github.com/anchore/test (devel)
|
||||
dep golang.org/x/net v0.0.0-20211006190231-62292e806868 h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=
|
||||
dep golang.org/x/sys v0.0.0-20211006194710-c8a6f5223071 h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=
|
||||
dep golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
=> golang.org/x/term v0.0.0-20210916214954-140adaaadfaf h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=`,
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "golang.org/x/net",
|
||||
Version: "v0.0.0-20211006190231-62292e806868",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: []source.Location{
|
||||
{
|
||||
Coordinates: source.Coordinates{
|
||||
RealPath: "/a-path",
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
arch: archDetails,
|
||||
mod: &debug.BuildInfo{
|
||||
GoVersion: goCompiledVersion,
|
||||
Main: debug.Module{Path: "github.com/anchore/syft"},
|
||||
Settings: []debug.BuildSetting{
|
||||
{Key: "GOARCH", Value: archDetails},
|
||||
{Key: "GOOS", Value: "darwin"},
|
||||
{Key: "GOAMD64", Value: "v1"},
|
||||
},
|
||||
Deps: []*debug.Module{
|
||||
{
|
||||
Path: "golang.org/x/sys",
|
||||
Version: "v0.0.0-20211006194710-c8a6f5223071",
|
||||
Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
|
||||
},
|
||||
{
|
||||
Path: "golang.org/x/term",
|
||||
Version: "v0.0.0-20210927222741-03fcf44c2211",
|
||||
Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
|
||||
Replace: &debug.Module{
|
||||
Path: "golang.org/x/term",
|
||||
Version: "v0.0.0-20210916214954-140adaaadfaf",
|
||||
Sum: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=",
|
||||
},
|
||||
},
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
Architecture: archDetails,
|
||||
H1Digest: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "golang.org/x/sys",
|
||||
FoundBy: catalogerName,
|
||||
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,
|
||||
Architecture: archDetails,
|
||||
H1Digest: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
|
||||
},
|
||||
},
|
||||
H1Digest: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE="}},
|
||||
{
|
||||
Name: "golang.org/x/term",
|
||||
FoundBy: catalogerName,
|
||||
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,
|
||||
Architecture: archDetails,
|
||||
H1Digest: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=",
|
||||
},
|
||||
H1Digest: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI="},
|
||||
},
|
||||
expectedMain,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -157,30 +377,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
FileSystemID: "layer-id",
|
||||
},
|
||||
}
|
||||
pkgs := buildGoPkgInfo(location, test.mod, goCompiledVersion, archDetails)
|
||||
pkgs := buildGoPkgInfo(location, test.mod, test.arch)
|
||||
assert.Equal(t, test.expected, pkgs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseGoBin_recoversFromPanic(t *testing.T) {
|
||||
freakOut := func(file io.ReadCloser) ([]exe, error) {
|
||||
panic("baaahhh!")
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
wantPkgs []pkg.Package
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "recovers from panic",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
pkgs, err := parseGoBin(source.NewLocation("some/path"), nil, freakOut)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, pkgs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"debug/buildinfo"
|
||||
"io"
|
||||
"runtime/debug"
|
||||
|
||||
macho "github.com/anchore/go-macholibre"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
// unionReader is a single interface with all reading functions used by golang bin
|
||||
// cataloger.
|
||||
type unionReader interface {
|
||||
io.Reader
|
||||
io.ReaderAt
|
||||
io.Seeker
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// scanFile scans file to try to report the Go and module versions.
|
||||
func scanFile(reader unionReader, filename string) ([]*debug.BuildInfo, []string) {
|
||||
// NOTE: multiple readers are returned to cover universal binaries, which are files
|
||||
// with more than one binary
|
||||
readers, err := getReaders(reader)
|
||||
if err != nil {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
archs := getArchs(readers, builds)
|
||||
|
||||
return builds, archs
|
||||
}
|
||||
|
||||
// getReaders extracts one or more io.ReaderAt objects representing binaries that can be processed (multiple binaries in the case for multi-architecture binaries).
|
||||
func getReaders(f unionReader) ([]io.ReaderAt, error) {
|
||||
if macho.IsUniversalMachoBinary(f) {
|
||||
machoReaders, err := macho.ExtractReaders(f)
|
||||
if err != nil {
|
||||
log.Debugf("extracting readers: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var readers []io.ReaderAt
|
||||
for _, e := range machoReaders {
|
||||
readers = append(readers, e.Reader)
|
||||
}
|
||||
|
||||
return readers, nil
|
||||
}
|
||||
|
||||
return []io.ReaderAt{f}, nil
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
binaries/
|
||||
@@ -0,0 +1,29 @@
|
||||
DESTINATION=binaries
|
||||
|
||||
all: $(DESTINATION)/hello-mach-o-arm64 $(DESTINATION)/hello-linux-arm $(DESTINATION)/hello-linux-ppc64le $(DESTINATION)/hello-win-amd64
|
||||
|
||||
$(DESTINATION)/hello-mach-o-arm64:
|
||||
mkdir -p $(DESTINATION)
|
||||
GOARCH=arm64 GOOS=darwin ./src/build.sh $(DESTINATION)/hello-mach-o-arm64
|
||||
|
||||
$(DESTINATION)/hello-linux-arm:
|
||||
mkdir -p $(DESTINATION)
|
||||
GOARCH=arm GOOS=linux ./src/build.sh $(DESTINATION)/hello-linux-arm
|
||||
|
||||
$(DESTINATION)/hello-linux-ppc64le:
|
||||
mkdir -p $(DESTINATION)
|
||||
GOARCH=ppc64le GOOS=linux ./src/build.sh $(DESTINATION)/hello-linux-ppc64le
|
||||
|
||||
$(DESTINATION)/hello-win-amd64:
|
||||
mkdir -p $(DESTINATION)
|
||||
GOARCH=amd64 GOOS=windows ./src/build.sh $(DESTINATION)/hello-win-amd64
|
||||
|
||||
# we need a way to determine if CI should bust the test cache based on the source material
|
||||
$(DESTINATION).fingerprint: clean
|
||||
mkdir -p $(DESTINATION)
|
||||
find src -type f -exec sha256sum {} \; | sort | tee /dev/stderr | tee $(DESTINATION).fingerprint
|
||||
sha256sum $(DESTINATION).fingerprint
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(DESTINATION)/*
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
set -uxe
|
||||
|
||||
# note: this can be easily done in a 1-liner, however circle CI does NOT allow volume mounts from the host in docker executors (since they are on remote hosts, where the host files are inaccessible)
|
||||
# note: gocache override is so we can run docker build not as root in a container without permission issues
|
||||
|
||||
BINARY=$1
|
||||
CTRID=$(docker create -e GOOS="${GOOS}" -e GOARCH="${GOARCH}" -u "$(id -u):$(id -g)" -e GOCACHE=/tmp -w /src golang:1.17 go build -o main main.go)
|
||||
|
||||
function cleanup() {
|
||||
docker rm "${CTRID}"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
set +e
|
||||
|
||||
# note: pwd = parent directory (archs)
|
||||
docker cp "$(pwd)/src" "${CTRID}:/"
|
||||
docker start -a "${CTRID}"
|
||||
docker cp "${CTRID}:/src/main" "$BINARY"
|
||||
@@ -0,0 +1,3 @@
|
||||
module arch/v1
|
||||
|
||||
go 1.17
|
||||
@@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("свобода!")
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
// This code was copied from the Go std library.
|
||||
// https://github.com/golang/go/blob/master/src/cmd/go/internal/version/version.go
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package version implements the ``go version'' command.
|
||||
package golang
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
// "cmd/go/internal/base"
|
||||
)
|
||||
|
||||
/*
|
||||
var CmdVersion = &base.Command{
|
||||
UsageLine: "go version [-m] [-v] [file ...]",
|
||||
Short: "print Go version",
|
||||
Long: `Version prints the build information for Go executables.
|
||||
|
||||
Go version reports the Go version used to build each of the named
|
||||
executable files.
|
||||
|
||||
If no files are named on the command line, go version prints its own
|
||||
version information.
|
||||
|
||||
If a directory is named, go version walks that directory, recursively,
|
||||
looking for recognized Go binaries and reporting their versions.
|
||||
By default, go version does not report unrecognized files found
|
||||
during a directory scan. The -v flag causes it to report unrecognized files.
|
||||
|
||||
The -m flag causes go version to print each executable's embedded
|
||||
module version information, when available. In the output, the module
|
||||
information consists of multiple lines following the version line, each
|
||||
indented by a leading tab character.
|
||||
|
||||
See also: go doc runtime/debug.BuildInfo.
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
CmdVersion.Run = runVersion // break init cycle
|
||||
}
|
||||
|
||||
var (
|
||||
versionM = CmdVersion.Flag.Bool("m", false, "")
|
||||
versionV = CmdVersion.Flag.Bool("v", false, "")
|
||||
)
|
||||
|
||||
func runVersion(ctx context.Context, cmd *base.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
// If any of this command's flags were passed explicitly, error
|
||||
// out, because they only make sense with arguments.
|
||||
//
|
||||
// Don't error if the flags came from GOFLAGS, since that can be
|
||||
// a reasonable use case. For example, imagine GOFLAGS=-v to
|
||||
// turn "verbose mode" on for all Go commands, which should not
|
||||
// break "go version".
|
||||
var argOnlyFlag string
|
||||
if !base.InGOFLAGS("-m") && *versionM {
|
||||
argOnlyFlag = "-m"
|
||||
} else if !base.InGOFLAGS("-v") && *versionV {
|
||||
argOnlyFlag = "-v"
|
||||
}
|
||||
if argOnlyFlag != "" {
|
||||
fmt.Fprintf(os.Stderr, "go: 'go version' only accepts %s flag with arguments\n", argOnlyFlag)
|
||||
base.SetExitStatus(2)
|
||||
return
|
||||
}
|
||||
fmt.Printf("go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
return
|
||||
}
|
||||
|
||||
for _, arg := range args {
|
||||
info, err := os.Stat(arg)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
base.SetExitStatus(1)
|
||||
continue
|
||||
}
|
||||
if info.IsDir() {
|
||||
scanDir(arg)
|
||||
} else {
|
||||
scanFile(arg, info, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// scanDir scans a directory for executables to run scanFile on.
|
||||
func scanDir(dir string) {
|
||||
filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||||
if d.Type().IsRegular() || d.Type()&fs.ModeSymlink != 0 {
|
||||
info, err := d.Info()
|
||||
if err != nil {
|
||||
if *versionV {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
scanFile(path, info, *versionV)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// isExe reports whether the file should be considered executable.
|
||||
func isExe(file string, info fs.FileInfo) bool {
|
||||
if runtime.GOOS == "windows" {
|
||||
return strings.HasSuffix(strings.ToLower(file), ".exe")
|
||||
}
|
||||
return info.Mode().IsRegular() && info.Mode()&0111 != 0
|
||||
}
|
||||
|
||||
// scanFile scans file to try to report the Go and module versions.
|
||||
// If mustPrint is true, scanFile will report any error reading file.
|
||||
// Otherwise (mustPrint is false, because scanFile is being called
|
||||
// by scanDir) scanFile prints nothing for non-Go executables.
|
||||
func scanFile(file string, info fs.FileInfo, mustPrint bool) {
|
||||
if info.Mode()&fs.ModeSymlink != 0 {
|
||||
// Accept file symlinks only.
|
||||
i, err := os.Stat(file)
|
||||
if err != nil || !i.Mode().IsRegular() {
|
||||
if mustPrint {
|
||||
fmt.Fprintf(os.Stderr, "%s: symlink\n", file)
|
||||
}
|
||||
return
|
||||
}
|
||||
info = i
|
||||
}
|
||||
|
||||
if !isExe(file, info) {
|
||||
if mustPrint {
|
||||
fmt.Fprintf(os.Stderr, "%s: not executable file\n", file)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
x, err := openExe(file)
|
||||
if err != nil {
|
||||
if mustPrint {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", file, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer x.Close()
|
||||
|
||||
vers, mod := findVers(x)
|
||||
if vers == "" {
|
||||
if mustPrint {
|
||||
fmt.Fprintf(os.Stderr, "%s: go version not found\n", file)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%s: %s\n", file, vers)
|
||||
if *versionM && mod != "" {
|
||||
fmt.Printf("\t%s\n", strings.ReplaceAll(mod[:len(mod)-1], "\n", "\n\t"))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// The build info blob left by the linker is identified by
|
||||
// a 16-byte header, consisting of buildInfoMagic (14 bytes),
|
||||
// the binary's pointer size (1 byte),
|
||||
// and whether the binary is big endian (1 byte).
|
||||
var buildInfoMagic = []byte("\xff Go buildinf:")
|
||||
|
||||
// findVers finds and returns the Go version and module version information
|
||||
// in the executable x.
|
||||
func findVers(x exe) (vers, mod string) {
|
||||
// Read the first 64kB of text to find the build info blob.
|
||||
text := x.DataStart()
|
||||
data, err := x.ReadData(text, 64*1024)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for ; !bytes.HasPrefix(data, buildInfoMagic); data = data[32:] {
|
||||
if len(data) < 32 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the blob.
|
||||
ptrSize := int(data[14])
|
||||
bigEndian := data[15] != 0
|
||||
var bo binary.ByteOrder
|
||||
if bigEndian {
|
||||
bo = binary.BigEndian
|
||||
} else {
|
||||
bo = binary.LittleEndian
|
||||
}
|
||||
var readPtr func([]byte) uint64
|
||||
if ptrSize == 4 {
|
||||
readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) }
|
||||
} else {
|
||||
readPtr = bo.Uint64
|
||||
}
|
||||
vers = readString(x, ptrSize, readPtr, readPtr(data[16:]))
|
||||
if vers == "" {
|
||||
return
|
||||
}
|
||||
mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:]))
|
||||
if len(mod) >= 33 && mod[len(mod)-17] == '\n' {
|
||||
// Strip module framing.
|
||||
mod = mod[16 : len(mod)-16]
|
||||
} else {
|
||||
mod = ""
|
||||
}
|
||||
return vers, mod
|
||||
}
|
||||
|
||||
// readString returns the string at address addr in the executable x.
|
||||
func readString(x exe, ptrSize int, readPtr func([]byte) uint64, addr uint64) string {
|
||||
hdr, err := x.ReadData(addr, uint64(2*ptrSize))
|
||||
if err != nil || len(hdr) < 2*ptrSize {
|
||||
return ""
|
||||
}
|
||||
dataAddr := readPtr(hdr)
|
||||
dataLen := readPtr(hdr[ptrSize:])
|
||||
data, err := x.ReadData(dataAddr, dataLen)
|
||||
if err != nil || uint64(len(data)) < dataLen {
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package java
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/file"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
syftFile "github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/common"
|
||||
)
|
||||
@@ -34,6 +37,11 @@ var archiveFormatGlobs = []string{
|
||||
// project that we can build in CI feel free to include it
|
||||
}
|
||||
|
||||
// javaArchiveHashes are all the current hash algorithms used to calculate archive digests
|
||||
var javaArchiveHashes = []crypto.Hash{
|
||||
crypto.SHA1,
|
||||
}
|
||||
|
||||
type archiveParser struct {
|
||||
fileManifest file.ZipFileManifest
|
||||
virtualPath string
|
||||
@@ -101,6 +109,7 @@ func (j *archiveParser) parse() ([]*pkg.Package, []artifact.Relationship, error)
|
||||
}
|
||||
|
||||
// find aux packages from pom.properties/pom.xml and potentially modify the existing parentPkg
|
||||
// NOTE: we cannot generate sha1 digests from packages discovered via pom.properties/pom.xml
|
||||
auxPkgs, err := j.discoverPkgsFromAllMavenFiles(parentPkg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -135,6 +144,7 @@ func (j *archiveParser) parse() ([]*pkg.Package, []artifact.Relationship, error)
|
||||
// discoverMainPackage parses the root Java manifest used as the parent package to all discovered nested packages.
|
||||
func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
|
||||
// search and parse java manifest files
|
||||
// TODO: do we want to prefer or check for pom files over manifest here?
|
||||
manifestMatches := j.fileManifest.GlobMatch(manifestGlob)
|
||||
if len(manifestMatches) > 1 {
|
||||
return nil, fmt.Errorf("found multiple manifests in the jar: %+v", manifestMatches)
|
||||
@@ -157,6 +167,18 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
archiveCloser, err := os.Open(j.archivePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open archive path (%s): %w", j.archivePath, err)
|
||||
}
|
||||
defer archiveCloser.Close()
|
||||
|
||||
// grab and assign digest for the entire archive
|
||||
digests, err := syftFile.DigestsFromFile(archiveCloser, javaArchiveHashes)
|
||||
if err != nil {
|
||||
log.Warnf("failed to create digest for file=%q: %+v", j.archivePath, err)
|
||||
}
|
||||
|
||||
return &pkg.Package{
|
||||
Name: selectName(manifest, j.fileInfo),
|
||||
Version: selectVersion(manifest, j.fileInfo),
|
||||
@@ -164,8 +186,9 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) {
|
||||
Type: j.fileInfo.pkgType(),
|
||||
MetadataType: pkg.JavaMetadataType,
|
||||
Metadata: pkg.JavaMetadata{
|
||||
VirtualPath: j.virtualPath,
|
||||
Manifest: manifest,
|
||||
VirtualPath: j.virtualPath,
|
||||
Manifest: manifest,
|
||||
ArchiveDigests: digests,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -181,12 +204,14 @@ func (j *archiveParser) discoverPkgsFromAllMavenFiles(parentPkg *pkg.Package) ([
|
||||
|
||||
var pkgs []*pkg.Package
|
||||
|
||||
properties, err := pomPropertiesByParentPath(j.archivePath, j.fileManifest.GlobMatch(pomPropertiesGlob), j.virtualPath)
|
||||
// pom.properties
|
||||
properties, err := pomPropertiesByParentPath(j.archivePath, j.virtualPath, j.fileManifest.GlobMatch(pomPropertiesGlob))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
projects, err := pomProjectByParentPath(j.archivePath, j.fileManifest.GlobMatch(pomXMLGlob), j.virtualPath)
|
||||
// pom.xml
|
||||
projects, err := pomProjectByParentPath(j.archivePath, j.virtualPath, j.fileManifest.GlobMatch(pomXMLGlob))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -273,7 +298,7 @@ func discoverPkgsFromOpener(virtualPath, pathWithinArchive string, archiveOpener
|
||||
return nestedPkgs, nestedRelationships, nil
|
||||
}
|
||||
|
||||
func pomPropertiesByParentPath(archivePath string, extractPaths []string, virtualPath string) (map[string]pkg.PomProperties, error) {
|
||||
func pomPropertiesByParentPath(archivePath, virtualPath string, extractPaths []string) (map[string]pkg.PomProperties, error) {
|
||||
contentsOfMavenPropertiesFiles, err := file.ContentsFromZip(archivePath, extractPaths...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to extract maven files: %w", err)
|
||||
@@ -298,10 +323,11 @@ func pomPropertiesByParentPath(archivePath string, extractPaths []string, virtua
|
||||
|
||||
propertiesByParentPath[path.Dir(filePath)] = *pomProperties
|
||||
}
|
||||
|
||||
return propertiesByParentPath, nil
|
||||
}
|
||||
|
||||
func pomProjectByParentPath(archivePath string, extractPaths []string, virtualPath string) (map[string]pkg.PomProject, error) {
|
||||
func pomProjectByParentPath(archivePath, virtualPath string, extractPaths []string) (map[string]pkg.PomProject, error) {
|
||||
contentsOfMavenProjectFiles, err := file.ContentsFromZip(archivePath, extractPaths...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to extract maven files: %w", err)
|
||||
|
||||
@@ -283,6 +283,11 @@ func TestParseJar(t *testing.T) {
|
||||
metadata := a.Metadata.(pkg.JavaMetadata)
|
||||
metadata.Parent = nil
|
||||
|
||||
// redact Digest which is computed differently between CI and local
|
||||
if len(metadata.ArchiveDigests) > 0 {
|
||||
metadata.ArchiveDigests = nil
|
||||
}
|
||||
|
||||
// ignore select fields (only works for the main section)
|
||||
for _, field := range test.ignoreExtras {
|
||||
if metadata.Manifest != nil && metadata.Manifest.Main != nil {
|
||||
@@ -567,7 +572,6 @@ func TestParseNestedJar(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user