Compare commits

..
Author SHA1 Message Date
Alex Goodman e841b03219 [wip] remove sqlite import
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-05-19 11:50:45 -04:00
62 changed files with 1206 additions and 5182 deletions
+17 -4
View File
@@ -34,7 +34,7 @@ tools:
# used for showing the changelog at release
- name: glow
version:
want: v2.1.1
want: v2.1.0
method: github-release
with:
repo: charmbracelet/glow
@@ -58,7 +58,7 @@ tools:
# used to release all artifacts
- name: goreleaser
version:
want: v2.10.2
want: v2.9.0
method: github-release
with:
repo: goreleaser/goreleaser
@@ -87,10 +87,23 @@ tools:
with:
repo: wagoodman/go-bouncer
# used for showing benchmark testing
- name: benchstat
version:
want: latest
method: go-proxy
with:
module: golang.org/x/perf
allow-unresolved-version: true
method: go-install
with:
entrypoint: cmd/benchstat
module: golang.org/x/perf
# used for running all local and CI tasks
- name: task
version:
want: v3.44.0
want: v3.43.3
method: github-release
with:
repo: go-task/task
@@ -98,7 +111,7 @@ tools:
# used for triggering a release
- name: gh
version:
want: v2.74.0
want: v2.72.0
method: github-release
with:
repo: cli/cli
+3 -3
View File
@@ -45,7 +45,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e #v3.28.19
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f #v3.28.18
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@fca7ace96b7d713c7035871441bd52efbe39e27e #v3.28.19
uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f #v3.28.18
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -70,4 +70,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e #v3.28.19
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f #v3.28.18
-3
View File
@@ -76,9 +76,6 @@ jobs:
- name: Build snapshot artifacts
run: make snapshot
- name: Smoke test snapshot build
run: make snapshot-smoke-test
# 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
+248 -238
View File
@@ -1,11 +1,11 @@
version: 2
project_name: syft
release:
prerelease: auto
draft: false
env:
# required to support multi architecture docker builds
- DOCKER_CLI_EXPERIMENTAL=enabled
- CGO_ENABLED=0
@@ -13,8 +13,11 @@ builds:
- id: linux-build
dir: ./cmd/syft
binary: syft
goos: [linux]
goarch: [amd64, arm64, ppc64le, s390x]
goos:
- windows
goarch:
- amd64
# set the modified timestamp on the output binary to the git timestamp to ensure a reproducible build
mod_timestamp: &build-timestamp '{{ .CommitTimestamp }}'
ldflags: &build-ldflags |
-w
@@ -25,239 +28,246 @@ builds:
-X main.buildDate={{.Date}}
-X main.gitDescription={{.Summary}}
- id: darwin-build
dir: ./cmd/syft
binary: syft
goos: [darwin]
goarch: [amd64, arm64]
mod_timestamp: *build-timestamp
ldflags: *build-ldflags
hooks:
post:
- cmd: .tool/quill sign-and-notarize "{{ .Path }}" --dry-run={{ .IsSnapshot }} --ad-hoc={{ .IsSnapshot }} -vv
env:
- QUILL_LOG_FILE=/tmp/quill-{{ .Target }}.log
# - id: darwin-build
# dir: ./cmd/syft
# binary: syft
# goos:
# - darwin
# goarch:
# - amd64
# - arm64
# mod_timestamp: *build-timestamp
# ldflags: *build-ldflags
# hooks:
# post:
# - cmd: .tool/quill sign-and-notarize "{{ .Path }}" --dry-run={{ .IsSnapshot }} --ad-hoc={{ .IsSnapshot }} -vv
# env:
# - QUILL_LOG_FILE=/tmp/quill-{{ .Target }}.log
#
# - id: windows-build
# dir: ./cmd/syft
# binary: syft
# goos:
# - windows
# goarch:
# - amd64
# mod_timestamp: *build-timestamp
# ldflags: *build-ldflags
- id: windows-build
dir: ./cmd/syft
binary: syft
goos: [windows]
goarch: [amd64]
mod_timestamp: *build-timestamp
ldflags: *build-ldflags
archives:
- id: linux-archives
builds: [linux-build]
- id: darwin-archives
builds: [darwin-build]
- id: windows-archives
format: zip
builds: [windows-build]
nfpms:
- license: "Apache 2.0"
maintainer: "Anchore, Inc"
homepage: &website "https://github.com/anchore/syft"
description: &description "A tool that generates a Software Bill Of Materials (SBOM) from container images and filesystems"
formats: [rpm, deb]
brews:
- repository:
owner: anchore
name: homebrew-syft
token: "{{.Env.GITHUB_BREW_TOKEN}}"
ids: [darwin-archives, linux-archives]
homepage: *website
description: *description
license: "Apache License 2.0"
dockers:
# production images...
- image_templates:
- anchore/syft:{{.Tag}}-amd64
- ghcr.io/anchore/syft:{{.Tag}}-amd64
goarch: amd64
dockerfile: Dockerfile
use: buildx
build_flag_templates:
- "--platform=linux/amd64"
- "--build-arg=BUILD_DATE={{.Date}}"
- "--build-arg=BUILD_VERSION={{.Version}}"
- "--build-arg=VCS_REF={{.FullCommit}}"
- "--build-arg=VCS_URL={{.GitURL}}"
- image_templates:
- anchore/syft:{{.Tag}}-arm64v8
- ghcr.io/anchore/syft:{{.Tag}}-arm64v8
goarch: arm64
dockerfile: Dockerfile
use: buildx
build_flag_templates:
- "--platform=linux/arm64/v8"
- "--build-arg=BUILD_DATE={{.Date}}"
- "--build-arg=BUILD_VERSION={{.Version}}"
- "--build-arg=VCS_REF={{.FullCommit}}"
- "--build-arg=VCS_URL={{.GitURL}}"
- image_templates:
- anchore/syft:{{.Tag}}-ppc64le
- ghcr.io/anchore/syft:{{.Tag}}-ppc64le
goarch: ppc64le
dockerfile: Dockerfile
use: buildx
build_flag_templates:
- "--platform=linux/ppc64le"
- "--build-arg=BUILD_DATE={{.Date}}"
- "--build-arg=BUILD_VERSION={{.Version}}"
- "--build-arg=VCS_REF={{.FullCommit}}"
- "--build-arg=VCS_URL={{.GitURL}}"
- image_templates:
- anchore/syft:{{.Tag}}-s390x
- ghcr.io/anchore/syft:{{.Tag}}-s390x
goarch: s390x
dockerfile: Dockerfile
use: buildx
build_flag_templates:
- "--platform=linux/s390x"
- "--build-arg=BUILD_DATE={{.Date}}"
- "--build-arg=BUILD_VERSION={{.Version}}"
- "--build-arg=VCS_REF={{.FullCommit}}"
- "--build-arg=VCS_URL={{.GitURL}}"
# debug images...
- image_templates:
- anchore/syft:{{.Tag}}-debug-amd64
- ghcr.io/anchore/syft:{{.Tag}}-debug-amd64
goarch: amd64
dockerfile: Dockerfile.debug
use: buildx
build_flag_templates:
- "--platform=linux/amd64"
- "--build-arg=BUILD_DATE={{.Date}}"
- "--build-arg=BUILD_VERSION={{.Version}}"
- "--build-arg=VCS_REF={{.FullCommit}}"
- "--build-arg=VCS_URL={{.GitURL}}"
- image_templates:
- anchore/syft:{{.Tag}}-debug-arm64v8
- ghcr.io/anchore/syft:{{.Tag}}-debug-arm64v8
goarch: arm64
dockerfile: Dockerfile.debug
use: buildx
build_flag_templates:
- "--platform=linux/arm64/v8"
- "--build-arg=BUILD_DATE={{.Date}}"
- "--build-arg=BUILD_VERSION={{.Version}}"
- "--build-arg=VCS_REF={{.FullCommit}}"
- "--build-arg=VCS_URL={{.GitURL}}"
- image_templates:
- anchore/syft:{{.Tag}}-debug-ppc64le
- ghcr.io/anchore/syft:{{.Tag}}-debug-ppc64le
goarch: ppc64le
dockerfile: Dockerfile.debug
use: buildx
build_flag_templates:
- "--platform=linux/ppc64le"
- "--build-arg=BUILD_DATE={{.Date}}"
- "--build-arg=BUILD_VERSION={{.Version}}"
- "--build-arg=VCS_REF={{.FullCommit}}"
- "--build-arg=VCS_URL={{.GitURL}}"
- image_templates:
- anchore/syft:{{.Tag}}-debug-s390x
- ghcr.io/anchore/syft:{{.Tag}}-debug-s390x
goarch: s390x
dockerfile: Dockerfile.debug
use: buildx
build_flag_templates:
- "--platform=linux/s390x"
- "--build-arg=BUILD_DATE={{.Date}}"
- "--build-arg=BUILD_VERSION={{.Version}}"
- "--build-arg=VCS_REF={{.FullCommit}}"
- "--build-arg=VCS_URL={{.GitURL}}"
docker_manifests:
# anchore/syft manifests...
- name_template: anchore/syft:latest
image_templates:
- anchore/syft:{{.Tag}}-amd64
- anchore/syft:{{.Tag}}-arm64v8
- anchore/syft:{{.Tag}}-ppc64le
- anchore/syft:{{.Tag}}-s390x
- name_template: anchore/syft:{{.Tag}}
image_templates:
- anchore/syft:{{.Tag}}-amd64
- anchore/syft:{{.Tag}}-arm64v8
- anchore/syft:{{.Tag}}-ppc64le
- anchore/syft:{{.Tag}}-s390x
- name_template: anchore/syft:debug
image_templates:
- anchore/syft:{{.Tag}}-debug-amd64
- anchore/syft:{{.Tag}}-debug-arm64v8
- anchore/syft:{{.Tag}}-debug-ppc64le
- anchore/syft:{{.Tag}}-debug-s390x
- name_template: anchore/syft:{{.Tag}}-debug
image_templates:
- anchore/syft:{{.Tag}}-debug-amd64
- anchore/syft:{{.Tag}}-debug-arm64v8
- anchore/syft:{{.Tag}}-debug-ppc64le
- anchore/syft:{{.Tag}}-debug-s390x
# ghcr.io/anchore/syft manifests...
- name_template: ghcr.io/anchore/syft:latest
image_templates:
- ghcr.io/anchore/syft:{{.Tag}}-amd64
- ghcr.io/anchore/syft:{{.Tag}}-arm64v8
- ghcr.io/anchore/syft:{{.Tag}}-ppc64le
- ghcr.io/anchore/syft:{{.Tag}}-s390x
- name_template: ghcr.io/anchore/syft:{{.Tag}}
image_templates:
- ghcr.io/anchore/syft:{{.Tag}}-amd64
- ghcr.io/anchore/syft:{{.Tag}}-arm64v8
- ghcr.io/anchore/syft:{{.Tag}}-ppc64le
- ghcr.io/anchore/syft:{{.Tag}}-s390x
- name_template: ghcr.io/anchore/syft:debug
image_templates:
- ghcr.io/anchore/syft:{{.Tag}}-debug-amd64
- ghcr.io/anchore/syft:{{.Tag}}-debug-arm64v8
- ghcr.io/anchore/syft:{{.Tag}}-debug-ppc64le
- ghcr.io/anchore/syft:{{.Tag}}-debug-s390x
- name_template: ghcr.io/anchore/syft:{{.Tag}}-debug
image_templates:
- ghcr.io/anchore/syft:{{.Tag}}-debug-amd64
- ghcr.io/anchore/syft:{{.Tag}}-debug-arm64v8
- ghcr.io/anchore/syft:{{.Tag}}-debug-ppc64le
- ghcr.io/anchore/syft:{{.Tag}}-debug-s390x
sboms:
- artifacts: archive
cmd: ../.tool/syft
documents:
- "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}.sbom"
args:
- "scan"
- "$artifact"
- "--output"
- "json=$document"
signs:
- cmd: .tool/cosign
signature: "${artifact}.sig"
certificate: "${artifact}.pem"
args:
- "sign-blob"
- "--oidc-issuer=https://token.actions.githubusercontent.com"
- "--output-certificate=${certificate}"
- "--output-signature=${signature}"
- "${artifact}"
- "--yes"
artifacts: checksum
#archives:
# - id: linux-archives
# builds:
# - linux-build
#
# # note: the signing process is depending on tar.gz archives. If this format changes then .github/scripts/apple-signing/*.sh will need to be adjusted
# - id: darwin-archives
# builds:
# - darwin-build
#
# - id: windows-archives
# format: zip
# builds:
# - windows-build
#
#nfpms:
# - license: "Apache 2.0"
# maintainer: "Anchore, Inc"
# homepage: &website "https://github.com/anchore/syft"
# description: &description "A tool that generates a Software Bill Of Materials (SBOM) from container images and filesystems"
# formats:
# - rpm
# - deb
#
#brews:
# - repository:
# owner: anchore
# name: homebrew-syft
# token: "{{.Env.GITHUB_BREW_TOKEN}}"
# ids:
# - darwin-archives
# - linux-archives
# homepage: *website
# description: *description
# license: "Apache License 2.0"
#
#dockers:
# - image_templates:
# - anchore/syft:debug
# - anchore/syft:{{.Tag}}-debug
# - ghcr.io/anchore/syft:debug
# - ghcr.io/anchore/syft:{{.Tag}}-debug
# goarch: amd64
# dockerfile: Dockerfile.debug
# use: buildx
# build_flag_templates:
# - "--platform=linux/amd64"
# - "--build-arg=BUILD_DATE={{.Date}}"
# - "--build-arg=BUILD_VERSION={{.Version}}"
# - "--build-arg=VCS_REF={{.FullCommit}}"
# - "--build-arg=VCS_URL={{.GitURL}}"
#
# - image_templates:
# - anchore/syft:debug-arm64v8
# - anchore/syft:{{.Tag}}-debug-arm64v8
# - ghcr.io/anchore/syft:debug-arm64v8
# - ghcr.io/anchore/syft:{{.Tag}}-debug-arm64v8
# goarch: arm64
# dockerfile: Dockerfile.debug
# use: buildx
# build_flag_templates:
# - "--platform=linux/arm64/v8"
# - "--build-arg=BUILD_DATE={{.Date}}"
# - "--build-arg=BUILD_VERSION={{.Version}}"
# - "--build-arg=VCS_REF={{.FullCommit}}"
# - "--build-arg=VCS_URL={{.GitURL}}"
#
# - image_templates:
# - anchore/syft:debug-ppc64le
# - anchore/syft:{{.Tag}}-debug-ppc64le
# - ghcr.io/anchore/syft:debug-ppc64le
# - ghcr.io/anchore/syft:{{.Tag}}-debug-ppc64le
# goarch: ppc64le
# dockerfile: Dockerfile.debug
# use: buildx
# build_flag_templates:
# - "--platform=linux/ppc64le"
# - "--build-arg=BUILD_DATE={{.Date}}"
# - "--build-arg=BUILD_VERSION={{.Version}}"
# - "--build-arg=VCS_REF={{.FullCommit}}"
# - "--build-arg=VCS_URL={{.GitURL}}"
#
# - image_templates:
# - anchore/syft:debug-s390x
# - anchore/syft:{{.Tag}}-debug-s390x
# - ghcr.io/anchore/syft:debug-s390x
# - ghcr.io/anchore/syft:{{.Tag}}-debug-s390x
# goarch: s390x
# dockerfile: Dockerfile.debug
# use: buildx
# build_flag_templates:
# - "--platform=linux/s390x"
# - "--build-arg=BUILD_DATE={{.Date}}"
# - "--build-arg=BUILD_VERSION={{.Version}}"
# - "--build-arg=VCS_REF={{.FullCommit}}"
# - "--build-arg=VCS_URL={{.GitURL}}"
#
# - image_templates:
# - anchore/syft:latest
# - anchore/syft:{{.Tag}}
# - ghcr.io/anchore/syft:latest
# - ghcr.io/anchore/syft:{{.Tag}}
# goarch: amd64
# dockerfile: Dockerfile
# use: buildx
# build_flag_templates:
# - "--platform=linux/amd64"
# - "--build-arg=BUILD_DATE={{.Date}}"
# - "--build-arg=BUILD_VERSION={{.Version}}"
# - "--build-arg=VCS_REF={{.FullCommit}}"
# - "--build-arg=VCS_URL={{.GitURL}}"
#
# - image_templates:
# - anchore/syft:{{.Tag}}-arm64v8
# - ghcr.io/anchore/syft:{{.Tag}}-arm64v8
# goarch: arm64
# dockerfile: Dockerfile
# use: buildx
# build_flag_templates:
# - "--platform=linux/arm64/v8"
# - "--build-arg=BUILD_DATE={{.Date}}"
# - "--build-arg=BUILD_VERSION={{.Version}}"
# - "--build-arg=VCS_REF={{.FullCommit}}"
# - "--build-arg=VCS_URL={{.GitURL}}"
#
# - image_templates:
# - anchore/syft:{{.Tag}}-ppc64le
# - ghcr.io/anchore/syft:{{.Tag}}-ppc64le
# goarch: ppc64le
# dockerfile: Dockerfile
# use: buildx
# build_flag_templates:
# - "--platform=linux/ppc64le"
# - "--build-arg=BUILD_DATE={{.Date}}"
# - "--build-arg=BUILD_VERSION={{.Version}}"
# - "--build-arg=VCS_REF={{.FullCommit}}"
# - "--build-arg=VCS_URL={{.GitURL}}"
#
# - image_templates:
# - anchore/syft:{{.Tag}}-s390x
# - ghcr.io/anchore/syft:{{.Tag}}-s390x
# goarch: s390x
# dockerfile: Dockerfile
# use: buildx
# build_flag_templates:
# - "--platform=linux/s390x"
# - "--build-arg=BUILD_DATE={{.Date}}"
# - "--build-arg=BUILD_VERSION={{.Version}}"
# - "--build-arg=VCS_REF={{.FullCommit}}"
# - "--build-arg=VCS_URL={{.GitURL}}"
#
#docker_manifests:
# - name_template: anchore/syft:latest
# image_templates:
# - anchore/syft:{{.Tag}}
# - anchore/syft:{{.Tag}}-arm64v8
# - anchore/syft:{{.Tag}}-ppc64le
# - anchore/syft:{{.Tag}}-s390x
#
# - name_template: anchore/syft:debug
# - anchore/syft:{{.Tag}}-debug
# - anchore/syft:{{.Tag}}-debug-arm64v8
# - anchore/syft:{{.Tag}}-debug-ppc64le
# - anchore/syft:{{.Tag}}-debug-s390x
#
# - name_template: anchore/syft:{{.Tag}}
# image_templates:
# - anchore/syft:{{.Tag}}
# - anchore/syft:{{.Tag}}-arm64v8
# - anchore/syft:{{.Tag}}-ppc64le
# - anchore/syft:{{.Tag}}-s390x
#
# - name_template: ghcr.io/anchore/syft:latest
# image_templates:
# - ghcr.io/anchore/syft:{{.Tag}}
# - ghcr.io/anchore/syft:{{.Tag}}-arm64v8
# - ghcr.io/anchore/syft:{{.Tag}}-ppc64le
# - ghcr.io/anchore/syft:{{.Tag}}-s390x
#
# - name_template: ghcr.io/anchore/syft:debug
# image_templates:
# - ghcr.io/anchore/syft:{{.Tag}}-debug
# - ghcr.io/anchore/syft:{{.Tag}}-debug-arm64v8
# - ghcr.io/anchore/syft:{{.Tag}}-debug-ppc64le
# - ghcr.io/anchore/syft:{{.Tag}}-debug-s390x
#
# - name_template: ghcr.io/anchore/syft:{{.Tag}}
# image_templates:
# - ghcr.io/anchore/syft:{{.Tag}}
# - ghcr.io/anchore/syft:{{.Tag}}-arm64v8
# - ghcr.io/anchore/syft:{{.Tag}}-ppc64le
# - ghcr.io/anchore/syft:{{.Tag}}-s390x
#
#sboms:
# - artifacts: archive
# # this is relative to the snapshot/dist directory, not the root of the repo
# cmd: ../.tool/syft
# documents:
# - "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}.sbom"
# args:
# - "scan"
# - "$artifact"
# - "--output"
# - "json=$document"
#
#signs:
# - cmd: .tool/cosign
# signature: "${artifact}.sig"
# certificate: "${artifact}.pem"
# args:
# - "sign-blob"
# - "--oidc-issuer=https://token.actions.githubusercontent.com"
# - "--output-certificate=${certificate}"
# - "--output-signature=${signature}"
# - "${artifact}"
# - "--yes"
# artifacts: checksum
+5 -3
View File
@@ -1,12 +1,14 @@
FROM gcr.io/distroless/static-debian12:nonroot
FROM gcr.io/distroless/static-debian12:latest AS build
FROM scratch
# needed for version check HTTPS request
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
# create the /tmp dir, which is needed for image content cache
WORKDIR /tmp
COPY syft /
USER nonroot
ARG BUILD_DATE
ARG BUILD_VERSION
ARG VCS_REF
+1 -3
View File
@@ -1,12 +1,10 @@
FROM gcr.io/distroless/static-debian12:debug-nonroot
FROM gcr.io/distroless/static-debian12:debug
# create the /tmp dir, which is needed for image content cache
WORKDIR /tmp
COPY syft /
USER nonroot
ARG BUILD_DATE
ARG BUILD_VERSION
ARG VCS_REF
+26 -16
View File
@@ -27,12 +27,10 @@ vars:
sh: uname -s | tr '[:upper:]' '[:lower:]'
ARCH:
sh: |
[ "$(uname -m)" = "x86_64" ] && echo "amd64_v1" || { [ "$(uname -m)" = "aarch64" ] && echo "arm64_v8.0" || [ "$(uname -m)" = "arm64" ] && echo "arm64_v8.0" || echo $(uname -m); }
[ "$(uname -m)" = "x86_64" ] && echo "amd64_v1" || { [ "$(uname -m)" = "aarch64" ] && echo "arm64_v8.0" || echo $(uname -m); }
PROJECT_ROOT:
sh: echo $PWD
# note: the snapshot dir must be a relative path starting with ./
# e.g. when installing snapshot debs from a local path, ./ forces the deb to be installed in the current working directory instead of referencing a package name
SNAPSHOT_DIR: ./snapshot
SNAPSHOT_BIN: "{{ .PROJECT_ROOT }}/{{ .SNAPSHOT_DIR }}/{{ .OS }}-build_{{ .OS }}_{{ .ARCH }}/{{ .PROJECT }}"
SNAPSHOT_CMD: "{{ .TOOL_DIR }}/goreleaser release --config {{ .TMP_DIR }}/goreleaser.yaml --clean --snapshot --skip=publish --skip=sign"
@@ -232,7 +230,10 @@ tasks:
cli:
desc: Run CLI tests
deps: [tools]
# note: we don't want to regenerate the snapshot unless we have to. In CI it's probable
# that the cache being restored with the correct binary will be rebuilt since the timestamps
# and local checksums will not line up.
deps: [tools, snapshot]
cmds:
- cmd: "echo 'testing binary: {{ .SNAPSHOT_BIN }}'"
silent: true
@@ -249,6 +250,27 @@ tasks:
- cmd: .github/scripts/labeler_test.py
## Benchmark test targets #################################
benchmark:
deps: [tmpdir]
generates:
- "{{ .TMP_DIR }}/benchmark-main.txt"
cmds:
- "go test -p 1 -run=^Benchmark -bench=. -count=7 -benchmem ./... | tee {{ .TMP_DIR }}/benchmark-{{ .VERSION }}.txt"
- |
bash -c "(test -s {{ .TMP_DIR }}/benchmark-main.txt && \
{{ .TOOL_DIR }}/benchstat {{ .TMP_DIR }}/benchmark-main.txt {{ .TMP_DIR }}/benchmark-{{ .VERSION }}.txt || \
{{ .TOOL_DIR }}/benchstat {{ .TMP_DIR }}/benchmark-{{ .VERSION }}.txt) \
| tee {{ .TMP_DIR }}/benchstat.txt"
show-benchstat:
deps: [benchmark, tmpdir]
cmds:
- cmd: "cat {{ .TMP_DIR }}/benchstat.txt"
silent: true
## Test-fixture-related targets #################################
fingerprints:
@@ -551,18 +573,6 @@ tasks:
- "{{ .SNAPSHOT_CMD }}"
snapshot-smoke-test:
desc: Run a smoke test on the snapshot builds + docker images
cmds:
- cmd: "echo 'testing snapshot binary: {{ .SNAPSHOT_BIN }}'"
silent: true
- cmd: "test -f {{ .SNAPSHOT_BIN }} || (find {{ .SNAPSHOT_DIR }} && echo '\nno snapshot found for {{ .SNAPSHOT_BIN }}' && false)"
silent: true
- "{{ .SNAPSHOT_BIN }} version"
- "{{ .SNAPSHOT_BIN }} scan alpine:latest"
- docker run --rm anchore/syft:latest version
- docker run --rm anchore/syft:latest scan alpine:latest
changelog:
desc: Generate a changelog
deps: [tools]
+1 -6
View File
@@ -10,7 +10,6 @@ import (
"github.com/anchore/clio"
"github.com/anchore/syft/cmd/syft/internal"
"github.com/anchore/syft/cmd/syft/internal/commands"
constants "github.com/anchore/syft/internal"
)
// Application constructs the `syft packages` command and aliases the root command to `syft packages`.
@@ -48,7 +47,7 @@ func create(id clio.Identification, out io.Writer) (clio.Application, *cobra.Com
commands.Cataloger(app),
commands.Attest(app),
commands.Convert(app),
clio.VersionCommand(id, schemaVersion),
clio.VersionCommand(id),
clio.ConfigCommand(app, nil),
cranecmd.NewCmdAuthLogin(id.Name), // syft login uses the same command as crane
)
@@ -60,7 +59,3 @@ func create(id clio.Identification, out io.Writer) (clio.Application, *cobra.Com
return app, rootCmd
}
func schemaVersion() (string, any) {
return "SchemaVersion", constants.JSONSchemaVersion
}
@@ -3,8 +3,6 @@ package integration
import (
"testing"
_ "modernc.org/sqlite"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
-2
View File
@@ -1,8 +1,6 @@
package main
import (
_ "modernc.org/sqlite"
"github.com/anchore/clio"
"github.com/anchore/syft/cmd/syft/cli"
"github.com/anchore/syft/cmd/syft/internal"
+23 -27
View File
@@ -24,7 +24,7 @@ require (
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115
github.com/anchore/stereoscope v0.1.5-0.20250604132324-344e29f37f05
github.com/anchore/stereoscope v0.1.4
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
github.com/aquasecurity/go-pep440-version v0.0.1
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
@@ -40,14 +40,14 @@ require (
github.com/elliotchance/phpserialize v1.4.0
github.com/facebookincubator/nvdtools v0.1.5
github.com/github/go-spdx/v2 v2.3.3
github.com/gkampitakis/go-snaps v0.5.13
github.com/gkampitakis/go-snaps v0.5.11
github.com/go-git/go-billy/v5 v5.6.2
github.com/go-git/go-git/v5 v5.16.2
github.com/go-git/go-git/v5 v5.16.0
github.com/go-test/deep v1.1.1
github.com/go-viper/mapstructure/v2 v2.2.1
github.com/gohugoio/hashstructure v0.5.0
github.com/google/go-cmp v0.7.0
github.com/google/go-containerregistry v0.20.5
github.com/google/go-containerregistry v0.20.3
github.com/google/licensecheck v0.3.1
github.com/google/uuid v1.6.0
github.com/gookit/color v1.5.4
@@ -72,7 +72,7 @@ require (
github.com/sassoftware/go-rpmutils v0.4.0
// pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
github.com/sergi/go-diff v1.4.0
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb
github.com/spdx/tools-golang v0.5.5
github.com/spf13/afero v1.14.0
@@ -85,11 +85,11 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
go.uber.org/goleak v1.3.0
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0
golang.org/x/mod v0.25.0
golang.org/x/net v0.41.0
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
golang.org/x/mod v0.24.0
golang.org/x/net v0.40.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.38.0
modernc.org/sqlite v1.37.0 // indirect
)
require (
@@ -133,10 +133,10 @@ require (
github.com/containerd/typeurl/v2 v2.1.1 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v28.2.2+incompatible // indirect
github.com/docker/cli v28.1.1+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v28.1.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
github.com/docker/go-units v0.5.0 // indirect
@@ -147,13 +147,13 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gkampitakis/ciinfo v0.3.2 // indirect
github.com/gkampitakis/ciinfo v0.3.1 // indirect
github.com/gkampitakis/go-diff v1.3.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/goccy/go-yaml v1.15.13 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/snappy v0.0.4 // indirect
@@ -191,7 +191,6 @@ require (
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/nwaples/rardecode/v2 v2.1.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
@@ -204,7 +203,6 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
@@ -226,7 +224,7 @@ require (
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/vbatts/tar-split v0.12.1 // indirect
github.com/vbatts/tar-split v0.11.6 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
@@ -235,28 +233,26 @@ require (
github.com/zclconf/go-cty v1.13.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
go.opentelemetry.io/otel v1.33.0 // indirect
go.opentelemetry.io/otel/metric v1.33.0 // indirect
go.opentelemetry.io/otel/trace v1.33.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/tools v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/tools v0.31.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
google.golang.org/grpc v1.67.3 // indirect
google.golang.org/protobuf v1.36.4 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
modernc.org/libc v1.65.10 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
modernc.org/libc v1.62.1 // indirect
)
retract (
+50 -68
View File
@@ -122,8 +122,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
github.com/anchore/stereoscope v0.1.5-0.20250604132324-344e29f37f05 h1:MKTDwRrC7A+eRZvGFJ8TzLiKytFH1GBpPdjLTlcoO4A=
github.com/anchore/stereoscope v0.1.5-0.20250604132324-344e29f37f05/go.mod h1:S5xxMIo1BK+V+p+6SF/wzS4pZ2cTnpk6L+UJbf5IjsQ=
github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek=
github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ=
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
@@ -264,14 +264,14 @@ github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4i
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A=
github.com/docker/cli v28.2.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k=
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I=
github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
@@ -325,12 +325,12 @@ github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFA
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/github/go-spdx/v2 v2.3.3 h1:QI7evnHWEfWkT54eJwkoV/f3a0xD3gLlnVmT5wQG6LE=
github.com/github/go-spdx/v2 v2.3.3/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/ciinfo v0.3.1 h1:lzjbemlGI4Q+XimPg64ss89x8Mf3xihJqy/0Mgagapo=
github.com/gkampitakis/ciinfo v0.3.1/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
github.com/gkampitakis/go-snaps v0.5.13 h1:Hhjmvv1WboSCxkR9iU2mj5PQ8tsz/y8ECGrIbjjPF8Q=
github.com/gkampitakis/go-snaps v0.5.13/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
github.com/gkampitakis/go-snaps v0.5.11 h1:LFG0ggUKR+KEiiaOvFCmLgJ5NO2zf93AxxddkBn3LdQ=
github.com/gkampitakis/go-snaps v0.5.11/go.mod h1:PcKmy8q5Se7p48ywpogN5Td13reipz1Iivah4wrTIvY=
github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4=
github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
@@ -341,8 +341,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ=
github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -366,8 +366,8 @@ github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlnd
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/goccy/go-yaml v1.15.13 h1:Xd87Yddmr2rC1SLLTm2MNDcTjeO/GYo0JGiww6gSTDg=
github.com/goccy/go-yaml v1.15.13/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -428,8 +428,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-containerregistry v0.20.5 h1:4RnlYcDs5hoA++CeFjlbZ/U9Yp1EuWr+UhhTyYQjOP0=
github.com/google/go-containerregistry v0.20.5/go.mod h1:Q14vdOOzug02bwnhMkZKD4e30pDaD9W65qzXpyzF49E=
github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs=
github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY=
@@ -751,8 +751,8 @@ github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEW
github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -836,8 +836,8 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vbatts/go-mtree v0.5.4 h1:OMAb8jaCyiFA7zXj0Zc/oARcxBDBoeu2LizjB8BVJl0=
github.com/vbatts/go-mtree v0.5.4/go.mod h1:5GqJbVhm9BBiCc4K5uc/c42FPgXulHaQs4sFUEfIWMo=
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0=
github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA=
@@ -885,22 +885,20 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
@@ -927,8 +925,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -939,8 +937,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -968,8 +966,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1014,8 +1012,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
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=
@@ -1045,8 +1043,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1139,8 +1137,8 @@ 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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
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=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1201,8 +1199,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1392,30 +1390,14 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
modernc.org/fileutil v1.3.3 h1:3qaU+7f7xxTUmvU1pJTZiDLAIoJVdUSSauJNHg9yXoA=
modernc.org/fileutil v1.3.3/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.65.10 h1:ZwEk8+jhW7qBjHIT+wd0d9VjitRyQef9BnzlzGwMODc=
modernc.org/libc v1.65.10/go.mod h1:StFvYpx7i/mXtBAfVOjaU0PWZOvIRoZSgXhrwXzr8Po=
modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s=
modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI=
modernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g=
modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI=
modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+1 -1
View File
@@ -3,5 +3,5 @@ package internal
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 = "16.0.34"
JSONSchemaVersion = "16.0.33"
)
+6 -63
View File
@@ -92,12 +92,6 @@ func findOwnershipByFilesRelationships(resolver file.Resolver, catalog *pkg.Coll
return relationships
}
// Build a map of real paths to packages that directly own them. We'll use this
// to check if a file is already owned by the same type of package when we're
// determining ownership via symlink.
directOwnership := directOwnersByPath(catalog)
// Now establish relationships, considering symlinks
for _, candidateOwnerPkg := range catalog.Sorted() {
id := candidateOwnerPkg.ID()
if candidateOwnerPkg.Metadata == nil {
@@ -111,34 +105,16 @@ func findOwnershipByFilesRelationships(resolver file.Resolver, catalog *pkg.Coll
}
for _, ownedFilePath := range pkgFileOwner.OwnedFiles() {
if ownedFilePath == "" {
continue
}
// find paths that result in a hit (includes resolving symlinks)
resolvedPaths := resolvePaths(ownedFilePath, resolver)
for _, resolvedPath := range resolvedPaths {
if matchesAny(resolvedPath, globsForbiddenFromBeingOwned) {
// find the first path that results in a hit
for _, ownedPath := range allPaths(ownedFilePath, resolver) {
if matchesAny(ownedPath, globsForbiddenFromBeingOwned) {
// we skip over known exceptions to file ownership, such as the RPM package owning
// the RPM DB path, otherwise the RPM package would "own" all RPMs, which is not intended
continue
}
// Skip claiming ownership via symlink if another package of the same type
// directly owns this real path. This is the specific fix for the issue where a
// symlink shouldn't allow a package to claim ownership when another package of
// the same type directly owns the real file.
if resolvedPath != ownedFilePath { // This is a resolved symlink path
// Check if another package of the same type directly owns this path
if paths := directOwnership[candidateOwnerPkg.Type]; paths != nil && paths.Has(resolvedPath) {
// Skip this path - a package of the same type directly owns it
continue
}
}
// look for package(s) in the catalog that may be owned by this package and mark the relationship
for _, subPackage := range catalog.PackagesByPath(resolvedPath) {
for _, subPackage := range catalog.PackagesByPath(ownedPath) {
subID := subPackage.ID()
if subID == id {
continue
@@ -150,7 +126,7 @@ func findOwnershipByFilesRelationships(resolver file.Resolver, catalog *pkg.Coll
if _, exists := relationships[id][subID]; !exists {
relationships[id][subID] = strset.New()
}
relationships[id][subID].Add(resolvedPath)
relationships[id][subID].Add(ownedPath)
}
}
}
@@ -159,40 +135,7 @@ func findOwnershipByFilesRelationships(resolver file.Resolver, catalog *pkg.Coll
return relationships
}
func directOwnersByPath(catalog *pkg.Collection) map[pkg.Type]*strset.Set {
directOwnership := map[pkg.Type]*strset.Set{}
// First, identify direct ownership of all files
for _, p := range catalog.Sorted() {
if p.Metadata == nil {
continue
}
// check to see if this is a file owner
pkgFileOwner, ok := p.Metadata.(pkg.FileOwner)
if !ok {
continue
}
for _, ownedFilePath := range pkgFileOwner.OwnedFiles() {
if ownedFilePath == "" {
continue
}
// Register direct ownership
paths := directOwnership[p.Type]
if paths == nil {
paths = strset.New()
directOwnership[p.Type] = paths
}
paths.Add(ownedFilePath)
}
}
return directOwnership
}
func resolvePaths(ownedFilePath string, resolver file.Resolver) []string {
func allPaths(ownedFilePath string, resolver file.Resolver) []string {
// though we have a string path, we need to resolve symlinks and other filesystem oddities since we cannot assume this is a real path
var locs []file.Location
var err error
@@ -78,64 +78,6 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
return []pkg.Package{parent, child}, []artifact.Relationship{relationship}
},
},
{
name: "only-real-file-owner-gets-ownership-relationship-not-symlink-owner",
resolver: mockFR{
translate: map[string]string{
"/usr/bin/jenkins.war-symlink": "/usr/share/jenkins/jenkins.war", // symlink to the real file
},
},
setup: func(t testing.TB) ([]pkg.Package, []artifact.Relationship) {
// Package that owns the symlink
symlinkOwner := pkg.Package{
Type: pkg.ApkPkg,
Metadata: pkg.ApkDBEntry{
Package: "jenkins-symlink-pkg",
Files: []pkg.ApkFileRecord{
{Path: "/usr/bin/jenkins.war-symlink"}, // this symlinks to the real file
},
},
}
symlinkOwner.SetID()
// Package that owns the real file
realFileOwner := pkg.Package{
Type: pkg.ApkPkg,
Metadata: pkg.ApkDBEntry{
Package: "jenkins-real-pkg",
Files: []pkg.ApkFileRecord{
{Path: "/usr/share/jenkins/jenkins.war"}, // the real file
},
},
}
realFileOwner.SetID()
// A third package that is "located at" the real file path
// This simulates a package being discovered at the jenkins.war location
consumerPackage := pkg.Package{
Type: pkg.JavaPkg,
Locations: file.NewLocationSet(
file.NewVirtualLocation("/usr/share/jenkins/jenkins.war", "/usr/share/jenkins/jenkins.war"),
),
}
consumerPackage.SetID()
// Only the real file owner should have an ownership relationship with the consumer package
// The symlink owner should NOT have a relationship with the consumer
relationship := artifact.Relationship{
From: realFileOwner,
To: consumerPackage,
Type: artifact.OwnershipByFileOverlapRelationship,
Data: ownershipByFilesMetadata{
Files: []string{
"/usr/share/jenkins/jenkins.war",
},
},
}
return []pkg.Package{symlinkOwner, realFileOwner, consumerPackage}, []artifact.Relationship{relationship}
},
},
{
name: "misses-by-dead-symlink",
resolver: mockFR{
+1 -2
View File
@@ -154,8 +154,7 @@ func DefaultPackageTaskFactories() Factories {
},
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary",
),
newSimplePackageTaskFactory(binary.NewELFPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "elf-package", "elf"),
newSimplePackageTaskFactory(binary.NewPEPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "pe-package", "pe", "dll", "exe"),
newSimplePackageTaskFactory(binary.NewELFPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "elf-package"),
newSimplePackageTaskFactory(githubactions.NewActionUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
newSimplePackageTaskFactory(githubactions.NewWorkflowUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
newSimplePackageTaskFactory(java.NewJvmDistributionCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "java", "jvm", "jdk", "jre"),
File diff suppressed because it is too large Load Diff
+1 -15
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "anchore.io/schema/syft/json/16.0.34/document",
"$id": "anchore.io/schema/syft/json/16.0.33/document",
"$ref": "#/$defs/Document",
"$defs": {
"AlpmDbEntry": {
@@ -1971,9 +1971,6 @@
{
"$ref": "#/$defs/OpamPackage"
},
{
"$ref": "#/$defs/PeBinary"
},
{
"$ref": "#/$defs/PhpComposerInstalledEntry"
},
@@ -2048,17 +2045,6 @@
"purl"
]
},
"PeBinary": {
"properties": {
"VersionResources": {
"$ref": "#/$defs/KeyValues"
}
},
"type": "object",
"required": [
"VersionResources"
]
},
"PhpComposerAuthors": {
"properties": {
"name": {
@@ -10,7 +10,6 @@ import (
"strconv"
"strings"
"github.com/scylladb/go-set/strset"
"github.com/spdx/tools-golang/spdx"
"github.com/spdx/tools-golang/spdx/v2/common"
@@ -46,7 +45,7 @@ func ToSyftModel(doc *spdx.Document) (*sbom.SBOM, error) {
},
}
collectSyftPackages(s, spdxIDMap, doc)
collectSyftPackages(s, spdxIDMap, doc.Packages)
collectSyftFiles(s, spdxIDMap, doc)
@@ -280,12 +279,8 @@ func findLinuxReleaseByPURL(doc *spdx.Document) *linux.Release {
return nil
}
func collectSyftPackages(s *sbom.SBOM, spdxIDMap map[string]any, doc *spdx.Document) {
skipIDs := packageIDsToSkip(doc)
for _, p := range doc.Packages {
if p == nil || skipIDs.Has(string(p.PackageSPDXIdentifier)) {
continue
}
func collectSyftPackages(s *sbom.SBOM, spdxIDMap map[string]any, packages []*spdx.Package) {
for _, p := range packages {
syftPkg := toSyftPackage(p)
spdxIDMap[string(p.PackageSPDXIdentifier)] = syftPkg
s.Artifacts.Packages.Add(syftPkg)
@@ -662,15 +657,3 @@ func extractCPEs(p *spdx.Package) (cpes []cpe.CPE) {
}
return cpes
}
// packageIDsToSkip returns a set of packageIDs that should not be imported
func packageIDsToSkip(doc *spdx.Document) *strset.Set {
skipIDs := strset.New()
for i := 0; i < len(doc.Relationships); i++ {
r := doc.Relationships[i]
if r != nil && r.Relationship == spdx.RelationshipGeneratedFrom {
skipIDs.Add(string(r.RefB.ElementRefID))
}
}
return skipIDs
}
@@ -759,37 +759,3 @@ func Test_useSPDXIdentifierOverDerivedSyftArtifactID(t *testing.T) {
assert.Nil(t, err)
assert.NotNil(t, s.Artifacts.Packages.Package("1"))
}
func Test_skipsPackagesWithGeneratedFromRelationship(t *testing.T) {
doc := &spdx.Document{
SPDXVersion: "SPDX-2.3",
Packages: []*spdx.Package{
{
PackageName: "package-1",
PackageSPDXIdentifier: "1",
PackageVersion: "1.0.5",
},
{
PackageName: "package-1-src",
PackageSPDXIdentifier: "1-src",
PackageVersion: "1.0.5-src",
},
},
Relationships: []*spdx.Relationship{
{
Relationship: spdx.RelationshipGeneratedFrom,
RefA: common.DocElementID{ // package 1
ElementRefID: spdx.ElementID("1"),
},
RefB: common.DocElementID{ // generated from package 1-src
ElementRefID: spdx.ElementID("1-src"),
},
},
},
}
s, err := ToSyftModel(doc)
assert.Nil(t, err)
assert.NotNil(t, s.Artifacts.Packages.Package("1"))
assert.Nil(t, s.Artifacts.Packages.Package("1-src"))
}
+22 -5
View File
@@ -71,14 +71,31 @@ func Backfill(p *pkg.Package) {
}
}
func setJavaMetadataFromPurl(p *pkg.Package, _ packageurl.PackageURL) {
func setJavaMetadataFromPurl(p *pkg.Package, purl packageurl.PackageURL) {
if p.Type != pkg.JavaPkg {
return
}
if p.Metadata == nil {
// since we don't know if the purl elements directly came from pom properties or the manifest,
// we can only go as far as to set the type to JavaArchive, but not fill in the group id and artifact id
p.Metadata = pkg.JavaArchive{}
if purl.Namespace != "" {
if p.Metadata == nil {
p.Metadata = pkg.JavaArchive{}
}
meta, got := p.Metadata.(pkg.JavaArchive)
if got && meta.PomProperties == nil {
meta.PomProperties = &pkg.JavaPomProperties{}
p.Metadata = meta
}
if meta.PomProperties != nil {
// capture the group id from the purl if it is not already set
if meta.PomProperties.ArtifactID == "" {
meta.PomProperties.ArtifactID = purl.Name
}
if meta.PomProperties.GroupID == "" {
meta.PomProperties.GroupID = purl.Namespace
}
if meta.PomProperties.Version == "" {
meta.PomProperties.Version = purl.Version
}
}
}
}
+7 -3
View File
@@ -101,9 +101,13 @@ func Test_Backfill(t *testing.T) {
Language: pkg.Java,
Name: "some-thing",
Version: "1.2.3",
// we intentionally don't claim we found a pom properties file with a groupID from the purl.
// but we do claim that we found java data with an empty type.
Metadata: pkg.JavaArchive{},
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "org.apache",
ArtifactID: "some-thing",
Version: "1.2.3",
},
},
},
},
}
@@ -3,7 +3,6 @@ package helpers
import (
"fmt"
"reflect"
"strings"
"github.com/CycloneDX/cyclonedx-go"
@@ -91,19 +90,15 @@ func decodeComponent(c *cyclonedx.Component) *pkg.Package {
Locations: decodeLocations(values),
Licenses: pkg.NewLicenseSet(decodeLicenses(c)...),
CPEs: decodeCPEs(c),
PURL: c.PackageURL,
}
// note: this may write in syft package type information
DecodeInto(p, values, "syft:package", CycloneDXFields)
metadataType := values["syft:package:metadataType"]
p.Metadata = decodePackageMetadata(values, c, metadataType)
// this will either use the purl from the component or generate a new one based off of any type information
// that was decoded above.
p.PURL = getPURL(c, p.Type)
if p.Type == "" {
p.Type = pkg.TypeFromPURL(p.PURL)
}
@@ -116,41 +111,6 @@ func decodeComponent(c *cyclonedx.Component) *pkg.Package {
return p
}
func getPURL(c *cyclonedx.Component, ty pkg.Type) string {
if c.PackageURL != "" {
// if there is a purl that where the namespace does not match the group information, we may
// accidentally drop group. We should consider adding group as a top-level syft package field.
return c.PackageURL
}
if strings.HasPrefix(c.BOMRef, "pkg:") {
// the bomref is a purl, so try to use that as the purl
_, err := packageurl.FromString(c.BOMRef)
if err == nil {
return c.BOMRef
}
}
if ty == "" {
return ""
}
tyStr := ty.PackageURLType()
switch tyStr {
case "", packageurl.TypeGeneric:
return ""
}
purl := packageurl.PackageURL{
Type: tyStr,
Namespace: c.Group,
Name: c.Name,
Version: c.Version,
}
return purl.ToString()
}
func setPackageName(p *pkg.Package, c *cyclonedx.Component) {
name := c.Name
if c.Group != "" {
@@ -275,7 +275,6 @@ func Test_decodeComponent(t *testing.T) {
component cyclonedx.Component
wantLanguage pkg.Language
wantMetadata any
wantPURL string
}{
{
name: "derive language from pURL if missing",
@@ -287,18 +286,6 @@ func Test_decodeComponent(t *testing.T) {
BOMRef: "pkg:maven/ch.qos.logback/logback-classic@1.2.3",
},
wantLanguage: pkg.Java,
wantPURL: "pkg:maven/ch.qos.logback/logback-classic@1.2.3",
},
{
name: "derive language from bomref if missing",
component: cyclonedx.Component{
Name: "ch.qos.logback/logback-classic",
Version: "1.2.3",
Type: "library",
BOMRef: "pkg:maven/ch.qos.logback/logback-classic@1.2.3",
},
wantLanguage: pkg.Java,
wantPURL: "pkg:maven/ch.qos.logback/logback-classic@1.2.3",
},
{
name: "handle RpmdbMetadata type without properties",
@@ -316,7 +303,6 @@ func Test_decodeComponent(t *testing.T) {
},
},
wantMetadata: pkg.RpmDBEntry{},
wantPURL: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8",
},
{
name: "handle RpmdbMetadata type with properties",
@@ -340,24 +326,6 @@ func Test_decodeComponent(t *testing.T) {
wantMetadata: pkg.RpmDBEntry{
Release: "some-release",
},
wantPURL: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8",
},
{
name: "generate a purl from package type",
component: cyclonedx.Component{
Name: "log4j",
Group: "org.apache.logging.log4j",
Version: "2.0.4",
Type: "library",
BOMRef: "log4j",
Properties: &[]cyclonedx.Property{
{
Name: "syft:package:type",
Value: "java-archive",
},
},
},
wantPURL: "pkg:maven/org.apache.logging.log4j/log4j@2.0.4",
},
}
@@ -370,122 +338,9 @@ func Test_decodeComponent(t *testing.T) {
if tt.wantMetadata != nil {
assert.Truef(t, reflect.DeepEqual(tt.wantMetadata, p.Metadata), "metadata should match: %+v != %+v", tt.wantMetadata, p.Metadata)
}
if tt.wantPURL != "" {
assert.Equal(t, tt.wantPURL, p.PURL, "purl should match")
}
if tt.wantMetadata == nil && tt.wantLanguage == "" && tt.wantPURL == "" {
if tt.wantMetadata == nil && tt.wantLanguage == "" {
t.Fatal("this is a useless test, please remove it")
}
})
}
}
func TestGetPURL(t *testing.T) {
tests := []struct {
name string
component *cyclonedx.Component
pkgType pkg.Type
expected string
}{
{
name: "component with PackageURL",
component: &cyclonedx.Component{
PackageURL: "pkg:npm/lodash@4.17.21",
Name: "lodash",
Version: "4.17.20", // different version to verify PackageURL is used
Group: "npm",
},
pkgType: pkg.NpmPkg,
expected: "pkg:npm/lodash@4.17.21",
},
{
name: "component with BOMRef as valid PURL",
component: &cyclonedx.Component{
BOMRef: "pkg:maven/org.apache.commons/commons-lang3@3.12.0",
Name: "commons-lang3",
Version: "3.11.0", // different version to verify BOMRef is used
Group: "org.apache.commons",
},
pkgType: pkg.JavaPkg,
expected: "pkg:maven/org.apache.commons/commons-lang3@3.12.0",
},
{
name: "component with BOMRef not a valid PURL",
component: &cyclonedx.Component{
BOMRef: "not-a-purl-ref",
Name: "commons-lang3",
Version: "3.12.0",
Group: "org.apache.commons",
},
pkgType: pkg.JavaPkg,
expected: "pkg:maven/org.apache.commons/commons-lang3@3.12.0",
},
{
name: "component with unknown package type",
component: &cyclonedx.Component{
Name: "some-component",
Version: "1.0.0",
Group: "org.example",
},
pkgType: pkg.UnknownPkg,
expected: "",
},
{
name: "component with empty package type",
component: &cyclonedx.Component{
Name: "some-component",
Version: "1.0.0",
Group: "org.example",
},
pkgType: "",
expected: "",
},
{
name: "component with generic package type",
component: &cyclonedx.Component{
Name: "some-component",
Version: "1.0.0",
Group: "org.example",
},
pkgType: pkg.LinuxKernelModulePkg,
expected: "",
},
{
name: "component with valid package type",
component: &cyclonedx.Component{
Name: "react",
Version: "18.2.0",
Group: "facebook",
},
pkgType: pkg.NpmPkg,
expected: "pkg:npm/facebook/react@18.2.0",
},
{
name: "component with no group",
component: &cyclonedx.Component{
Name: "express",
Version: "4.18.2",
},
pkgType: pkg.NpmPkg,
expected: "pkg:npm/express@4.18.2",
},
{
name: "component with no version",
component: &cyclonedx.Component{
Name: "express",
Group: "npm",
},
pkgType: pkg.NpmPkg,
expected: "pkg:npm/npm/express",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := getPURL(tt.component, tt.pkgType)
assert.Equal(t, tt.expected, result)
})
}
}
@@ -50,14 +50,6 @@ func Originator(p pkg.Package) (typ string, author string) { //nolint: gocyclo,f
case pkg.DotnetPortableExecutableEntry:
typ = orgType
author = metadata.CompanyName
case pkg.PEBinary:
// this is a known common keyword used in version resources
// for more info see: https://learn.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource
val, ok := metadata.VersionResources.Get("CompanyName")
if ok {
typ = orgType
author = val
}
case pkg.DpkgDBEntry:
author = metadata.Maintainer
@@ -112,21 +112,6 @@ func Test_OriginatorSupplier(t *testing.T) {
originator: "Organization: Microsoft Corporation",
supplier: "Organization: Microsoft Corporation",
},
{
name: "from PE binary",
input: pkg.Package{
Metadata: pkg.PEBinary{
VersionResources: pkg.KeyValues{
{
Key: "CompanyName",
Value: "Microsoft Corporation",
},
},
},
},
originator: "Organization: Microsoft Corporation",
supplier: "Organization: Microsoft Corporation",
},
{
name: "from dpkg DB",
input: pkg.Package{
+7 -3
View File
@@ -165,9 +165,13 @@ func TestDecoder_Decode(t *testing.T) {
Type: pkg.JavaPkg,
PURL: "pkg:maven/org.apache/some-pkg@4.11.3",
Language: pkg.Java,
// we intentionally do not claim we found a pom properties file (don't derive this from the purl).
// but we need a metadata allocated since all Java packages have a this metadata type (a consistency point)
Metadata: pkg.JavaArchive{},
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "org.apache",
ArtifactID: "some-pkg",
Version: "4.11.3",
},
},
},
},
},
@@ -1451,7 +1451,7 @@ func TestFileResolver_FilesByGlob(t *testing.T) {
resolver, err := NewFromFile(parentPath, filePath)
assert.NoError(t, err)
refs, err := resolver.FilesByGlob("**/*.txt")
refs, err := resolver.FilesByGlob("*.txt")
assert.NoError(t, err)
assert.Len(t, refs, 1)
@@ -42,7 +42,6 @@ func AllTypes() []any {
pkg.NpmPackage{},
pkg.NpmPackageLockEntry{},
pkg.OpamPackage{},
pkg.PEBinary{},
pkg.PhpComposerInstalledEntry{},
pkg.PhpComposerLockEntry{},
pkg.PhpPearEntry{},
-1
View File
@@ -94,7 +94,6 @@ var jsonTypes = makeJSONTypes(
jsonNames(pkg.NpmPackage{}, "javascript-npm-package", "NpmPackageJsonMetadata"),
jsonNames(pkg.NpmPackageLockEntry{}, "javascript-npm-package-lock-entry", "NpmPackageLockJsonMetadata"),
jsonNames(pkg.YarnLockEntry{}, "javascript-yarn-lock-entry", "YarnLockJsonMetadata"),
jsonNames(pkg.PEBinary{}, "pe-binary"),
jsonNames(pkg.PhpComposerLockEntry{}, "php-composer-lock-entry", "PhpComposerJsonMetadata"),
jsonNamesWithoutLookup(pkg.PhpComposerInstalledEntry{}, "php-composer-installed-entry", "PhpComposerJsonMetadata"), // the legacy value is split into two types, where the other is preferred
jsonNames(pkg.PhpPeclEntry{}, "php-pecl-entry", "PhpPeclMetadata"),
-5
View File
@@ -46,8 +46,3 @@ type ELFBinaryPackageNoteJSONPayload struct {
// Commit is the commit hash of the source repository for which the binary was built from
Commit string `json:"commit,omitempty"`
}
// PEBinary represents metadata captured from a Portable Executable formatted binary (dll, exe, etc.)
type PEBinary struct {
VersionResources KeyValues
}
+2 -2
View File
@@ -15,7 +15,7 @@ func newELFPackage(ctx context.Context, metadata elfBinaryPackageNotes, location
Name: metadata.Name,
Version: metadata.Version,
Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, metadata.License)),
PURL: elfPackageURL(metadata),
PURL: packageURL(metadata),
Type: pkgType(metadata.Type),
Locations: locations,
Metadata: metadata.ELFBinaryPackageNoteJSONPayload,
@@ -26,7 +26,7 @@ func newELFPackage(ctx context.Context, metadata elfBinaryPackageNotes, location
return p
}
func elfPackageURL(metadata elfBinaryPackageNotes) string {
func packageURL(metadata elfBinaryPackageNotes) string {
var qualifiers []packageurl.Qualifier
os, osVersion := osNameAndVersionFromMetadata(metadata)
@@ -10,7 +10,7 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
func Test_ELFPackageCataloger(t *testing.T) {
func Test_ELF_Package_Cataloger(t *testing.T) {
cases := []struct {
name string
@@ -130,7 +130,7 @@ func Test_packageURL(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
assert.Equal(t, test.want, elfPackageURL(test.metadata))
assert.Equal(t, test.want, packageURL(test.metadata))
})
}
}
-134
View File
@@ -1,134 +0,0 @@
package binary
import (
"path"
"regexp"
"sort"
"strings"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
var (
// spaceRegex includes nbsp (#160) considered to be a space character
spaceRegex = regexp.MustCompile(`[\s\xa0]+`)
numberRegex = regexp.MustCompile(`\d`)
)
func newPEPackage(versionResources map[string]string, f file.Location) pkg.Package {
name := findNameFromVR(versionResources)
if name == "" {
// it's possible that the version resources are empty, so we fall back to the file name
name = strings.TrimSuffix(strings.TrimSuffix(path.Base(f.RealPath), ".exe"), ".dll")
}
p := pkg.Package{
Name: name,
Version: findVersionFromVR(versionResources),
Locations: file.NewLocationSet(f.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
Type: pkg.BinaryPkg,
Metadata: newPEBinaryVersionResourcesFromMap(versionResources),
}
p.SetID()
return p
}
func newPEBinaryVersionResourcesFromMap(vr map[string]string) pkg.PEBinary {
var kvs pkg.KeyValues
for k, v := range vr {
if v == "" {
continue
}
kvs = append(kvs, pkg.KeyValue{
Key: k,
Value: spaceNormalize(v),
})
}
sort.Slice(kvs, func(i, j int) bool {
return kvs[i].Key < kvs[j].Key
})
return pkg.PEBinary{
VersionResources: kvs,
}
}
func findNameFromVR(versionResources map[string]string) string {
// PE files not authored by Microsoft tend to use ProductName as an identifier.
nameFields := []string{"ProductName", "FileDescription", "InternalName", "OriginalFilename"}
if isMicrosoftVR(versionResources) {
// for Microsoft files, prioritize FileDescription.
nameFields = []string{"FileDescription", "InternalName", "OriginalFilename", "ProductName"}
}
var name string
for _, field := range nameFields {
value := spaceNormalize(versionResources[field])
if value == "" {
continue
}
name = value
break
}
return name
}
func isMicrosoftVR(versionResources map[string]string) bool {
return strings.Contains(strings.ToLower(versionResources["CompanyName"]), "microsoft") ||
strings.Contains(strings.ToLower(versionResources["ProductName"]), "microsoft")
}
// spaceNormalize trims and normalizes whitespace in a string.
func spaceNormalize(value string) string {
value = strings.TrimSpace(value)
if value == "" {
return ""
}
// ensure valid UTF-8.
value = strings.ToValidUTF8(value, "")
// consolidate all whitespace.
value = spaceRegex.ReplaceAllString(value, " ")
// remove non-printable characters.
value = regexp.MustCompile(`[\x00-\x1f]`).ReplaceAllString(value, "")
// consolidate again and trim.
value = spaceRegex.ReplaceAllString(value, " ")
value = strings.TrimSpace(value)
return value
}
func findVersionFromVR(versionResources map[string]string) string {
productVersion := extractVersionFromResourcesValue(versionResources["ProductVersion"])
fileVersion := extractVersionFromResourcesValue(versionResources["FileVersion"])
if productVersion != "" {
return productVersion
}
return fileVersion
}
func extractVersionFromResourcesValue(version string) string {
version = strings.TrimSpace(version)
out := ""
for i, f := range strings.Fields(version) {
if containsNumber(out) && !containsNumber(f) {
return out
}
if i == 0 {
out = f
} else {
out += " " + f
}
}
return out
}
func containsNumber(s string) bool {
return numberRegex.MatchString(s)
}
@@ -1,39 +0,0 @@
package binary
import (
"context"
"fmt"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pe"
)
// NewPEPackageCataloger returns a cataloger that interprets packages from DLL and EXE files.
func NewPEPackageCataloger() pkg.Cataloger {
return generic.NewCataloger("pe-binary-package-cataloger").
WithParserByGlobs(parsePE, "**/*.dll", "**/*.exe")
}
func parsePE(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
f, err := pe.Read(reader)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse PE file %q: %w", reader.RealPath, err)
}
if f == nil {
return nil, nil, unknown.Newf(reader, "unable to determine packages")
}
if f.CLR.HasEvidenceOfCLR() {
// this is for a .NET application, which is covered by other catalogers already
return nil, nil, nil
}
p := newPEPackage(f.VersionResources, f.Location)
return []pkg.Package{p}, nil, nil
}
@@ -1,64 +0,0 @@
package binary
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
func Test_PEPackageCataloger(t *testing.T) {
cases := []struct {
name string
fixture string
expected []pkg.Package
wantErr require.ErrorAssertionFunc
}{
{
name: "non-.NET package",
fixture: "image-jruby",
expected: []pkg.Package{
{
Name: "JRuby",
Version: "9.3.15.0",
Type: pkg.BinaryPkg,
Locations: file.NewLocationSet(
file.NewLocation("/jruby_windows_9_3_15_0.exe"),
),
FoundBy: "pe-binary-package-cataloger",
Metadata: pkg.PEBinary{
VersionResources: pkg.KeyValues{
{Key: "CompanyName", Value: "JRuby Dev Team"},
{Key: "FileDescription", Value: "JRuby"},
{Key: "FileVersion", Value: "9.3.15.0"},
{Key: "InternalName", Value: "jruby"},
{Key: "LegalCopyright", Value: "JRuby Dev Team"},
{Key: "OriginalFilename", Value: "jruby_windows-x32_9_3_15_0.exe"},
{Key: "ProductName", Value: "JRuby"},
{Key: "ProductVersion", Value: "9.3.15.0"},
},
},
},
},
},
{
name: "ignore .NET packages",
fixture: "image-dotnet-app",
expected: nil, // expect nothing!
},
}
for _, v := range cases {
t.Run(v.name, func(t *testing.T) {
pkgtest.NewCatalogTester().
WithImageResolver(t, v.fixture).
IgnoreLocationLayer(). // this fixture can be rebuilt, thus the layer ID will change
Expects(v.expected, nil).
TestCataloger(t, NewPEPackageCataloger())
})
}
}
@@ -1,10 +0,0 @@
FROM alpine:latest AS builder
RUN apk add --no-cache wget unzip
RUN mkdir -p /app
RUN wget -O /app/minver.nupkg https://www.nuget.org/api/v2/package/minver-cli/4.3.0
RUN unzip /app/minver.nupkg -d /app/minver
from busybox:latest
COPY --from=builder /app/minver /minver
@@ -1,7 +0,0 @@
FROM --platform=linux/amd64 alpine:latest AS builder
RUN wget -O jruby_windows_9_3_15_0.exe https://s3.amazonaws.com/jruby.org/downloads/9.3.15.0/jruby_windows_9_3_15_0.exe
FROM scratch
COPY --from=builder /jruby_windows_9_3_15_0.exe /jruby_windows_9_3_15_0.exe
@@ -473,7 +473,7 @@ func readPEFile(resolver file.Resolver, loc file.Location) (*logicalPE, error) {
}
defer internal.CloseAndLogError(reader, loc.RealPath)
ldpe, err := readLogicalPE(file.NewLocationReadCloser(loc, reader))
ldpe, err := getLogicalDotnetPE(file.NewLocationReadCloser(loc, reader))
if err != nil {
return nil, unknown.New(loc, fmt.Errorf("unable to parse PE file: %w", err))
}
@@ -482,7 +482,7 @@ func readPEFile(resolver file.Resolver, loc file.Location) (*logicalPE, error) {
return nil, nil
}
if !ldpe.CLR.HasEvidenceOfCLR() {
if !ldpe.CLR.hasEvidenceOfCLR() {
// this is not a .NET binary
return nil, nil
}
+8 -12
View File
@@ -252,8 +252,8 @@ func getLogicalDepsJSON(deps depsJSON, lm *libmanJSON) logicalDepsJSON {
if !bundlingDetected && knownBundlers.Has(name) {
bundlingDetected = true
}
p.AnyChildClaimsDLLs = searchForDLLClaims(packageMap, strset.New(), p.dependencyNameVersions()...)
p.AnyChildHasDLLs = searchForDLLEvidence(packageMap, strset.New(), p.dependencyNameVersions()...)
p.AnyChildClaimsDLLs = searchForDLLClaims(packageMap, p.dependencyNameVersions()...)
p.AnyChildHasDLLs = searchForDLLEvidence(packageMap, p.dependencyNameVersions()...)
packages[p.NameVersion] = *p
}
@@ -286,35 +286,31 @@ func mergeSets(s1, s2 *strset.Set) *strset.Set {
type visitorFunc func(p *logicalDepsJSONPackage) bool
// searchForDLLEvidence recursively searches for executables found for any of the given nameVersions and children recursively.
func searchForDLLEvidence(packageMap map[string]*logicalDepsJSONPackage, visited *strset.Set, nameVersions ...string) bool {
func searchForDLLEvidence(packageMap map[string]*logicalDepsJSONPackage, nameVersions ...string) bool {
return traverseDependencies(packageMap, func(p *logicalDepsJSONPackage) bool {
return p.FoundDLLs(true)
}, visited, nameVersions...)
}, nameVersions...)
}
// searchForDLLClaims recursively searches for DLL claims in the deps.json for any of the given nameVersions and children recursively.
func searchForDLLClaims(packageMap map[string]*logicalDepsJSONPackage, visited *strset.Set, nameVersions ...string) bool {
func searchForDLLClaims(packageMap map[string]*logicalDepsJSONPackage, nameVersions ...string) bool {
return traverseDependencies(packageMap, func(p *logicalDepsJSONPackage) bool {
return p.ClaimsDLLs(true)
}, visited, nameVersions...)
}, nameVersions...)
}
func traverseDependencies(packageMap map[string]*logicalDepsJSONPackage, visitor visitorFunc, visited *strset.Set, nameVersions ...string) bool {
func traverseDependencies(packageMap map[string]*logicalDepsJSONPackage, visitor visitorFunc, nameVersions ...string) bool {
if len(nameVersions) == 0 {
return false
}
for _, nameVersion := range nameVersions {
if visited.Has(nameVersion) {
continue
}
visited.Add(nameVersion)
if p, ok := packageMap[nameVersion]; ok {
if visitor(p) {
return true
}
if traverseDependencies(packageMap, visitor, visited, p.dependencyNameVersions()...) {
if traverseDependencies(packageMap, visitor, p.dependencyNameVersions()...) {
return true
}
}
@@ -149,11 +149,6 @@ func TestGetLogicalDepsJSON_MergeTargets(t *testing.T) {
"lib/netcoreapp3.1/Microsoft.CodeAnalysis.CSharp.dll": {},
},
},
"Microsoft.CodeAnalysis.Common/4.0.0": {
Dependencies: map[string]string{
"Microsoft.CodeAnalysis.CSharp": "4.0.0",
},
},
},
},
Libraries: map[string]depsLibrary{
+1 -1
View File
@@ -150,7 +150,7 @@ func newDotnetDepsEntry(lp logicalDepsJSONPackage) pkg.DotnetDepsEntry {
}
}
// newDotnetPortableExecutableEntry creates a portable executable entry from a File.
// newDotnetPortableExecutableEntry creates a portable executable entry from a logicalPE.
func newDotnetPortableExecutableEntry(pe logicalPE) pkg.DotnetPortableExecutableEntry {
return newDotnetPortableExecutableEntryFromMap(pe.VersionResources)
}
+718 -8
View File
@@ -1,30 +1,740 @@
package dotnet
import (
"bytes"
"debug/pe"
"encoding/binary"
"errors"
"fmt"
"io"
"unicode/utf16"
"github.com/scylladb/go-set/strset"
"github.com/scylladb/go-set/u32set"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pe"
"github.com/anchore/syft/syft/internal/unionreader"
)
// logicalPE represents a PE file within the context of a .NET project (considering the deps.json file).
const peMaxAllowedDirectoryEntries = 0x1000
var imageDirectoryEntryIndexes = []int{
pe.IMAGE_DIRECTORY_ENTRY_RESOURCE, // where version resources are stored
pe.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, // where info about the CLR is stored
}
// logicalPE does not directly represent a binary shape to be parsed, instead it represents the
// information of interest extracted from a PE file.
type logicalPE struct {
pe.File
// Location is where the PE file was found
Location file.Location
// TargetPath is the path is the deps.json target entry. This is not present in the PE file
// but instead is used in downstream processing to track associations between the PE file and the deps.json file.
TargetPath string
// CLR is the information about the CLR (common language runtime) version found in the PE file which helps
// understand if this executable is even a .NET application.
CLR *clrEvidence
// VersionResources is a map of version resource keys to their values found in the VERSIONINFO resource directory.
VersionResources map[string]string
}
func readLogicalPE(reader file.LocationReadCloser) (*logicalPE, error) {
peFile, err := pe.Read(reader)
// clrEvidence is basic info about the CLR (common language runtime) version from the COM descriptor.
// This is not a complete representation of the CLR version, but rather a subset of the information that is
// useful to us.
type clrEvidence struct {
// HasClrResourceNames is true if there are CLR resource names found in the PE file (e.g. "CLRDEBUGINFO").
HasClrResourceNames bool
// MajorVersion is the minimum supported major version of the CLR.
MajorVersion uint16
// MinorVersion is the minimum supported minor version of the CLR.
MinorVersion uint16
}
// hasEvidenceOfCLR returns true if the PE file has evidence of a CLR (common language runtime) version.
func (c *clrEvidence) hasEvidenceOfCLR() bool {
return c != nil && (c.MajorVersion != 0 && c.MinorVersion != 0 || c.HasClrResourceNames)
}
type peDosHeader struct {
Magic [2]byte // "MZ"
Unused [58]byte
AddressOfNewEXEHeader uint32 // offset to PE header
}
// peImageCore20 represents the .NET Core 2.0 header structure.
// Source: https://github.com/dotnet/msbuild/blob/9fa9d800dabce3bfcf8365f651f3a713e01f8a85/src/Tasks/NativeMethods.cs#L761-L775
type peImageCore20 struct {
Cb uint32
MajorRuntimeVersion uint16
MinorRuntimeVersion uint16
}
// peImageResourceDirectory represents the resource directory structure.
type peImageResourceDirectory struct {
Characteristics uint32
TimeDateStamp uint32
MajorVersion uint16
MinorVersion uint16
NumberOfNamedEntries uint16
NumberOfIDEntries uint16
}
// peImageResourceDirectoryEntry represents an entry in the resource directory entries.
type peImageResourceDirectoryEntry struct {
Name uint32
OffsetToData uint32
}
// peImageResourceDataEntry is the unit of raw data in the Resource Data area.
type peImageResourceDataEntry struct {
OffsetToData uint32
Size uint32
CodePage uint32
Reserved uint32
}
// peVsFixedFileInfo represents the fixed file information structure.
type peVsFixedFileInfo struct {
Signature uint32
StructVersion uint32
FileVersionMS uint32
FileVersionLS uint32
ProductVersionMS uint32
ProductVersionLS uint32
FileFlagsMask uint32
FileFlags uint32
FileOS uint32
FileType uint32
FileSubtype uint32
FileDateMS uint32
FileDateLS uint32
}
type peVsVersionInfo peLenValLenType
type peStringFileInfo peLenValLenType
type peStringTable peLenValLenType
type peString peLenValLenType
type peLenValLenType struct {
Length uint16
ValueLength uint16
Type uint16
}
type extractedSection struct {
RVA uint32
BaseRVA uint32
Size uint32
Reader *bytes.Reader
}
func (s extractedSection) exists() bool {
return s.RVA != 0 && s.Size != 0
}
func directoryName(i int) string {
switch i {
case pe.IMAGE_DIRECTORY_ENTRY_RESOURCE:
return "Resource"
case pe.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:
return "COM Descriptor"
}
return fmt.Sprintf("Unknown (%d)", i)
}
func getLogicalDotnetPE(f file.LocationReadCloser) (*logicalPE, error) {
r, err := unionreader.GetUnionReader(f)
if err != nil {
return nil, err
}
if peFile == nil {
return nil, nil
sections, _, err := parsePEFile(r)
if err != nil {
return nil, fmt.Errorf("unable to parse PE sections: %w", err)
}
dirs := u32set.New() // keep track of the RVAs we have already parsed (prevent infinite recursion edge cases)
versionResources := make(map[string]string) // map of version resource keys to their values
resourceNames := strset.New() // set of resource names found in the PE file
err = parseResourceDirectory(sections[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE], dirs, versionResources, resourceNames)
if err != nil {
return nil, err
}
c, err := parseCLR(sections[pe.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR], resourceNames)
if err != nil {
return nil, fmt.Errorf("unable to parse PE CLR directory: %w", err)
}
return &logicalPE{
File: *peFile,
Location: f.Location,
CLR: c,
VersionResources: versionResources,
}, nil
}
// parsePEFile creates readers for targeted sections of the binary used by downstream processing.
func parsePEFile(file unionreader.UnionReader) (map[int]*extractedSection, []pe.SectionHeader32, error) {
fileHeader, magic, err := parsePEHeader(file)
if err != nil {
return nil, nil, fmt.Errorf("error parsing PE header: %w", err)
}
soi, headers, err := parseSectionHeaders(file, magic, fileHeader.NumberOfSections)
if err != nil {
return nil, nil, fmt.Errorf("error parsing section headers: %w", err)
}
for i, sec := range soi {
if !sec.exists() {
continue
}
data, err := readDataFromRVA(file, sec.RVA, sec.Size, headers)
if err != nil {
return nil, nil, fmt.Errorf("error reading %q section data: %w", directoryName(i), err)
}
sec.Reader = data
}
return soi, headers, nil
}
// parsePEHeader reads the beginning of a PE formatted file, returning the file header and "magic" indicator
// for downstream logic to determine 32/64 bit parsing.
func parsePEHeader(file unionreader.UnionReader) (*pe.FileHeader, uint16, error) {
var dosHeader peDosHeader
if err := binary.Read(file, binary.LittleEndian, &dosHeader); err != nil {
return nil, 0, fmt.Errorf("error reading DOS header: %w", err)
}
if string(dosHeader.Magic[:]) != "MZ" {
return nil, 0, fmt.Errorf("invalid DOS header magic")
}
peOffset := int64(dosHeader.AddressOfNewEXEHeader)
if _, err := file.Seek(peOffset, io.SeekStart); err != nil {
return nil, 0, fmt.Errorf("error seeking to PE header: %w", err)
}
var signature [4]byte
if err := binary.Read(file, binary.LittleEndian, &signature); err != nil {
return nil, 0, fmt.Errorf("error reading PE signature: %w", err)
}
if !bytes.Equal(signature[:], []byte("PE\x00\x00")) {
return nil, 0, fmt.Errorf("invalid PE signature")
}
var fileHeader pe.FileHeader
if err := binary.Read(file, binary.LittleEndian, &fileHeader); err != nil {
return nil, 0, fmt.Errorf("error reading file header: %w", err)
}
var magic uint16
if err := binary.Read(file, binary.LittleEndian, &magic); err != nil {
return nil, 0, fmt.Errorf("error reading optional header magic: %w", err)
}
// seek back to before reading magic (since that value is in the header)
if _, err := file.Seek(-2, io.SeekCurrent); err != nil {
return nil, 0, fmt.Errorf("error seeking back to before reading magic: %w", err)
}
return &fileHeader, magic, nil
}
// parseSectionHeaders reads the section headers from the PE file and extracts the virtual addresses + section size
// information for the sections of interest. Additionally, all section headers are returned to aid in downstream processing.
func parseSectionHeaders(file unionreader.UnionReader, magic uint16, numberOfSections uint16) (map[int]*extractedSection, []pe.SectionHeader32, error) {
soi := make(map[int]*extractedSection)
switch magic {
case 0x10B: // PE32
var optHeader pe.OptionalHeader32
if err := binary.Read(file, binary.LittleEndian, &optHeader); err != nil {
return nil, nil, fmt.Errorf("error reading optional header (PE32): %w", err)
}
for _, i := range imageDirectoryEntryIndexes {
sectionHeader := optHeader.DataDirectory[i]
if sectionHeader.Size == 0 {
continue
}
soi[i] = &extractedSection{
RVA: sectionHeader.VirtualAddress,
Size: sectionHeader.Size,
}
}
case 0x20B: // PE32+ (64 bit)
var optHeader pe.OptionalHeader64
if err := binary.Read(file, binary.LittleEndian, &optHeader); err != nil {
return nil, nil, fmt.Errorf("error reading optional header (PE32+): %w", err)
}
for _, i := range imageDirectoryEntryIndexes {
sectionHeader := optHeader.DataDirectory[i]
if sectionHeader.Size == 0 {
continue
}
soi[i] = &extractedSection{
RVA: sectionHeader.VirtualAddress,
Size: sectionHeader.Size,
}
}
default:
return nil, nil, fmt.Errorf("unknown optional header magic: 0x%x", magic)
}
// read section headers
headers := make([]pe.SectionHeader32, numberOfSections)
for i := 0; i < int(numberOfSections); i++ {
if err := binary.Read(file, binary.LittleEndian, &headers[i]); err != nil {
return nil, nil, fmt.Errorf("error reading section header: %w", err)
}
}
return soi, headers, nil
}
// parseCLR extracts the CLR (common language runtime) version information from the COM descriptor and makes
// present/not-present determination based on the presence of CLR resource names.
func parseCLR(sec *extractedSection, resourceNames *strset.Set) (*clrEvidence, error) {
hasCLRDebugResourceNames := resourceNames.HasAny("CLRDEBUGINFO")
if sec == nil || sec.Reader == nil {
return &clrEvidence{
HasClrResourceNames: hasCLRDebugResourceNames,
}, nil
}
reader := sec.Reader
var c peImageCore20
if err := binary.Read(reader, binary.LittleEndian, &c); err != nil {
return nil, fmt.Errorf("error reading CLR header: %w", err)
}
return &clrEvidence{
HasClrResourceNames: hasCLRDebugResourceNames,
MajorVersion: c.MajorRuntimeVersion,
MinorVersion: c.MinorRuntimeVersion,
}, nil
}
// rvaToFileOffset is a helper function to convert RVA to file offset using section headers
func rvaToFileOffset(rva uint32, sections []pe.SectionHeader32) (uint32, error) {
for _, section := range sections {
if rva >= section.VirtualAddress && rva < section.VirtualAddress+section.VirtualSize {
return section.PointerToRawData + (rva - section.VirtualAddress), nil
}
}
return 0, fmt.Errorf("RVA 0x%x not found in any section", rva)
}
// readDataFromRVA will read data from a specific RVA in the PE file
func readDataFromRVA(file io.ReadSeeker, rva, size uint32, sections []pe.SectionHeader32) (*bytes.Reader, error) {
if size == 0 {
return nil, fmt.Errorf("zero size specified")
}
offset, err := rvaToFileOffset(rva, sections)
if err != nil {
return nil, err
}
if _, err := file.Seek(int64(offset), io.SeekStart); err != nil {
return nil, fmt.Errorf("error seeking to data: %w", err)
}
data := make([]byte, size)
if _, err := io.ReadFull(file, data); err != nil {
return nil, fmt.Errorf("error reading data: %w", err)
}
return bytes.NewReader(data), nil
}
// parseResourceDirectory recursively parses a PE resource directory. This takes a relative virtual address (offset of
// a piece of data or code relative to the base address), the size of the resource directory, the set of RVAs already
// parsed, and the map to populate discovered version resource values.
//
// .rsrc Section
// +------------------------------+
// | Resource Directory Table |
// +------------------------------+
// | Resource Directory Entries |
// | +------------------------+ |
// | | Subdirectory or Data | |
// | +------------------------+ |
// +------------------------------+
// | Resource Data Entries |
// | +------------------------+ |
// | | Resource Data | |
// | +------------------------+ |
// +------------------------------+
// | Actual Resource Data |
// +------------------------------+
//
// sources:
// - https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-rsrc-section
// - https://learn.microsoft.com/en-us/previous-versions/ms809762(v=msdn.10)#pe-file-resources
func parseResourceDirectory(sec *extractedSection, dirs *u32set.Set, fields map[string]string, names *strset.Set) error {
if sec == nil || sec.Size <= 0 {
return nil
}
if sec.Reader == nil {
return errors.New("resource section not found")
}
baseRVA := sec.BaseRVA
if baseRVA == 0 {
baseRVA = sec.RVA
}
offset := int64(sec.RVA - baseRVA)
if _, err := sec.Reader.Seek(offset, io.SeekStart); err != nil {
return fmt.Errorf("error seeking to directory offset: %w", err)
}
var directoryHeader peImageResourceDirectory
if err := readIntoStruct(sec.Reader, &directoryHeader); err != nil {
return fmt.Errorf("error reading directory header: %w", err)
}
numEntries := int(directoryHeader.NumberOfNamedEntries + directoryHeader.NumberOfIDEntries)
switch {
case numEntries > peMaxAllowedDirectoryEntries:
return fmt.Errorf("too many entries in resource directory: %d", numEntries)
case numEntries == 0:
return fmt.Errorf("no entries in resource directory")
case numEntries < 0:
return fmt.Errorf("invalid number of entries in resource directory: %d", numEntries)
}
for i := 0; i < numEntries; i++ {
var entry peImageResourceDirectoryEntry
entryOffset := offset + int64(binary.Size(directoryHeader)) + int64(i*binary.Size(entry))
if _, err := sec.Reader.Seek(entryOffset, io.SeekStart); err != nil {
log.Tracef("error seeking to PE entry offset: %v", err)
continue
}
if err := readIntoStruct(sec.Reader, &entry); err != nil {
continue
}
if err := processResourceEntry(entry, baseRVA, sec, dirs, fields, names); err != nil {
log.Tracef("error processing resource entry: %v", err)
continue
}
}
return nil
}
func processResourceEntry(entry peImageResourceDirectoryEntry, baseRVA uint32, sec *extractedSection, dirs *u32set.Set, fields map[string]string, names *strset.Set) error {
// if the high bit is set, this is a directory entry, otherwise it is a data entry
isDirectory := entry.OffsetToData&0x80000000 != 0
// note: the offset is relative to the beginning of the resource section, not an RVA
entryOffsetToData := entry.OffsetToData & 0x7FFFFFFF
nameIsString := entry.Name&0x80000000 != 0
nameOffset := entry.Name & 0x7FFFFFFF
// read the string name of the resource directory
if nameIsString {
currentPos, err := sec.Reader.Seek(0, io.SeekCurrent)
if err != nil {
return fmt.Errorf("error getting current reader position: %w", err)
}
if _, err := sec.Reader.Seek(int64(nameOffset), io.SeekStart); err != nil {
return fmt.Errorf("error restoring reader position: %w", err)
}
name, err := readUTF16WithLength(sec.Reader)
if err == nil {
names.Add(name)
}
if _, err := sec.Reader.Seek(currentPos, io.SeekStart); err != nil {
return fmt.Errorf("error restoring reader position: %w", err)
}
}
if isDirectory {
subRVA := baseRVA + entryOffsetToData
if dirs.Has(subRVA) {
// some malware uses recursive PE references to evade analysis
return fmt.Errorf("recursive PE reference detected; skipping directory at baseRVA=0x%x subRVA=0x%x", baseRVA, subRVA)
}
dirs.Add(subRVA)
err := parseResourceDirectory(
&extractedSection{
RVA: subRVA,
BaseRVA: baseRVA,
Size: sec.Size - (sec.RVA - baseRVA),
Reader: sec.Reader,
},
dirs, fields, names)
if err != nil {
return err
}
return nil
}
return parseResourceDataEntry(sec.Reader, baseRVA, baseRVA+entryOffsetToData, sec.Size, fields)
}
func parseResourceDataEntry(reader *bytes.Reader, baseRVA, rva, remainingSize uint32, fields map[string]string) error {
var dataEntry peImageResourceDataEntry
offset := int64(rva - baseRVA)
if _, err := reader.Seek(offset, io.SeekStart); err != nil {
return fmt.Errorf("error seeking to data entry offset: %w", err)
}
if err := readIntoStruct(reader, &dataEntry); err != nil {
return fmt.Errorf("error reading resource data entry: %w", err)
}
if remainingSize < dataEntry.Size {
return fmt.Errorf("resource data entry size exceeds remaining size")
}
data := make([]byte, dataEntry.Size)
if _, err := reader.Seek(int64(dataEntry.OffsetToData-baseRVA), io.SeekStart); err != nil {
return fmt.Errorf("error seeking to resource data: %w", err)
}
if _, err := reader.Read(data); err != nil {
return fmt.Errorf("error reading resource data: %w", err)
}
return parseVersionResourceSection(bytes.NewReader(data), fields)
}
// parseVersionResourceSection parses a PE version resource section from within a resource directory.
//
// "The main structure in a version resource is the VS_FIXEDFILEINFO structure. Additional structures include the
// VarFileInfo structure to store language information data, and StringFileInfo for user-defined string information.
// All strings in a version resource are in Unicode format. Each block of information is aligned on a DWORD boundary."
//
// "VS_VERSIONINFO" (utf16)
// +---------------------------------------------------+
// | wLength (2 bytes) |
// | wValueLength (2 bytes) |
// | wType (2 bytes) |
// | szKey ("VS_VERSION_INFO") (utf16) |
// | Padding (to DWORD) |
// +---------------------------------------------------+
// | VS_FIXEDFILEINFO (52 bytes) |
// +---------------------------------------------------+
// | "StringFileInfo" (utf16) |
// +---------------------------------------------------+
// | wLength (2 bytes) |
// | wValueLength (2 bytes) |
// | wType (2 bytes) |
// | szKey ("StringFileInfo") (utf16) |
// | Padding (to DWORD) |
// | StringTable |
// | +--------------------------------------------+ |
// | | wLength (2 bytes) | |
// | | wValueLength (2 bytes) | |
// | | wType (2 bytes) | |
// | | szKey ("040904b0") | |
// | | Padding (to DWORD) | |
// | | String | |
// | | +--------------------------------------+ | |
// | | | wLength (2 bytes) | | |
// | | | wValueLength (2 bytes) | | |
// | | | wType (2 bytes) | | |
// | | | szKey ("FileVersion") | | |
// | | | Padding (to DWORD) | | |
// | | | szValue ("15.00.0913.015") | | |
// | | | Padding (to DWORD) | | |
// | +--------------------------------------------+ |
// +---------------------------------------------------+
// | VarFileInfo (utf16) |
// +---------------------------------------------------+
// | (skip!) |
// +---------------------------------------------------+
//
// sources:
// - https://learn.microsoft.com/en-us/windows/win32/menurc/resource-file-formats
// - https://learn.microsoft.com/en-us/windows/win32/menurc/vs-versioninfo
// - https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
// - https://learn.microsoft.com/en-us/windows/win32/menurc/varfileinfo
// - https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo
// - https://learn.microsoft.com/en-us/windows/win32/menurc/stringtable
func parseVersionResourceSection(reader *bytes.Reader, fields map[string]string) error {
offset := 0
var info peVsVersionInfo
if szKey, err := readIntoStructAndSzKey(reader, &info, &offset); err != nil {
return fmt.Errorf("error reading PE version info: %v", err)
} else if szKey != "VS_VERSION_INFO" {
// this is a resource section, but not the version resources
return nil
}
if err := alignAndSeek(reader, &offset); err != nil {
return fmt.Errorf("error aligning past PE version info: %w", err)
}
var fixedFileInfo peVsFixedFileInfo
if err := readIntoStruct(reader, &fixedFileInfo, &offset); err != nil {
return fmt.Errorf("error reading PE FixedFileInfo: %v", err)
}
for reader.Len() > 0 {
if err := alignAndSeek(reader, &offset); err != nil {
return fmt.Errorf("error seeking to PE StringFileInfo: %w", err)
}
var sfiHeader peStringFileInfo
if szKey, err := readIntoStructAndSzKey(reader, &sfiHeader, &offset); err != nil {
return fmt.Errorf("error reading PE string file info header: %v", err)
} else if szKey != "StringFileInfo" {
// we only care about extracting strings from any string tables, skip this
offset += int(sfiHeader.ValueLength)
continue
}
var stOffset int
// note: the szKey for the prStringTable is the language
var stHeader peStringTable
if _, err := readIntoStructAndSzKey(reader, &stHeader, &offset, &stOffset); err != nil {
return fmt.Errorf("error reading PE string table header: %v", err)
}
for stOffset < int(stHeader.Length) {
var stringHeader peString
if err := readIntoStruct(reader, &stringHeader, &offset, &stOffset); err != nil {
break
}
key := readUTF16(reader, &offset, &stOffset)
if err := alignAndSeek(reader, &offset, &stOffset); err != nil {
return fmt.Errorf("error aligning to next PE string table value: %w", err)
}
var value string
if stringHeader.ValueLength > 0 {
value = readUTF16(reader, &offset, &stOffset)
}
fields[key] = value
if err := alignAndSeek(reader, &offset, &stOffset); err != nil {
return fmt.Errorf("error aligning to next PE string table key: %w", err)
}
}
}
if fields["FileVersion"] == "" {
// we can derive the file version from the fixed file info if it is not already specified as a string entry... neat!
fields["FileVersion"] = fmt.Sprintf("%d.%d.%d.%d",
fixedFileInfo.FileVersionMS>>16, fixedFileInfo.FileVersionMS&0xFFFF,
fixedFileInfo.FileVersionLS>>16, fixedFileInfo.FileVersionLS&0xFFFF)
}
return nil
}
// readIntoStructAndSzKey reads a struct from the reader and updates the offsets if provided, returning the szKey value.
// This is only useful in the context of the resource directory parsing in narrow cases (this is invalid to use outside of that context).
func readIntoStructAndSzKey[T any](reader *bytes.Reader, data *T, offsets ...*int) (string, error) {
if err := readIntoStruct(reader, data, offsets...); err != nil {
return "", err
}
return readUTF16(reader, offsets...), nil
}
// readIntoStruct reads a struct from the reader and updates the offsets if provided.
func readIntoStruct[T any](reader io.Reader, data *T, offsets ...*int) error {
if err := binary.Read(reader, binary.LittleEndian, data); err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return err
}
for i := range offsets {
*offsets[i] += binary.Size(*data)
}
return nil
}
// alignAndSeek aligns the reader to the next DWORD boundary and seeks to the new offset (updating any provided trackOffsets).
func alignAndSeek(reader io.Seeker, offset *int, trackOffsets ...*int) error {
ogOffset := *offset
*offset = alignToDWORD(*offset)
diff := *offset - ogOffset
for i := range trackOffsets {
*trackOffsets[i] += diff
}
_, err := reader.Seek(int64(*offset), io.SeekStart)
return err
}
// alignToDWORD aligns the offset to the next DWORD boundary (4 byte boundary)
func alignToDWORD(offset int) int {
return (offset + 3) & ^3
}
// readUTF16 is a helper function to read a null-terminated UTF16 string
func readUTF16(reader *bytes.Reader, offsets ...*int) string {
startPos, err := reader.Seek(0, io.SeekCurrent)
if err != nil {
return ""
}
var result []rune
for {
var char uint16
err := binary.Read(reader, binary.LittleEndian, &char)
if err != nil || char == 0 {
break
}
result = append(result, rune(char))
}
// calculate how many bytes we've actually read (including null terminator)
endPos, _ := reader.Seek(0, io.SeekCurrent)
bytesRead := int(endPos - startPos)
for i := range offsets {
*offsets[i] += bytesRead
}
return string(result)
}
// readUTF16WithLength reads a length-prefixed UTF-16 string from reader.
// The first 2 bytes represent the number of UTF-16 code units.
func readUTF16WithLength(reader *bytes.Reader) (string, error) {
var length uint16
if err := binary.Read(reader, binary.LittleEndian, &length); err != nil {
return "", err
}
if length == 0 {
return "", nil
}
// read length UTF-16 code units.
codes := make([]uint16, length)
if err := binary.Read(reader, binary.LittleEndian, &codes); err != nil {
return "", err
}
return string(utf16.Decode(codes)), nil
}
@@ -1,4 +1,4 @@
package pe
package dotnet
import (
"testing"
@@ -13,7 +13,7 @@ import (
"github.com/anchore/syft/syft/source/stereoscopesource"
)
func Test_Read_DotNetDetection(t *testing.T) {
func Test_getLogicalDotnetPE(t *testing.T) {
tests := []struct {
name string
fixture string
@@ -126,7 +126,7 @@ func Test_Read_DotNetDetection(t *testing.T) {
reader := fixtureFile(t, tt.fixture, tt.path)
got, err := Read(reader)
got, err := getLogicalDotnetPE(reader)
tt.wantErr(t, err)
if err != nil {
return
@@ -136,7 +136,7 @@ func Test_Read_DotNetDetection(t *testing.T) {
t.Errorf("unexpected version resources (-want +got): %s", d)
}
assert.Equal(t, tt.wantCLR, got.CLR.HasEvidenceOfCLR())
assert.Equal(t, tt.wantCLR, got.CLR.hasEvidenceOfCLR())
})
}
}
@@ -486,6 +486,7 @@ var defaultCandidateAdditions = buildCandidateLookup(
candidateKey{PkgName: "dnsmasq", Vendor: "dnsmasq"},
candidateAddition{AdditionalVendors: []string{"thekelleys"}},
},
//
// Binary packages
{
pkg.BinaryPkg,
@@ -629,38 +630,7 @@ var defaultCandidateRemovals = buildCandidateRemovalLookup(
candidateKey{PkgName: "grpc"},
candidateRemovals{ProductsToRemove: []string{"grpc"}},
},
// Rust packages
{
pkg.RustPkg,
candidateKey{PkgName: "hyper"},
// Avoid matching CVE-2024-23741
candidateRemovals{VendorsToRemove: []string{"vercel"}},
},
{
pkg.RustPkg,
candidateKey{PkgName: "opentelemetry"},
// Avoid matching CVE-2023-45142
candidateRemovals{ProductsToRemove: []string{"opentelemetry"}},
},
{
pkg.RustPkg,
candidateKey{PkgName: "prometheus"},
// Avoid matching CVE-2019-3826
candidateRemovals{VendorsToRemove: []string{"prometheus"}},
},
{
pkg.RustPkg,
candidateKey{PkgName: "phf"},
// Avoid matching CVE-2000-1186
candidateRemovals{VendorsToRemove: []string{"phf"}},
},
{
pkg.RustPkg,
candidateKey{PkgName: "redis"},
// Avoid matching CVE-2022-24735
candidateRemovals{VendorsToRemove: []string{"redis"}},
},
// PHP packages
// PHP Packages
{
pkg.PhpPearPkg,
candidateKey{PkgName: "redis"},
@@ -3848,9 +3848,6 @@
"localhost-now": [
"cpe:2.3:a:localhost-now_project:localhost-now:*:*:*:*:*:node.js:*:*"
],
"lockfile-lint-api": [
"cpe:2.3:a:lirantal:lockfile-lint-api:*:*:*:*:*:node.js:*:*"
],
"locutus": [
"cpe:2.3:a:locutus:locutus:*:*:*:*:*:node.js:*:*"
],
@@ -7075,9 +7072,6 @@
"versionize": [
"cpe:2.3:a:versionize_project:versionize:*:*:*:*:*:rust:*:*"
],
"wasmi": [
"cpe:2.3:a:wasmi-labs:wasmi:*:*:*:*:*:rust:*:*"
],
"wasmtime": [
"cpe:2.3:a:bytecodealliance:wasmtime:*:*:*:*:*:rust:*:*"
],
@@ -7552,7 +7546,6 @@
"advanced-custom-fields": [
"cpe:2.3:a:advancedcustomfields:advanced_custom_fields:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:advancedcustomfields:advanced_custom_fields:*:*:*:*:-:wordpress:*:*",
"cpe:2.3:a:advancedcustomfields:advanced_custom_fields:*:*:*:*:free:wordpress:*:*",
"cpe:2.3:a:advancedcustomfields:advanced_custom_fields:*:*:*:*:pro:wordpress:*:*"
],
"advanced-database-cleaner": [
@@ -7682,9 +7675,6 @@
"ajax-bootmodal-login": [
"cpe:2.3:a:ajax_bootmodal_login_project:ajax_bootmodal_login:*:*:*:*:*:wordpress:*:*"
],
"ajax-filter-posts": [
"cpe:2.3:a:addonmaster:post_grid_master:*:*:*:*:*:wordpress:*:*"
],
"ajax-load-more": [
"cpe:2.3:a:connekthq:ajax_load_more:*:*:*:*:*:wordpress:*:*"
],
@@ -7890,9 +7880,6 @@
"animate-it": [
"cpe:2.3:a:eleopard:animate_it\\!:*:*:*:*:*:wordpress:*:*"
],
"animated-al-list": [
"cpe:2.3:a:alexdtn:animated_al_list:*:*:*:*:*:wordpress:*:*"
],
"animated-counters": [
"cpe:2.3:a:eralion:animated_counters:*:*:*:*:*:wordpress:*:*"
],
@@ -8038,9 +8025,6 @@
"arprice-responsive-pricing-table": [
"cpe:2.3:a:reputeinfosystems:arprice_lite:*:*:*:*:*:wordpress:*:*"
],
"artibot": [
"cpe:2.3:a:artibot:artibot:*:*:*:*:*:wordpress:*:*"
],
"article-directory": [
"cpe:2.3:a:article_directory_project:article_directory:*:*:*:*:*:wordpress:*:*"
],
@@ -8227,9 +8211,6 @@
"awesome-weather": [
"cpe:2.3:a:awesome_weather_widget_project:awesome_weather_widget:*:*:*:*:*:wordpress:*:*"
],
"awin-advertiser-tracking": [
"cpe:2.3:a:awin:awin_-_advertiser_tracking_for_woocommerce:*:*:*:*:*:wordpress:*:*"
],
"awin-data-feed": [
"cpe:2.3:a:awin:awin_data_feed:*:*:*:*:*:wordpress:*:*"
],
@@ -8388,9 +8369,6 @@
"cpe:2.3:a:web-x:be-popia-compliant:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:web-x:be_popia_compliant:*:*:*:*:*:wordpress:*:*"
],
"beaf-before-and-after-gallery": [
"cpe:2.3:a:themefic:ultimate_before_after_image_slider_\\\u0026_gallery:*:*:*:*:*:wordpress:*:*"
],
"beam-me-up-scotty": [
"cpe:2.3:a:outtheboxthemes:beam_me_up_scotty:*:*:*:*:*:wordpress:*:*"
],
@@ -8833,9 +8811,6 @@
"button-block": [
"cpe:2.3:a:bplugins:button_block:*:*:*:*:*:wordpress:*:*"
],
"button-contact-vr": [
"cpe:2.3:a:buttonizer:call_\\/_chat_\\/_contact_button:*:*:*:*:*:wordpress:*:*"
],
"button-generation": [
"cpe:2.3:a:wow-company:button_generator:*:*:*:*:*:wordpress:*:*"
],
@@ -8968,9 +8943,6 @@
"canto": [
"cpe:2.3:a:canto:canto:*:*:*:*:*:wordpress:*:*"
],
"canvasflow": [
"cpe:2.3:a:canvasflow:canvasflow:*:*:*:*:*:wordpress:*:*"
],
"canvasio3d-light": [
"cpe:2.3:a:virtuellwerk:canvasio3d_light:*:*:*:*:*:wordpress:*:*"
],
@@ -9262,9 +9234,6 @@
"clickdesk-live-support-chat-plugin": [
"cpe:2.3:a:clickdesk:clickdesk:*:*:*:*:*:wordpress:*:*"
],
"clickwhale": [
"cpe:2.3:a:flowdee:clickwhale:*:*:*:*:*:wordpress:*:*"
],
"clictracker": [
"cpe:2.3:a:wp_clictracker_project:wp_clictracker:*:*:*:*:*:wordpress:*:*"
],
@@ -9381,9 +9350,6 @@
"colorful-categories": [
"cpe:2.3:a:gesundheit-bewegt:colorful_categories:*:*:*:*:*:wordpress:*:*"
],
"colorlib-coming-soon-maintenance": [
"cpe:2.3:a:colorlib:coming_soon_\\\u0026_maintenance_mode:*:*:*:*:*:wordpress:*:*"
],
"column-matic": [
"cpe:2.3:a:column-matic_project:column-matic:*:*:*:*:*:wordpress:*:*"
],
@@ -9755,9 +9721,6 @@
"core-web-vitals-pagespeed-booster": [
"cpe:2.3:a:magazine3:core_web_vitals_\\\u0026_pagespeed_booster:*:*:*:*:*:wordpress:*:*"
],
"coreactivity": [
"cpe:2.3:a:dev4press:coreactivity:*:*:*:*:*:wordpress:*:*"
],
"corner-ad": [
"cpe:2.3:a:dwbooster:corner_ad:*:*:*:*:*:wordpress:*:*"
],
@@ -10120,9 +10083,6 @@
"cpe:2.3:a:fastlinemedia:customizer_export\\/import:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wpbeaverbuilder:customizer_export\\/import:*:*:*:*:*:wordpress:*:*"
],
"cww-companion": [
"cpe:2.3:a:codeworkweb:cww_companion:*:*:*:*:*:wordpress:*:*"
],
"cybersoldier": [
"cpe:2.3:a:webbigt:cybersoldier:*:*:*:*:*:wordpress:*:*"
],
@@ -10219,8 +10179,7 @@
"cpe:2.3:a:webtoffee:decorator:*:*:*:*:*:wordpress:*:*"
],
"defender-security": [
"cpe:2.3:a:wpmudev:defender:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wpmudev:defender:*:*:*:*:free:wordpress:*:*",
"cpe:2.3:a:wmpudev:defender_security:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wpmudev:defender_security:*:*:*:*:*:wordpress:*:*"
],
"definitive-addons-for-elementor": [
@@ -10348,15 +10307,9 @@
"dmsguestbook": [
"cpe:2.3:a:dmsguestbook_project:dmsguestbook:*:*:*:*:*:wordpress:*:*"
],
"dn-footer-contacts": [
"cpe:2.3:a:digireturn:footer_contacts_bar:*:*:*:*:*:wordpress:*:*"
],
"dn-popup": [
"cpe:2.3:a:digireturn:dn_popup:*:*:*:*:*:wordpress:*:*"
],
"dn-shipping-by-weight": [
"cpe:2.3:a:digireturn:shipping_by_weight_for_woocommerce:*:*:*:*:*:wordpress:*:*"
],
"document-emberdder": [
"cpe:2.3:a:bplugins:document_embedder:*:*:*:*:*:wordpress:*:*"
],
@@ -10374,7 +10327,7 @@
"cpe:2.3:a:wedevs:dokan:*:*:*:*:*:wordpress:*:*"
],
"dokan-pro": [
"cpe:2.3:a:dokan:dokan:*:*:*:*:pro:wordpress:*:*"
"cpe:2.3:a:dokan:dokan_pro_plugin:*:*:*:*:*:wordpress:*:*"
],
"dologin": [
"cpe:2.3:a:wpdo:dologin_security:*:*:*:*:*:wordpress:*:*"
@@ -10791,9 +10744,6 @@
"eg-attachments": [
"cpe:2.3:a:egeorjon:eg-attachments:*:*:*:*:*:wordpress:*:*"
],
"ekc-tournament-manager": [
"cpe:2.3:a:lukashuser:ekc_tournament_manager:*:*:*:*:*:wordpress:*:*"
],
"elastic-email-sender": [
"cpe:2.3:a:elasticemail:elastic_email_sender:*:*:*:*:*:wordpress:*:*"
],
@@ -11028,8 +10978,7 @@
"cpe:2.3:a:estatik:estatik:*:*:*:*:*:wordpress:*:*"
],
"estatik-mortgage-calculator": [
"cpe:2.3:a:estatik:estatik_mortgage_calculator:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:estatik:mortgage_calculator:*:*:*:*:*:wordpress:*:*"
"cpe:2.3:a:estatik:estatik_mortgage_calculator:*:*:*:*:*:wordpress:*:*"
],
"etsy-shop": [
"cpe:2.3:a:etsy_shop_project:etsy_shop:*:*:*:*:*:wordpress:*:*"
@@ -11286,9 +11235,6 @@
"fast-search-powered-by-solr": [
"cpe:2.3:a:fast-search-powered-by-solr_project:fast-search-powered-by-solr:*:*:*:*:*:wordpress:*:*"
],
"fast-tube": [
"cpe:2.3:a:caspie:fast_tube:*:*:*:*:*:wordpress:*:*"
],
"fast-velocity-minify": [
"cpe:2.3:a:fastvelocity:minify:*:*:*:*:*:wordpress:*:*"
],
@@ -11335,9 +11281,6 @@
"featured-image-from-url": [
"cpe:2.3:a:fifu:featured_image_from_url:*:*:*:*:*:wordpress:*:*"
],
"featured-image-plus": [
"cpe:2.3:a:krasenslavov:featured_image_plus:*:*:*:*:*:wordpress:*:*"
],
"feed-changer": [
"cpe:2.3:a:wp-master:feed_changer_\\\u0026_remover:*:*:*:*:*:wordpress:*:*"
],
@@ -11359,9 +11302,6 @@
"feedzy-rss-feeds": [
"cpe:2.3:a:themeisle:rss_aggregator_by_feedzy:*:*:*:*:*:wordpress:*:*"
],
"fetch-jft": [
"cpe:2.3:a:pjaudiomv:fetch_jft:*:*:*:*:*:wordpress:*:*"
],
"fg-drupal-to-wp": [
"cpe:2.3:a:fredericgilles:fg_drupal:*:*:*:*:*:wordpress:*:*"
],
@@ -11428,7 +11368,6 @@
"cpe:2.3:a:find_my_blocks_project:find_my_blocks:*:*:*:*:*:wordpress:*:*"
],
"first-graders-toolbox": [
"cpe:2.3:a:atakanau:1_click_disable_all:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:atakanau:one_click_disable_all:*:*:*:*:*:wordpress:*:*"
],
"fish-and-ships": [
@@ -11487,9 +11426,6 @@
"floating-button": [
"cpe:2.3:a:wow-company:floating_button:*:*:*:*:*:wordpress:*:*"
],
"floating-contact": [
"cpe:2.3:a:just-a-web-developer:floating_contact_button:*:*:*:*:*:wordpress:*:*"
],
"floating-div": [
"cpe:2.3:a:floating_div_project:floating_div:*:*:*:*:*:wordpress:*:*"
],
@@ -11539,9 +11475,6 @@
"fluid-responsive-slideshow": [
"cpe:2.3:a:tonjoostudio:fluid-responsive-slideshow:*:*:*:*:*:wordpress:*:*"
],
"flynax-bridge": [
"cpe:2.3:a:flynax:flynax_bridge:*:*:*:*:*:wordpress:*:*"
],
"folders": [
"cpe:2.3:a:premio:folders:*:*:*:*:*:wordpress:*:*"
],
@@ -11916,9 +11849,6 @@
"cpe:2.3:a:ghost:ghost:*:*:*:*:*:node.js:*:*",
"cpe:2.3:a:ghost:ghost:*:*:*:*:*:wordpress:*:*"
],
"gianism": [
"cpe:2.3:a:takahashifumiki:gianism:*:*:*:*:*:wordpress:*:*"
],
"gift-certificate-creator": [
"cpe:2.3:a:bobcares:gift-certificate-creator:*:*:*:*:*:wordpress:*:*"
],
@@ -12141,8 +12071,7 @@
"cpe:2.3:a:groundhogg:groundhogg:*:*:*:*:*:wordpress:*:*"
],
"gs-logo-slider": [
"cpe:2.3:a:gsplugins:gs_logo_slider:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:gsplugins:logo_slider:*:*:*:*:*:wordpress:*:*"
"cpe:2.3:a:gsplugins:gs_logo_slider:*:*:*:*:*:wordpress:*:*"
],
"gs-portfolio": [
"cpe:2.3:a:gsplugins:gs_filterable_portfolio:*:*:*:*:*:wordpress:*:*"
@@ -12496,9 +12425,6 @@
"ideapush": [
"cpe:2.3:a:northernbeacheswebsites:ideapush:*:*:*:*:*:wordpress:*:*"
],
"idonate": [
"cpe:2.3:a:themeatelier:idonate:*:*:*:*:*:wordpress:*:*"
],
"idx-broker-platinum": [
"cpe:2.3:a:idxbroker:idx_broker:*:*:*:*:platinum:wordpress:*:*",
"cpe:2.3:a:idxbroker:impress_for_idx_broker:*:*:*:*:platinum:wordpress:*:*"
@@ -12754,9 +12680,6 @@
"instant-images": [
"cpe:2.3:a:connekthq:instant_images_-_one_click_unsplash_uploads:*:*:*:*:*:wordpress:*:*"
],
"instantio": [
"cpe:2.3:a:themefic:instantio:*:*:*:*:*:wordpress:*:*"
],
"instawp-connect": [
"cpe:2.3:a:instawp:instawp_connect:*:*:*:*:*:wordpress:*:*"
],
@@ -12892,9 +12815,6 @@
"jetpack": [
"cpe:2.3:a:automattic:jetpack:*:*:*:*:*:wordpress:*:*"
],
"jetpack-boost": [
"cpe:2.3:a:automattic:jetpack_boost:*:*:*:*:*:wordpress:*:*"
],
"jetwidgets-for-elementor": [
"cpe:2.3:a:crocoblock:jetwidgets_for_elementor:*:*:*:*:*:wordpress:*:*"
],
@@ -13000,9 +12920,6 @@
"just-tables": [
"cpe:2.3:a:hasthemes:justtables:*:*:*:*:*:wordpress:*:*"
],
"justrows-free": [
"cpe:2.3:a:canaveralstudio:justrows_free:*:*:*:*:*:wordpress:*:*"
],
"jvm-rich-text-icons": [
"cpe:2.3:a:jorisvm:jvm_gutenberg_rich_text_icons:*:*:*:*:*:wordpress:*:*"
],
@@ -13036,9 +12953,6 @@
"kb-support": [
"cpe:2.3:a:logon:kb_support:*:*:*:*:*:wordpress:*:*"
],
"kbucket": [
"cpe:2.3:a:optimalaccess:kbucket:*:*:*:*:*:wordpress:*:*"
],
"keep-backup-daily": [
"cpe:2.3:a:androidbubbles:keep_backup_daily:*:*:*:*:*:wordpress:*:*"
],
@@ -13078,9 +12992,6 @@
"kk-star-ratings": [
"cpe:2.3:a:kamalkhan:kk_star_ratings:*:*:*:*:*:wordpress:*:*"
],
"kkprogressbar": [
"cpe:2.3:a:krzysztof-furtak:kkprogressbar2:*:*:*:*:free:wordpress:*:*"
],
"klarna-checkout-for-woocommerce": [
"cpe:2.3:a:klarna:klarna_checkout_for_woocommerce:*:*:*:*:*:wordpress:*:*"
],
@@ -13244,9 +13155,6 @@
"legal-pages": [
"cpe:2.3:a:wpwax:legal_pages:*:*:*:*:*:wordpress:*:*"
],
"legull": [
"cpe:2.3:a:alwayscurious:legull:*:*:*:*:*:wordpress:*:*"
],
"leira-cron-jobs": [
"cpe:2.3:a:leira:cron_jobs:*:*:*:*:*:wordpress:*:*"
],
@@ -13465,9 +13373,6 @@
"logwpmail": [
"cpe:2.3:a:premierethemes:log_wp_mail:*:*:*:*:*:wordpress:*:*"
],
"lokalyze-call-now": [
"cpe:2.3:a:lokalyze:call_me_now:*:*:*:*:*:wordpress:*:*"
],
"ltl-freight-quotes-unishippers-edition": [
"cpe:2.3:a:eniture:ltl_freight_quotes:*:*:*:*:unishippers:wordpress:*:*"
],
@@ -13562,7 +13467,7 @@
"cpe:2.3:a:mailoptin:mailoptin:*:*:*:*:*:wordpress:*:*"
],
"mailpoet": [
"cpe:2.3:a:automattic:mailpoet:*:*:*:*:free:wordpress:*:*"
"cpe:2.3:a:mailpoet:mailpoet:*:*:*:*:*:wordpress:*:*"
],
"mailtree-log-mail": [
"cpe:2.3:a:oacstudio:mailtree_log_mail:*:*:*:*:*:wordpress:*:*"
@@ -13570,9 +13475,6 @@
"maintenance": [
"cpe:2.3:a:webfactoryltd:maintenance:*:*:*:*:*:wordpress:*:*"
],
"maintenance-notice": [
"cpe:2.3:a:codevibrant:maintenance_notice:*:*:*:*:*:wordpress:*:*"
],
"maintenance-page": [
"cpe:2.3:a:themegrill:maintenance_page:*:*:*:*:*:wordpress:*:*"
],
@@ -13741,9 +13643,6 @@
"media-library-plus": [
"cpe:2.3:a:maxfoundry:media_library_folders:*:*:*:*:*:wordpress:*:*"
],
"media-library-tools": [
"cpe:2.3:a:wptinysolutions:media_library_tools:*:*:*:*:*:wordpress:*:*"
],
"media-list": [
"cpe:2.3:a:drelton:medialist:*:*:*:*:*:wordpress:*:*"
],
@@ -14305,9 +14204,6 @@
"newsletter-optin-box": [
"cpe:2.3:a:noptin:noptin:*:*:*:*:*:wordpress:*:*"
],
"newsletter-popup": [
"cpe:2.3:a:mndpsingh287:newsletter_popup:*:*:*:*:*:wordpress:*:*"
],
"newsletters-lite": [
"cpe:2.3:a:tribulant:newsletters:*:*:*:*:*:wordpress:*:*"
],
@@ -14572,9 +14468,6 @@
"openpgp-form-encryption": [
"cpe:2.3:a:arnesonium:openpgp_form_encryption:*:*:*:*:*:wordpress:*:*"
],
"opensheetmusicdisplay": [
"cpe:2.3:a:opensheetmusicdisplay:opensheetmusicdisplay:*:*:*:*:*:wordpress:*:*"
],
"ops-robots-txt": [
"cpe:2.3:a:ops-robots-txt_project:ops-robots-txt:*:*:*:*:*:wordpress:*:*"
],
@@ -14710,9 +14603,6 @@
"pagepost-content-shortcode": [
"cpe:2.3:a:page\\/post_content_shortcode_project:page\\/post_content_shortcode:*:*:*:*:*:wordpress:*:*"
],
"pagerank-tools": [
"cpe:2.3:a:mahype:pagerank_tools:*:*:*:*:*:wordpress:*:*"
],
"pagination": [
"cpe:2.3:a:bestwebsoft:pagination:*:*:*:*:*:wordpress:*:*"
],
@@ -15167,9 +15057,6 @@
"post-highlights": [
"cpe:2.3:a:post_highlights_projects:post_highlights:*:*:*:*:*:wordpress:*:*"
],
"post-in-page-for-elementor": [
"cpe:2.3:a:migaweb:post_in_page_for_elementor:*:*:*:*:*:wordpress:*:*"
],
"post-index": [
"cpe:2.3:a:post_index_project:post_index:*:*:*:*:*:wordpress:*:*"
],
@@ -15190,7 +15077,8 @@
"cpe:2.3:a:i13websolution:post_sliders_\\\u0026_post_grids:*:*:*:*:*:wordpress:*:*"
],
"post-smtp": [
"cpe:2.3:a:wpexperts:post_smtp:*:*:*:*:*:wordpress:*:*"
"cpe:2.3:a:wpexperts:post_smtp:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wpexperts:post_smtp_mailer:*:*:*:*:*:wordpress:*:*"
],
"post-snippets": [
"cpe:2.3:a:postsnippets:post_snippets:*:*:*:*:*:wordpress:*:*"
@@ -15539,9 +15427,6 @@
"cpe:2.3:a:quick_chat_project:quick_chat:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:techytalk:quick_chat:*:*:*:*:*:wordpress:*:*"
],
"quick-code": [
"cpe:2.3:a:gwycon:quick_code:*:*:*:*:*:wordpress:*:*"
],
"quick-contact-form": [
"cpe:2.3:a:fullworksplugins:quick_contact_form:*:*:*:*:*:wordpress:*:*"
],
@@ -15573,9 +15458,6 @@
"quickswish": [
"cpe:2.3:a:hasthemes:quickswish:*:*:*:*:*:wordpress:*:*"
],
"quillforms": [
"cpe:2.3:a:mdmag:quill_forms:*:*:*:*:*:wordpress:*:*"
],
"quiz-expert": [
"cpe:2.3:a:wepupil:quiz_expert_-_easy_quiz_maker\\,_exam_and_test_manager:*:*:*:*:*:wordpress:*:*"
],
@@ -16249,9 +16131,6 @@
"sendpress": [
"cpe:2.3:a:pressified:sendpress:*:*:*:*:*:wordpress:*:*"
],
"sendpulse-email-marketing-newsletter": [
"cpe:2.3:a:sendpulse:sendpulse_email_marketing_newsletter:*:*:*:*:*:wordpress:*:*"
],
"sendpulse-web-push": [
"cpe:2.3:a:sendpulse:free_web_push:*:*:*:*:*:wordpress:*:*"
],
@@ -16326,9 +16205,6 @@
"cpe:2.3:a:legalweb:wp_dsgvo_tools:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:shapepress:wp_dsgvo_tools:*:*:*:*:*:wordpress:*:*"
],
"share-buttons": [
"cpe:2.3:a:artlosk:social_share_buttons:*:*:*:*:*:wordpress:*:*"
],
"share-on-diaspora": [
"cpe:2.3:a:share_on_diaspora_project:share_on_diaspora:*:*:*:*:*:wordpress:*:*"
],
@@ -16409,8 +16285,7 @@
],
"shortcodes-ultimate": [
"cpe:2.3:a:getshortcodes:shortcodes_ultimate:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:getshortcodes:shortcodes_ultimate:*:*:*:*:-:wordpress:*:*",
"cpe:2.3:a:getshortcodes:shortcodes_ultimate:*:*:*:*:pro:wordpress:*:*"
"cpe:2.3:a:getshortcodes:shortcodes_ultimate:*:*:*:*:-:wordpress:*:*"
],
"shortcut-macros": [
"cpe:2.3:a:shortcut_macros_project:shortcut_macros:*:*:*:*:*:wordpress:*:*"
@@ -16561,9 +16436,6 @@
"cpe:2.3:a:simplefilelist:simple-file-list:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:simplefilelist:simple_file_list:*:*:*:*:*:wordpress:*:*"
],
"simple-form": [
"cpe:2.3:a:devsabbirahmed:simple_form:*:*:*:*:*:wordpress:*:*"
],
"simple-history": [
"cpe:2.3:a:simple_history_project:simple_history:*:*:*:*:*:wordpress:*:*"
],
@@ -16592,9 +16464,6 @@
"simple-ldap-login": [
"cpe:2.3:a:objectiv:simple_ldap_login:*:*:*:*:*:wordpress:*:*"
],
"simple-lightbox": [
"cpe:2.3:a:archetyped:simple_lightbox:*:*:*:*:*:wordpress:*:*"
],
"simple-link-directory": [
"cpe:2.3:a:quantumcloud:simple_link_directory:*:*:*:*:*:wordpress:*:*"
],
@@ -16977,8 +16846,7 @@
"cpe:2.3:a:wpmudev:smartcrawl:*:*:*:*:*:wordpress:*:*"
],
"smartsearchwp": [
"cpe:2.3:a:smartsearchwp:smartsearchwp:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:webdigit:chatbot_with_chatgpt:*:*:*:*:*:wordpress:*:*"
"cpe:2.3:a:smartsearchwp:smartsearchwp:*:*:*:*:*:wordpress:*:*"
],
"smartsoftbutton-widget-de-botones-de-chat": [
"cpe:2.3:a:smartsoft:button_widget_smartsoft:*:*:*:*:*:wordpress:*:*"
@@ -17052,9 +16920,6 @@
"social-media-feather": [
"cpe:2.3:a:sharethis:social_media_feather:*:*:*:*:*:wordpress:*:*"
],
"social-media-widget": [
"cpe:2.3:a:bmwebproperties:social_media_widget:*:*:*:*:*:wordpress:*:*"
],
"social-networks-auto-poster-facebook-twitter-g": [
"cpe:2.3:a:nextscripts:social_networks_auto_poster:*:*:*:*:*:wordpress:*:*"
],
@@ -17158,9 +17023,6 @@
"spice-starter-sites": [
"cpe:2.3:a:spicethemes:spice_starter_sites:*:*:*:*:*:wordpress:*:*"
],
"spider-contacts": [
"cpe:2.3:a:10web:spidercontacts:*:*:*:*:*:wordpress:*:*"
],
"spider-event-calendar": [
"cpe:2.3:a:web-dorado:spider_calendar:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:web-dorado:spider_event_calendar:*:*:*:*:*:*:*:*"
@@ -17402,9 +17264,6 @@
"support-chat": [
"cpe:2.3:a:ninjateam:click_to_chat:*:*:*:*:*:wordpress:*:*"
],
"support-svg": [
"cpe:2.3:a:sayedulsayem:support_svg:*:*:*:*:*:wordpress:*:*"
],
"supportcandy": [
"cpe:2.3:a:supportcandy:supportcandy:*:*:*:*:*:wordpress:*:*"
],
@@ -17832,9 +17691,6 @@
"tourfic": [
"cpe:2.3:a:themefic:tourfic:*:*:*:*:*:wordpress:*:*"
],
"tournamatch": [
"cpe:2.3:a:tournamatch:tournamatch:*:*:*:*:*:wordpress:*:*"
],
"tp-education": [
"cpe:2.3:a:themepalace:tp_education:*:*:*:*:*:wordpress:*:*"
],
@@ -17850,9 +17706,6 @@
"tracking-code-manager": [
"cpe:2.3:a:data443:tracking_code_manager:*:*:*:*:*:wordpress:*:*"
],
"tradedoubler-affiliate-tracker": [
"cpe:2.3:a:tradedoubler:grow:*:*:*:*:*:wordpress:*:*"
],
"tradetracker-store": [
"cpe:2.3:a:wpaffiliatefeed:tradetracker-store:*:*:*:*:*:wordpress:*:*"
],
@@ -18710,9 +18563,6 @@
"widget-twitter": [
"cpe:2.3:a:web-dorado:wd_widgettwitter:*:*:*:*:*:wordpress:*:*"
],
"widget4call": [
"cpe:2.3:a:apidaze:widget4call:*:*:*:*:*:wordpress:*:*"
],
"widgetkit-for-elementor": [
"cpe:2.3:a:themesgrove:all-in-one_addons_for_elementor:*:*:*:*:*:wordpress:*:*"
],
@@ -18962,9 +18812,6 @@
"woocommerce-amazon-affiliates-light-version": [
"cpe:2.3:a:wzone_project:wzone:*:*:*:*:lite:wordpress:*:*"
],
"woocommerce-and-wp-emember-integration": [
"cpe:2.3:a:tipsandtricks-hq:wp_emember:*:*:*:*:*:wordpress:*:*"
],
"woocommerce-catalog-enquiry": [
"cpe:2.3:a:multivendorx:product_catalog_mode_for_woocommerce:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wc-marketplace:wc_catalog_enquiry:*:*:*:*:*:wordpress:*:*"
@@ -19248,9 +19095,6 @@
"wp-ban": [
"cpe:2.3:a:wp-ban_project:wp-ban:*:*:*:*:*:wordpress:*:*"
],
"wp-base-booking-of-appointments-services-and-events": [
"cpe:2.3:a:wp-base:wp_base_booking_of_appointments\\,_services_and_events:*:*:*:*:*:wordpress:*:*"
],
"wp-best-quiz": [
"cpe:2.3:a:subina:wp_best_quiz:*:*:*:*:*:wordpress:*:*"
],
@@ -19384,9 +19228,6 @@
"wp-commentnavi": [
"cpe:2.3:a:wp-commentnavi_project:wp-commentnavi:*:*:*:*:*:wordpress:*:*"
],
"wp-compress-image-optimizer": [
"cpe:2.3:a:wpcompress:wp_compress:*:*:*:*:*:wordpress:*:*"
],
"wp-concours": [
"cpe:2.3:a:olyos:wp-concours:*:*:*:*:*:wordpress:*:*"
],
@@ -19559,9 +19400,6 @@
"wp-education": [
"cpe:2.3:a:hasthemes:wp_education:*:*:*:*:*:wordpress:*:*"
],
"wp-eggdrop": [
"cpe:2.3:a:backie:wp-eggdrop:*:*:*:*:*:wordpress:*:*"
],
"wp-email": [
"cpe:2.3:a:wp-email_project:wp-email:*:*:*:*:*:wordpress:*:*"
],
@@ -19789,9 +19627,6 @@
"wp-imageflow2": [
"cpe:2.3:a:spiffyplugins:wp_flow_plus:*:*:*:*:*:wordpress:*:*"
],
"wp-imagezoom": [
"cpe:2.3:a:aueda:wp-imagezoom:*:*:*:*:*:wordpress:*:*"
],
"wp-import-export-lite": [
"cpe:2.3:a:vjinfotech:wp_import_export_lite:*:*:*:*:*:wordpress:*:*"
],
@@ -20396,7 +20231,7 @@
"cpe:2.3:a:wp-t-wap_project:wp-t-wap:*:*:*:*:*:wordpress:*:*"
],
"wp-table-builder": [
"cpe:2.3:a:dotcamp:wp_table_builder:*:*:*:*:*:wordpress:*:*"
"cpe:2.3:a:wptablebuilder:wp_table_builder:*:*:*:*:*:wordpress:*:*"
],
"wp-table-reloaded": [
"cpe:2.3:a:wp-table_reloaded_project:wp-table_reloaded:*:*:*:*:*:wordpress:*:*"
@@ -20658,8 +20493,7 @@
"cpe:2.3:a:wpforms:wpforms:*:*:*:*:pro:wordpress:*:*"
],
"wpforms-lite": [
"cpe:2.3:a:wpforms:contact_form:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wpforms:wpforms:*:*:*:*:lite:wordpress:*:*"
"cpe:2.3:a:wpforms:contact_form:*:*:*:*:*:wordpress:*:*"
],
"wpforo": [
"cpe:2.3:a:gvectors:wpforo:*:*:*:*:*:wordpress:*:*",
@@ -20718,9 +20552,6 @@
"wpmarketplace": [
"cpe:2.3:a:wpmarketplace_project:wpmarketplace:*:*:*:*:*:wordpress:*:*"
],
"wpmovielibrary": [
"cpe:2.3:a:caercam:wpmovielibrary:*:*:*:*:*:wordpress:*:*"
],
"wpo365-login": [
"cpe:2.3:a:wpo365:wordpress_\\+_azure_ad_\\/_microsoft_office_365:*:*:*:*:*:wordpress:*:*"
],
@@ -21005,9 +20836,6 @@
"z-companion": [
"cpe:2.3:a:wpzita:z_companion:*:*:*:*:*:wordpress:*:*"
],
"z-downloads": [
"cpe:2.3:a:urbanbase:z-downloads:*:*:*:*:*:wordpress:*:*"
],
"z-url-preview": [
"cpe:2.3:a:z-url_preview_project:z-url_preview:*:*:*:*:*:wordpress:*:*"
],
-736
View File
@@ -1,736 +0,0 @@
package pe
import (
"bytes"
"debug/pe"
"encoding/binary"
"errors"
"fmt"
"io"
"unicode/utf16"
"github.com/scylladb/go-set/strset"
"github.com/scylladb/go-set/u32set"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/unionreader"
)
const peMaxAllowedDirectoryEntries = 0x1000
var imageDirectoryEntryIndexes = []int{
pe.IMAGE_DIRECTORY_ENTRY_RESOURCE, // where version resources are stored
pe.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, // where info about the CLR is stored
}
// File does not directly represent a binary shape to be parsed, instead it represents the
// information of interest extracted from a PE file.
type File struct {
// Location is where the PE file was found
Location file.Location
// CLR is the information about the CLR (common language runtime) version found in the PE file which helps
// understand if this executable is even a .NET application.
CLR *CLREvidence
// VersionResources is a map of version resource keys to their values found in the VERSIONINFO resource directory.
VersionResources map[string]string
}
// CLREvidence is basic info about the CLR (common language runtime) version from the COM descriptor.
// This is not a complete representation of the CLR version, but rather a subset of the information that is
// useful to us.
type CLREvidence struct {
// HasClrResourceNames is true if there are CLR resource names found in the PE file (e.g. "CLRDEBUGINFO").
HasClrResourceNames bool
// MajorVersion is the minimum supported major version of the CLR.
MajorVersion uint16
// MinorVersion is the minimum supported minor version of the CLR.
MinorVersion uint16
}
// HasEvidenceOfCLR returns true if the PE file has evidence of a CLR (common language runtime) version.
func (c *CLREvidence) HasEvidenceOfCLR() bool {
return c != nil && (c.MajorVersion != 0 && c.MinorVersion != 0 || c.HasClrResourceNames)
}
type peDosHeader struct {
Magic [2]byte // "MZ"
Unused [58]byte
AddressOfNewEXEHeader uint32 // offset to PE header
}
// peImageCore20 represents the .NET Core 2.0 header structure.
// Source: https://github.com/dotnet/msbuild/blob/9fa9d800dabce3bfcf8365f651f3a713e01f8a85/src/Tasks/NativeMethods.cs#L761-L775
type peImageCore20 struct {
Cb uint32
MajorRuntimeVersion uint16
MinorRuntimeVersion uint16
}
// peImageResourceDirectory represents the resource directory structure.
type peImageResourceDirectory struct {
Characteristics uint32
TimeDateStamp uint32
MajorVersion uint16
MinorVersion uint16
NumberOfNamedEntries uint16
NumberOfIDEntries uint16
}
// peImageResourceDirectoryEntry represents an entry in the resource directory entries.
type peImageResourceDirectoryEntry struct {
Name uint32
OffsetToData uint32
}
// peImageResourceDataEntry is the unit of raw data in the Resource Data area.
type peImageResourceDataEntry struct {
OffsetToData uint32
Size uint32
CodePage uint32
Reserved uint32
}
// peVsFixedFileInfo represents the fixed file information structure.
type peVsFixedFileInfo struct {
Signature uint32
StructVersion uint32
FileVersionMS uint32
FileVersionLS uint32
ProductVersionMS uint32
ProductVersionLS uint32
FileFlagsMask uint32
FileFlags uint32
FileOS uint32
FileType uint32
FileSubtype uint32
FileDateMS uint32
FileDateLS uint32
}
type peVsVersionInfo peLenValLenType
type peStringFileInfo peLenValLenType
type peStringTable peLenValLenType
type peString peLenValLenType
type peLenValLenType struct {
Length uint16
ValueLength uint16
Type uint16
}
type extractedSection struct {
RVA uint32
BaseRVA uint32
Size uint32
Reader *bytes.Reader
}
func (s extractedSection) exists() bool {
return s.RVA != 0 && s.Size != 0
}
func directoryName(i int) string {
switch i {
case pe.IMAGE_DIRECTORY_ENTRY_RESOURCE:
return "Resource"
case pe.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:
return "COM Descriptor"
}
return fmt.Sprintf("Unknown (%d)", i)
}
func Read(f file.LocationReadCloser) (*File, error) {
r, err := unionreader.GetUnionReader(f)
if err != nil {
return nil, err
}
sections, _, err := parsePEFile(r)
if err != nil {
return nil, fmt.Errorf("unable to parse PE sections: %w", err)
}
dirs := u32set.New() // keep track of the RVAs we have already parsed (prevent infinite recursion edge cases)
versionResources := make(map[string]string) // map of version resource keys to their values
resourceNames := strset.New() // set of resource names found in the PE file
err = parseResourceDirectory(sections[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE], dirs, versionResources, resourceNames)
if err != nil {
return nil, err
}
c, err := parseCLR(sections[pe.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR], resourceNames)
if err != nil {
return nil, fmt.Errorf("unable to parse PE CLR directory: %w", err)
}
return &File{
Location: f.Location,
CLR: c,
VersionResources: versionResources,
}, nil
}
// parsePEFile creates readers for targeted sections of the binary used by downstream processing.
func parsePEFile(file unionreader.UnionReader) (map[int]*extractedSection, []pe.SectionHeader32, error) {
fileHeader, magic, err := parsePEHeader(file)
if err != nil {
return nil, nil, fmt.Errorf("error parsing PE header: %w", err)
}
soi, headers, err := parseSectionHeaders(file, magic, fileHeader.NumberOfSections)
if err != nil {
return nil, nil, fmt.Errorf("error parsing section headers: %w", err)
}
for i, sec := range soi {
if !sec.exists() {
continue
}
data, err := readDataFromRVA(file, sec.RVA, sec.Size, headers)
if err != nil {
return nil, nil, fmt.Errorf("error reading %q section data: %w", directoryName(i), err)
}
sec.Reader = data
}
return soi, headers, nil
}
// parsePEHeader reads the beginning of a PE formatted file, returning the file header and "magic" indicator
// for downstream logic to determine 32/64 bit parsing.
func parsePEHeader(file unionreader.UnionReader) (*pe.FileHeader, uint16, error) {
var dosHeader peDosHeader
if err := binary.Read(file, binary.LittleEndian, &dosHeader); err != nil {
return nil, 0, fmt.Errorf("error reading DOS header: %w", err)
}
if string(dosHeader.Magic[:]) != "MZ" {
return nil, 0, fmt.Errorf("invalid DOS header magic")
}
peOffset := int64(dosHeader.AddressOfNewEXEHeader)
if _, err := file.Seek(peOffset, io.SeekStart); err != nil {
return nil, 0, fmt.Errorf("error seeking to PE header: %w", err)
}
var signature [4]byte
if err := binary.Read(file, binary.LittleEndian, &signature); err != nil {
return nil, 0, fmt.Errorf("error reading PE signature: %w", err)
}
if !bytes.Equal(signature[:], []byte("PE\x00\x00")) {
return nil, 0, fmt.Errorf("invalid PE signature")
}
var fileHeader pe.FileHeader
if err := binary.Read(file, binary.LittleEndian, &fileHeader); err != nil {
return nil, 0, fmt.Errorf("error reading file header: %w", err)
}
var magic uint16
if err := binary.Read(file, binary.LittleEndian, &magic); err != nil {
return nil, 0, fmt.Errorf("error reading optional header magic: %w", err)
}
// seek back to before reading magic (since that value is in the header)
if _, err := file.Seek(-2, io.SeekCurrent); err != nil {
return nil, 0, fmt.Errorf("error seeking back to before reading magic: %w", err)
}
return &fileHeader, magic, nil
}
// parseSectionHeaders reads the section headers from the PE file and extracts the virtual addresses + section size
// information for the sections of interest. Additionally, all section headers are returned to aid in downstream processing.
func parseSectionHeaders(file unionreader.UnionReader, magic uint16, numberOfSections uint16) (map[int]*extractedSection, []pe.SectionHeader32, error) {
soi := make(map[int]*extractedSection)
switch magic {
case 0x10B: // PE32
var optHeader pe.OptionalHeader32
if err := binary.Read(file, binary.LittleEndian, &optHeader); err != nil {
return nil, nil, fmt.Errorf("error reading optional header (PE32): %w", err)
}
for _, i := range imageDirectoryEntryIndexes {
sectionHeader := optHeader.DataDirectory[i]
if sectionHeader.Size == 0 {
continue
}
soi[i] = &extractedSection{
RVA: sectionHeader.VirtualAddress,
Size: sectionHeader.Size,
}
}
case 0x20B: // PE32+ (64 bit)
var optHeader pe.OptionalHeader64
if err := binary.Read(file, binary.LittleEndian, &optHeader); err != nil {
return nil, nil, fmt.Errorf("error reading optional header (PE32+): %w", err)
}
for _, i := range imageDirectoryEntryIndexes {
sectionHeader := optHeader.DataDirectory[i]
if sectionHeader.Size == 0 {
continue
}
soi[i] = &extractedSection{
RVA: sectionHeader.VirtualAddress,
Size: sectionHeader.Size,
}
}
default:
return nil, nil, fmt.Errorf("unknown optional header magic: 0x%x", magic)
}
// read section headers
headers := make([]pe.SectionHeader32, numberOfSections)
for i := 0; i < int(numberOfSections); i++ {
if err := binary.Read(file, binary.LittleEndian, &headers[i]); err != nil {
return nil, nil, fmt.Errorf("error reading section header: %w", err)
}
}
return soi, headers, nil
}
// parseCLR extracts the CLR (common language runtime) version information from the COM descriptor and makes
// present/not-present determination based on the presence of CLR resource names.
func parseCLR(sec *extractedSection, resourceNames *strset.Set) (*CLREvidence, error) {
hasCLRDebugResourceNames := resourceNames.HasAny("CLRDEBUGINFO")
if sec == nil || sec.Reader == nil {
return &CLREvidence{
HasClrResourceNames: hasCLRDebugResourceNames,
}, nil
}
reader := sec.Reader
var c peImageCore20
if err := binary.Read(reader, binary.LittleEndian, &c); err != nil {
return nil, fmt.Errorf("error reading CLR header: %w", err)
}
return &CLREvidence{
HasClrResourceNames: hasCLRDebugResourceNames,
MajorVersion: c.MajorRuntimeVersion,
MinorVersion: c.MinorRuntimeVersion,
}, nil
}
// rvaToFileOffset is a helper function to convert RVA to file offset using section headers
func rvaToFileOffset(rva uint32, sections []pe.SectionHeader32) (uint32, error) {
for _, section := range sections {
if rva >= section.VirtualAddress && rva < section.VirtualAddress+section.VirtualSize {
return section.PointerToRawData + (rva - section.VirtualAddress), nil
}
}
return 0, fmt.Errorf("RVA 0x%x not found in any section", rva)
}
// readDataFromRVA will read data from a specific RVA in the PE file
func readDataFromRVA(file io.ReadSeeker, rva, size uint32, sections []pe.SectionHeader32) (*bytes.Reader, error) {
if size == 0 {
return nil, fmt.Errorf("zero size specified")
}
offset, err := rvaToFileOffset(rva, sections)
if err != nil {
return nil, err
}
if _, err := file.Seek(int64(offset), io.SeekStart); err != nil {
return nil, fmt.Errorf("error seeking to data: %w", err)
}
data := make([]byte, size)
if _, err := io.ReadFull(file, data); err != nil {
return nil, fmt.Errorf("error reading data: %w", err)
}
return bytes.NewReader(data), nil
}
// parseResourceDirectory recursively parses a PE resource directory. This takes a relative virtual address (offset of
// a piece of data or code relative to the base address), the size of the resource directory, the set of RVAs already
// parsed, and the map to populate discovered version resource values.
//
// .rsrc Section
// +------------------------------+
// | Resource Directory Table |
// +------------------------------+
// | Resource Directory Entries |
// | +------------------------+ |
// | | Subdirectory or Data | |
// | +------------------------+ |
// +------------------------------+
// | Resource Data Entries |
// | +------------------------+ |
// | | Resource Data | |
// | +------------------------+ |
// +------------------------------+
// | Actual Resource Data |
// +------------------------------+
//
// sources:
// - https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-rsrc-section
// - https://learn.microsoft.com/en-us/previous-versions/ms809762(v=msdn.10)#pe-file-resources
func parseResourceDirectory(sec *extractedSection, dirs *u32set.Set, fields map[string]string, names *strset.Set) error {
if sec == nil || sec.Size <= 0 {
return nil
}
if sec.Reader == nil {
return errors.New("resource section not found")
}
baseRVA := sec.BaseRVA
if baseRVA == 0 {
baseRVA = sec.RVA
}
offset := int64(sec.RVA - baseRVA)
if _, err := sec.Reader.Seek(offset, io.SeekStart); err != nil {
return fmt.Errorf("error seeking to directory offset: %w", err)
}
var directoryHeader peImageResourceDirectory
if err := readIntoStruct(sec.Reader, &directoryHeader); err != nil {
return fmt.Errorf("error reading directory header: %w", err)
}
numEntries := int(directoryHeader.NumberOfNamedEntries + directoryHeader.NumberOfIDEntries)
switch {
case numEntries > peMaxAllowedDirectoryEntries:
return fmt.Errorf("too many entries in resource directory: %d", numEntries)
case numEntries == 0:
return fmt.Errorf("no entries in resource directory")
case numEntries < 0:
return fmt.Errorf("invalid number of entries in resource directory: %d", numEntries)
}
for i := 0; i < numEntries; i++ {
var entry peImageResourceDirectoryEntry
entryOffset := offset + int64(binary.Size(directoryHeader)) + int64(i*binary.Size(entry))
if _, err := sec.Reader.Seek(entryOffset, io.SeekStart); err != nil {
log.Tracef("error seeking to PE entry offset: %v", err)
continue
}
if err := readIntoStruct(sec.Reader, &entry); err != nil {
continue
}
if err := processResourceEntry(entry, baseRVA, sec, dirs, fields, names); err != nil {
log.Tracef("error processing resource entry: %v", err)
continue
}
}
return nil
}
func processResourceEntry(entry peImageResourceDirectoryEntry, baseRVA uint32, sec *extractedSection, dirs *u32set.Set, fields map[string]string, names *strset.Set) error {
// if the high bit is set, this is a directory entry, otherwise it is a data entry
isDirectory := entry.OffsetToData&0x80000000 != 0
// note: the offset is relative to the beginning of the resource section, not an RVA
entryOffsetToData := entry.OffsetToData & 0x7FFFFFFF
nameIsString := entry.Name&0x80000000 != 0
nameOffset := entry.Name & 0x7FFFFFFF
// read the string name of the resource directory
if nameIsString {
currentPos, err := sec.Reader.Seek(0, io.SeekCurrent)
if err != nil {
return fmt.Errorf("error getting current reader position: %w", err)
}
if _, err := sec.Reader.Seek(int64(nameOffset), io.SeekStart); err != nil {
return fmt.Errorf("error restoring reader position: %w", err)
}
name, err := readUTF16WithLength(sec.Reader)
if err == nil {
names.Add(name)
}
if _, err := sec.Reader.Seek(currentPos, io.SeekStart); err != nil {
return fmt.Errorf("error restoring reader position: %w", err)
}
}
if isDirectory {
subRVA := baseRVA + entryOffsetToData
if dirs.Has(subRVA) {
// some malware uses recursive PE references to evade analysis
return fmt.Errorf("recursive PE reference detected; skipping directory at baseRVA=0x%x subRVA=0x%x", baseRVA, subRVA)
}
dirs.Add(subRVA)
err := parseResourceDirectory(
&extractedSection{
RVA: subRVA,
BaseRVA: baseRVA,
Size: sec.Size - (sec.RVA - baseRVA),
Reader: sec.Reader,
},
dirs, fields, names)
if err != nil {
return err
}
return nil
}
return parseResourceDataEntry(sec.Reader, baseRVA, baseRVA+entryOffsetToData, sec.Size, fields)
}
func parseResourceDataEntry(reader *bytes.Reader, baseRVA, rva, remainingSize uint32, fields map[string]string) error {
var dataEntry peImageResourceDataEntry
offset := int64(rva - baseRVA)
if _, err := reader.Seek(offset, io.SeekStart); err != nil {
return fmt.Errorf("error seeking to data entry offset: %w", err)
}
if err := readIntoStruct(reader, &dataEntry); err != nil {
return fmt.Errorf("error reading resource data entry: %w", err)
}
if remainingSize < dataEntry.Size {
return fmt.Errorf("resource data entry size exceeds remaining size")
}
data := make([]byte, dataEntry.Size)
if _, err := reader.Seek(int64(dataEntry.OffsetToData-baseRVA), io.SeekStart); err != nil {
return fmt.Errorf("error seeking to resource data: %w", err)
}
if _, err := reader.Read(data); err != nil {
return fmt.Errorf("error reading resource data: %w", err)
}
return parseVersionResourceSection(bytes.NewReader(data), fields)
}
// parseVersionResourceSection parses a PE version resource section from within a resource directory.
//
// "The main structure in a version resource is the VS_FIXEDFILEINFO structure. Additional structures include the
// VarFileInfo structure to store language information data, and StringFileInfo for user-defined string information.
// All strings in a version resource are in Unicode format. Each block of information is aligned on a DWORD boundary."
//
// "VS_VERSIONINFO" (utf16)
// +---------------------------------------------------+
// | wLength (2 bytes) |
// | wValueLength (2 bytes) |
// | wType (2 bytes) |
// | szKey ("VS_VERSION_INFO") (utf16) |
// | Padding (to DWORD) |
// +---------------------------------------------------+
// | VS_FIXEDFILEINFO (52 bytes) |
// +---------------------------------------------------+
// | "StringFileInfo" (utf16) |
// +---------------------------------------------------+
// | wLength (2 bytes) |
// | wValueLength (2 bytes) |
// | wType (2 bytes) |
// | szKey ("StringFileInfo") (utf16) |
// | Padding (to DWORD) |
// | StringTable |
// | +--------------------------------------------+ |
// | | wLength (2 bytes) | |
// | | wValueLength (2 bytes) | |
// | | wType (2 bytes) | |
// | | szKey ("040904b0") | |
// | | Padding (to DWORD) | |
// | | String | |
// | | +--------------------------------------+ | |
// | | | wLength (2 bytes) | | |
// | | | wValueLength (2 bytes) | | |
// | | | wType (2 bytes) | | |
// | | | szKey ("FileVersion") | | |
// | | | Padding (to DWORD) | | |
// | | | szValue ("15.00.0913.015") | | |
// | | | Padding (to DWORD) | | |
// | +--------------------------------------------+ |
// +---------------------------------------------------+
// | VarFileInfo (utf16) |
// +---------------------------------------------------+
// | (skip!) |
// +---------------------------------------------------+
//
// sources:
// - https://learn.microsoft.com/en-us/windows/win32/menurc/resource-file-formats
// - https://learn.microsoft.com/en-us/windows/win32/menurc/vs-versioninfo
// - https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
// - https://learn.microsoft.com/en-us/windows/win32/menurc/varfileinfo
// - https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo
// - https://learn.microsoft.com/en-us/windows/win32/menurc/stringtable
func parseVersionResourceSection(reader *bytes.Reader, fields map[string]string) error {
offset := 0
var info peVsVersionInfo
if szKey, err := readIntoStructAndSzKey(reader, &info, &offset); err != nil {
return fmt.Errorf("error reading PE version info: %v", err)
} else if szKey != "VS_VERSION_INFO" {
// this is a resource section, but not the version resources
return nil
}
if err := alignAndSeek(reader, &offset); err != nil {
return fmt.Errorf("error aligning past PE version info: %w", err)
}
var fixedFileInfo peVsFixedFileInfo
if err := readIntoStruct(reader, &fixedFileInfo, &offset); err != nil {
return fmt.Errorf("error reading PE FixedFileInfo: %v", err)
}
for reader.Len() > 0 {
if err := alignAndSeek(reader, &offset); err != nil {
return fmt.Errorf("error seeking to PE StringFileInfo: %w", err)
}
var sfiHeader peStringFileInfo
if szKey, err := readIntoStructAndSzKey(reader, &sfiHeader, &offset); err != nil {
return fmt.Errorf("error reading PE string file info header: %v", err)
} else if szKey != "StringFileInfo" {
// we only care about extracting strings from any string tables, skip this
offset += int(sfiHeader.ValueLength)
continue
}
var stOffset int
// note: the szKey for the prStringTable is the language
var stHeader peStringTable
if _, err := readIntoStructAndSzKey(reader, &stHeader, &offset, &stOffset); err != nil {
return fmt.Errorf("error reading PE string table header: %v", err)
}
for stOffset < int(stHeader.Length) {
var stringHeader peString
if err := readIntoStruct(reader, &stringHeader, &offset, &stOffset); err != nil {
break
}
key := readUTF16(reader, &offset, &stOffset)
if err := alignAndSeek(reader, &offset, &stOffset); err != nil {
return fmt.Errorf("error aligning to next PE string table value: %w", err)
}
var value string
if stringHeader.ValueLength > 0 {
value = readUTF16(reader, &offset, &stOffset)
}
fields[key] = value
if err := alignAndSeek(reader, &offset, &stOffset); err != nil {
return fmt.Errorf("error aligning to next PE string table key: %w", err)
}
}
}
if fields["FileVersion"] == "" {
// we can derive the file version from the fixed file info if it is not already specified as a string entry... neat!
fields["FileVersion"] = fmt.Sprintf("%d.%d.%d.%d",
fixedFileInfo.FileVersionMS>>16, fixedFileInfo.FileVersionMS&0xFFFF,
fixedFileInfo.FileVersionLS>>16, fixedFileInfo.FileVersionLS&0xFFFF)
}
return nil
}
// readIntoStructAndSzKey reads a struct from the reader and updates the offsets if provided, returning the szKey value.
// This is only useful in the context of the resource directory parsing in narrow cases (this is invalid to use outside of that context).
func readIntoStructAndSzKey[T any](reader *bytes.Reader, data *T, offsets ...*int) (string, error) {
if err := readIntoStruct(reader, data, offsets...); err != nil {
return "", err
}
return readUTF16(reader, offsets...), nil
}
// readIntoStruct reads a struct from the reader and updates the offsets if provided.
func readIntoStruct[T any](reader io.Reader, data *T, offsets ...*int) error {
if err := binary.Read(reader, binary.LittleEndian, data); err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return err
}
for i := range offsets {
*offsets[i] += binary.Size(*data)
}
return nil
}
// alignAndSeek aligns the reader to the next DWORD boundary and seeks to the new offset (updating any provided trackOffsets).
func alignAndSeek(reader io.Seeker, offset *int, trackOffsets ...*int) error {
ogOffset := *offset
*offset = alignToDWORD(*offset)
diff := *offset - ogOffset
for i := range trackOffsets {
*trackOffsets[i] += diff
}
_, err := reader.Seek(int64(*offset), io.SeekStart)
return err
}
// alignToDWORD aligns the offset to the next DWORD boundary (4 byte boundary)
func alignToDWORD(offset int) int {
return (offset + 3) & ^3
}
// readUTF16 is a helper function to read a null-terminated UTF16 string
func readUTF16(reader *bytes.Reader, offsets ...*int) string {
startPos, err := reader.Seek(0, io.SeekCurrent)
if err != nil {
return ""
}
var result []rune
for {
var char uint16
err := binary.Read(reader, binary.LittleEndian, &char)
if err != nil || char == 0 {
break
}
result = append(result, rune(char))
}
// calculate how many bytes we've actually read (including null terminator)
endPos, _ := reader.Seek(0, io.SeekCurrent)
bytesRead := int(endPos - startPos)
for i := range offsets {
*offsets[i] += bytesRead
}
return string(result)
}
// readUTF16WithLength reads a length-prefixed UTF-16 string from reader.
// The first 2 bytes represent the number of UTF-16 code units.
func readUTF16WithLength(reader *bytes.Reader) (string, error) {
var length uint16
if err := binary.Read(reader, binary.LittleEndian, &length); err != nil {
return "", err
}
if length == 0 {
return "", nil
}
// read length UTF-16 code units.
codes := make([]uint16, length)
if err := binary.Read(reader, binary.LittleEndian, &codes); err != nil {
return "", err
}
return string(utf16.Decode(codes)), nil
}
@@ -1 +0,0 @@
../../../dotnet/test-fixtures/image-net8-app
@@ -1 +0,0 @@
../../../dotnet/test-fixtures/image-net8-app-single-file
@@ -16,6 +16,7 @@ import (
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/mimetype"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/format/cyclonedxjson"
@@ -508,7 +509,7 @@ func (ni nativeImagePE) fetchPkgs() (pkgs []pkg.Package, relationships []artifac
}
// fetchPkgs provides the packages available in a UnionReader.
func fetchPkgs(reader unionreader.UnionReader, filename string) ([]pkg.Package, []artifact.Relationship) {
func fetchPkgs(reader unionreader.UnionReader, filename string) ([]pkg.Package, []artifact.Relationship, error) {
var pkgs []pkg.Package
var relationships []artifact.Relationship
imageFormats := []func(string, io.ReaderAt) (nativeImage, error){newElf, newMachO, newPE}
@@ -518,8 +519,9 @@ func fetchPkgs(reader unionreader.UnionReader, filename string) ([]pkg.Package,
readers, err := unionreader.GetReaders(reader)
if err != nil {
log.Debugf("failed to open the java native-image binary: %v", err)
return nil, nil
return nil, nil, nil
}
var unknowns error
for _, r := range readers {
for _, makeNativeImage := range imageFormats {
ni, err := makeNativeImage(filename, r)
@@ -532,13 +534,14 @@ func fetchPkgs(reader unionreader.UnionReader, filename string) ([]pkg.Package,
newPkgs, newRelationships, err := ni.fetchPkgs()
if err != nil {
log.Tracef("unable to extract SBOM from possible java native-image %s: %v", filename, err)
unknowns = unknown.Join(unknowns, fmt.Errorf("unable to extract SBOM from possible java native-image %s: %w", filename, err))
continue
}
pkgs = append(pkgs, newPkgs...)
relationships = append(relationships, newRelationships...)
}
}
return pkgs, relationships
return pkgs, relationships, unknowns
}
// Catalog attempts to find any native image executables reachable from a resolver.
@@ -549,17 +552,18 @@ func (c *nativeImageCataloger) Catalog(_ context.Context, resolver file.Resolver
if err != nil {
return pkgs, nil, fmt.Errorf("failed to find binaries by mime types: %w", err)
}
var errs error
for _, location := range fileMatches {
newPkgs, newRelationships, err := processLocation(location, resolver)
if err != nil {
return nil, nil, err
errs = unknown.Append(errs, location, err)
continue
}
pkgs = append(pkgs, newPkgs...)
relationships = append(relationships, newRelationships...)
}
return pkgs, relationships, nil
return pkgs, relationships, errs
}
func processLocation(location file.Location, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
@@ -574,6 +578,6 @@ func processLocation(location file.Location, resolver file.Resolver) ([]pkg.Pack
if err != nil {
return nil, nil, err
}
pkgs, relationships := fetchPkgs(reader, location.RealPath)
return pkgs, relationships, nil
pkgs, relationships, err := fetchPkgs(reader, location.RealPath)
return pkgs, relationships, err
}
+4 -6
View File
@@ -3,16 +3,14 @@ package nix
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
_ "modernc.org/sqlite"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/google/go-cmp/cmp"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCataloger_Image(t *testing.T) {
+1 -1
View File
@@ -11,5 +11,5 @@ import (
// NewOpamPackageManagerCataloger returns a new cataloger object for OCaml opam.
func NewOpamPackageManagerCataloger() pkg.Cataloger {
return generic.NewCataloger("opam-cataloger").
WithParserByGlobs(parseOpamPackage, "**/*opam")
WithParserByGlobs(parseOpamPackage, "*opam")
}
@@ -716,15 +716,10 @@ func Test_PackageCataloger_SitePackageRelationships(t *testing.T) {
"certifi @ 2020.12.5 (/usr/local/lib/python3.9/dist-packages) [dependency-of] requests @ 2.25.0 (/usr/local/lib/python3.9/dist-packages)",
"certifi @ 2020.12.5 (/usr/local/lib/python3.9/dist-packages) [dependency-of] urllib3 @ 1.26.18 (/usr/local/lib/python3.9/dist-packages)", // available when extra == "secure", but another dependency is primarily installing it
"chardet @ 3.0.4 (/usr/local/lib/python3.9/dist-packages) [dependency-of] requests @ 2.25.0 (/usr/local/lib/python3.9/dist-packages)",
"distlib @ 0.3.9 (/usr/local/lib/python3.9/dist-packages) [dependency-of] virtualenv @ 20.31.2 (/usr/local/lib/python3.9/dist-packages)",
"filelock @ 3.18.0 (/usr/local/lib/python3.9/dist-packages) [dependency-of] virtualenv @ 20.31.2 (/usr/local/lib/python3.9/dist-packages)",
"idna @ 2.10 (/usr/local/lib/python3.9/dist-packages) [dependency-of] requests @ 2.25.0 (/usr/local/lib/python3.9/dist-packages)",
"idna @ 2.10 (/usr/local/lib/python3.9/dist-packages) [dependency-of] urllib3 @ 1.26.18 (/usr/local/lib/python3.9/dist-packages)", // available when extra == "secure", but another dependency is primarily installing it
"platformdirs @ 4.3.8 (/usr/local/lib/python3.9/dist-packages) [dependency-of] virtualenv @ 20.31.2 (/usr/local/lib/python3.9/dist-packages)",
"six @ 1.16.0 (/usr/local/lib/python3.9/dist-packages) [dependency-of] blessed @ 1.20.0 (/usr/local/lib/python3.9/dist-packages)",
"soupsieve @ 2.2.1 (/usr/local/lib/python3.9/dist-packages) [dependency-of] beautifulsoup4 @ 4.9.3 (/usr/local/lib/python3.9/dist-packages)",
"urllib3 @ 1.26.18 (/usr/local/lib/python3.9/dist-packages) [dependency-of] requests @ 2.25.0 (/usr/local/lib/python3.9/dist-packages)",
"virtualenv @ 20.31.2 (/usr/local/lib/python3.9/dist-packages) [dependency-of] filelock @ 3.18.0 (/usr/local/lib/python3.9/dist-packages)", // available when extra == "testing", but we are installing it
"wcwidth @ 0.2.13 (/usr/local/lib/python3.9/dist-packages) [dependency-of] blessed @ 1.20.0 (/usr/local/lib/python3.9/dist-packages)",
// purely python 3.8 dist-packages
@@ -741,14 +736,9 @@ func Test_PackageCataloger_SitePackageRelationships(t *testing.T) {
"certifi @ 2020.12.5 (/usr/local/lib/python3.8/dist-packages) [dependency-of] requests @ 2.25.0 (/usr/local/lib/python3.8/dist-packages)",
"certifi @ 2020.12.5 (/usr/local/lib/python3.8/dist-packages) [dependency-of] urllib3 @ 1.26.18 (/usr/local/lib/python3.8/dist-packages)", // available when extra == "secure", but another dependency is primarily installing it
"chardet @ 3.0.4 (/usr/local/lib/python3.8/dist-packages) [dependency-of] requests @ 2.25.0 (/usr/local/lib/python3.8/dist-packages)",
"distlib @ 0.3.9 (/usr/local/lib/python3.8/dist-packages) [dependency-of] virtualenv @ 20.31.2 (/usr/local/lib/python3.8/dist-packages)",
"filelock @ 3.16.1 (/usr/local/lib/python3.8/dist-packages) [dependency-of] virtualenv @ 20.31.2 (/usr/local/lib/python3.8/dist-packages)",
"idna @ 2.10 (/usr/local/lib/python3.8/dist-packages) [dependency-of] requests @ 2.25.0 (/usr/local/lib/python3.8/dist-packages)",
"idna @ 2.10 (/usr/local/lib/python3.8/dist-packages) [dependency-of] urllib3 @ 1.26.18 (/usr/local/lib/python3.8/dist-packages)", // available when extra == "secure", but another dependency is primarily installing it
"platformdirs @ 4.3.6 (/usr/local/lib/python3.8/dist-packages) [dependency-of] virtualenv @ 20.31.2 (/usr/local/lib/python3.8/dist-packages)",
"soupsieve @ 2.2 (/usr/local/lib/python3.8/dist-packages) [dependency-of] beautifulsoup4 @ 4.9.2 (/usr/local/lib/python3.8/dist-packages)",
"urllib3 @ 1.26.18 (/usr/local/lib/python3.8/dist-packages) [dependency-of] requests @ 2.25.0 (/usr/local/lib/python3.8/dist-packages)",
"virtualenv @ 20.31.2 (/usr/local/lib/python3.8/dist-packages) [dependency-of] filelock @ 3.16.1 (/usr/local/lib/python3.8/dist-packages)", // available when extra == "testing", but we are installing it
"xmod @ 1.8.1 (/usr/local/lib/python3.8/dist-packages) [dependency-of] runs @ 1.2.2 (/usr/local/lib/python3.8/dist-packages)",
// project 1 virtual env
@@ -769,7 +759,6 @@ func Test_PackageCataloger_SitePackageRelationships(t *testing.T) {
"blessed @ 1.20.0 (/usr/local/lib/python3.9/dist-packages) [dependency-of] inquirer @ 3.0.0 (/app/project1/venv/lib/python3.9/site-packages)", // note: depends on global site package!
"python-editor @ 1.0.4 (/usr/local/lib/python3.9/dist-packages) [dependency-of] inquirer @ 3.0.0 (/app/project1/venv/lib/python3.9/site-packages)", // note: depends on global site package!
"readchar @ 4.2.1 (/app/project1/venv/lib/python3.9/site-packages) [dependency-of] inquirer @ 3.0.0 (/app/project1/venv/lib/python3.9/site-packages)",
"setuptools @ 44.0.0 (/app/project1/venv/lib/python3.9/site-packages) [dependency-of] virtualenv @ 20.31.2 (/usr/local/lib/python3.9/dist-packages)", // available when extra == "test", but we are installing it
"soupsieve @ 2.3 (/app/project1/venv/lib/python3.9/site-packages) [dependency-of] beautifulsoup4 @ 4.10.0 (/app/project1/venv/lib/python3.9/site-packages)",
// project 2 virtual env
+4 -6
View File
@@ -115,16 +115,14 @@ func wheelEggDependencySpecifier(p pkg.Package) dependency.Specification {
}
}
// extractPackageName removes any extras, version constraints or environment markers from a given Requires-Dist field value (and
// extractPackageName removes any extras or version constraints from a given Requires-Dist field value (and
// semantically similar fields), leaving only the package name.
func extractPackageName(s string) string {
// examples:
// requests [security,tests] --> requests
// requests >= 2.8.1 --> requests
// requests (>= 2.8.1) --> requests
// requests ; python_version < "2.7" --> requests
// html5lib ; extra == 'html5lib' --> html5lib
// soupsieve (>1.2) --> soupsieve
return strings.TrimSpace(internal.SplitAny(s, "[(<!=>~;")[0])
return strings.TrimSpace(internal.SplitAny(s, "(;")[0])
}
func extractPackageNames(ss []string) []string {
var names []string
+4 -6
View File
@@ -5,15 +5,13 @@ import (
"errors"
"testing"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
_ "modernc.org/sqlite"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_DBCataloger(t *testing.T) {
@@ -184,7 +182,7 @@ func Test_DBCataloger(t *testing.T) {
pkgtest.NewCatalogTester().
WithImageResolver(t, "image-minimal").
IgnoreLocationLayer(). // this fixture can be rebuilt, thus the layer ID will change
IgnoreLocationLayer(). // this fixture can be rebuilt, thus the layer ID will change
WithCompareOptions(cmpopts.IgnoreFields(pkg.RpmDBEntry{}, "Files")). // this is rather long... ano not the point of the test
Expects(expectedPackages, expectedRelationships).
TestCataloger(t, NewDBCataloger())
@@ -10,9 +10,9 @@ import (
"strconv"
"time"
rpmdb "github.com/anchore/go-rpmdb/pkg"
"github.com/sassoftware/go-rpmutils"
rpmdb "github.com/anchore/go-rpmdb/pkg"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
@@ -9,6 +9,7 @@ import (
"strings"
rpmdb "github.com/anchore/go-rpmdb/pkg"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
@@ -59,7 +59,7 @@ func TestTerraformCataloger(t *testing.T) {
Metadata: pkg.TerraformLockProviderEntry{
URL: "registry.terraform.io/hashicorp/google",
Version: "6.8.0",
Constraints: "",
Constraints: "6.8.0",
Hashes: []string{
"h1:GlCaVPk6eKMg2ZbRY7C5tUeHGNIABT+qFtMl8+XWZHM=",
"zh:1b78f4451f1617092eb6891c9c13eda79671060601c40947feea6794c732157a",
@@ -26,6 +26,7 @@ provider "registry.terraform.io/hashicorp/aws" {
provider "registry.terraform.io/hashicorp/google" {
version = "6.8.0"
constraints = "6.8.0"
hashes = [
"h1:GlCaVPk6eKMg2ZbRY7C5tUeHGNIABT+qFtMl8+XWZHM=",
"zh:1b78f4451f1617092eb6891c9c13eda79671060601c40947feea6794c732157a",
+1 -1
View File
@@ -3,7 +3,7 @@ package pkg
// TerraformLockProviderEntry represents a single provider entry in a Terraform dependency lock file (.terraform.lock.hcl).
type TerraformLockProviderEntry struct {
URL string `hcl:",label" json:"url"`
Constraints string `hcl:"constraints,optional" json:"constraints"`
Constraints string `hcl:"constraints" json:"constraints"`
Version string `hcl:"version" json:"version"`
Hashes []string `hcl:"hashes" json:"hashes"`
}
+1
View File
@@ -22,6 +22,7 @@ func Test_Unknowns(t *testing.T) {
assertInOutput(`no package identified in executable file`),
assertInOutput(`unable to read files from java archive`),
assertInOutput(`no package identified in archive`),
assertInOutput(`cycle during symlink resolution`),
assertSuccessfulReturnCode,
},
},