mirror of
https://github.com/anchore/syft.git
synced 2026-10-20 18:12:32 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03971ace43 | ||
|
|
07aee798b0 | ||
|
|
6cf668f749 | ||
|
|
2ec4371c95 | ||
|
|
fc4d28f365 | ||
|
|
5290dfb9c2 | ||
|
|
05611c283d | ||
|
|
934644232a | ||
|
|
641bccc79b | ||
|
|
ac94bf530c | ||
|
|
e87cfe7319 |
@@ -0,0 +1,81 @@
|
||||
name: "Bootstrap"
|
||||
description: "Bootstrap all tools and dependencies"
|
||||
inputs:
|
||||
go-version:
|
||||
description: "Go version to install"
|
||||
required: true
|
||||
default: "1.18.x"
|
||||
use-go-cache:
|
||||
description: "Restore go cache"
|
||||
required: true
|
||||
default: "true"
|
||||
cache-key-prefix:
|
||||
description: "Prefix all cache keys with this value"
|
||||
required: true
|
||||
default: "831180ac25"
|
||||
build-cache-key-prefix:
|
||||
description: "Prefix build cache key with this value"
|
||||
required: true
|
||||
default: "f8b6d31dea"
|
||||
bootstrap-apt-packages:
|
||||
description: "Space delimited list of tools to install via apt"
|
||||
default: "libxml2-utils"
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
|
||||
- name: Restore tool cache
|
||||
id: tool-cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp
|
||||
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
||||
|
||||
# note: we need to keep restoring the go mod cache before bootstrapping tools since `go install` is used in
|
||||
# some installations of project tools.
|
||||
- name: Restore go module cache
|
||||
id: go-mod-cache
|
||||
if: inputs.use-go-cache == 'true'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-
|
||||
|
||||
- name: (cache-miss) Bootstrap project tools
|
||||
shell: bash
|
||||
if: steps.tool-cache.outputs.cache-hit != 'true'
|
||||
run: make bootstrap-tools
|
||||
|
||||
- name: Restore go build cache
|
||||
id: go-cache
|
||||
if: inputs.use-go-cache == 'true'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
key: ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-
|
||||
|
||||
- name: (cache-miss) Bootstrap go dependencies
|
||||
shell: bash
|
||||
if: steps.go-mod-cache.outputs.cache-hit != 'true' && inputs.use-go-cache == 'true'
|
||||
run: make bootstrap-go
|
||||
|
||||
- name: Install apt packages
|
||||
if: inputs.bootstrap-apt-packages != ''
|
||||
shell: bash
|
||||
run: |
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt update && sudo -E apt install -y ${{ inputs.bootstrap-apt-packages }}
|
||||
|
||||
- name: Create all cache fingerprints
|
||||
shell: bash
|
||||
run: make fingerprints
|
||||
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
red=$(tput setaf 1)
|
||||
bold=$(tput bold)
|
||||
normal=$(tput sgr0)
|
||||
|
||||
# assert we are running in CI (or die!)
|
||||
if [[ -z "$CI" ]]; then
|
||||
echo "${bold}${red}This script should ONLY be run in CI. Exiting...${normal}"
|
||||
exit 1
|
||||
fi
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess
|
||||
import sys
|
||||
import shlex
|
||||
|
||||
|
||||
class bcolors:
|
||||
HEADER = '\033[95m'
|
||||
OKBLUE = '\033[94m'
|
||||
OKCYAN = '\033[96m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: coverage.py [threshold] [go-coverage-report]")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
threshold = float(sys.argv[1])
|
||||
report = sys.argv[2]
|
||||
|
||||
|
||||
args = shlex.split(f"go tool cover -func {report}")
|
||||
p = subprocess.run(args, capture_output=True, text=True)
|
||||
|
||||
percent_coverage = float(p.stdout.splitlines()[-1].split()[-1].replace("%", ""))
|
||||
print(f"{bcolors.BOLD}Coverage: {percent_coverage}%{bcolors.ENDC}")
|
||||
|
||||
if percent_coverage < threshold:
|
||||
print(f"{bcolors.BOLD}{bcolors.FAIL}Coverage below threshold of {threshold}%{bcolors.ENDC}")
|
||||
sys.exit(1)
|
||||
@@ -4,19 +4,18 @@ set -eu
|
||||
ORIGINAL_STATE_DIR=$(mktemp -d "TEMP-original-state-XXXXXXXXX")
|
||||
TIDY_STATE_DIR=$(mktemp -d "TEMP-tidy-state-XXXXXXXXX")
|
||||
|
||||
trap "cp -v ${ORIGINAL_STATE_DIR}/* ./ && rm -fR ${ORIGINAL_STATE_DIR} ${TIDY_STATE_DIR}" EXIT
|
||||
trap "cp ${ORIGINAL_STATE_DIR}/* ./ && rm -fR ${ORIGINAL_STATE_DIR} ${TIDY_STATE_DIR}" EXIT
|
||||
|
||||
echo "Capturing original state of files..."
|
||||
cp -v go.mod go.sum "${ORIGINAL_STATE_DIR}"
|
||||
# capturing original state of files...
|
||||
cp go.mod go.sum "${ORIGINAL_STATE_DIR}"
|
||||
|
||||
echo "Capturing state of go.mod and go.sum after running go mod tidy..."
|
||||
# capturing state of go.mod and go.sum after running go mod tidy...
|
||||
go mod tidy
|
||||
cp -v go.mod go.sum "${TIDY_STATE_DIR}"
|
||||
echo ""
|
||||
cp go.mod go.sum "${TIDY_STATE_DIR}"
|
||||
|
||||
set +e
|
||||
|
||||
# Detect difference between the git HEAD state and the go mod tidy state
|
||||
# detect difference between the git HEAD state and the go mod tidy state
|
||||
DIFF_MOD=$(diff -u "${ORIGINAL_STATE_DIR}/go.mod" "${TIDY_STATE_DIR}/go.mod")
|
||||
DIFF_SUM=$(diff -u "${ORIGINAL_STATE_DIR}/go.sum" "${TIDY_STATE_DIR}/go.sum")
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
name: "Benchmark testing"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
||||
Benchmark-Test:
|
||||
name: "Benchmark tests"
|
||||
runs-on: ubuntu-20.04
|
||||
# note: we want benchmarks to run on pull_request events in order to publish results to a sticky comment, and
|
||||
# we also want to run on push such that merges to main are recorded to the cache. For this reason we don't filter
|
||||
# the job by event.
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
- name: Restore base benchmark result
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: test/results/benchmark-main.txt
|
||||
# use base sha for PR or new commit hash for main push in benchmark result key
|
||||
key: ${{ runner.os }}-bench-${{ (github.event.pull_request.base.sha != github.event.after) && github.event.pull_request.base.sha || github.event.after }}
|
||||
|
||||
- name: Run benchmark tests
|
||||
id: benchmark
|
||||
run: |
|
||||
REF_NAME=${GITHUB_REF##*/} make benchmark
|
||||
OUTPUT=$(make show-benchstat)
|
||||
OUTPUT="${OUTPUT//'%'/'%25'}" # URL encode all '%' characters
|
||||
OUTPUT="${OUTPUT//$'\n'/'%0A'}" # URL encode all '\n' characters
|
||||
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
|
||||
echo "::set-output name=result::$OUTPUT"
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: benchmark-test-results
|
||||
path: test/results/**/*
|
||||
|
||||
- name: Update PR benchmark results comment
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
continue-on-error: true
|
||||
with:
|
||||
header: benchmark
|
||||
message: |
|
||||
### Benchmark Test Results
|
||||
|
||||
<details>
|
||||
<summary>Benchmark results from the latest changes vs base branch</summary>
|
||||
|
||||
```
|
||||
${{ steps.benchmark.outputs.result }}
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
environment: release
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# we don't want to release commits that have been pushed and tagged, but not necessarily merged onto main
|
||||
- name: Ensure tagged commit is on main
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
git merge-base --is-ancestor ${GITHUB_REF##*/} origin/main && echo "${GITHUB_REF##*/} is a commit on main!"
|
||||
|
||||
- name: Check static analysis results
|
||||
uses: fountainhead/action-wait-for-check@v1.0.0
|
||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
||||
id: static-analysis
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Check unit test results
|
||||
uses: fountainhead/action-wait-for-check@v1.0.0
|
||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
||||
id: unit
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Check integration test results
|
||||
uses: fountainhead/action-wait-for-check@v1.0.0
|
||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
||||
id: integration
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Check acceptance test results (linux)
|
||||
uses: fountainhead/action-wait-for-check@v1.0.0
|
||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
||||
id: acceptance-linux
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Check acceptance test results (mac)
|
||||
uses: fountainhead/action-wait-for-check@v1.0.0
|
||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
||||
id: acceptance-mac
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Check cli test results (linux)
|
||||
uses: fountainhead/action-wait-for-check@v1.0.0
|
||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
||||
id: cli-linux
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -97,33 +97,15 @@ jobs:
|
||||
contents: write
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Restore tool cache
|
||||
id: tool-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp
|
||||
key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
||||
|
||||
- name: Restore go cache
|
||||
id: go-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-${{ env.GO_VERSION }}-
|
||||
|
||||
- name: (cache-miss) Bootstrap all project dependencies
|
||||
if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true'
|
||||
run: make bootstrap
|
||||
# use the same cache we used for building snapshots
|
||||
build-cache-key-prefix: "snapshot"
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
|
||||
@@ -39,10 +39,6 @@ jobs:
|
||||
sed -r -i -e 's/^(YAJSV_VERSION = ).*/\1'${YAJSV_LATEST_VERSION}'/' Makefile
|
||||
sed -r -i -e 's/^(COSIGN_VERSION = ).*/\1'${COSIGN_LATEST_VERSION}'/' Makefile
|
||||
|
||||
# update cosign in go.mod as well
|
||||
go get github.com/sigstore/cosign@$COSIGN_LATEST_VERSION
|
||||
go mod tidy
|
||||
|
||||
# export the versions for use with create-pull-request
|
||||
echo "::set-output name=GOLANGCILINT::$GOLANGCILINT_LATEST_VERSION"
|
||||
echo "::set-output name=BOUNCER::$BOUNCER_LATEST_VERSION"
|
||||
@@ -76,4 +72,4 @@ jobs:
|
||||
- [yajsv ${{ steps.latest-versions.outputs.YAJSV }}](https://github.com/neilpa/yajsv/releases/tag/${{ steps.latest-versions.outputs.YAJSV }})
|
||||
- [cosign ${{ steps.latest-versions.outputs.COSIGN }}](https://github.com/sigstore/cosign/releases/tag/${{ steps.latest-versions.outputs.COSIGN }})
|
||||
This is an auto-generated pull request to update all of the bootstrap tools to the latest versions.
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
name: "Validations"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.18.x"
|
||||
GO_STABLE_VERSION: true
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -17,100 +14,42 @@ jobs:
|
||||
name: "Static analysis"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Restore tool cache
|
||||
id: tool-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp
|
||||
key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
||||
|
||||
- name: Restore go cache
|
||||
id: go-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-${{ env.GO_VERSION }}-
|
||||
|
||||
- name: (cache-miss) Bootstrap all project dependencies
|
||||
if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true'
|
||||
run: make bootstrap
|
||||
|
||||
- name: Bootstrap CI environment dependencies
|
||||
run: make ci-bootstrap
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
- name: Run static analysis
|
||||
run: make static-analysis
|
||||
|
||||
|
||||
Unit-Test:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Unit tests"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Restore tool cache
|
||||
id: tool-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp
|
||||
key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
||||
|
||||
- name: Restore go cache
|
||||
id: go-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-${{ env.GO_VERSION }}-
|
||||
|
||||
- name: (cache-miss) Bootstrap all project dependencies
|
||||
if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true'
|
||||
run: make bootstrap
|
||||
|
||||
- name: Bootstrap CI environment dependencies
|
||||
run: make ci-bootstrap
|
||||
|
||||
- name: Build cache key for java test-fixture blobs (for unit tests)
|
||||
run: make java-packages-fingerprint
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
- name: Restore Java test-fixture cache
|
||||
id: unit-java-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
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 rpm test-fixture blobs (for unit tests)
|
||||
run: make rpm-binaries-fingerprint
|
||||
|
||||
- name: Restore RPM test-fixture cache
|
||||
id: unit-rpm-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: syft/pkg/cataloger/rpm/test-fixtures/rpms
|
||||
key: ${{ runner.os }}-unit-rpm-cache-${{ hashFiles( 'syft/pkg/cataloger/rpm/test-fixtures/rpms.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
|
||||
- name: Restore go binary test-fixture cache
|
||||
id: unit-go-binary-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
uses: actions/cache@v3
|
||||
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' ) }}
|
||||
@@ -118,54 +57,22 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: make unit
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: unit-test-results
|
||||
path: test/results/**/*
|
||||
|
||||
Integration-Test:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Integration tests"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Restore tool cache
|
||||
id: tool-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp
|
||||
key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
||||
|
||||
- name: Restore go cache
|
||||
id: go-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-${{ env.GO_VERSION }}-
|
||||
|
||||
- name: (cache-miss) Bootstrap all project dependencies
|
||||
if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true'
|
||||
run: make bootstrap
|
||||
|
||||
- name: Bootstrap CI environment dependencies
|
||||
run: make ci-bootstrap
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
- name: Validate syft output against the CycloneDX schema
|
||||
run: make validate-cyclonedx-schema
|
||||
|
||||
- name: Build key for tar cache
|
||||
run: make integration-fingerprint
|
||||
|
||||
- name: Restore integration test cache
|
||||
uses: actions/cache@v2.1.3
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/test/integration/test-fixtures/cache
|
||||
key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('test/integration/test-fixtures/cache.fingerprint') }}
|
||||
@@ -173,123 +80,35 @@ jobs:
|
||||
- name: Run integration tests
|
||||
run: make integration
|
||||
|
||||
Benchmark-Test:
|
||||
name: "Benchmark tests"
|
||||
runs-on: ubuntu-20.04
|
||||
# note: we want benchmarks to run on pull_request events in order to publish results to a sticky comment, and
|
||||
# we also want to run on push such that merges to main are recorded to the cache. For this reason we don't filter
|
||||
# the job by event.
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Restore tool cache
|
||||
id: tool-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp
|
||||
key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
||||
|
||||
- name: Restore go cache
|
||||
id: go-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-${{ env.GO_VERSION }}-
|
||||
|
||||
- name: (cache-miss) Bootstrap all project dependencies
|
||||
if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true'
|
||||
run: make bootstrap
|
||||
|
||||
- name: Bootstrap CI environment dependencies
|
||||
run: make ci-bootstrap
|
||||
|
||||
- name: Restore base benchmark result
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: test/results/benchmark-main.txt
|
||||
# use base sha for PR or new commit hash for main push in benchmark result key
|
||||
key: ${{ runner.os }}-bench-${{ (github.event.pull_request.base.sha != github.event.after) && github.event.pull_request.base.sha || github.event.after }}
|
||||
|
||||
- name: Run benchmark tests
|
||||
id: benchmark
|
||||
run: |
|
||||
REF_NAME=${GITHUB_REF##*/} make benchmark
|
||||
OUTPUT=$(make show-benchstat)
|
||||
OUTPUT="${OUTPUT//'%'/'%25'}" # URL encode all '%' characters
|
||||
OUTPUT="${OUTPUT//$'\n'/'%0A'}" # URL encode all '\n' characters
|
||||
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
|
||||
echo "::set-output name=result::$OUTPUT"
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: benchmark-test-results
|
||||
path: test/results/**/*
|
||||
|
||||
- name: Update PR benchmark results comment
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
continue-on-error: true
|
||||
with:
|
||||
header: benchmark
|
||||
message: |
|
||||
### Benchmark Test Results
|
||||
|
||||
<details>
|
||||
<summary>Benchmark results from the latest changes vs base branch</summary>
|
||||
|
||||
```
|
||||
${{ steps.benchmark.outputs.result }}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Build-Snapshot-Artifacts:
|
||||
name: "Build snapshot artifacts"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Restore tool cache
|
||||
id: tool-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp
|
||||
key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
||||
|
||||
- name: Restore go cache
|
||||
id: go-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-${{ env.GO_VERSION }}-
|
||||
|
||||
- name: (cache-miss) Bootstrap all project dependencies
|
||||
if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true'
|
||||
run: make bootstrap
|
||||
# why have another build cache key? We don't want unit/integration/etc test build caches to replace
|
||||
# the snapshot build cache, which includes builds for all OSs and architectures. As long as this key is
|
||||
# unique from the build-cache-key-prefix in other CI jobs, we should be fine.
|
||||
#
|
||||
# note: ideally this value should match what is used in release (just to help with build times).
|
||||
build-cache-key-prefix: "snapshot"
|
||||
bootstrap-apt-packages: ""
|
||||
|
||||
- name: Build snapshot artifacts
|
||||
run: make snapshot
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
|
||||
# see https://github.com/actions/upload-artifact/issues/199 for more info
|
||||
- name: Upload snapshot artifacts
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: snapshot/**/*
|
||||
path: snapshot
|
||||
key: snapshot-build-${{ github.run_id }}
|
||||
|
||||
|
||||
Acceptance-Linux:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
@@ -297,22 +116,20 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- name: Download snapshot build
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: snapshot
|
||||
key: snapshot-build-${{ github.run_id }}
|
||||
|
||||
- name: Run comparison tests (Linux)
|
||||
run: make compare-linux
|
||||
|
||||
- name: Build key for image cache
|
||||
run: make install-fingerprint
|
||||
|
||||
- name: Restore install.sh test image cache
|
||||
id: install-test-image-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/test/install/cache
|
||||
key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }}
|
||||
@@ -335,16 +152,17 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- name: Download snapshot build
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: snapshot
|
||||
key: snapshot-build-${{ github.run_id }}
|
||||
|
||||
- name: Restore docker image cache for compare testing
|
||||
id: mac-compare-testing-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: image.tar
|
||||
key: ${{ runner.os }}-${{ hashFiles('test/compare/mac.sh') }}
|
||||
@@ -355,52 +173,29 @@ jobs:
|
||||
- name: Run install.sh tests (Mac)
|
||||
run: make install-test-ci-mac
|
||||
|
||||
|
||||
Cli-Linux:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "CLI tests (Linux)"
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
- name: Restore go cache
|
||||
id: go-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-${{ env.GO_VERSION }}-
|
||||
|
||||
- name: Restore tool cache
|
||||
id: tool-cache
|
||||
uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp
|
||||
key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
||||
|
||||
- name: (cache-miss) Bootstrap all project dependencies
|
||||
if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true'
|
||||
run: make bootstrap
|
||||
|
||||
- name: Build key for tar cache
|
||||
run: make cli-fingerprint
|
||||
|
||||
- name: Restore CLI test cache
|
||||
uses: actions/cache@v2.1.3
|
||||
- name: Restore CLI test-fixture cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/test/cli/test-fixtures/cache
|
||||
key: ${{ runner.os }}-cli-test-cache-${{ hashFiles('test/cli/test-fixtures/cache.fingerprint') }}
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- name: Download snapshot build
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
name: artifacts
|
||||
path: snapshot
|
||||
key: snapshot-build-${{ github.run_id }}
|
||||
|
||||
- name: Run CLI Tests (Linux)
|
||||
run: make cli
|
||||
|
||||
+6
-2
@@ -1,9 +1,12 @@
|
||||
# TODO: enable this when we have coverage on docstring comments
|
||||
#issues:
|
||||
issues:
|
||||
max-same-issues: 25
|
||||
|
||||
# TODO: enable this when we have coverage on docstring comments
|
||||
# # The list of ids of default excludes to include or disable.
|
||||
# include:
|
||||
# - EXC0002 # disable excluding of issues about comments from golint
|
||||
|
||||
|
||||
linters:
|
||||
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
|
||||
disable-all: true
|
||||
@@ -15,6 +18,7 @@ linters:
|
||||
- errcheck
|
||||
- errorlint
|
||||
- exportloopref
|
||||
- forcetypeassert
|
||||
- funlen
|
||||
- gocognit
|
||||
- goconst
|
||||
|
||||
+1
-4
@@ -5,6 +5,7 @@ release:
|
||||
env:
|
||||
# required to support multi architecture docker builds
|
||||
- DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
- CGO_ENABLED=0
|
||||
|
||||
builds:
|
||||
- id: linux-build
|
||||
@@ -19,8 +20,6 @@ builds:
|
||||
- s390x
|
||||
# set the modified timestamp on the output binary to the git timestamp to ensure a reproducible build
|
||||
mod_timestamp: &build-timestamp '{{ .CommitTimestamp }}'
|
||||
env: &build-env
|
||||
- CGO_ENABLED=0
|
||||
ldflags: &build-ldflags |
|
||||
-w
|
||||
-s
|
||||
@@ -39,7 +38,6 @@ builds:
|
||||
- amd64
|
||||
- arm64
|
||||
mod_timestamp: *build-timestamp
|
||||
env: *build-env
|
||||
ldflags: *build-ldflags
|
||||
hooks:
|
||||
post:
|
||||
@@ -55,7 +53,6 @@ builds:
|
||||
goarch:
|
||||
- amd64
|
||||
mod_timestamp: *build-timestamp
|
||||
env: *build-env
|
||||
ldflags: *build-ldflags
|
||||
|
||||
archives:
|
||||
|
||||
+22
-40
@@ -15,37 +15,16 @@ After cloning the following step can help you get setup:
|
||||
- this command `go run cmd/syft/main.go alpine:latest` will compile and run syft against `alpine:latest`
|
||||
5. view the README or syft help output for more output options
|
||||
|
||||
#### Make output
|
||||
The main make tasks for common static analysis and testing are `lint`, `lint-fix`, `unit`, `integration`, and `cli`.
|
||||
```
|
||||
all Run all linux-based checks (linting, license check, unit, integration, and linux compare tests)
|
||||
benchmark Run benchmark tests and compare against the baseline (if available)
|
||||
bootstrap Download and install all tooling dependencies (+ prep tooling in the ./tmp dir)
|
||||
build Build release snapshot binaries and packages
|
||||
check-licenses Ensure transitive dependencies are compliant with the current license policy
|
||||
clean-test-image-cache Clean test image cache
|
||||
clean Remove previous builds, result reports, and test cache
|
||||
cli Run CLI tests
|
||||
compare-linux Run compare tests on build snapshot binaries and packages (Linux)
|
||||
compare-mac Run compare tests on build snapshot binaries and packages (Mac)
|
||||
generate-json-schema Generate a new json schema
|
||||
generate-license-list Generate an updated spdx license list
|
||||
help Display this help
|
||||
integration Run integration tests
|
||||
lint-fix Auto-format all source code + run golangci lint fixers
|
||||
lint Run gofmt + golangci lint checks
|
||||
show-test-image-cache Show all docker and image tar cache
|
||||
show-test-snapshots Show all test snapshots
|
||||
snapshot-with-signing Build snapshot release binaries and packages (with dummy signing)
|
||||
test Run all tests (currently unit, integration, linux compare, and cli tests)
|
||||
unit Run unit tests (with coverage)
|
||||
```
|
||||
|
||||
See `make help` for all the current make tasks.
|
||||
|
||||
## Architecture
|
||||
|
||||
Syft is used to generate a Software Bill of Materials (SBOM) from different kinds of input.
|
||||
|
||||
### Code organization for the cmd package
|
||||
|
||||
Syft's entrypoint can be found in the `cmd` package at `cmd/syft/main.go`. `main.go` builds a new syft `cli` via `cli.New()`
|
||||
and then executes the `cli` via `cli.Execute()`. The `cli` package is responsible for parsing command line arguments,
|
||||
setting up the application context and configuration, and executing the application. Each of syft's commands
|
||||
@@ -55,23 +34,24 @@ They are registered in `syft/cli/commands/go`.
|
||||
.
|
||||
└── syft/
|
||||
├── cli/
|
||||
│ ├── attest/
|
||||
│ ├── attest.go
|
||||
│ ├── commands.go
|
||||
│ ├── completion.go
|
||||
│ ├── convert/
|
||||
│ ├── convert.go
|
||||
│ ├── eventloop/
|
||||
│ ├── options/
|
||||
│ ├── packages/
|
||||
│ ├── packages.go
|
||||
│ ├── poweruser/
|
||||
│ ├── poweruser.go
|
||||
│ └── version.go
|
||||
│ ├── attest/
|
||||
│ ├── attest.go
|
||||
│ ├── commands.go
|
||||
│ ├── completion.go
|
||||
│ ├── convert/
|
||||
│ ├── convert.go
|
||||
│ ├── eventloop/
|
||||
│ ├── options/
|
||||
│ ├── packages/
|
||||
│ ├── packages.go
|
||||
│ ├── poweruser/
|
||||
│ ├── poweruser.go
|
||||
│ └── version.go
|
||||
└── main.go
|
||||
```
|
||||
|
||||
#### Execution flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant main as cmd/syft/main
|
||||
@@ -100,19 +80,19 @@ sequenceDiagram
|
||||
Syft's core library (see, exported) functionality is implemented in the `syft` package. The `syft` package is responsible for organizing the core
|
||||
SBOM data model, it's translated output formats, and the core SBOM generation logic.
|
||||
|
||||
#### Organization and design notes for the syft library
|
||||
- analysis creates a static SBOM which can be encoded and decoded
|
||||
- format objects, should strive to not add or enrich data in encoding that could otherwise be done during analysis
|
||||
- package catalogers and their organization can be viewed/added to the `syft/pkg/cataloger` package
|
||||
- file catalogers and their organization can be viewed/added to the `syft/file` package
|
||||
- The source package provides an abstraction to allow a user to loosely define a data source that can be cataloged
|
||||
- Logging Abstraction ...
|
||||
|
||||
#### Code example of syft as a library
|
||||
Here is a gist of using syft as a library to generate a SBOM from a docker image: [link](https://gist.github.com/wagoodman/57ed59a6d57600c23913071b8470175b).
|
||||
|
||||
Here is a gist of using syft as a library to generate a SBOM for a docker image: [link](https://gist.github.com/wagoodman/57ed59a6d57600c23913071b8470175b).
|
||||
The execution flow for the example is detailed below.
|
||||
|
||||
#### Execution flow examples for the syft library
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant source as source.New(ubuntu:latest)
|
||||
@@ -135,12 +115,14 @@ sequenceDiagram
|
||||
### Syft Catalogers
|
||||
|
||||
##### Summary
|
||||
|
||||
Catalogers are the way in which syft is able to identify and construct packages given some amount of source metadata.
|
||||
For example, Syft can locate and process `package-lock.json` files when performing filesystem scans.
|
||||
See: [how to specify file globs](https://github.com/anchore/syft/blob/main/syft/pkg/cataloger/javascript/cataloger.go#L16-L21)
|
||||
and an implementation of the [package-lock.json parser](https://github.com/anchore/syft/blob/main/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) fora quick review.
|
||||
|
||||
#### Building a new Cataloger
|
||||
|
||||
Catalogers must fulfill the interface [found here](https://github.com/anchore/syft/blob/main/syft/pkg/cataloger.go).
|
||||
This means that when building a new cataloger, the new struct must implement both method signatures of `Catalog` and `Name`.
|
||||
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
BIN = syft
|
||||
VERSION=$(shell git describe --dirty --always --tags)
|
||||
TEMPDIR = ./.tmp
|
||||
BIN := syft
|
||||
TEMP_DIR := ./.tmp
|
||||
|
||||
# commands and versions
|
||||
LINTCMD = $(TEMPDIR)/golangci-lint run --tests=false
|
||||
GOIMPORTS_CMD = $(TEMPDIR)/gosimports -local github.com/anchore
|
||||
RELEASE_CMD=$(TEMPDIR)/goreleaser release --rm-dist
|
||||
SNAPSHOT_CMD=$(RELEASE_CMD) --skip-publish --snapshot
|
||||
# Command templates #################################
|
||||
LINT_CMD := $(TEMP_DIR)/golangci-lint run --tests=false
|
||||
GOIMPORTS_CMD := $(TEMP_DIR)/gosimports -local github.com/anchore
|
||||
RELEASE_CMD := $(TEMP_DIR)/goreleaser release --rm-dist
|
||||
SNAPSHOT_CMD := $(RELEASE_CMD) --skip-publish --skip-sign --snapshot
|
||||
|
||||
# tool versions
|
||||
GOLANGCILINT_VERSION = v1.50.1
|
||||
GOSIMPORTS_VERSION = v0.3.5
|
||||
BOUNCER_VERSION = v0.4.0
|
||||
CHRONICLE_VERSION = v0.4.2
|
||||
GORELEASER_VERSION = v1.14.1
|
||||
YAJSV_VERSION = v1.4.1
|
||||
COSIGN_VERSION = v1.13.1
|
||||
QUILL_VERSION = v0.2.0
|
||||
# Tool versions #################################
|
||||
GOLANGCILINT_VERSION := v1.50.1
|
||||
GOSIMPORTS_VERSION := v0.3.5
|
||||
BOUNCER_VERSION := v0.4.0
|
||||
CHRONICLE_VERSION := v0.4.2
|
||||
GORELEASER_VERSION := v1.14.1
|
||||
YAJSV_VERSION := v1.4.1
|
||||
COSIGN_VERSION := v1.13.1
|
||||
QUILL_VERSION := v0.2.0
|
||||
|
||||
# formatting variables
|
||||
# Formatting variables #################################
|
||||
BOLD := $(shell tput -T linux bold)
|
||||
PURPLE := $(shell tput -T linux setaf 5)
|
||||
GREEN := $(shell tput -T linux setaf 2)
|
||||
@@ -28,55 +27,23 @@ RESET := $(shell tput -T linux sgr0)
|
||||
TITLE := $(BOLD)$(PURPLE)
|
||||
SUCCESS := $(BOLD)$(GREEN)
|
||||
|
||||
# test variables
|
||||
RESULTSDIR = test/results
|
||||
COMPARE_DIR = ./test/compare
|
||||
COMPARE_TEST_IMAGE = centos:8.2.2004
|
||||
COVER_REPORT = $(RESULTSDIR)/unit-coverage-details.txt
|
||||
COVER_TOTAL = $(RESULTSDIR)/unit-coverage-summary.txt
|
||||
# the quality gate lower threshold for unit test total % coverage (by function statements)
|
||||
COVERAGE_THRESHOLD := 62
|
||||
# Test variables #################################
|
||||
COMPARE_DIR := ./test/compare
|
||||
COMPARE_TEST_IMAGE := centos:8.2.2004
|
||||
COVERAGE_THRESHOLD := 62 # the quality gate lower threshold for unit test total % coverage (by function statements)
|
||||
|
||||
# CI cache busting values; change these if you want CI to not use previous stored cache
|
||||
INTEGRATION_CACHE_BUSTER="894d8ca"
|
||||
CLI_CACHE_BUSTER="e5cdfd8"
|
||||
BOOTSTRAP_CACHE="c7afb99ad"
|
||||
|
||||
## Build variables
|
||||
DISTDIR=./dist
|
||||
SNAPSHOTDIR=./snapshot
|
||||
OS=$(shell uname | tr '[:upper:]' '[:lower:]')
|
||||
SNAPSHOT_BIN=$(realpath $(shell pwd)/$(SNAPSHOTDIR)/$(OS)-build_$(OS)_amd64_v1/$(BIN))
|
||||
|
||||
## Variable assertions
|
||||
ifndef TEMPDIR
|
||||
$(error TEMPDIR is not set)
|
||||
endif
|
||||
|
||||
ifndef RESULTSDIR
|
||||
$(error RESULTSDIR is not set)
|
||||
endif
|
||||
|
||||
ifndef COMPARE_DIR
|
||||
$(error COMPARE_DIR is not set)
|
||||
endif
|
||||
|
||||
ifndef DISTDIR
|
||||
$(error DISTDIR is not set)
|
||||
endif
|
||||
|
||||
ifndef SNAPSHOTDIR
|
||||
$(error SNAPSHOTDIR is not set)
|
||||
endif
|
||||
## Build variables #################################
|
||||
VERSION := $(shell git describe --dirty --always --tags)
|
||||
DIST_DIR := ./dist
|
||||
SNAPSHOT_DIR := ./snapshot
|
||||
CHANGELOG := CHANGELOG.md
|
||||
OS := $(shell uname | tr '[:upper:]' '[:lower:]')
|
||||
SNAPSHOT_BIN := $(realpath $(shell pwd)/$(SNAPSHOT_DIR)/$(OS)-build_$(OS)_amd64_v1/$(BIN))
|
||||
|
||||
ifndef VERSION
|
||||
$(error VERSION is not set)
|
||||
endif
|
||||
|
||||
ifndef REF_NAME
|
||||
REF_NAME = $(VERSION)
|
||||
endif
|
||||
|
||||
define title
|
||||
@printf '$(TITLE)$(1)$(RESET)\n'
|
||||
endef
|
||||
@@ -89,65 +56,58 @@ define safe_rm_rf_children
|
||||
bash -c 'test -z "$(1)" && false || rm -rf $(1)/*'
|
||||
endef
|
||||
|
||||
## Default Task
|
||||
.DEFAULT_GOAL:=help
|
||||
|
||||
## Tasks
|
||||
|
||||
.PHONY: all
|
||||
all: clean static-analysis test ## Run all linux-based checks (linting, license check, unit, integration, and linux compare tests)
|
||||
all: static-analysis test ## Run all linux-based checks (linting, license check, unit, integration, and linux compare tests)
|
||||
@printf '$(SUCCESS)All checks pass!$(RESET)\n'
|
||||
|
||||
.PHONY: static-analysis
|
||||
static-analysis: check-go-mod-tidy lint check-licenses check-json-schema-drift ## Run all static analysis checks
|
||||
|
||||
.PHONY: test
|
||||
test: unit validate-cyclonedx-schema integration benchmark compare-linux cli ## Run all tests (currently unit, integration, linux compare, and cli tests)
|
||||
test: unit integration validate-cyclonedx-schema benchmark cli ## Run all tests (currently unit, integration, linux compare, and cli tests)
|
||||
|
||||
.PHONY: ci-bootstrap
|
||||
ci-bootstrap:
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt update && sudo -E apt install -y bc jq libxml2-utils
|
||||
|
||||
.PHONY:
|
||||
ci-bootstrap-mac:
|
||||
github_changelog_generator --version || sudo gem install github_changelog_generator
|
||||
## Bootstrapping targets #################################
|
||||
|
||||
$(RESULTSDIR):
|
||||
mkdir -p $(RESULTSDIR)
|
||||
|
||||
$(TEMPDIR):
|
||||
mkdir -p $(TEMPDIR)
|
||||
.PHONY: bootstrap
|
||||
bootstrap: $(TEMP_DIR) bootstrap-go bootstrap-tools ## Download and install all tooling dependencies (+ prep tooling in the ./tmp dir)
|
||||
$(call title,Bootstrapping dependencies)
|
||||
|
||||
.PHONY: bootstrap-tools
|
||||
bootstrap-tools: $(TEMPDIR)
|
||||
curl -sSfL https://raw.githubusercontent.com/anchore/quill/main/install.sh | sh -s -- -b $(TEMPDIR)/ $(QUILL_VERSION)
|
||||
GO111MODULE=off GOBIN=$(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)/ $(GOLANGCILINT_VERSION)
|
||||
curl -sSfL https://raw.githubusercontent.com/wagoodman/go-bouncer/master/bouncer.sh | sh -s -- -b $(TEMPDIR)/ $(BOUNCER_VERSION)
|
||||
curl -sSfL https://raw.githubusercontent.com/anchore/chronicle/main/install.sh | sh -s -- -b $(TEMPDIR)/ $(CHRONICLE_VERSION)
|
||||
.github/scripts/goreleaser-install.sh -d -b $(TEMPDIR)/ $(GORELEASER_VERSION)
|
||||
bootstrap-tools: $(TEMP_DIR)
|
||||
curl -sSfL https://raw.githubusercontent.com/anchore/quill/main/install.sh | sh -s -- -b $(TEMP_DIR)/ $(QUILL_VERSION)
|
||||
GO111MODULE=off GOBIN=$(realpath $(TEMP_DIR)) go get -u golang.org/x/perf/cmd/benchstat
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TEMP_DIR)/ $(GOLANGCILINT_VERSION)
|
||||
curl -sSfL https://raw.githubusercontent.com/wagoodman/go-bouncer/master/bouncer.sh | sh -s -- -b $(TEMP_DIR)/ $(BOUNCER_VERSION)
|
||||
curl -sSfL https://raw.githubusercontent.com/anchore/chronicle/main/install.sh | sh -s -- -b $(TEMP_DIR)/ $(CHRONICLE_VERSION)
|
||||
.github/scripts/goreleaser-install.sh -d -b $(TEMP_DIR)/ $(GORELEASER_VERSION)
|
||||
# the only difference between goimports and gosimports is that gosimports removes extra whitespace between import blocks (see https://github.com/golang/go/issues/20818)
|
||||
GOBIN="$(realpath $(TEMPDIR))" go install github.com/rinchsan/gosimports/cmd/gosimports@$(GOSIMPORTS_VERSION)
|
||||
GOBIN="$(realpath $(TEMPDIR))" go install github.com/neilpa/yajsv@$(YAJSV_VERSION)
|
||||
GOBIN="$(realpath $(TEMPDIR))" go install github.com/sigstore/cosign/cmd/cosign@$(COSIGN_VERSION)
|
||||
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/rinchsan/gosimports/cmd/gosimports@$(GOSIMPORTS_VERSION)
|
||||
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/neilpa/yajsv@$(YAJSV_VERSION)
|
||||
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/sigstore/cosign/cmd/cosign@$(COSIGN_VERSION)
|
||||
|
||||
.PHONY: bootstrap-go
|
||||
bootstrap-go:
|
||||
go mod download
|
||||
|
||||
.PHONY: bootstrap
|
||||
bootstrap: $(RESULTSDIR) bootstrap-go bootstrap-tools ## Download and install all tooling dependencies (+ prep tooling in the ./tmp dir)
|
||||
$(call title,Bootstrapping dependencies)
|
||||
$(TEMP_DIR):
|
||||
mkdir -p $(TEMP_DIR)
|
||||
|
||||
.PHONY: static-analysis
|
||||
static-analysis: check-go-mod-tidy check-licenses lint
|
||||
|
||||
## Static analysis targets #################################
|
||||
|
||||
.PHONY: lint
|
||||
lint: ## Run gofmt + golangci lint checks
|
||||
lint: ## Run gofmt + golangci lint checks
|
||||
$(call title,Running linters)
|
||||
# ensure there are no go fmt differences
|
||||
@printf "files with gofmt issues: [$(shell gofmt -l -s .)]\n"
|
||||
@test -z "$(shell gofmt -l -s .)"
|
||||
|
||||
# run all golangci-lint rules
|
||||
$(LINTCMD)
|
||||
$(LINT_CMD)
|
||||
@[ -z "$(shell $(GOIMPORTS_CMD) -d .)" ] || (echo "goimports needs to be fixed" && false)
|
||||
|
||||
# go tooling does not play well with certain filename characters, ensure the common cases don't result in future "go get" failures
|
||||
@@ -155,243 +115,107 @@ lint: ## Run gofmt + golangci lint checks
|
||||
@bash -c "[[ '$(MALFORMED_FILENAMES)' == '' ]] || (printf '\nfound unsupported filename characters:\n$(MALFORMED_FILENAMES)\n\n' && false)"
|
||||
|
||||
.PHONY: lint-fix
|
||||
lint-fix: ## Auto-format all source code + run golangci lint fixers
|
||||
lint-fix: ## Auto-format all source code + run golangci lint fixers
|
||||
$(call title,Running lint fixers)
|
||||
gofmt -w -s .
|
||||
$(GOIMPORTS_CMD) -w .
|
||||
$(LINTCMD) --fix
|
||||
$(LINT_CMD) --fix
|
||||
go mod tidy
|
||||
|
||||
.PHONY: check-licenses
|
||||
check-licenses: ## Ensure transitive dependencies are compliant with the current license policy
|
||||
$(TEMPDIR)/bouncer check ./...
|
||||
check-licenses: ## Ensure transitive dependencies are compliant with the current license policy
|
||||
$(call title,Checking for license compliance)
|
||||
$(TEMP_DIR)/bouncer check ./...
|
||||
|
||||
check-go-mod-tidy:
|
||||
@ .github/scripts/go-mod-tidy-check.sh && echo "go.mod and go.sum are tidy!"
|
||||
|
||||
check-json-schema-drift:
|
||||
$(call title,Ensure there is no drift between the JSON schema and the code)
|
||||
@git diff-index --quiet HEAD -- || (echo "there are uncommitted changes, please commit them before running this check" && false)
|
||||
@make generate-json-schema || (echo "$(RED)$(BOLD)JSON schema drift detected!$(RESET)" && false)
|
||||
@git diff-index --quiet HEAD -- || (echo "$(RED)$(BOLD)JSON schema drift detected!$(RESET)" && false)
|
||||
|
||||
|
||||
## Testing targets #################################
|
||||
|
||||
.PHONY: unit
|
||||
unit: $(TEMP_DIR) fixtures ## Run unit tests (with coverage)
|
||||
$(call title,Running unit tests)
|
||||
go test -coverprofile $(TEMP_DIR)/unit-coverage-details.txt $(shell go list ./... | grep -v anchore/syft/test)
|
||||
@.github/scripts/coverage.py $(COVERAGE_THRESHOLD) $(TEMP_DIR)/unit-coverage-details.txt
|
||||
|
||||
.PHONY: integration
|
||||
integration: ## Run integration tests
|
||||
$(call title,Running integration tests)
|
||||
go test -v ./test/integration
|
||||
|
||||
.PHONY: validate-cyclonedx-schema
|
||||
validate-cyclonedx-schema:
|
||||
cd schema/cyclonedx && make
|
||||
|
||||
.PHONY: unit
|
||||
unit: $(RESULTSDIR) fixtures ## Run unit tests (with coverage)
|
||||
$(call title,Running unit tests)
|
||||
go test -coverprofile $(COVER_REPORT) $(shell go list ./... | grep -v anchore/syft/test)
|
||||
@go tool cover -func $(COVER_REPORT) | grep total | awk '{print substr($$3, 1, length($$3)-1)}' > $(COVER_TOTAL)
|
||||
@echo "Coverage: $$(cat $(COVER_TOTAL))"
|
||||
@if [ $$(echo "$$(cat $(COVER_TOTAL)) >= $(COVERAGE_THRESHOLD)" | bc -l) -ne 1 ]; then echo "$(RED)$(BOLD)Failed coverage quality gate (> $(COVERAGE_THRESHOLD)%)$(RESET)" && false; fi
|
||||
.PHONY: cli
|
||||
cli: $(SNAPSHOT_DIR) ## Run CLI tests
|
||||
chmod 755 "$(SNAPSHOT_BIN)"
|
||||
$(SNAPSHOT_BIN) version
|
||||
SYFT_BINARY_LOCATION='$(SNAPSHOT_BIN)' \
|
||||
go test -count=1 -timeout=15m -v ./test/cli
|
||||
|
||||
|
||||
## Benchmark test targets #################################
|
||||
|
||||
.PHONY: benchmark
|
||||
benchmark: $(RESULTSDIR) ## Run benchmark tests and compare against the baseline (if available)
|
||||
benchmark: $(TEMP_DIR) ## Run benchmark tests and compare against the baseline (if available)
|
||||
$(call title,Running benchmark tests)
|
||||
go test -p 1 -run=^Benchmark -bench=. -count=5 -benchmem ./... | tee $(RESULTSDIR)/benchmark-$(REF_NAME).txt
|
||||
(test -s $(RESULTSDIR)/benchmark-main.txt && \
|
||||
$(TEMPDIR)/benchstat $(RESULTSDIR)/benchmark-main.txt $(RESULTSDIR)/benchmark-$(REF_NAME).txt || \
|
||||
$(TEMPDIR)/benchstat $(RESULTSDIR)/benchmark-$(REF_NAME).txt) \
|
||||
| tee $(RESULTSDIR)/benchstat.txt
|
||||
go test -p 1 -run=^Benchmark -bench=. -count=7 -benchmem ./... | tee $(TEMP_DIR)/benchmark-$(VERSION).txt
|
||||
(test -s $(TEMP_DIR)/benchmark-main.txt && \
|
||||
$(TEMP_DIR)/benchstat $(TEMP_DIR)/benchmark-main.txt $(TEMP_DIR)/benchmark-$(VERSION).txt || \
|
||||
$(TEMP_DIR)/benchstat $(TEMP_DIR)/benchmark-$(VERSION).txt) \
|
||||
| tee $(TEMP_DIR)/benchstat.txt
|
||||
|
||||
.PHONY: show-benchstat
|
||||
show-benchstat:
|
||||
@cat $(RESULTSDIR)/benchstat.txt
|
||||
@cat $(TEMP_DIR)/benchstat.txt
|
||||
|
||||
# note: this is used by CI to determine if the install test fixture cache (docker image tars) should be busted
|
||||
install-fingerprint:
|
||||
cd test/install && \
|
||||
|
||||
## Test-fixture-related targets #################################
|
||||
|
||||
# note: this is used by CI to determine if various test fixture cache should be restored or recreated
|
||||
fingerprints:
|
||||
$(call title,Creating all test cache input fingerprints)
|
||||
|
||||
# for IMAGE integration test fixtures
|
||||
cd test/integration/test-fixtures && \
|
||||
make cache.fingerprint
|
||||
|
||||
install-test: $(SNAPSHOTDIR)
|
||||
cd test/install && \
|
||||
make
|
||||
|
||||
install-test-cache-save: $(SNAPSHOTDIR)
|
||||
cd test/install && \
|
||||
make save
|
||||
|
||||
install-test-cache-load: $(SNAPSHOTDIR)
|
||||
cd test/install && \
|
||||
make load
|
||||
|
||||
install-test-ci-mac: $(SNAPSHOTDIR)
|
||||
cd test/install && \
|
||||
make ci-test-mac
|
||||
|
||||
.PHONY: integration
|
||||
integration: ## Run integration tests
|
||||
$(call title,Running integration tests)
|
||||
go test -v ./test/integration
|
||||
|
||||
# note: this is used by CI to determine if the integration test fixture cache (docker image tars) should be busted
|
||||
integration-fingerprint:
|
||||
$(call title,Integration test fixture fingerprint)
|
||||
find test/integration/test-fixtures/image-* -type f -exec md5sum {} + | awk '{print $1}' | sort | tee /dev/stderr | md5sum | tee test/integration/test-fixtures/cache.fingerprint && echo "$(INTEGRATION_CACHE_BUSTER)" >> test/integration/test-fixtures/cache.fingerprint
|
||||
|
||||
.PHONY: java-packages-fingerprint
|
||||
java-packages-fingerprint:
|
||||
$(call title,Java test fixture fingerprint)
|
||||
# for JAVA BUILD test fixtures
|
||||
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)
|
||||
# for GO BINARY test fixtures
|
||||
cd syft/pkg/cataloger/golang/test-fixtures/archs && \
|
||||
make binaries.fingerprint
|
||||
|
||||
.PHONY: rpm-binaries-fingerprint
|
||||
rpm-binaries-fingerprint:
|
||||
$(call title,RPM binary test fixture fingerprint)
|
||||
# for RPM test fixtures
|
||||
cd syft/pkg/cataloger/rpm/test-fixtures && \
|
||||
make rpms.fingerprint
|
||||
|
||||
# for INSTALL integration test fixtures
|
||||
cd test/install && \
|
||||
make cache.fingerprint
|
||||
|
||||
# for CLI test fixtures
|
||||
cd test/cli/test-fixtures && \
|
||||
make cache.fingerprint
|
||||
|
||||
.PHONY: fixtures
|
||||
fixtures:
|
||||
$(call title,Generating test fixtures)
|
||||
cd syft/pkg/cataloger/java/test-fixtures/java-builds && make
|
||||
cd syft/pkg/cataloger/rpm/test-fixtures && make
|
||||
|
||||
.PHONY: generate-json-schema
|
||||
generate-json-schema: ## Generate a new json schema
|
||||
cd schema/json && go run generate.go
|
||||
|
||||
.PHONY: generate-license-list
|
||||
generate-license-list: ## Generate an updated spdx license list
|
||||
go generate ./internal/spdxlicense/...
|
||||
gofmt -s -w ./internal/spdxlicense
|
||||
|
||||
.PHONY: build
|
||||
build: $(SNAPSHOTDIR) ## Build release snapshot binaries and packages
|
||||
|
||||
$(SNAPSHOTDIR): ## Build snapshot release binaries and packages
|
||||
$(call title,Building snapshot artifacts)
|
||||
|
||||
# create a config with the dist dir overridden
|
||||
echo "dist: $(SNAPSHOTDIR)" > $(TEMPDIR)/goreleaser.yaml
|
||||
cat .goreleaser.yaml >> $(TEMPDIR)/goreleaser.yaml
|
||||
|
||||
# build release snapshots
|
||||
bash -c "SKIP_SIGNING=true $(SNAPSHOT_CMD) --skip-sign --config $(TEMPDIR)/goreleaser.yaml"
|
||||
|
||||
.PHONY: snapshot-with-signing
|
||||
snapshot-with-signing: ## Build snapshot release binaries and packages (with dummy signing)
|
||||
$(call title,Building snapshot artifacts (+ signing))
|
||||
|
||||
# create a config with the dist dir overridden
|
||||
echo "dist: $(SNAPSHOTDIR)" > $(TEMPDIR)/goreleaser.yaml
|
||||
cat .goreleaser.yaml >> $(TEMPDIR)/goreleaser.yaml
|
||||
|
||||
# build release snapshots
|
||||
bash -c "\
|
||||
$(SNAPSHOT_CMD) \
|
||||
--config $(TEMPDIR)/goreleaser.yaml \
|
||||
|| (cat /tmp/quill-*.log && false)"
|
||||
|
||||
# note: we cannot clean the snapshot directory since the pipeline builds the snapshot separately
|
||||
.PHONY: compare-mac
|
||||
compare-mac: $(RESULTSDIR) $(SNAPSHOTDIR) ## Run compare tests on build snapshot binaries and packages (Mac)
|
||||
$(call title,Running compare test: Run on Mac)
|
||||
$(COMPARE_DIR)/mac.sh \
|
||||
$(SNAPSHOTDIR) \
|
||||
$(COMPARE_DIR) \
|
||||
$(COMPARE_TEST_IMAGE) \
|
||||
$(RESULTSDIR)
|
||||
|
||||
# note: we cannot clean the snapshot directory since the pipeline builds the snapshot separately
|
||||
.PHONY: compare-linux
|
||||
compare-linux: compare-test-deb-package-install compare-test-rpm-package-install ## Run compare tests on build snapshot binaries and packages (Linux)
|
||||
|
||||
.PHONY: compare-test-deb-package-install
|
||||
compare-test-deb-package-install: $(RESULTSDIR) $(SNAPSHOTDIR)
|
||||
$(call title,Running compare test: DEB install)
|
||||
$(COMPARE_DIR)/deb.sh \
|
||||
$(SNAPSHOTDIR) \
|
||||
$(COMPARE_DIR) \
|
||||
$(COMPARE_TEST_IMAGE) \
|
||||
$(RESULTSDIR)
|
||||
|
||||
.PHONY: compare-test-rpm-package-install
|
||||
compare-test-rpm-package-install: $(RESULTSDIR) $(SNAPSHOTDIR)
|
||||
$(call title,Running compare test: RPM install)
|
||||
$(COMPARE_DIR)/rpm.sh \
|
||||
$(SNAPSHOTDIR) \
|
||||
$(COMPARE_DIR) \
|
||||
$(COMPARE_TEST_IMAGE) \
|
||||
$(RESULTSDIR)
|
||||
|
||||
# note: this is used by CI to determine if the integration test fixture cache (docker image tars) should be busted
|
||||
cli-fingerprint:
|
||||
$(call title,CLI test fixture fingerprint)
|
||||
find test/cli/test-fixtures/image-* -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum | tee test/cli/test-fixtures/cache.fingerprint && echo "$(CLI_CACHE_BUSTER)" >> test/cli/test-fixtures/cache.fingerprint
|
||||
|
||||
.PHONY: cli
|
||||
cli: $(SNAPSHOTDIR) ## Run CLI tests
|
||||
chmod 755 "$(SNAPSHOT_BIN)"
|
||||
$(SNAPSHOT_BIN) version
|
||||
SYFT_BINARY_LOCATION='$(SNAPSHOT_BIN)' \
|
||||
go test -count=1 -timeout=15m -v ./test/cli
|
||||
|
||||
.PHONY: changelog
|
||||
changelog: clean-changelog CHANGELOG.md
|
||||
@docker run -it --rm \
|
||||
-v $(shell pwd)/CHANGELOG.md:/CHANGELOG.md \
|
||||
rawkode/mdv \
|
||||
-t 748.5989 \
|
||||
/CHANGELOG.md
|
||||
|
||||
CHANGELOG.md:
|
||||
$(TEMPDIR)/chronicle -vv > CHANGELOG.md
|
||||
|
||||
.PHONY: release
|
||||
release: clean-dist CHANGELOG.md
|
||||
$(call title,Publishing release artifacts)
|
||||
|
||||
# create a config with the dist dir overridden
|
||||
echo "dist: $(DISTDIR)" > $(TEMPDIR)/goreleaser.yaml
|
||||
cat .goreleaser.yaml >> $(TEMPDIR)/goreleaser.yaml
|
||||
|
||||
# note: notarization cannot be done in parallel, thus --parallelism 1
|
||||
bash -c "\
|
||||
$(RELEASE_CMD) \
|
||||
--config $(TEMPDIR)/goreleaser.yaml \
|
||||
--parallelism 1 \
|
||||
--release-notes <(cat CHANGELOG.md) \
|
||||
|| (cat /tmp/quill-*.log && false)"
|
||||
|
||||
# TODO: turn this into a post-release hook
|
||||
# upload the version file that supports the application version update check (excluding pre-releases)
|
||||
.github/scripts/update-version-file.sh "$(DISTDIR)" "$(VERSION)"
|
||||
|
||||
.PHONY: clean
|
||||
clean: clean-dist clean-snapshot clean-test-image-cache ## Remove previous builds, result reports, and test cache
|
||||
$(call safe_rm_rf_children,$(RESULTSDIR))
|
||||
|
||||
.PHONY: clean-snapshot
|
||||
clean-snapshot:
|
||||
$(call safe_rm_rf,$(SNAPSHOTDIR))
|
||||
rm -f $(TEMPDIR)/goreleaser.yaml
|
||||
|
||||
.PHONY: clean-dist
|
||||
clean-dist: clean-changelog
|
||||
$(call safe_rm_rf,$(DISTDIR))
|
||||
rm -f $(TEMPDIR)/goreleaser.yaml
|
||||
|
||||
.PHONY: clean-changelog
|
||||
clean-changelog:
|
||||
rm -f CHANGELOG.md
|
||||
|
||||
clean-test-image-cache: clean-test-image-tar-cache clean-test-image-docker-cache ## Clean test image cache
|
||||
|
||||
.PHONY: clear-test-image-tar-cache
|
||||
clean-test-image-tar-cache:
|
||||
## Delete all test cache (built docker image tars)
|
||||
find . -type f -wholename "**/test-fixtures/cache/stereoscope-fixture-*.tar" -delete
|
||||
|
||||
.PHONY: clear-test-image-docker-cache
|
||||
clean-test-image-docker-cache:
|
||||
## Purge all test docker images
|
||||
docker images --format '{{.ID}} {{.Repository}}' | grep stereoscope-fixture- | awk '{print $$1}' | uniq | xargs -r docker rmi --force
|
||||
|
||||
.PHONY: show-test-image-cache
|
||||
show-test-image-cache: ## Show all docker and image tar cache
|
||||
show-test-image-cache: ## Show all docker and image tar cache
|
||||
$(call title,Docker daemon cache)
|
||||
@docker images --format '{{.ID}} {{.Repository}}:{{.Tag}}' | grep stereoscope-fixture- | sort
|
||||
|
||||
@@ -399,10 +223,150 @@ show-test-image-cache: ## Show all docker and image tar cache
|
||||
@find . -type f -wholename "**/test-fixtures/cache/stereoscope-fixture-*.tar" | sort
|
||||
|
||||
.PHONY: show-test-snapshots
|
||||
show-test-snapshots: ## Show all test snapshots
|
||||
show-test-snapshots: ## Show all test snapshots
|
||||
$(call title,Test snapshots)
|
||||
@find . -type f -wholename "**/test-fixtures/snapshot/*" | sort
|
||||
|
||||
|
||||
## install.sh testing targets #################################
|
||||
|
||||
install-test: $(SNAPSHOT_DIR)
|
||||
cd test/install && \
|
||||
make
|
||||
|
||||
install-test-cache-save: $(SNAPSHOT_DIR)
|
||||
cd test/install && \
|
||||
make save
|
||||
|
||||
install-test-cache-load: $(SNAPSHOT_DIR)
|
||||
cd test/install && \
|
||||
make load
|
||||
|
||||
install-test-ci-mac: $(SNAPSHOT_DIR)
|
||||
cd test/install && \
|
||||
make ci-test-mac
|
||||
|
||||
# note: we cannot clean the snapshot directory since the pipeline builds the snapshot separately
|
||||
.PHONY: compare-mac
|
||||
compare-mac: $(TEMP_DIR) $(SNAPSHOT_DIR) ## Run compare tests on build snapshot binaries and packages (Mac)
|
||||
$(call title,Running compare test: Run on Mac)
|
||||
$(COMPARE_DIR)/mac.sh \
|
||||
$(SNAPSHOT_DIR) \
|
||||
$(COMPARE_DIR) \
|
||||
$(COMPARE_TEST_IMAGE) \
|
||||
$(TEMP_DIR)
|
||||
|
||||
# note: we cannot clean the snapshot directory since the pipeline builds the snapshot separately
|
||||
.PHONY: compare-linux
|
||||
compare-linux: compare-test-deb-package-install compare-test-rpm-package-install ## Run compare tests on build snapshot binaries and packages (Linux)
|
||||
|
||||
.PHONY: compare-test-deb-package-install
|
||||
compare-test-deb-package-install: $(TEMP_DIR) $(SNAPSHOT_DIR)
|
||||
$(call title,Running compare test: DEB install)
|
||||
$(COMPARE_DIR)/deb.sh \
|
||||
$(SNAPSHOT_DIR) \
|
||||
$(COMPARE_DIR) \
|
||||
$(COMPARE_TEST_IMAGE) \
|
||||
$(TEMP_DIR)
|
||||
|
||||
.PHONY: compare-test-rpm-package-install
|
||||
compare-test-rpm-package-install: $(TEMP_DIR) $(SNAPSHOT_DIR)
|
||||
$(call title,Running compare test: RPM install)
|
||||
$(COMPARE_DIR)/rpm.sh \
|
||||
$(SNAPSHOT_DIR) \
|
||||
$(COMPARE_DIR) \
|
||||
$(COMPARE_TEST_IMAGE) \
|
||||
$(TEMP_DIR)
|
||||
|
||||
|
||||
## Code generation targets #################################
|
||||
|
||||
.PHONY: generate-json-schema
|
||||
generate-json-schema: ## Generate a new json schema
|
||||
cd schema/json && go run generate.go
|
||||
|
||||
.PHONY: generate-license-list
|
||||
generate-license-list: ## Generate an updated spdx license list
|
||||
go generate ./internal/spdxlicense/...
|
||||
gofmt -s -w ./internal/spdxlicense
|
||||
|
||||
|
||||
## Build-related targets #################################
|
||||
|
||||
.PHONY: build
|
||||
build: $(SNAPSHOT_DIR) ## Build release snapshot binaries and packages
|
||||
|
||||
$(SNAPSHOT_DIR): ## Build snapshot release binaries and packages
|
||||
$(call title,Building snapshot artifacts)
|
||||
|
||||
# create a config with the dist dir overridden
|
||||
echo "dist: $(SNAPSHOT_DIR)" > $(TEMP_DIR)/goreleaser.yaml
|
||||
cat .goreleaser.yaml >> $(TEMP_DIR)/goreleaser.yaml
|
||||
|
||||
# build release snapshots
|
||||
$(SNAPSHOT_CMD) --config $(TEMP_DIR)/goreleaser.yaml
|
||||
|
||||
.PHONY: changelog
|
||||
changelog: clean-changelog $(CHANGELOG) ## Generate and show the changelog for the current unreleased version
|
||||
@docker run -it --rm \
|
||||
-v $(shell pwd)/$(CHANGELOG):/$(CHANGELOG) \
|
||||
rawkode/mdv \
|
||||
-t 748.5989 \
|
||||
/$(CHANGELOG)
|
||||
|
||||
$(CHANGELOG):
|
||||
$(TEMP_DIR)/chronicle -vv > $(CHANGELOG)
|
||||
|
||||
.PHONY: release
|
||||
release: clean-dist $(CHANGELOG)
|
||||
$(call title,Publishing release artifacts)
|
||||
@.github/scripts/ci-check.sh
|
||||
|
||||
# create a config with the dist dir overridden
|
||||
echo "dist: $(DIST_DIR)" > $(TEMP_DIR)/goreleaser.yaml
|
||||
cat .goreleaser.yaml >> $(TEMP_DIR)/goreleaser.yaml
|
||||
|
||||
bash -c "\
|
||||
$(RELEASE_CMD) \
|
||||
--config $(TEMP_DIR)/goreleaser.yaml \
|
||||
--release-notes <(cat $(CHANGELOG)) \
|
||||
|| (cat /tmp/quill-*.log && false)"
|
||||
|
||||
# upload the version file that supports the application version update check (excluding pre-releases)
|
||||
.github/scripts/update-version-file.sh "$(DIST_DIR)" "$(VERSION)"
|
||||
|
||||
## Cleanup targets #################################
|
||||
|
||||
.PHONY: clean
|
||||
clean: clean-dist clean-snapshot clean-test-image-cache ## Remove previous builds, result reports, and test cache
|
||||
$(call safe_rm_rf_children,$(TEMP_DIR))
|
||||
|
||||
.PHONY: clean-snapshot
|
||||
clean-snapshot:
|
||||
$(call safe_rm_rf,$(SNAPSHOT_DIR))
|
||||
rm -f $(TEMP_DIR)/goreleaser.yaml
|
||||
|
||||
.PHONY: clean-dist
|
||||
clean-dist: clean-changelog
|
||||
$(call safe_rm_rf,$(DIST_DIR))
|
||||
rm -f $(TEMP_DIR)/goreleaser.yaml
|
||||
|
||||
.PHONY: clean-changelog
|
||||
clean-changelog:
|
||||
rm -f $(CHANGELOG)
|
||||
|
||||
clean-test-image-cache: clean-test-image-tar-cache clean-test-image-docker-cache ## Clean test image cache
|
||||
|
||||
.PHONY: clear-test-image-tar-cache
|
||||
clean-test-image-tar-cache: ## Delete all test cache (built docker image tars)
|
||||
find . -type f -wholename "**/test-fixtures/cache/stereoscope-fixture-*.tar" -delete
|
||||
|
||||
.PHONY: clear-test-image-docker-cache
|
||||
clean-test-image-docker-cache: ## Purge all test docker images
|
||||
docker images --format '{{.ID}} {{.Repository}}' | grep stereoscope-fixture- | awk '{print $$1}' | uniq | xargs -r docker rmi --force
|
||||
|
||||
## Halp! #################################
|
||||
|
||||
.PHONY: help
|
||||
help: ## Display this help
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(BOLD)$(CYAN)%-25s$(RESET)%s\n", $$1, $$2}'
|
||||
|
||||
@@ -82,7 +82,7 @@ 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). 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 available in the [stable channel](https://nixos.wiki/wiki/Nix_channels#The_official_channels) since NixOS `22.05`.
|
||||
|
||||
```bash
|
||||
nix-env -i syft
|
||||
|
||||
@@ -43,8 +43,8 @@ require (
|
||||
github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
|
||||
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458
|
||||
golang.org/x/term v0.0.0-20220919170432-7a66f970e087
|
||||
golang.org/x/net v0.4.0
|
||||
golang.org/x/term v0.3.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
@@ -124,8 +124,8 @@ require (
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
|
||||
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
|
||||
@@ -1564,8 +1564,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458 h1:MgJ6t2zo8v0tbmLCueaCbF1RM+TtB0rs3Lv8DGtOIpY=
|
||||
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1720,15 +1720,15 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w=
|
||||
golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1738,8 +1738,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0=
|
||||
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -6,5 +6,5 @@ const (
|
||||
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "6.1.0"
|
||||
JSONSchemaVersion = "6.2.0"
|
||||
)
|
||||
|
||||
@@ -40,7 +40,8 @@ type artifactMetadataContainer struct {
|
||||
Dotnet pkg.DotnetDepsMetadata
|
||||
Dpkg pkg.DpkgMetadata
|
||||
Gem pkg.GemMetadata
|
||||
Go pkg.GolangMetadata
|
||||
GoBin pkg.GolangBinMetadata
|
||||
GoMod pkg.GolangModMetadata
|
||||
Hackage pkg.HackageMetadata
|
||||
Java pkg.JavaMetadata
|
||||
KbPackage pkg.KbPackageMetadata
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -366,10 +366,10 @@
|
||||
"type" : "string"
|
||||
},
|
||||
"licenseInfoFromFiles" : {
|
||||
"description" : "The licensing information that was discovered directly within the package. There will be an instance of this property for each distinct value of alllicenseInfoInFile properties of all files contained in the package.\n\nIf the licenseInfoFromFiles field is not present for a package and filesAnalyzed property for that same pacakge is true or omitted, it implies an equivalent meaning to NOASSERTION.",
|
||||
"description" : "The licensing information that was discovered directly within the package. There will be an instance of this property for each distinct value of alllicenseInfoInFile properties of all files contained in the package.\n\nIf the licenseInfoFromFiles field is not present for a package and filesAnalyzed property for that same package is true or omitted, it implies an equivalent meaning to NOASSERTION.",
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"description" : "License expression for licenseInfoFromFiles. See SPDX Annex D for the license expression syntax. The licensing information that was discovered directly within the package. There will be an instance of this property for each distinct value of alllicenseInfoInFile properties of all files contained in the package.\n\nIf the licenseInfoFromFiles field is not present for a package and filesAnalyzed property for that same pacakge is true or omitted, it implies an equivalent meaning to NOASSERTION.",
|
||||
"description" : "License expression for licenseInfoFromFiles. See SPDX Annex D for the license expression syntax. The licensing information that was discovered directly within the package. There will be an instance of this property for each distinct value of alllicenseInfoInFile properties of all files contained in the package.\n\nIf the licenseInfoFromFiles field is not present for a package and filesAnalyzed property for that same package is true or omitted, it implies an equivalent meaning to NOASSERTION.",
|
||||
"type" : "string"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -90,8 +90,8 @@ func Test_encodeComponentProperties(t *testing.T) {
|
||||
Version: "v0.0.0-20211006190231-62292e806868",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: "1.17",
|
||||
Architecture: "amd64",
|
||||
H1Digest: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=",
|
||||
@@ -99,13 +99,32 @@ func Test_encodeComponentProperties(t *testing.T) {
|
||||
},
|
||||
expected: &[]cyclonedx.Property{
|
||||
{Name: "syft:package:language", Value: pkg.Go.String()},
|
||||
{Name: "syft:package:metadataType", Value: "GolangMetadata"},
|
||||
{Name: "syft:package:metadataType", Value: "GolangBinMetadata"},
|
||||
{Name: "syft:package:type", Value: "go-module"},
|
||||
{Name: "syft:metadata:architecture", Value: "amd64"},
|
||||
{Name: "syft:metadata:goCompiledVersion", Value: "1.17"},
|
||||
{Name: "syft:metadata:h1Digest", Value: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k="},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from go mod",
|
||||
input: pkg.Package{
|
||||
Name: "golang.org/x/net",
|
||||
Version: "v0.0.0-20211006190231-62292e806868",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{
|
||||
H1Digest: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=",
|
||||
},
|
||||
},
|
||||
expected: &[]cyclonedx.Property{
|
||||
{Name: "syft:package:language", Value: pkg.Go.String()},
|
||||
{Name: "syft:package:metadataType", Value: "GolangModMetadata"},
|
||||
{Name: "syft:package:type", Value: "go-module"},
|
||||
{Name: "syft:metadata:h1Digest", Value: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k="},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from rpm",
|
||||
input: pkg.Package{
|
||||
|
||||
@@ -331,7 +331,7 @@ func toPackageChecksums(p pkg.Package) ([]common.Checksum, bool) {
|
||||
})
|
||||
}
|
||||
}
|
||||
case pkg.GolangMetadata:
|
||||
case pkg.GolangBinMetadata:
|
||||
// because the H1 digest is found in the Golang metadata we cannot claim that the files were analyzed
|
||||
algo, hexStr, err := util.HDigestToSHA(meta.H1Digest)
|
||||
if err != nil {
|
||||
|
||||
@@ -76,8 +76,8 @@ func Test_toPackageChecksums(t *testing.T) {
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
Language: pkg.Go,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
H1Digest: "h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=",
|
||||
},
|
||||
},
|
||||
@@ -381,8 +381,8 @@ func Test_H1Digest(t *testing.T) {
|
||||
pkg: pkg.Package{
|
||||
Name: "github.com/googleapis/gnostic",
|
||||
Version: "v0.5.5",
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
H1Digest: "h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=",
|
||||
},
|
||||
},
|
||||
@@ -393,8 +393,8 @@ func Test_H1Digest(t *testing.T) {
|
||||
pkg: pkg.Package{
|
||||
Name: "github.com/googleapis/gnostic",
|
||||
Version: "v0.5.5",
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
H1Digest: "h1:9fHAtK0uzzz",
|
||||
},
|
||||
},
|
||||
@@ -405,8 +405,8 @@ func Test_H1Digest(t *testing.T) {
|
||||
pkg: pkg.Package{
|
||||
Name: "github.com/googleapis/gnostic",
|
||||
Version: "v0.5.5",
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
H1Digest: "h12:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -366,7 +366,7 @@ func extractMetadata(p *spdx.Package, info pkgInfo) (pkg.MetadataType, interface
|
||||
h1Digest = digest
|
||||
break
|
||||
}
|
||||
return pkg.GolangMetadataType, pkg.GolangMetadata{
|
||||
return pkg.GolangBinMetadataType, pkg.GolangBinMetadata{
|
||||
H1Digest: h1Digest,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,8 +302,8 @@ func TestH1Digest(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
p := toSyftPackage(&test.pkg)
|
||||
require.Equal(t, pkg.GolangMetadataType, p.MetadataType)
|
||||
meta := p.Metadata.(pkg.GolangMetadata)
|
||||
require.Equal(t, pkg.GolangBinMetadataType, p.MetadataType)
|
||||
meta := p.Metadata.(pkg.GolangBinMetadata)
|
||||
require.Equal(t, test.expectedDigest, meta.H1Digest)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestUnmarshalPackageGolang(t *testing.T) {
|
||||
"language": "go",
|
||||
"cpes": [],
|
||||
"purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0",
|
||||
"metadataType": "GolangMetadata",
|
||||
"metadataType": "GolangBinMetadata",
|
||||
"metadata": {
|
||||
"goCompiledVersion": "go1.18",
|
||||
"architecture": "amd64",
|
||||
@@ -43,7 +43,7 @@ func TestUnmarshalPackageGolang(t *testing.T) {
|
||||
}`),
|
||||
assert: func(p *Package) {
|
||||
assert.NotNil(t, p.Metadata)
|
||||
golangMetadata := p.Metadata.(pkg.GolangMetadata)
|
||||
golangMetadata := p.Metadata.(pkg.GolangBinMetadata)
|
||||
assert.NotEmpty(t, golangMetadata)
|
||||
assert.Equal(t, "go1.18", golangMetadata.GoCompiledVersion)
|
||||
},
|
||||
@@ -93,7 +93,7 @@ func Test_unpackMetadata(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "unmarshal package metadata",
|
||||
metadataType: pkg.GolangMetadataType,
|
||||
metadataType: pkg.GolangBinMetadataType,
|
||||
packageData: []byte(`{
|
||||
"id": "8b594519bc23da50",
|
||||
"name": "gopkg.in/square/go-jose.v2",
|
||||
@@ -109,7 +109,7 @@ func Test_unpackMetadata(t *testing.T) {
|
||||
"language": "go",
|
||||
"cpes": [],
|
||||
"purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0",
|
||||
"metadataType": "GolangMetadata",
|
||||
"metadataType": "GolangBinMetadata",
|
||||
"metadata": {
|
||||
"goCompiledVersion": "go1.18",
|
||||
"architecture": "amd64",
|
||||
@@ -215,18 +215,18 @@ func Test_unpackMetadata(t *testing.T) {
|
||||
{
|
||||
name: "can handle package with metadata type but missing metadata",
|
||||
packageData: []byte(`{
|
||||
"metadataType": "GolangMetadata"
|
||||
"metadataType": "GolangBinMetadata"
|
||||
}`),
|
||||
metadataType: pkg.GolangMetadataType,
|
||||
wantMetadata: pkg.GolangMetadata{},
|
||||
metadataType: pkg.GolangBinMetadataType,
|
||||
wantMetadata: pkg.GolangBinMetadata{},
|
||||
},
|
||||
{
|
||||
name: "can handle package with golang bin metadata type",
|
||||
packageData: []byte(`{
|
||||
"metadataType": "GolangBinMetadata"
|
||||
}`),
|
||||
metadataType: pkg.GolangMetadataType,
|
||||
wantMetadata: pkg.GolangMetadata{},
|
||||
metadataType: pkg.GolangBinMetadataType,
|
||||
wantMetadata: pkg.GolangBinMetadata{},
|
||||
},
|
||||
{
|
||||
name: "can handle package with unknonwn metadata type and missing metadata",
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"version": "6.0.0",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.0.0.json"
|
||||
"version": "6.1.0",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.1.0.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"version": "6.1.0",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.1.0.json"
|
||||
"version": "6.2.0",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.2.0.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"locations": [
|
||||
{
|
||||
"path": "/somefile-1.txt",
|
||||
"layerID": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59"
|
||||
"layerID": "sha256:6afd1cb55939d87ba4c298907d0a53059bb3742c2d65314643e2464071cf0a2d"
|
||||
}
|
||||
],
|
||||
"licenses": [
|
||||
@@ -40,7 +40,7 @@
|
||||
"locations": [
|
||||
{
|
||||
"path": "/somefile-2.txt",
|
||||
"layerID": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec"
|
||||
"layerID": "sha256:657997cff9a836139186239bdfe77250239a700d0ed97d57e101c295e8244319"
|
||||
}
|
||||
],
|
||||
"licenses": [],
|
||||
@@ -64,11 +64,11 @@
|
||||
],
|
||||
"artifactRelationships": [],
|
||||
"source": {
|
||||
"id": "1a678f111c8ddc66fd82687bb024e0dd6af61314404937a80e810c0cf317b796",
|
||||
"id": "c85f7ae1b0ac38342c1cf1a6f7ea2b4b1ddc49cd1b24219ebd05dc10b3303491",
|
||||
"type": "image",
|
||||
"target": {
|
||||
"userInput": "user-image-input",
|
||||
"imageID": "sha256:3c51b06feb0cda8ee62d0e3755ef2a8496a6b71f8a55b245f07f31c4bb813d31",
|
||||
"imageID": "sha256:b5c0bfa8bcf70c75d92ebebbf76af667906d56e6fad50c37e7f93df824a64b79",
|
||||
"manifestDigest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"tags": [
|
||||
@@ -78,17 +78,17 @@
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"digest": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59",
|
||||
"digest": "sha256:6afd1cb55939d87ba4c298907d0a53059bb3742c2d65314643e2464071cf0a2d",
|
||||
"size": 22
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
|
||||
"digest": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec",
|
||||
"digest": "sha256:657997cff9a836139186239bdfe77250239a700d0ed97d57e101c295e8244319",
|
||||
"size": 16
|
||||
}
|
||||
],
|
||||
"manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1NjozYzUxYjA2ZmViMGNkYThlZTYyZDBlMzc1NWVmMmE4NDk2YTZiNzFmOGE1NWIyNDVmMDdmMzFjNGJiODEzZDMxIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpmYjZiZWVjYjc1YjM5ZjRiYjgxM2RiZjE3N2U1MDFlZGQ1ZGRiM2U2OWJiNDVjZWRlYjc4YzY3NmVlMWI3YTU5In0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjMxOWI1ODhjZTY0MjUzYTg3YjUzM2M4ZWQwMWNmMDAyNWUwZWFjOThlN2I1MTZlMTI1MzI5NTdlMTI0NGZkZWMifV19",
|
||||
"config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjItMDgtMDFUMjA6MDk6MjIuNTA5NDIxNzEyWiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTA4LTAxVDIwOjA5OjIyLjQ4Nzg5NTUxOVoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjItMDgtMDFUMjA6MDk6MjIuNTA5NDIxNzEyWiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6ZmI2YmVlY2I3NWIzOWY0YmI4MTNkYmYxNzdlNTAxZWRkNWRkYjNlNjliYjQ1Y2VkZWI3OGM2NzZlZTFiN2E1OSIsInNoYTI1NjozMTliNTg4Y2U2NDI1M2E4N2I1MzNjOGVkMDFjZjAwMjVlMGVhYzk4ZTdiNTE2ZTEyNTMyOTU3ZTEyNDRmZGVjIl19fQ==",
|
||||
"manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1NjpiNWMwYmZhOGJjZjcwYzc1ZDkyZWJlYmJmNzZhZjY2NzkwNmQ1NmU2ZmFkNTBjMzdlN2Y5M2RmODI0YTY0Yjc5In0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1Njo2YWZkMWNiNTU5MzlkODdiYTRjMjk4OTA3ZDBhNTMwNTliYjM3NDJjMmQ2NTMxNDY0M2UyNDY0MDcxY2YwYTJkIn0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjY1Nzk5N2NmZjlhODM2MTM5MTg2MjM5YmRmZTc3MjUwMjM5YTcwMGQwZWQ5N2Q1N2UxMDFjMjk1ZTgyNDQzMTkifV19",
|
||||
"config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjItMDgtMjVUMTY6MjI6MDguODkxMzY0Mjc4WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTA4LTI1VDE2OjIyOjA4Ljc2MzMzMDMyM1oiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjItMDgtMjVUMTY6MjI6MDguODkxMzY0Mjc4WiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6NmFmZDFjYjU1OTM5ZDg3YmE0YzI5ODkwN2QwYTUzMDU5YmIzNzQyYzJkNjUzMTQ2NDNlMjQ2NDA3MWNmMGEyZCIsInNoYTI1Njo2NTc5OTdjZmY5YTgzNjEzOTE4NjIzOWJkZmU3NzI1MDIzOWE3MDBkMGVkOTdkNTdlMTAxYzI5NWU4MjQ0MzE5Il19fQ==",
|
||||
"repoDigests": [],
|
||||
"architecture": "",
|
||||
"os": ""
|
||||
@@ -112,7 +112,7 @@
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"version": "6.0.0",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.0.0.json"
|
||||
"version": "6.1.0",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-6.1.0.json"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -145,22 +145,37 @@ func toSyftDescriptor(d model.Descriptor) sbom.Descriptor {
|
||||
func toSyftSourceData(s model.Source) *source.Metadata {
|
||||
switch s.Type {
|
||||
case "directory":
|
||||
path, ok := s.Target.(string)
|
||||
if !ok {
|
||||
log.Warnf("unable to parse source target as string: %+v", s.Target)
|
||||
return nil
|
||||
}
|
||||
return &source.Metadata{
|
||||
ID: s.ID,
|
||||
Scheme: source.DirectoryScheme,
|
||||
Path: s.Target.(string),
|
||||
Path: path,
|
||||
}
|
||||
case "file":
|
||||
path, ok := s.Target.(string)
|
||||
if !ok {
|
||||
log.Warnf("unable to parse source target as string: %+v", s.Target)
|
||||
return nil
|
||||
}
|
||||
return &source.Metadata{
|
||||
ID: s.ID,
|
||||
Scheme: source.FileScheme,
|
||||
Path: s.Target.(string),
|
||||
Path: path,
|
||||
}
|
||||
case "image":
|
||||
metadata, ok := s.Target.(source.ImageMetadata)
|
||||
if !ok {
|
||||
log.Warnf("unable to parse source target as image metadata: %+v", s.Target)
|
||||
return nil
|
||||
}
|
||||
return &source.Metadata{
|
||||
ID: s.ID,
|
||||
Scheme: source.ImageScheme,
|
||||
ImageMetadata: s.Target.(source.ImageMetadata),
|
||||
ImageMetadata: metadata,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -81,7 +81,11 @@ func packageURL(m pkg.DpkgMetadata, distro *linux.Release) string {
|
||||
}
|
||||
|
||||
func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) {
|
||||
metadata := p.Metadata.(pkg.DpkgMetadata)
|
||||
metadata, ok := p.Metadata.(pkg.DpkgMetadata)
|
||||
if !ok {
|
||||
log.WithFields("package", p.String()).Warn("unable to extract DPKG metadata to add licenses")
|
||||
return
|
||||
}
|
||||
|
||||
// get license information from the copyright file
|
||||
copyrightReader, copyrightLocation := fetchCopyrightContents(resolver, dbLocation, metadata)
|
||||
@@ -97,7 +101,11 @@ func addLicenses(resolver source.FileResolver, dbLocation source.Location, p *pk
|
||||
}
|
||||
|
||||
func mergeFileListing(resolver source.FileResolver, dbLocation source.Location, p *pkg.Package) {
|
||||
metadata := p.Metadata.(pkg.DpkgMetadata)
|
||||
metadata, ok := p.Metadata.(pkg.DpkgMetadata)
|
||||
if !ok {
|
||||
log.WithFields("package", p.String()).Warn("unable to extract DPKG metadata to file listing")
|
||||
return
|
||||
}
|
||||
|
||||
// get file listing (package files + additional config files)
|
||||
files, infoLocations := getAdditionalFileListing(resolver, dbLocation, metadata)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"regexp"
|
||||
|
||||
"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/generic"
|
||||
@@ -42,7 +43,12 @@ loop:
|
||||
if len(tokens) < 5 {
|
||||
name, hash := tokens[1], tokens[2]
|
||||
sourcePkg := pkgMap[name]
|
||||
metadata := sourcePkg.Metadata.(pkg.RebarLockMetadata)
|
||||
metadata, ok := sourcePkg.Metadata.(pkg.RebarLockMetadata)
|
||||
if !ok {
|
||||
log.WithFields("package", name).Warn("unable to extract rebar.lock metadata to add hash metadata")
|
||||
continue
|
||||
}
|
||||
|
||||
if metadata.PkgHash == "" {
|
||||
metadata.PkgHash = hash
|
||||
} else {
|
||||
|
||||
@@ -22,8 +22,8 @@ func newGoBinaryPackage(dep *debug.Module, mainModule, goVersion, architecture s
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
Locations: source.NewLocationSet(locations...),
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goVersion,
|
||||
H1Digest: dep.Sum,
|
||||
Architecture: architecture,
|
||||
|
||||
@@ -143,8 +143,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
),
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
Architecture: archDetails,
|
||||
BuildSettings: defaultBuildSettings,
|
||||
@@ -190,8 +190,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
),
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{},
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -233,8 +233,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
),
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
Architecture: archDetails,
|
||||
H1Digest: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
|
||||
@@ -285,8 +285,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
),
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
Architecture: archDetails,
|
||||
BuildSettings: map[string]string{
|
||||
@@ -340,8 +340,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
),
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
Architecture: archDetails,
|
||||
H1Digest: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
|
||||
@@ -362,8 +362,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
),
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
Architecture: archDetails,
|
||||
H1Digest: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=",
|
||||
@@ -417,8 +417,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
),
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
Architecture: archDetails,
|
||||
H1Digest: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
|
||||
@@ -438,8 +438,8 @@ func TestBuildGoPkgInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
),
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangBinMetadataType,
|
||||
Metadata: pkg.GolangBinMetadata{
|
||||
GoCompiledVersion: goCompiledVersion,
|
||||
Architecture: archDetails,
|
||||
H1Digest: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=",
|
||||
|
||||
@@ -43,8 +43,8 @@ func parseGoModFile(resolver source.FileResolver, _ *generic.Environment, reader
|
||||
PURL: packageURL(m.Mod.Path, m.Mod.Version),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{
|
||||
H1Digest: digests[fmt.Sprintf("%s %s", m.Mod.Path, m.Mod.Version)],
|
||||
},
|
||||
}
|
||||
@@ -59,8 +59,8 @@ func parseGoModFile(resolver source.FileResolver, _ *generic.Environment, reader
|
||||
PURL: packageURL(m.New.Path, m.New.Version),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{
|
||||
H1Digest: digests[fmt.Sprintf("%s %s", m.New.Path, m.New.Version)],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ func TestParseGoMod(t *testing.T) {
|
||||
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/one-package")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{},
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -39,8 +39,8 @@ func TestParseGoMod(t *testing.T) {
|
||||
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{},
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{},
|
||||
},
|
||||
{
|
||||
Name: "github.com/anchore/go-version",
|
||||
@@ -49,8 +49,8 @@ func TestParseGoMod(t *testing.T) {
|
||||
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{},
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{},
|
||||
},
|
||||
{
|
||||
Name: "github.com/anchore/stereoscope",
|
||||
@@ -59,8 +59,8 @@ func TestParseGoMod(t *testing.T) {
|
||||
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{},
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{},
|
||||
},
|
||||
{
|
||||
Name: "github.com/bmatcuk/doublestar",
|
||||
@@ -69,8 +69,8 @@ func TestParseGoMod(t *testing.T) {
|
||||
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{},
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{},
|
||||
},
|
||||
{
|
||||
Name: "github.com/go-test/deep",
|
||||
@@ -79,8 +79,8 @@ func TestParseGoMod(t *testing.T) {
|
||||
Locations: source.NewLocationSet(source.NewLocation("test-fixtures/many-packages")),
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{},
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -112,8 +112,8 @@ func Test_GoSumHashes(t *testing.T) {
|
||||
FoundBy: "go-mod-file-cataloger",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{},
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{},
|
||||
},
|
||||
{
|
||||
Name: "github.com/acarl005/stripansi",
|
||||
@@ -123,8 +123,8 @@ func Test_GoSumHashes(t *testing.T) {
|
||||
FoundBy: "go-mod-file-cataloger",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{
|
||||
H1Digest: "h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=",
|
||||
},
|
||||
},
|
||||
@@ -136,8 +136,8 @@ func Test_GoSumHashes(t *testing.T) {
|
||||
FoundBy: "go-mod-file-cataloger",
|
||||
Language: pkg.Go,
|
||||
Type: pkg.GoModulePkg,
|
||||
MetadataType: pkg.GolangMetadataType,
|
||||
Metadata: pkg.GolangMetadata{
|
||||
MetadataType: pkg.GolangModMetadataType,
|
||||
Metadata: pkg.GolangModMetadata{
|
||||
H1Digest: "h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -134,7 +134,11 @@ func (j *archiveParser) parse() ([]pkg.Package, []artifact.Relationship, error)
|
||||
// note: since package information may change after initial creation when parsing multiple locations within the
|
||||
// jar, we wait until the conclusion of the parsing process before synthesizing pURLs.
|
||||
for i, p := range pkgs {
|
||||
pkgs[i].PURL = packageURL(p.Name, p.Version, p.Metadata.(pkg.JavaMetadata))
|
||||
if m, ok := p.Metadata.(pkg.JavaMetadata); ok {
|
||||
pkgs[i].PURL = packageURL(p.Name, p.Version, m)
|
||||
} else {
|
||||
log.WithFields("package", p.String()).Warn("unable to extract java metadata to generate purl")
|
||||
}
|
||||
}
|
||||
|
||||
return pkgs, relationships, nil
|
||||
@@ -401,10 +405,20 @@ func packageIdentitiesMatch(p pkg.Package, parentPkg *pkg.Package) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
metadata := p.Metadata.(pkg.JavaMetadata)
|
||||
metadata, ok := p.Metadata.(pkg.JavaMetadata)
|
||||
if !ok {
|
||||
log.WithFields("package", p.String()).Warn("unable to extract java metadata to check for matching package identity")
|
||||
return false
|
||||
}
|
||||
|
||||
parentMetadata, ok := parentPkg.Metadata.(pkg.JavaMetadata)
|
||||
if !ok {
|
||||
log.WithFields("package", p.String()).Warn("unable to extract java metadata from parent for verifying virtual path")
|
||||
return false
|
||||
}
|
||||
|
||||
// the virtual path matches...
|
||||
if parentPkg.Metadata.(pkg.JavaMetadata).VirtualPath == metadata.VirtualPath {
|
||||
if parentMetadata.VirtualPath == metadata.VirtualPath {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -115,12 +115,12 @@ func (c *NativeImageCataloger) Name() string {
|
||||
func getPackage(component nativeImageComponent) pkg.Package {
|
||||
var cpes []cpe.CPE
|
||||
for _, property := range component.Properties {
|
||||
cpe, err := cpe.New(property.Value)
|
||||
c, err := cpe.New(property.Value)
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: could not parse CPE: %v.", err)
|
||||
continue
|
||||
}
|
||||
cpes = append(cpes, cpe)
|
||||
cpes = append(cpes, c)
|
||||
}
|
||||
return pkg.Package{
|
||||
Name: component.Name,
|
||||
@@ -139,59 +139,59 @@ func getPackage(component nativeImageComponent) pkg.Package {
|
||||
}
|
||||
|
||||
// decompressSbom returns the packages given within a native image executable's SBOM.
|
||||
func decompressSbom(databuf []byte, sbomStart uint64, lengthStart uint64) ([]pkg.Package, error) {
|
||||
func decompressSbom(dataBuf []byte, sbomStart uint64, lengthStart uint64) ([]pkg.Package, error) {
|
||||
var pkgs []pkg.Package
|
||||
|
||||
lengthEnd := lengthStart + 8
|
||||
buflen := len(databuf)
|
||||
if lengthEnd > uint64(buflen) {
|
||||
return nil, errors.New("the sbom_length symbol overflows the binary")
|
||||
bufLen := len(dataBuf)
|
||||
if lengthEnd > uint64(bufLen) {
|
||||
return nil, errors.New("the 'sbom_length' symbol overflows the binary")
|
||||
}
|
||||
|
||||
length := databuf[lengthStart:lengthEnd]
|
||||
length := dataBuf[lengthStart:lengthEnd]
|
||||
p := bytes.NewBuffer(length)
|
||||
var storedLength uint64
|
||||
err := binary.Read(p, binary.LittleEndian, &storedLength)
|
||||
if err != nil {
|
||||
log.Debugf("native-image-cataloger: could not read from binary file.")
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not read from binary file: %w", err)
|
||||
}
|
||||
log.Tracef("native-image cataloger: found SBOM of length %d.", storedLength)
|
||||
|
||||
log.WithFields("len", storedLength).Trace("native-image cataloger: found SBOM")
|
||||
sbomEnd := sbomStart + storedLength
|
||||
if sbomEnd > uint64(buflen) {
|
||||
if sbomEnd > uint64(bufLen) {
|
||||
return nil, errors.New("the sbom symbol overflows the binary")
|
||||
}
|
||||
sbomCompressed := databuf[sbomStart:sbomEnd]
|
||||
|
||||
sbomCompressed := dataBuf[sbomStart:sbomEnd]
|
||||
p = bytes.NewBuffer(sbomCompressed)
|
||||
gzreader, err := gzip.NewReader(p)
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: could not decompress the SBOM.")
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not decompress the native-image SBOM: %w", err)
|
||||
}
|
||||
|
||||
output, err := io.ReadAll(gzreader)
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: could not read the decompressed SBOM.")
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not read the native-image SBOM: %w", err)
|
||||
}
|
||||
|
||||
var sbomContent nativeImageCycloneDX
|
||||
err = json.Unmarshal(output, &sbomContent)
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: could not unmarshal JSON.")
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not unmarshal the native-image SBOM: %w", err)
|
||||
}
|
||||
|
||||
for _, component := range sbomContent.Components {
|
||||
p := getPackage(component)
|
||||
pkgs = append(pkgs, p)
|
||||
}
|
||||
|
||||
return pkgs, nil
|
||||
}
|
||||
|
||||
// fileError logs an error message when an executable cannot be read.
|
||||
func fileError(filename string, err error) (nativeImage, error) {
|
||||
// We could not read the file as a binary for the desired platform, but it may still be a native-image executable.
|
||||
log.Debugf("native-image cataloger: unable to read executable (file=%q): %v.", filename, err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to read executable (file=%q): %w", filename, err)
|
||||
}
|
||||
|
||||
// newElf reads a Native Image from an ELF executable.
|
||||
@@ -231,8 +231,15 @@ func newPE(filename string, r io.ReaderAt) (nativeImage, error) {
|
||||
if err != nil {
|
||||
return fileError(filename, err)
|
||||
}
|
||||
optionalHeader := bi.OptionalHeader.(*pe.OptionalHeader64)
|
||||
exportSymbolsDataDirectory := optionalHeader.DataDirectory[0]
|
||||
var exportSymbolsDataDirectory pe.DataDirectory
|
||||
switch h := bi.OptionalHeader.(type) {
|
||||
case *pe.OptionalHeader32:
|
||||
exportSymbolsDataDirectory = h.DataDirectory[0]
|
||||
case *pe.OptionalHeader64:
|
||||
exportSymbolsDataDirectory = h.DataDirectory[0]
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to get 'exportSymbolsDataDirectory' from binary: %s", filename)
|
||||
}
|
||||
// If we have no exported symbols it is not a Native Image
|
||||
if exportSymbolsDataDirectory.Size == 0 {
|
||||
return fileError(filename, errors.New(nativeImageMissingExportedDataDirectoryError))
|
||||
@@ -241,8 +248,7 @@ func newPE(filename string, r io.ReaderAt) (nativeImage, error) {
|
||||
exports := make([]byte, exportSymbolsDataDirectory.Size)
|
||||
_, err = r.ReadAt(exports, int64(exportSymbolsOffset))
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: could not read the exported symbols data directory: %v.", err)
|
||||
return fileError(filename, err)
|
||||
return fileError(filename, fmt.Errorf("could not read the exported symbols data directory: %w", err))
|
||||
}
|
||||
return nativeImagePE{
|
||||
file: bi,
|
||||
@@ -266,16 +272,30 @@ func newPE(filename string, r io.ReaderAt) (nativeImage, error) {
|
||||
}
|
||||
|
||||
// fetchPkgs obtains the packages given in the binary.
|
||||
func (ni nativeImageElf) fetchPkgs() ([]pkg.Package, error) {
|
||||
func (ni nativeImageElf) fetchPkgs() (pkgs []pkg.Package, retErr error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// this can happen in cases where a malformed binary is passed in can be initially parsed, but not
|
||||
// used without error later down the line.
|
||||
retErr = fmt.Errorf("recovered from panic: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
bi := ni.file
|
||||
if bi == nil {
|
||||
log.Debugf("native-image cataloger: file is nil")
|
||||
return nil, nil
|
||||
}
|
||||
var sbom elf.Symbol
|
||||
var sbomLength elf.Symbol
|
||||
var svmVersion elf.Symbol
|
||||
|
||||
si, err := bi.Symbols()
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: no symbols found.")
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("no symbols found in binary: %w", err)
|
||||
}
|
||||
if si == nil {
|
||||
return nil, errors.New(nativeImageMissingSymbolsError)
|
||||
}
|
||||
for _, s := range si {
|
||||
switch s.Name {
|
||||
@@ -288,19 +308,16 @@ func (ni nativeImageElf) fetchPkgs() ([]pkg.Package, error) {
|
||||
}
|
||||
}
|
||||
if sbom.Value == 0 || sbomLength.Value == 0 || svmVersion.Value == 0 {
|
||||
log.Debugf("native-image cataloger: %v", nativeImageMissingSymbolsError)
|
||||
return nil, errors.New(nativeImageMissingSymbolsError)
|
||||
}
|
||||
dataSection := bi.Section(".data")
|
||||
if dataSection == nil {
|
||||
log.Debugf("native-image cataloger: .data section missing from ELF file.")
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("no .data section found in binary: %w", err)
|
||||
}
|
||||
dataSectionBase := dataSection.SectionHeader.Addr
|
||||
data, err := dataSection.Data()
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: cannot read the .data section.")
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("cannot read the .data section: %w", err)
|
||||
}
|
||||
sbomLocation := sbom.Value - dataSectionBase
|
||||
lengthLocation := sbomLength.Value - dataSectionBase
|
||||
@@ -309,12 +326,27 @@ func (ni nativeImageElf) fetchPkgs() ([]pkg.Package, error) {
|
||||
}
|
||||
|
||||
// fetchPkgs obtains the packages from a Native Image given as a Mach O file.
|
||||
func (ni nativeImageMachO) fetchPkgs() ([]pkg.Package, error) {
|
||||
func (ni nativeImageMachO) fetchPkgs() (pkgs []pkg.Package, retErr error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// this can happen in cases where a malformed binary is passed in can be initially parsed, but not
|
||||
// used without error later down the line.
|
||||
retErr = fmt.Errorf("recovered from panic: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
var sbom macho.Symbol
|
||||
var sbomLength macho.Symbol
|
||||
var svmVersion macho.Symbol
|
||||
|
||||
bi := ni.file
|
||||
if bi == nil {
|
||||
log.Debugf("native-image cataloger: file is nil")
|
||||
return nil, nil
|
||||
}
|
||||
if bi.Symtab == nil {
|
||||
return nil, errors.New(nativeImageMissingSymbolsError)
|
||||
}
|
||||
for _, s := range bi.Symtab.Syms {
|
||||
switch s.Name {
|
||||
case "_" + nativeImageSbomSymbol:
|
||||
@@ -326,7 +358,6 @@ func (ni nativeImageMachO) fetchPkgs() ([]pkg.Package, error) {
|
||||
}
|
||||
}
|
||||
if sbom.Value == 0 || sbomLength.Value == 0 || svmVersion.Value == 0 {
|
||||
log.Debugf("native-image cataloger: %v.", nativeImageMissingSymbolsError)
|
||||
return nil, errors.New(nativeImageMissingSymbolsError)
|
||||
}
|
||||
|
||||
@@ -334,7 +365,7 @@ func (ni nativeImageMachO) fetchPkgs() ([]pkg.Package, error) {
|
||||
if dataSegment == nil {
|
||||
return nil, nil
|
||||
}
|
||||
databuf, err := dataSegment.Data()
|
||||
dataBuf, err := dataSegment.Data()
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: cannot obtain buffer from data segment.")
|
||||
return nil, nil
|
||||
@@ -342,7 +373,7 @@ func (ni nativeImageMachO) fetchPkgs() ([]pkg.Package, error) {
|
||||
sbomLocation := sbom.Value - dataSegment.Addr
|
||||
lengthLocation := sbomLength.Value - dataSegment.Addr
|
||||
|
||||
return decompressSbom(databuf, sbomLocation, lengthLocation)
|
||||
return decompressSbom(dataBuf, sbomLocation, lengthLocation)
|
||||
}
|
||||
|
||||
// fetchExportAttribute obtains an attribute from the exported symbols directory entry.
|
||||
@@ -389,23 +420,19 @@ func (ni nativeImagePE) fetchExportContent() (*exportContentPE, error) {
|
||||
var err error
|
||||
content.numberOfFunctions, err = ni.fetchExportAttribute(0)
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: could not find the number of exported functions attribute: %v", err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not find the number of exported 'number of functions' attribute: %w", err)
|
||||
}
|
||||
content.numberOfNames, err = ni.fetchExportAttribute(1)
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: could not find the number of exported names attribute: %v", err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not find the number of exported 'number of names' attribute: %w", err)
|
||||
}
|
||||
content.addressOfFunctions, err = ni.fetchExportAttribute(2)
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: could not find the exported functions attribute: %v", err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not find the exported 'address of functions' attribute: %w", err)
|
||||
}
|
||||
content.addressOfNames, err = ni.fetchExportAttribute(3)
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: could not find the exported names attribute: %v", err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not find the exported 'address of names' attribute: %w", err)
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
@@ -453,7 +480,15 @@ func (ni nativeImagePE) fetchSbomSymbols(content *exportContentPE) {
|
||||
}
|
||||
|
||||
// fetchPkgs obtains the packages from a Native Image given as a PE file.
|
||||
func (ni nativeImagePE) fetchPkgs() ([]pkg.Package, error) {
|
||||
func (ni nativeImagePE) fetchPkgs() (pkgs []pkg.Package, retErr error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// this can happen in cases where a malformed binary is passed in can be initially parsed, but not
|
||||
// used without error later down the line.
|
||||
retErr = fmt.Errorf("recovered from panic: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
content, err := ni.fetchExportContent()
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: could not fetch the content of the export directory entry: %v.", err)
|
||||
@@ -461,28 +496,25 @@ func (ni nativeImagePE) fetchPkgs() ([]pkg.Package, error) {
|
||||
}
|
||||
ni.fetchSbomSymbols(content)
|
||||
if content.addressOfSbom == uint32(0) || content.addressOfSbomLength == uint32(0) || content.addressOfSvmVersion == uint32(0) {
|
||||
log.Debugf("native-image cataloger: %v.", nativeImageMissingSymbolsError)
|
||||
return nil, errors.New(nativeImageMissingSymbolsError)
|
||||
}
|
||||
functionsBase := content.addressOfFunctions - ni.exportSymbols.VirtualAddress
|
||||
sbomOffset := content.addressOfSbom
|
||||
sbomAddress, err := ni.fetchExportFunctionPointer(functionsBase, sbomOffset)
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: cannot fetch SBOM pointer from exported functions: %v.", err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not fetch SBOM pointer from exported functions: %w", err)
|
||||
}
|
||||
sbomLengthOffset := content.addressOfSbomLength
|
||||
sbomLengthAddress, err := ni.fetchExportFunctionPointer(functionsBase, sbomLengthOffset)
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: cannot fetch SBOM length pointer from exported functions: %v.", err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not fetch SBOM length pointer from exported functions: %w", err)
|
||||
}
|
||||
bi := ni.file
|
||||
dataSection := bi.Section(".data")
|
||||
if dataSection == nil {
|
||||
return nil, nil
|
||||
}
|
||||
databuf, err := dataSection.Data()
|
||||
dataBuf, err := dataSection.Data()
|
||||
if err != nil {
|
||||
log.Debugf("native-image cataloger: cannot obtain buffer from .data section.")
|
||||
return nil, nil
|
||||
@@ -490,7 +522,7 @@ func (ni nativeImagePE) fetchPkgs() ([]pkg.Package, error) {
|
||||
sbomLocation := sbomAddress - dataSection.VirtualAddress
|
||||
lengthLocation := sbomLengthAddress - dataSection.VirtualAddress
|
||||
|
||||
return decompressSbom(databuf, uint64(sbomLocation), uint64(lengthLocation))
|
||||
return decompressSbom(dataBuf, uint64(sbomLocation), uint64(lengthLocation))
|
||||
}
|
||||
|
||||
// fetchPkgs provides the packages available in a UnionReader.
|
||||
|
||||
@@ -21,14 +21,20 @@ import (
|
||||
func TestParseNativeImage(t *testing.T) {
|
||||
tests := []struct {
|
||||
fixture string
|
||||
newFn func(filename string, r io.ReaderAt) (nativeImage, error)
|
||||
}{
|
||||
{
|
||||
fixture: "test-fixtures/java-builds/packages/example-java-app",
|
||||
fixture: "example-java-app",
|
||||
newFn: newElf,
|
||||
},
|
||||
{
|
||||
fixture: "gcc-amd64-darwin-exec-debug",
|
||||
newFn: newMachO,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(path.Base(test.fixture), func(t *testing.T) {
|
||||
f, err := os.Open(test.fixture)
|
||||
t.Run(test.fixture, func(t *testing.T) {
|
||||
f, err := os.Open("test-fixtures/java-builds/packages/" + test.fixture)
|
||||
assert.NoError(t, err)
|
||||
readerCloser := io.ReadCloser(ioutil.NopCloser(f))
|
||||
reader, err := unionreader.GetUnionReader(readerCloser)
|
||||
@@ -36,7 +42,7 @@ func TestParseNativeImage(t *testing.T) {
|
||||
parsed := false
|
||||
readers, err := unionreader.GetReaders(reader)
|
||||
for _, r := range readers {
|
||||
ni, err := newElf(test.fixture, r)
|
||||
ni, err := test.newFn(test.fixture, r)
|
||||
assert.NoError(t, err)
|
||||
_, err = ni.fetchPkgs()
|
||||
if err == nil {
|
||||
@@ -108,12 +114,12 @@ func TestParseNativeImageSbom(t *testing.T) {
|
||||
z := gzip.NewWriter(writebytes)
|
||||
_, err = z.Write(sbom)
|
||||
assert.NoError(t, err)
|
||||
z.Close()
|
||||
writebytes.Flush()
|
||||
_ = z.Close()
|
||||
_ = writebytes.Flush()
|
||||
compressedsbom := b.Bytes()
|
||||
sbomlength := uint64(len(compressedsbom))
|
||||
binary.Write(writebytes, binary.LittleEndian, sbomlength)
|
||||
writebytes.Flush()
|
||||
_ = binary.Write(writebytes, binary.LittleEndian, sbomlength)
|
||||
_ = writebytes.Flush()
|
||||
compressedsbom = b.Bytes()
|
||||
actual, err := decompressSbom(compressedsbom, 0, sbomlength)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -17,7 +17,7 @@ jars: $(PKGSDIR)/example-java-app-maven-0.1.0.jar $(PKGSDIR)/example-java-app-gr
|
||||
|
||||
archives: $(PKGSDIR)/example-java-app-maven-0.1.0.zip $(PKGSDIR)/example-java-app-maven-0.1.0.tar $(PKGSDIR)/example-java-app-maven-0.1.0.tar.gz
|
||||
|
||||
native-image: $(PKGSDIR)/example-java-app
|
||||
native-image: $(PKGSDIR)/example-java-app $(PKGSDIR)/gcc-amd64-darwin-exec-debug
|
||||
|
||||
# jars within archives...
|
||||
|
||||
@@ -68,8 +68,11 @@ clean-jenkins:
|
||||
$(PKGSDIR)/example-java-app: $(PKGSDIR)/example-java-app-maven-0.1.0.jar
|
||||
./build-example-java-app-native-image.sh $(PKGSDIR)
|
||||
|
||||
$(PKGSDIR)/gcc-amd64-darwin-exec-debug:
|
||||
./build-example-macho-binary.sh $(PKGSDIR)
|
||||
|
||||
# we need a way to determine if CI should bust the test cache based on the source material
|
||||
$(PKGSDIR).fingerprint: clean-examples
|
||||
mkdir -p $(PKGSDIR)
|
||||
find example-* -type f -exec sha256sum {} \; | sort | tee /dev/stderr | tee $(PKGSDIR).fingerprint
|
||||
find example-* build-* Makefile -type f -exec sha256sum {} \; | sort | tee /dev/stderr | tee $(PKGSDIR).fingerprint
|
||||
sha256sum $(PKGSDIR).fingerprint
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -uxe
|
||||
|
||||
PKGSDIR=$1
|
||||
|
||||
curl https://raw.githubusercontent.com/blacktop/go-macho/master/internal/testdata/gcc-amd64-darwin-exec-debug.base64 |
|
||||
base64 -d > $PKGSDIR/gcc-amd64-darwin-exec-debug
|
||||
@@ -1,10 +1,15 @@
|
||||
package pkg
|
||||
|
||||
// GolangMetadata represents all captured data for a Golang Binary
|
||||
type GolangMetadata struct {
|
||||
// GolangBinMetadata represents all captured data for a Golang binary
|
||||
type GolangBinMetadata struct {
|
||||
BuildSettings map[string]string `json:"goBuildSettings,omitempty" cyclonedx:"goBuildSettings"`
|
||||
GoCompiledVersion string `json:"goCompiledVersion" cyclonedx:"goCompiledVersion"`
|
||||
Architecture string `json:"architecture" cyclonedx:"architecture"`
|
||||
H1Digest string `json:"h1Digest,omitempty" cyclonedx:"h1Digest"`
|
||||
MainModule string `json:"mainModule,omitempty" cyclonedx:"mainModule"`
|
||||
}
|
||||
|
||||
// GolangModMetadata represents all captured data for a Golang source scan with go.mod/go.sum
|
||||
type GolangModMetadata struct {
|
||||
H1Digest string `json:"h1Digest,omitempty" cyclonedx:"h1Digest"`
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ const (
|
||||
DotnetDepsMetadataType MetadataType = "DotnetDepsMetadata"
|
||||
DpkgMetadataType MetadataType = "DpkgMetadata"
|
||||
GemMetadataType MetadataType = "GemMetadata"
|
||||
GolangMetadataType MetadataType = "GolangMetadata"
|
||||
GolangBinMetadataType MetadataType = "GolangBinMetadata"
|
||||
GolangModMetadataType MetadataType = "GolangModMetadata"
|
||||
HackageMetadataType MetadataType = "HackageMetadataType"
|
||||
JavaMetadataType MetadataType = "JavaMetadata"
|
||||
KbPackageMetadataType MetadataType = "KbPackageMetadata"
|
||||
@@ -47,7 +48,8 @@ var AllMetadataTypes = []MetadataType{
|
||||
DotnetDepsMetadataType,
|
||||
DpkgMetadataType,
|
||||
GemMetadataType,
|
||||
GolangMetadataType,
|
||||
GolangBinMetadataType,
|
||||
GolangModMetadataType,
|
||||
HackageMetadataType,
|
||||
JavaMetadataType,
|
||||
KbPackageMetadataType,
|
||||
@@ -74,7 +76,8 @@ var MetadataTypeByName = map[MetadataType]reflect.Type{
|
||||
DotnetDepsMetadataType: reflect.TypeOf(DotnetDepsMetadata{}),
|
||||
DpkgMetadataType: reflect.TypeOf(DpkgMetadata{}),
|
||||
GemMetadataType: reflect.TypeOf(GemMetadata{}),
|
||||
GolangMetadataType: reflect.TypeOf(GolangMetadata{}),
|
||||
GolangBinMetadataType: reflect.TypeOf(GolangBinMetadata{}),
|
||||
GolangModMetadataType: reflect.TypeOf(GolangModMetadata{}),
|
||||
HackageMetadataType: reflect.TypeOf(HackageMetadata{}),
|
||||
JavaMetadataType: reflect.TypeOf(JavaMetadata{}),
|
||||
KbPackageMetadataType: reflect.TypeOf(KbPackageMetadata{}),
|
||||
@@ -94,8 +97,8 @@ func CleanMetadataType(typ MetadataType) MetadataType {
|
||||
if typ == "RpmdbMetadata" {
|
||||
return RpmMetadataType
|
||||
}
|
||||
if typ == "GolangBinMetadata" {
|
||||
return GolangMetadataType
|
||||
if typ == "GolangMetadata" {
|
||||
return GolangBinMetadataType
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# change these if you want CI to not use previous stored cache
|
||||
CLI_CACHE_BUSTER := "e5cdfd8"
|
||||
|
||||
.PHONY: cache.fingerprint
|
||||
cache.fingerprint:
|
||||
find image-* -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum | tee cache.fingerprint && echo "$(CLI_CACHE_BUSTER)" >> cache.fingerprint
|
||||
@@ -0,0 +1,6 @@
|
||||
# change these if you want CI to not use previous stored cache
|
||||
INTEGRATION_CACHE_BUSTER := "894d8ca"
|
||||
|
||||
.PHONY: cache.fingerprint
|
||||
cache.fingerprint:
|
||||
find image-* -type f -exec md5sum {} + | awk '{print $1}' | sort | tee /dev/stderr | md5sum | tee cache.fingerprint && echo "$(INTEGRATION_CACHE_BUSTER)" >> cache.fingerprint
|
||||
@@ -604,7 +604,10 @@ func AttestationStartedHandler(ctx context.Context, fr *frame.Frame, event party
|
||||
line, _ := fr.Append()
|
||||
if l.Len() > 5 {
|
||||
elem := l.Front()
|
||||
line := elem.Value.(*frame.Line)
|
||||
line, ok := elem.Value.(*frame.Line)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
err = line.Remove()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -627,7 +630,10 @@ func AttestationStartedHandler(ctx context.Context, fr *frame.Frame, event party
|
||||
if !failed {
|
||||
// roll up logs into completed status (only if successful)
|
||||
for e := l.Back(); e != nil; e = e.Prev() {
|
||||
line := e.Value.(*frame.Line)
|
||||
line, ok := e.Value.(*frame.Line)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
err = line.Remove()
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user