diff --git a/cmd/osv-scanner/__snapshots__/main_test.snap b/cmd/osv-scanner/__snapshots__/main_test.snap index 9482c9aa8c..e638bf62e3 100755 --- a/cmd/osv-scanner/__snapshots__/main_test.snap +++ b/cmd/osv-scanner/__snapshots__/main_test.snap @@ -933,7 +933,8 @@ Scanned /fixtures/call-analysis-go-project/go.mod file and found 4 pack --- [TestRun_Docker/Fake_alpine_image - 1] -Pulling docker image ("alpine:non-existent-tag")... +Checking if docker image ("alpine:non-existent-tag") exists locally... +Image not found locally, pulling docker image ("alpine:non-existent-tag")... --- @@ -941,12 +942,13 @@ Pulling docker image ("alpine:non-existent-tag")... Docker command exited with code ("/usr/bin/docker pull -q alpine:non-existent-tag"): 1 STDERR: > Error response from daemon: manifest for alpine:non-existent-tag not found: manifest unknown: manifest unknown -failed to run docker command +failed to pull container image: failed to run docker command --- [TestRun_Docker/Fake_image_entirely - 1] -Pulling docker image ("this-image-definitely-does-not-exist-abcde")... +Checking if docker image ("this-image-definitely-does-not-exist-abcde") exists locally... +Image not found locally, pulling docker image ("this-image-definitely-does-not-exist-abcde")... --- @@ -954,15 +956,28 @@ Pulling docker image ("this-image-definitely-does-not-exist-abcde")... Docker command exited with code ("/usr/bin/docker pull -q this-image-definitely-does-not-exist-abcde"): 1 STDERR: > Error response from daemon: pull access denied for this-image-definitely-does-not-exist-abcde, repository does not exist or may require 'docker login': denied: requested access to the resource is denied -failed to run docker command +failed to pull container image: failed to run docker command --- [TestRun_Docker/Real_Alpine_image - 1] -Pulling docker image ("alpine:3.18.9")... +Checking if docker image ("alpine:3.18.9") exists locally... Saving docker image ("alpine:3.18.9") to temporary file... -Scanning image... -No issues found +Scanning image "alpine:3.18.9" +Total 1 packages affected by 1 vulnerabilities (0 Critical, 0 High, 0 Medium, 0 Low, 1 Unknown) from 1 ecosystems. +1 vulnerabilities have fixes available. + +Alpine:v3.18 ++----------------------------------------------------------+ +| Source:os:lib/apk/db/installed | ++---------+-------------------+---------------+------------+ +| PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | ++---------+-------------------+---------------+------------+ +| openssl | 3.1.7-r0 | Fix Available | 1 | ++---------+-------------------+---------------+------------+ + +For the most comprehensive scan results, we recommend using the HTML output: `osv-scanner --format html --output results.html`. +You can also view the full vulnerability list in your terminal with: `osv-scanner --format vertical`. --- @@ -971,9 +986,9 @@ No issues found --- [TestRun_Docker/Real_empty_image - 1] -Pulling docker image ("hello-world")... +Checking if docker image ("hello-world") exists locally... Saving docker image ("hello-world") to temporary file... -Scanning image... +Scanning image "hello-world" --- @@ -983,9 +998,9 @@ No package sources found, --help for usage information. --- [TestRun_Docker/Real_empty_image_with_tag - 1] -Pulling docker image ("hello-world:linux")... +Checking if docker image ("hello-world:linux") exists locally... Saving docker image ("hello-world:linux") to temporary file... -Scanning image... +Scanning image "hello-world:linux" --- @@ -2001,6 +2016,7 @@ Loaded OSS-Fuzz local db from /osv-scanner/OSS-Fuzz/all.zip | https://osv.dev/DLA-3684-1 | | Debian | tzdata | 2021a-0+deb9u3 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | | https://osv.dev/DLA-3788-1 | | Debian | tzdata | 2021a-0+deb9u3 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | | https://osv.dev/DLA-3972-1 | | Debian | tzdata | 2021a-0+deb9u3 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | +| https://osv.dev/DLA-4016-1 | | Debian | ucf | 3.0036 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | | https://osv.dev/CVE-2016-2779 | 7.8 | Debian | util-linux | 2.29.2-1+deb9u1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | | https://osv.dev/DSA-5055-1 | 5.5 | Debian | util-linux | 2.29.2-1+deb9u1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | | https://osv.dev/DSA-5650-1 | | Debian | util-linux | 2.29.2-1+deb9u1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | @@ -2199,6 +2215,7 @@ Loaded OSS-Fuzz local db from /osv-scanner/OSS-Fuzz/all.zip | https://osv.dev/DLA-3684-1 | | Debian | tzdata | 2021a-0+deb9u3 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | | https://osv.dev/DLA-3788-1 | | Debian | tzdata | 2021a-0+deb9u3 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | | https://osv.dev/DLA-3972-1 | | Debian | tzdata | 2021a-0+deb9u3 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | +| https://osv.dev/DLA-4016-1 | | Debian | ucf | 3.0036 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | | https://osv.dev/CVE-2016-2779 | 7.8 | Debian | util-linux | 2.29.2-1+deb9u1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | | https://osv.dev/DSA-5055-1 | 5.5 | Debian | util-linux | 2.29.2-1+deb9u1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | | https://osv.dev/DSA-5650-1 | | Debian | util-linux | 2.29.2-1+deb9u1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml | @@ -2691,17 +2708,17 @@ Scanned /fixtures/maven-transitive/pom.xml file and found 3 packages --- [TestRun_OCIImage/Alpine_3.10_image_tar_with_3.18_version_file - 1] -Scanning image ../../internal/image/fixtures/test-alpine.tar -Total 1 packages affected by 2 vulnerabilities (1 Critical, 1 High, 0 Medium, 0 Low, 0 Unknown) from 1 ecosystems. -2 vulnerabilities have fixes available. +Scanning image "../../internal/image/fixtures/test-alpine.tar" +Total 2 packages affected by 40 vulnerabilities (2 Critical, 17 High, 14 Medium, 0 Low, 7 Unknown) from 1 ecosystems. +40 vulnerabilities have fixes available. Alpine:v3.18 +----------------------------------------------------------+ -| Source:docker:../../internal/image/fixtures/test-alpine. | -| tar:/lib/apk/db/installed | +| Source:os:lib/apk/db/installed | +---------+-------------------+---------------+------------+ | PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | +---------+-------------------+---------------+------------+ +| openssl | 1.1.1k-r0 | Fix Available | 38 | | zlib | 1.2.11-r1 | Fix Available | 2 | +---------+-------------------+---------------+------------+ @@ -2715,28 +2732,94 @@ You can also view the full vulnerability list in your terminal with: `osv-scanne --- [TestRun_OCIImage/Invalid_path - 1] -Scanning image ./fixtures/oci-image/no-file-here.tar +Scanning image "./fixtures/oci-image/no-file-here.tar" --- [TestRun_OCIImage/Invalid_path - 2] -failed to load image ./fixtures/oci-image/no-file-here.tar: open ./fixtures/oci-image/no-file-here.tar: no such file or directory +failed to load image from tarball with path "./fixtures/oci-image/no-file-here.tar": open ./fixtures/oci-image/no-file-here.tar: no such file or directory + +--- + +[TestRun_OCIImage/scanning_image_with_go_binary - 1] +Scanning image "../../internal/image/fixtures/test-package-tracing.tar" +Total 7 packages affected by 27 vulnerabilities (0 Critical, 0 High, 0 Medium, 0 Low, 27 Unknown) from 2 ecosystems. +27 vulnerabilities have fixes available. + +Go ++----------------------------------------------------------+ +| Source:lockfile:go/bin/more-vuln-overwrite-less-vuln | ++---------+-------------------+---------------+------------+ +| PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | ++---------+-------------------+---------------+------------+ +| stdlib | 1.22.4 | Fix Available | 4 | ++---------+-------------------+---------------+------------+ ++----------------------------------------------------------+ +| Source:lockfile:go/bin/ptf-1.2.0 | ++---------+-------------------+---------------+------------+ +| PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | ++---------+-------------------+---------------+------------+ +| stdlib | 1.22.4 | Fix Available | 4 | ++---------+-------------------+---------------+------------+ ++----------------------------------------------------------+ +| Source:lockfile:go/bin/ptf-1.3.0 | ++---------+-------------------+---------------+------------+ +| PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | ++---------+-------------------+---------------+------------+ +| stdlib | 1.22.4 | Fix Available | 4 | ++---------+-------------------+---------------+------------+ ++----------------------------------------------------------+ +| Source:lockfile:go/bin/ptf-1.3.0-moved | ++---------+-------------------+---------------+------------+ +| PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | ++---------+-------------------+---------------+------------+ +| stdlib | 1.22.4 | Fix Available | 4 | ++---------+-------------------+---------------+------------+ ++----------------------------------------------------------+ +| Source:lockfile:go/bin/ptf-1.4.0 | ++---------+-------------------+---------------+------------+ +| PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | ++---------+-------------------+---------------+------------+ +| stdlib | 1.22.4 | Fix Available | 4 | ++---------+-------------------+---------------+------------+ ++----------------------------------------------------------+ +| Source:lockfile:go/bin/ptf-vulnerable | ++---------+-------------------+---------------+------------+ +| PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | ++---------+-------------------+---------------+------------+ +| stdlib | 1.22.4 | Fix Available | 4 | ++---------+-------------------+---------------+------------+ +Alpine:v3.20 ++----------------------------------------------------------+ +| Source:os:lib/apk/db/installed | ++---------+-------------------+---------------+------------+ +| PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | ++---------+-------------------+---------------+------------+ +| openssl | 3.3.1-r0 | Fix Available | 3 | ++---------+-------------------+---------------+------------+ + +For the most comprehensive scan results, we recommend using the HTML output: `osv-scanner --format html --output results.html`. +You can also view the full vulnerability list in your terminal with: `osv-scanner --format vertical`. + +--- + +[TestRun_OCIImage/scanning_image_with_go_binary - 2] --- [TestRun_OCIImage/scanning_node_modules_using_npm_with_no_packages - 1] -Scanning image ../../internal/image/fixtures/test-node_modules-npm-empty.tar -Total 1 packages affected by 4 vulnerabilities (0 Critical, 0 High, 4 Medium, 0 Low, 0 Unknown) from 1 ecosystems. -4 vulnerabilities have fixes available. +Scanning image "../../internal/image/fixtures/test-node_modules-npm-empty.tar" +Total 2 packages affected by 10 vulnerabilities (0 Critical, 0 High, 4 Medium, 0 Low, 6 Unknown) from 1 ecosystems. +10 vulnerabilities have fixes available. Alpine:v3.19 +----------------------------------------------------------+ -| Source:docker:../../internal/image/fixtures/test-node_mo | -| dules-npm-empty.tar:/lib/apk/db/installed | +| Source:os:lib/apk/db/installed | +---------+-------------------+---------------+------------+ | PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | +---------+-------------------+---------------+------------+ | busybox | 1.36.1-r15 | Fix Available | 4 | +| openssl | 3.1.4-r5 | Fix Available | 6 | +---------+-------------------+---------------+------------+ For the most comprehensive scan results, we recommend using the HTML output: `osv-scanner --format html --output results.html`. @@ -2749,28 +2832,27 @@ You can also view the full vulnerability list in your terminal with: `osv-scanne --- [TestRun_OCIImage/scanning_node_modules_using_npm_with_some_packages - 1] -Scanning image ../../internal/image/fixtures/test-node_modules-npm-full.tar -Total 3 packages affected by 6 vulnerabilities (2 Critical, 0 High, 4 Medium, 0 Low, 0 Unknown) from 2 ecosystems. -5 vulnerabilities have fixes available. +Scanning image "../../internal/image/fixtures/test-node_modules-npm-full.tar" +Total 4 packages affected by 13 vulnerabilities (2 Critical, 0 High, 5 Medium, 0 Low, 6 Unknown) from 2 ecosystems. +12 vulnerabilities have fixes available. npm +--------------------------------------------------------------+ -| Source:docker:../../internal/image/fixtures/test-node_module | -| s-npm-full.tar:/prod/app/node_modules/.package-lock.json | +| Source:lockfile:prod/app/node_modules/.package-lock.json | +----------+-------------------+------------------+------------+ | PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | +----------+-------------------+------------------+------------+ | cryo | 0.0.6 | No fix available | 1 | -| minimist | 0.0.8 | Fix Available | 1 | +| minimist | 0.0.8 | Fix Available | 2 | +----------+-------------------+------------------+------------+ Alpine:v3.19 +----------------------------------------------------------+ -| Source:docker:../../internal/image/fixtures/test-node_mo | -| dules-npm-full.tar:/lib/apk/db/installed | +| Source:os:lib/apk/db/installed | +---------+-------------------+---------------+------------+ | PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | +---------+-------------------+---------------+------------+ | busybox | 1.36.1-r15 | Fix Available | 4 | +| openssl | 3.1.4-r5 | Fix Available | 6 | +---------+-------------------+---------------+------------+ For the most comprehensive scan results, we recommend using the HTML output: `osv-scanner --format html --output results.html`. @@ -2783,18 +2865,18 @@ You can also view the full vulnerability list in your terminal with: `osv-scanne --- [TestRun_OCIImage/scanning_node_modules_using_pnpm_with_no_packages - 1] -Scanning image ../../internal/image/fixtures/test-node_modules-pnpm-empty.tar -Total 1 packages affected by 4 vulnerabilities (0 Critical, 0 High, 4 Medium, 0 Low, 0 Unknown) from 1 ecosystems. -4 vulnerabilities have fixes available. +Scanning image "../../internal/image/fixtures/test-node_modules-pnpm-empty.tar" +Total 2 packages affected by 10 vulnerabilities (0 Critical, 0 High, 4 Medium, 0 Low, 6 Unknown) from 1 ecosystems. +10 vulnerabilities have fixes available. Alpine:v3.19 +----------------------------------------------------------+ -| Source:docker:../../internal/image/fixtures/test-node_mo | -| dules-pnpm-empty.tar:/lib/apk/db/installed | +| Source:os:lib/apk/db/installed | +---------+-------------------+---------------+------------+ | PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | +---------+-------------------+---------------+------------+ | busybox | 1.36.1-r15 | Fix Available | 4 | +| openssl | 3.1.4-r5 | Fix Available | 6 | +---------+-------------------+---------------+------------+ For the most comprehensive scan results, we recommend using the HTML output: `osv-scanner --format html --output results.html`. @@ -2807,18 +2889,18 @@ You can also view the full vulnerability list in your terminal with: `osv-scanne --- [TestRun_OCIImage/scanning_node_modules_using_pnpm_with_some_packages - 1] -Scanning image ../../internal/image/fixtures/test-node_modules-pnpm-full.tar -Total 1 packages affected by 4 vulnerabilities (0 Critical, 0 High, 4 Medium, 0 Low, 0 Unknown) from 1 ecosystems. -4 vulnerabilities have fixes available. +Scanning image "../../internal/image/fixtures/test-node_modules-pnpm-full.tar" +Total 2 packages affected by 10 vulnerabilities (0 Critical, 0 High, 4 Medium, 0 Low, 6 Unknown) from 1 ecosystems. +10 vulnerabilities have fixes available. Alpine:v3.19 +----------------------------------------------------------+ -| Source:docker:../../internal/image/fixtures/test-node_mo | -| dules-pnpm-full.tar:/lib/apk/db/installed | +| Source:os:lib/apk/db/installed | +---------+-------------------+---------------+------------+ | PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | +---------+-------------------+---------------+------------+ | busybox | 1.36.1-r15 | Fix Available | 4 | +| openssl | 3.1.4-r5 | Fix Available | 6 | +---------+-------------------+---------------+------------+ For the most comprehensive scan results, we recommend using the HTML output: `osv-scanner --format html --output results.html`. @@ -2831,18 +2913,18 @@ You can also view the full vulnerability list in your terminal with: `osv-scanne --- [TestRun_OCIImage/scanning_node_modules_using_yarn_with_no_packages - 1] -Scanning image ../../internal/image/fixtures/test-node_modules-yarn-empty.tar -Total 1 packages affected by 4 vulnerabilities (0 Critical, 0 High, 4 Medium, 0 Low, 0 Unknown) from 1 ecosystems. -4 vulnerabilities have fixes available. +Scanning image "../../internal/image/fixtures/test-node_modules-yarn-empty.tar" +Total 2 packages affected by 10 vulnerabilities (0 Critical, 0 High, 4 Medium, 0 Low, 6 Unknown) from 1 ecosystems. +10 vulnerabilities have fixes available. Alpine:v3.19 +----------------------------------------------------------+ -| Source:docker:../../internal/image/fixtures/test-node_mo | -| dules-yarn-empty.tar:/lib/apk/db/installed | +| Source:os:lib/apk/db/installed | +---------+-------------------+---------------+------------+ | PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | +---------+-------------------+---------------+------------+ | busybox | 1.36.1-r15 | Fix Available | 4 | +| openssl | 3.1.4-r5 | Fix Available | 6 | +---------+-------------------+---------------+------------+ For the most comprehensive scan results, we recommend using the HTML output: `osv-scanner --format html --output results.html`. @@ -2855,18 +2937,18 @@ You can also view the full vulnerability list in your terminal with: `osv-scanne --- [TestRun_OCIImage/scanning_node_modules_using_yarn_with_some_packages - 1] -Scanning image ../../internal/image/fixtures/test-node_modules-yarn-full.tar -Total 1 packages affected by 4 vulnerabilities (0 Critical, 0 High, 4 Medium, 0 Low, 0 Unknown) from 1 ecosystems. -4 vulnerabilities have fixes available. +Scanning image "../../internal/image/fixtures/test-node_modules-yarn-full.tar" +Total 2 packages affected by 10 vulnerabilities (0 Critical, 0 High, 4 Medium, 0 Low, 6 Unknown) from 1 ecosystems. +10 vulnerabilities have fixes available. Alpine:v3.19 +----------------------------------------------------------+ -| Source:docker:../../internal/image/fixtures/test-node_mo | -| dules-yarn-full.tar:/lib/apk/db/installed | +| Source:os:lib/apk/db/installed | +---------+-------------------+---------------+------------+ | PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | +---------+-------------------+---------------+------------+ | busybox | 1.36.1-r15 | Fix Available | 4 | +| openssl | 3.1.4-r5 | Fix Available | 6 | +---------+-------------------+---------------+------------+ For the most comprehensive scan results, we recommend using the HTML output: `osv-scanner --format html --output results.html`. diff --git a/cmd/osv-scanner/main_test.go b/cmd/osv-scanner/main_test.go index 681e2f1afb..565c8d2298 100644 --- a/cmd/osv-scanner/main_test.go +++ b/cmd/osv-scanner/main_test.go @@ -762,11 +762,8 @@ func TestRun_Licenses(t *testing.T) { } } -// TODO(v2): Image scanning is not temporarily disabled - func TestRun_Docker(t *testing.T) { t.Parallel() - t.Skip("Skipping until image scanning is reenabled") testutility.SkipIfNotAcceptanceTesting(t, "Takes a long time to pull down images") @@ -795,7 +792,7 @@ func TestRun_Docker(t *testing.T) { { name: "Real Alpine image", args: []string{"", "--docker", "alpine:3.18.9"}, - exit: 0, + exit: 1, }, } for _, tt := range tests { @@ -812,7 +809,6 @@ func TestRun_Docker(t *testing.T) { func TestRun_OCIImage(t *testing.T) { t.Parallel() - t.Skip("Skipping until image scanning is reenabled") testutility.SkipIfNotAcceptanceTesting(t, "Not consistent on MacOS/Windows") diff --git a/cmd/osv-scanner/scan/main.go b/cmd/osv-scanner/scan/main.go index a9f73a4d93..abbd8e34d4 100644 --- a/cmd/osv-scanner/scan/main.go +++ b/cmd/osv-scanner/scan/main.go @@ -14,6 +14,7 @@ import ( "time" "github.com/google/osv-scanner/internal/spdx" + "github.com/google/osv-scanner/pkg/models" "github.com/google/osv-scanner/pkg/osvscanner" "github.com/google/osv-scanner/pkg/reporter" "golang.org/x/term" @@ -282,10 +283,10 @@ func action(context *cli.Context, stdout, stderr io.Writer) (reporter.Reporter, scanLicensesAllowlist = []string{} } - vulnResult, err := osvscanner.DoScan(osvscanner.ScannerActions{ + scannerAction := osvscanner.ScannerActions{ LockfilePaths: context.StringSlice("lockfile"), SBOMPaths: context.StringSlice("sbom"), - DockerImageName: context.String("docker"), + Image: context.String("docker"), Recursive: context.Bool("recursive"), SkipGit: context.Bool("skip-git"), NoIgnore: context.Bool("no-ignore"), @@ -311,7 +312,14 @@ func action(context *cli.Context, stdout, stderr io.Writer) (reporter.Reporter, MavenRegistry: context.String("experimental-maven-registry"), }, }, - }, r) + } + + var vulnResult models.VulnerabilityResults + if context.String("docker") != "" || context.String("experimental-oci-image") != "" { + vulnResult, err = osvscanner.DoContainerScan(scannerAction, r) + } else { + vulnResult, err = osvscanner.DoScan(scannerAction, r) + } if err != nil && !errors.Is(err, osvscanner.ErrVulnerabilitiesFound) { return r, err diff --git a/go.mod b/go.mod index 95021fbf03..1fc0bffbe6 100644 --- a/go.mod +++ b/go.mod @@ -17,11 +17,11 @@ require ( github.com/go-git/go-billy/v5 v5.6.0 github.com/go-git/go-git/v5 v5.13.0 github.com/google/go-cmp v0.6.0 - github.com/google/go-containerregistry v0.20.2 - github.com/google/osv-scalibr v0.1.6-0.20250105222824-56e5c3bfb149 + github.com/google/osv-scalibr v0.1.6-0.20250116021232-6d1c6b6edc63 github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd github.com/jedib0t/go-pretty/v6 v6.6.5 github.com/muesli/reflow v0.3.0 + github.com/opencontainers/go-digest v1.0.0 github.com/ossf/osv-schema/bindings/go v0.0.0-20241210213101-57fd3ddb15aa github.com/owenrumney/go-sarif/v2 v2.3.3 github.com/package-url/packageurl-go v0.1.3 @@ -44,7 +44,10 @@ require ( require ( dario.cat/mergo v1.0.0 // indirect + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect + github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/Microsoft/hcsshim v0.11.5 // indirect github.com/ProtonMail/go-crypto v1.1.3 // indirect github.com/alecthomas/chroma/v2 v2.14.0 // indirect github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 // indirect @@ -54,19 +57,39 @@ require ( github.com/charmbracelet/x/ansi v0.4.5 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect + github.com/containerd/cgroups v1.1.0 // indirect + github.com/containerd/containerd v1.7.18 // indirect + github.com/containerd/continuity v0.4.2 // indirect + github.com/containerd/errdefs v0.1.0 // indirect + github.com/containerd/fifo v1.1.0 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect + github.com/containerd/ttrpc v1.2.4 // indirect + github.com/containerd/typeurl/v2 v2.1.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/cyphar/filepath-securejoin v0.2.5 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect + github.com/docker/cli v27.1.1+incompatible // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/docker-credential-helpers v0.8.1 // indirect + github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/erikvarga/go-rpmdb v0.0.0-20240208180226-b97e041ef9af // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect github.com/gkampitakis/ciinfo v0.3.0 // 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/gobwas/glob v0.2.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/go-containerregistry v0.20.2 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/css v1.0.1 // indirect + github.com/groob/plist v0.1.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.17.7 // indirect @@ -79,34 +102,51 @@ require ( github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/microcosm-cc/bluemonday v1.0.27 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/sys/mountinfo v0.6.2 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/signal v0.7.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/opencontainers/runtime-spec v1.1.0 // indirect + github.com/opencontainers/selinux v1.11.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sahilm/fuzzy v0.1.1 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.0 // indirect github.com/spdx/gordf v0.0.0-20221230105357-b735bd5aac89 // indirect github.com/spdx/tools-golang v0.5.5 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/vbatts/tar-split v0.11.5 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yuin/goldmark v1.7.4 // indirect github.com/yuin/goldmark-emoji v1.0.3 // indirect + go.etcd.io/bbolt v1.3.10 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/tools v0.28.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect sigs.k8s.io/yaml v1.4.0 // indirect + www.velocidex.com/golang/regparser v0.0.0-20240404115756-2169ac0e3c09 // indirect ) diff --git a/go.sum b/go.sum index 01f6650610..566678aeb5 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= deps.dev/api/v3 v3.0.0-20241223232618-f8b47b9fbbab h1:Smg+XqOvTHNXb4qzbztq8OgRiHieeE9vHCz7Iypnjfg= @@ -8,6 +9,11 @@ deps.dev/util/resolve v0.0.0-20241223234119-d36e05e6460f h1:jqmUQujU4ReUqHlZoD5v deps.dev/util/resolve v0.0.0-20241223234119-d36e05e6460f/go.mod h1:6AvyUZc8710/zuSpCSs0ugtxP1fR+yUOaqjQvXYR8M4= deps.dev/util/semver v0.0.0-20241223233905-018358ffdd50 h1:R075qWegHtrG+TGopdnuPg0kha5SRglps3+oFfHN7vQ= deps.dev/util/semver v0.0.0-20241223233905-018358ffdd50/go.mod h1:biofJPTJdTY6eu7X3bz3GgYlC/fbtLiT6AqIaE8SeYI= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/CycloneDX/cyclonedx-go v0.9.2 h1:688QHn2X/5nRezKe2ueIVCt+NRqf7fl3AVQk+vaFcIo= @@ -15,6 +21,8 @@ github.com/CycloneDX/cyclonedx-go v0.9.2/go.mod h1:vcK6pKgO1WanCdd61qx4bFnSsDJQ6 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/hcsshim v0.11.5 h1:haEcLNpj9Ka1gd3B3tAEs9CpE0c+1IhoL59w/exYU38= +github.com/Microsoft/hcsshim v0.11.5/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= @@ -41,6 +49,7 @@ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuP github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= @@ -55,10 +64,28 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAM github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= +github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= +github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= +github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= +github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= +github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU= github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk= +github.com/containerd/ttrpc v1.2.4 h1:eQCQK4h9dxDmpOb9QOOMh2NHTfzroH1IkmHiKZi05Oo= +github.com/containerd/ttrpc v1.2.4/go.mod h1:ojvb8SJBSch0XkqNO0L0YX/5NxR3UnVk2LzFKBK0upc= +github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= +github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -75,16 +102,24 @@ github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBi github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo= github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug= github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/erikvarga/go-rpmdb v0.0.0-20240208180226-b97e041ef9af h1:JXdZ7gz1cike1HMJJiP57Ll3/wb7zEjFOBKVDMEFi4M= github.com/erikvarga/go-rpmdb v0.0.0-20240208180226-b97e041ef9af/go.mod h1:MiEorPk0IChAoCwpg2FXyqVgbNvOlPWZAYHqqIoDNoY= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8= github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= @@ -103,30 +138,59 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj 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.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= -github.com/google/osv-scalibr v0.1.6-0.20250105222824-56e5c3bfb149 h1:NR/j8m7lWb1V/izQi7oJlCZ5U/Z6GqM8hkoHghABdTQ= -github.com/google/osv-scalibr v0.1.6-0.20250105222824-56e5c3bfb149/go.mod h1:S8mrRjoWESAOOTq25lJqzxiKR6tbWSFYG8SVb5EFLHk= +github.com/google/osv-scalibr v0.1.6-0.20250116021232-6d1c6b6edc63 h1:IxlxwHbra+NQihY7L2/RowEb2+o612dijgTBRixj/Jg= +github.com/google/osv-scalibr v0.1.6-0.20250116021232-6d1c6b6edc63/go.mod h1:cwPTti0kznoGCTRgQEb/4XUjzbuEXKSgq0mcXxITXQM= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= +github.com/groob/plist v0.1.1 h1:JUsmXVPGJ0HqG4Ta1z3HYbO0XwOHsgc0PqahpvgU5Q0= +github.com/groob/plist v0.1.1/go.mod h1:itkABA+w2cw7x5nYUS/pLRef6ludkZKOigbROmCTaFw= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd h1:EVX1s+XNss9jkRW9K6XGJn2jL2lB1h5H804oKPsxOec= @@ -137,6 +201,8 @@ github.com/jedib0t/go-pretty/v6 v6.6.5 h1:9PgMJOVBedpgYLI56jQRJYqngxYAAzfEUua+3N github.com/jedib0t/go-pretty/v6 v6.6.5/go.mod h1:Uq/HrbhuFty5WSVNfjpQQe47x16RwVGXIveNGEyGtHs= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -165,6 +231,16 @@ github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwX github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= +github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= @@ -179,6 +255,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= +github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= +github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/ossf/osv-schema/bindings/go v0.0.0-20241210213101-57fd3ddb15aa h1:nl8hYBxl9gAOwcp8iDlmdUJCAUA9fBu67gkt5DjsMns= github.com/ossf/osv-schema/bindings/go v0.0.0-20241210213101-57fd3ddb15aa/go.mod h1:lILztSxHU7VsdlYqCnwgxSDBhbXMf7iEQWtldJCDXPo= github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U= @@ -195,6 +275,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578 h1:VstopitMQi3hZP0fzvnsLmzXZdQGc4bEcgu24cp+d4M= github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -229,6 +312,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -261,12 +345,20 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4= github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= @@ -277,28 +369,58 @@ go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4Jjx go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= @@ -308,24 +430,59 @@ golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/vuln v1.0.4 h1:SP0mPeg2PmGCu03V+61EcQiOjmpri2XijexKdzv8Z1I= golang.org/x/vuln v1.0.4/go.mod h1:NbJdUQhX8jY++FtuhrXs2Eyx0yePo9pF7nPlIjo9aaQ= +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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U= google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4= google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -342,6 +499,10 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= @@ -352,3 +513,5 @@ modernc.org/sqlite v1.20.3 h1:SqGJMMxjj1PHusLxdYxeQSodg7Jxn9WWkaAQjKrntZs= modernc.org/sqlite v1.20.3/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +www.velocidex.com/golang/regparser v0.0.0-20240404115756-2169ac0e3c09 h1:G1RWYBXP2lSzxKcrAU1YhiUlBetZ7hGIzIiWuuazvfo= +www.velocidex.com/golang/regparser v0.0.0-20240404115756-2169ac0e3c09/go.mod h1:pxSECT5mWM3goJ4sxB4HCJNKnKqiAlpyT8XnvBwkLGU= diff --git a/internal/ci/__snapshots__/vulnerability_result_diff_test.snap b/internal/ci/__snapshots__/vulnerability_result_diff_test.snap index bb4a93ff9b..ab68d01c97 100755 --- a/internal/ci/__snapshots__/vulnerability_result_diff_test.snap +++ b/internal/ci/__snapshots__/vulnerability_result_diff_test.snap @@ -381,7 +381,7 @@ "CVE-2021-3121", "GHSA-c3h9-896r-86jm" ], - "max_severity": "" + "max_severity": "8.6" } ] } @@ -886,7 +886,7 @@ "CVE-2021-3121", "GHSA-c3h9-896r-86jm" ], - "max_severity": "" + "max_severity": "8.6" } ] } diff --git a/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go b/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go new file mode 100644 index 0000000000..9c5645bd22 --- /dev/null +++ b/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go @@ -0,0 +1,160 @@ +package baseimagematcher + +import ( + "context" + "encoding/json" + "net/http" + "slices" + "strings" + + "github.com/google/osv-scanner/pkg/models" + "github.com/google/osv-scanner/pkg/reporter" + "github.com/opencontainers/go-digest" + "golang.org/x/sync/errgroup" +) + +const ( + maxConcurrentRequests = 1000 +) + +// OSVMatcher implements the VulnerabilityMatcher interface with a osv.dev client. +// It sends out requests for every package version and does not perform caching. +type DepsDevBaseImageMatcher struct { + Client http.Client + Reporter reporter.Reporter +} + +func (matcher *DepsDevBaseImageMatcher) MatchBaseImages(ctx context.Context, layerMetadata []models.LayerMetadata) ([][]models.BaseImageDetails, error) { + baseImagesMap := make([][]models.BaseImageDetails, len(layerMetadata)) + g, ctx := errgroup.WithContext(ctx) + g.SetLimit(maxConcurrentRequests) + + var runningDigest digest.Digest + for i, l := range layerMetadata { + if l.DiffID == "" { + continue + } + + if runningDigest == "" { + runningDigest = l.DiffID + } else { + runningDigest = digest.FromBytes([]byte(runningDigest + " " + l.DiffID)) + } + + chainID := runningDigest + g.Go(func() error { + if ctx.Err() != nil { + return ctx.Err() // this value doesn't matter to errgroup.Wait(), it will be ctx.Err() + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.deps.dev/v3alpha/querycontainerimages/"+chainID.String(), nil) + if err != nil { + matcher.Reporter.Errorf("failed to build request: %s\n", err) + return nil + } + + resp, err := matcher.Client.Do(req) + if err != nil { + matcher.Reporter.Errorf("deps.dev API error: %s\n", err) + return nil + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + return nil + } + + if resp.StatusCode != http.StatusOK { + matcher.Reporter.Errorf("deps.dev API error: %s\n", resp.Status) + return nil + } + + var results struct { + Results []struct { + Repository string `json:"repository"` + } `json:"results"` + } + + d := json.NewDecoder(resp.Body) + err = d.Decode(&results) + if err != nil { + matcher.Reporter.Errorf("Unexpected return type from deps.dev base image endpoint: %s", err) + return nil + } + + // Found some base images! + baseImagePossibilities := []models.BaseImageDetails{} + for _, r := range results.Results { + baseImagePossibilities = append(baseImagePossibilities, models.BaseImageDetails{ + Name: r.Repository, + }) + } + // TODO(v2): Temporary heuristic for what is more popular + // Ideally this is done by deps.dev before release + slices.SortFunc(baseImagePossibilities, func(a, b models.BaseImageDetails) int { + return len(a.Name) - len(b.Name) + }) + baseImagesMap[i] = baseImagePossibilities + + return nil + }) + } + + if err := g.Wait(); err != nil { + return nil, context.DeadlineExceeded + } + + allBaseImages := [][]models.BaseImageDetails{ + // The base image at index 0 is a placeholder representing your image, so always empty + // This is the case even if your image is a base image, in that case no layers point to index 0 + {}, + } + + currentBaseImageIndex := 0 + for i, baseImages := range slices.Backward(baseImagesMap) { + if len(baseImages) == 0 { + layerMetadata[i].BaseImageIndex = currentBaseImageIndex + continue + } + + // This layer is a base image boundary + allBaseImages = append(allBaseImages, baseImages) + currentBaseImageIndex += 1 + layerMetadata[i].BaseImageIndex = currentBaseImageIndex + + // Backfill with heuristic: + // The goal here is to replace empty layers that is currently categorized as the previous base image + // with this base image if it actually belongs to this layer. + // + // We do this by guessing the boundary of empty layers by checking for the following commands, + // which are commonly the *last* layer. + // + // Remember we are looping backwards in the outer loop, + // so this backfill is actually filling down the layer stack, not up. + possibleFinalBaseImageCommands := []string{ + "/bin/sh -c #(nop) CMD", + "CMD", + "/bin/sh -c #(nop) ENTRYPOINT", + "ENTRYPOINT", + } + BackfillLoop: + for i2 := i; i2 < len(layerMetadata); i2++ { + if !layerMetadata[i2].IsEmpty { + // If the layer is not empty, whatever base image it is current assigned + // would be already correct, we only need to adjust empty layers. + break + } + buildCommand := layerMetadata[i2].Command + layerMetadata[i2].BaseImageIndex = currentBaseImageIndex + + // Check if this is the last layer and we can stop looping + for _, prefix := range possibleFinalBaseImageCommands { + if strings.HasPrefix(buildCommand, prefix) { + break BackfillLoop + } + } + } + } + + return allBaseImages, nil +} diff --git a/internal/clients/clientinterfaces/baseimagematcher.go b/internal/clients/clientinterfaces/baseimagematcher.go new file mode 100644 index 0000000000..f38b56e132 --- /dev/null +++ b/internal/clients/clientinterfaces/baseimagematcher.go @@ -0,0 +1,11 @@ +package clientinterfaces + +import ( + "context" + + "github.com/google/osv-scanner/pkg/models" +) + +type BaseImageMatcher interface { + MatchBaseImages(ctx context.Context, layerMetadata []models.LayerMetadata) ([][]models.BaseImageDetails, error) +} diff --git a/internal/image/__snapshots__/image_test.snap b/internal/image/__snapshots__/image_test.snap deleted file mode 100755 index 58b8b54897..0000000000 --- a/internal/image/__snapshots__/image_test.snap +++ /dev/null @@ -1,1879 +0,0 @@ - -[TestScanImage/Alpine_3.10_image_tar_with_3.18_version_file - 1] -{ - "Lockfiles": [ - { - "filePath": "/lib/apk/db/installed", - "parsedAs": "os/apk", - "packages": [ - { - "name": "alpine-baselayout", - "version": "3.1.2-r0", - "commit": "770d8ce7c6c556d952884ad436dd82b17ceb1a9a", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-keys", - "version": "2.1-r2", - "commit": "bdc861e495d33e961b7b9884324bea64a16d2b91", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "apk-tools", - "version": "2.10.6-r0", - "commit": "ee458ccae264321745e9622c759baf110130eb2f", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "busybox", - "version": "1.30.1-r5", - "commit": "26527b0535f65a4ac0ae7f3c9afb2294885b21cc", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "ca-certificates-cacert", - "version": "20191127-r2", - "commit": "9677580919b73ca6eff94d3d31b9a846b4e40612", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "libc-utils", - "version": "0.7.1-r0", - "commit": "cdca45021830765ad71e58af7ed31f42d1d3d644", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "libcrypto1.1", - "version": "1.1.1k-r0", - "commit": "b5417b32170f2c945de1735ea728199291ff97b6", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "libssl1.1", - "version": "1.1.1k-r0", - "commit": "b5417b32170f2c945de1735ea728199291ff97b6", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "libtls-standalone", - "version": "2.9.1-r0", - "commit": "981bf8f8fb3cbbc210ee4f2a2fb5b55d0132e02a", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "musl", - "version": "1.1.22-r4", - "commit": "5c22bb085e8e49c9cb402315efad998f7f992dff", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "musl-utils", - "version": "1.1.22-r4", - "commit": "5c22bb085e8e49c9cb402315efad998f7f992dff", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "scanelf", - "version": "1.2.3-r0", - "commit": "7768569c07c52f01b11e62e523cd6ddcb4690889", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "ssl_client", - "version": "1.30.1-r5", - "commit": "26527b0535f65a4ac0ae7f3c9afb2294885b21cc", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - }, - { - "name": "zlib", - "version": "1.2.11-r1", - "commit": "d2bfb22c8e8f67ad7d8d02704f35ec4d2a19f9b9", - "ecosystem": "Alpine:v3.18", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / ", - "inBaseImage": true - } - } - ] - } - ], - "ImagePath": "fixtures/test-alpine.tar" -} ---- - -[TestScanImage/scanning_go_binaries_that's_been_overwritten_for_package_tracing - 1] -{ - "Lockfiles": [ - { - "filePath": "/go/bin/more-vuln-overwrite-less-vuln", - "parsedAs": "go/binary", - "packages": [ - { - "name": "github.com/BurntSushi/toml", - "version": "1.2.0", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "RUN /bin/sh -c cp /go/bin/ptf-1.2.0 /go/bin/more-vuln-overwrite-less-vuln # buildkit", - "inBaseImage": false - } - }, - { - "name": "stdlib", - "version": "1.22.4", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "RUN /bin/sh -c cp /go/bin/ptf-1.4.0 /go/bin/more-vuln-overwrite-less-vuln # buildkit", - "inBaseImage": false - } - } - ] - }, - { - "filePath": "/go/bin/ptf-1.2.0", - "parsedAs": "go/binary", - "packages": [ - { - "name": "github.com/BurntSushi/toml", - "version": "1.2.0", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", - "inBaseImage": true - } - }, - { - "name": "stdlib", - "version": "1.22.4", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", - "inBaseImage": true - } - } - ] - }, - { - "filePath": "/go/bin/ptf-1.3.0", - "parsedAs": "go/binary", - "packages": [ - { - "name": "github.com/BurntSushi/toml", - "version": "1.3.0", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "RUN /bin/sh -c cp /go/bin/ptf-1.3.0-moved /go/bin/ptf-1.3.0 # buildkit", - "inBaseImage": false - } - }, - { - "name": "stdlib", - "version": "1.22.4", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "RUN /bin/sh -c cp /go/bin/ptf-1.3.0-moved /go/bin/ptf-1.3.0 # buildkit", - "inBaseImage": false - } - } - ] - }, - { - "filePath": "/go/bin/ptf-1.3.0-moved", - "parsedAs": "go/binary", - "packages": [ - { - "name": "github.com/BurntSushi/toml", - "version": "1.3.0", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "RUN /bin/sh -c mv /go/bin/ptf-1.3.0 /go/bin/ptf-1.3.0-moved # buildkit", - "inBaseImage": false - } - }, - { - "name": "stdlib", - "version": "1.22.4", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "RUN /bin/sh -c mv /go/bin/ptf-1.3.0 /go/bin/ptf-1.3.0-moved # buildkit", - "inBaseImage": false - } - } - ] - }, - { - "filePath": "/go/bin/ptf-1.4.0", - "parsedAs": "go/binary", - "packages": [ - { - "name": "github.com/BurntSushi/toml", - "version": "1.4.0", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", - "inBaseImage": true - } - }, - { - "name": "stdlib", - "version": "1.22.4", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", - "inBaseImage": true - } - } - ] - }, - { - "filePath": "/go/bin/ptf-vulnerable", - "parsedAs": "go/binary", - "packages": [ - { - "name": "github.com/BurntSushi/toml", - "version": "1.4.0", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "RUN /bin/sh -c cp /go/bin/ptf-1.4.0 /go/bin/ptf-vulnerable # buildkit", - "inBaseImage": false - } - }, - { - "name": "stdlib", - "version": "1.22.4", - "ecosystem": "Go", - "compareAs": "Go", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "RUN /bin/sh -c cp /go/bin/ptf-1.3.0 /go/bin/ptf-vulnerable # buildkit", - "inBaseImage": false - } - } - ] - }, - { - "filePath": "/lib/apk/db/installed", - "parsedAs": "os/apk", - "packages": [ - { - "name": "alpine-baselayout", - "version": "3.6.5-r0", - "commit": "66187892e05b03a41d08e9acabd19b7576a1c875", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-baselayout-data", - "version": "3.6.5-r0", - "commit": "66187892e05b03a41d08e9acabd19b7576a1c875", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-keys", - "version": "2.4-r1", - "commit": "aab68f8c9ab434a46710de8e12fb3206e2930a59", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "apk-tools", - "version": "2.14.4-r0", - "commit": "d435c805af8af4171438da3ec3429c094aac4c6e", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "busybox", - "version": "1.36.1-r29", - "commit": "1747c01fb96905f101c25609011589d28e01cbb8", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "busybox-binsh", - "version": "1.36.1-r29", - "commit": "1747c01fb96905f101c25609011589d28e01cbb8", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "ca-certificates-bundle", - "version": "20240226-r0", - "commit": "56fb003da0adcea3b59373ef6a633d0c5bfef3ac", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "libcrypto3", - "version": "3.3.1-r0", - "commit": "15cc530882e1e6f3dc8a77200ee8bd01cb98f53c", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "libssl3", - "version": "3.3.1-r0", - "commit": "15cc530882e1e6f3dc8a77200ee8bd01cb98f53c", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "musl", - "version": "1.2.5-r0", - "commit": "4fe5bdbe47b100daa6380f81c4c8ea3f99b61362", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "musl-utils", - "version": "1.2.5-r0", - "commit": "4fe5bdbe47b100daa6380f81c4c8ea3f99b61362", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "scanelf", - "version": "1.3.7-r2", - "commit": "e65a4f2d0470e70d862ef2b5c412ecf2cb9ad0a6", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "ssl_client", - "version": "1.36.1-r29", - "commit": "1747c01fb96905f101c25609011589d28e01cbb8", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - }, - { - "name": "zlib", - "version": "1.3.1-r1", - "commit": "fad2d175bd85eb4c5566765375392a7394dfbcf2", - "ecosystem": "Alpine:v3.20", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "inBaseImage": true - } - } - ] - } - ], - "ImagePath": "fixtures/test-package-tracing.tar" -} ---- - -[TestScanImage/scanning_node_modules_using_npm_with_no_packages - 1] -{ - "Lockfiles": [ - { - "filePath": "/lib/apk/db/installed", - "parsedAs": "os/apk", - "packages": [ - { - "name": "alpine-baselayout", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-baselayout-data", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-keys", - "version": "2.4-r1", - "commit": "aab68f8c9ab434a46710de8e12fb3206e2930a59", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "apk-tools", - "version": "2.14.0-r5", - "commit": "33283848034c9885d984c8e8697c645c57324938", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox-binsh", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ca-certificates-bundle", - "version": "20230506-r0", - "commit": "59534a02716a92a10d177a118c34066162eff4a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libc-utils", - "version": "0.7.2-r5", - "commit": "988f183cc9d6699930c3e18ccf4a9e36010afb56", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libcrypto3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libgcc", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "libssl3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libstdc++", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "musl", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "musl-utils", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "scanelf", - "version": "1.3.7-r2", - "commit": "e65a4f2d0470e70d862ef2b5c412ecf2cb9ad0a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ssl_client", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "zlib", - "version": "1.3.1-r0", - "commit": "9406f6fc5fca057d990eb0d260d75839eeb34d83", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - } - ] - } - ], - "ImagePath": "fixtures/test-node_modules-npm-empty.tar" -} ---- - -[TestScanImage/scanning_node_modules_using_npm_with_some_packages - 1] -{ - "Lockfiles": [ - { - "filePath": "/lib/apk/db/installed", - "parsedAs": "os/apk", - "packages": [ - { - "name": "alpine-baselayout", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-baselayout-data", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-keys", - "version": "2.4-r1", - "commit": "aab68f8c9ab434a46710de8e12fb3206e2930a59", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "apk-tools", - "version": "2.14.0-r5", - "commit": "33283848034c9885d984c8e8697c645c57324938", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox-binsh", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ca-certificates-bundle", - "version": "20230506-r0", - "commit": "59534a02716a92a10d177a118c34066162eff4a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libc-utils", - "version": "0.7.2-r5", - "commit": "988f183cc9d6699930c3e18ccf4a9e36010afb56", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libcrypto3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libgcc", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "libssl3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libstdc++", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "musl", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "musl-utils", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "scanelf", - "version": "1.3.7-r2", - "commit": "e65a4f2d0470e70d862ef2b5c412ecf2cb9ad0a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ssl_client", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "zlib", - "version": "1.3.1-r0", - "commit": "9406f6fc5fca057d990eb0d260d75839eeb34d83", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - } - ] - }, - { - "filePath": "/prod/app/node_modules/.package-lock.json", - "parsedAs": "javascript/nodemodules", - "packages": [ - { - "name": "cryo", - "version": "0.0.6", - "ecosystem": "npm", - "compareAs": "npm", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "RUN /bin/sh -c npm i -d cryo@0.0.6 # buildkit", - "inBaseImage": false - } - }, - { - "name": "minimist", - "version": "0.0.8", - "ecosystem": "npm", - "compareAs": "npm", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "RUN /bin/sh -c npm i mkdirp@0.5.0 # buildkit", - "inBaseImage": false - } - }, - { - "name": "mkdirp", - "version": "0.5.0", - "ecosystem": "npm", - "compareAs": "npm", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "RUN /bin/sh -c npm i mkdirp@0.5.0 # buildkit", - "inBaseImage": false - } - } - ] - } - ], - "ImagePath": "fixtures/test-node_modules-npm-full.tar" -} ---- - -[TestScanImage/scanning_node_modules_using_pnpm_with_no_packages - 1] -{ - "Lockfiles": [ - { - "filePath": "/lib/apk/db/installed", - "parsedAs": "os/apk", - "packages": [ - { - "name": "alpine-baselayout", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-baselayout-data", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-keys", - "version": "2.4-r1", - "commit": "aab68f8c9ab434a46710de8e12fb3206e2930a59", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "apk-tools", - "version": "2.14.0-r5", - "commit": "33283848034c9885d984c8e8697c645c57324938", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox-binsh", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ca-certificates-bundle", - "version": "20230506-r0", - "commit": "59534a02716a92a10d177a118c34066162eff4a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libc-utils", - "version": "0.7.2-r5", - "commit": "988f183cc9d6699930c3e18ccf4a9e36010afb56", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libcrypto3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libgcc", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "libssl3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libstdc++", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "musl", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "musl-utils", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "scanelf", - "version": "1.3.7-r2", - "commit": "e65a4f2d0470e70d862ef2b5c412ecf2cb9ad0a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ssl_client", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "zlib", - "version": "1.3.1-r0", - "commit": "9406f6fc5fca057d990eb0d260d75839eeb34d83", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - } - ] - } - ], - "ImagePath": "fixtures/test-node_modules-pnpm-empty.tar" -} ---- - -[TestScanImage/scanning_node_modules_using_pnpm_with_some_packages - 1] -{ - "Lockfiles": [ - { - "filePath": "/lib/apk/db/installed", - "parsedAs": "os/apk", - "packages": [ - { - "name": "alpine-baselayout", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-baselayout-data", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-keys", - "version": "2.4-r1", - "commit": "aab68f8c9ab434a46710de8e12fb3206e2930a59", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "apk-tools", - "version": "2.14.0-r5", - "commit": "33283848034c9885d984c8e8697c645c57324938", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox-binsh", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ca-certificates-bundle", - "version": "20230506-r0", - "commit": "59534a02716a92a10d177a118c34066162eff4a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libc-utils", - "version": "0.7.2-r5", - "commit": "988f183cc9d6699930c3e18ccf4a9e36010afb56", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libcrypto3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libgcc", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "libssl3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libstdc++", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "musl", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "musl-utils", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "scanelf", - "version": "1.3.7-r2", - "commit": "e65a4f2d0470e70d862ef2b5c412ecf2cb9ad0a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ssl_client", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "zlib", - "version": "1.3.1-r0", - "commit": "9406f6fc5fca057d990eb0d260d75839eeb34d83", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - } - ] - } - ], - "ImagePath": "fixtures/test-node_modules-pnpm-full.tar" -} ---- - -[TestScanImage/scanning_node_modules_using_yarn_with_no_packages - 1] -{ - "Lockfiles": [ - { - "filePath": "/lib/apk/db/installed", - "parsedAs": "os/apk", - "packages": [ - { - "name": "alpine-baselayout", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-baselayout-data", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-keys", - "version": "2.4-r1", - "commit": "aab68f8c9ab434a46710de8e12fb3206e2930a59", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "apk-tools", - "version": "2.14.0-r5", - "commit": "33283848034c9885d984c8e8697c645c57324938", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox-binsh", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ca-certificates-bundle", - "version": "20230506-r0", - "commit": "59534a02716a92a10d177a118c34066162eff4a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libc-utils", - "version": "0.7.2-r5", - "commit": "988f183cc9d6699930c3e18ccf4a9e36010afb56", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libcrypto3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libgcc", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "libssl3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libstdc++", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "musl", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "musl-utils", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "scanelf", - "version": "1.3.7-r2", - "commit": "e65a4f2d0470e70d862ef2b5c412ecf2cb9ad0a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ssl_client", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "zlib", - "version": "1.3.1-r0", - "commit": "9406f6fc5fca057d990eb0d260d75839eeb34d83", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - } - ] - } - ], - "ImagePath": "fixtures/test-node_modules-yarn-empty.tar" -} ---- - -[TestScanImage/scanning_node_modules_using_yarn_with_some_packages - 1] -{ - "Lockfiles": [ - { - "filePath": "/lib/apk/db/installed", - "parsedAs": "os/apk", - "packages": [ - { - "name": "alpine-baselayout", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-baselayout-data", - "version": "3.4.3-r2", - "commit": "7749273fed55f6e1df7c9ee6a127f18099f98a94", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "alpine-keys", - "version": "2.4-r1", - "commit": "aab68f8c9ab434a46710de8e12fb3206e2930a59", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "apk-tools", - "version": "2.14.0-r5", - "commit": "33283848034c9885d984c8e8697c645c57324938", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "busybox-binsh", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ca-certificates-bundle", - "version": "20230506-r0", - "commit": "59534a02716a92a10d177a118c34066162eff4a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libc-utils", - "version": "0.7.2-r5", - "commit": "988f183cc9d6699930c3e18ccf4a9e36010afb56", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libcrypto3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libgcc", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "libssl3", - "version": "3.1.4-r5", - "commit": "b784a22cad0c452586b438cb7a597d846fc09ff4", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "libstdc++", - "version": "13.2.1_git20231014-r0", - "commit": "090e168783a86e5c2ba31fc65921b9715bac62ff", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps curl \u0026\u0026 ARCH= OPENSSL_ARCH='linux*' \u0026\u0026 alpineArch=\"$(apk --print-arch)\" \u0026\u0026 case \"${alpineArch##*-}\" in x86_64) ARCH='x64' CHECKSUM=\"5da733c21c3b51193a4fe9fc5be6cfa9a694d13b8d766eb02dbe4b8996547050\" OPENSSL_ARCH=linux-x86_64;; x86) OPENSSL_ARCH=linux-elf;; aarch64) OPENSSL_ARCH=linux-aarch64;; arm*) OPENSSL_ARCH=linux-armv4;; ppc64le) OPENSSL_ARCH=linux-ppc64le;; s390x) OPENSSL_ARCH=linux-s390x;; *) ;; esac \u0026\u0026 if [ -n \"${CHECKSUM}\" ]; then set -eu; curl -fsSLO --compressed \"https://unofficial-builds.nodejs.org/download/release/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\"; echo \"$CHECKSUM node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" | sha256sum -c - \u0026\u0026 tar -xJf \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" -C /usr/local --strip-components=1 --no-same-owner \u0026\u0026 ln -s /usr/local/bin/node /usr/local/bin/nodejs; else echo \"Building from source\" \u0026\u0026 apk add --no-cache --virtual .build-deps-full binutils-gold g++ gcc gnupg libgcc linux-headers make python3 \u0026\u0026 export GNUPGHOME=\"$(mktemp -d)\" \u0026\u0026 for key in 4ED778F539E3634C779C87C6D7062848A1AB005C 141F07595B7B3FFE74309A937405533BE57C7D57 74F12602B6F1C4E913FAA37AD3A89613643B6201 DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 61FC681DFB92A079F1685E77973F295594EC4689 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 108F52B48DB57BB0CC439B2997B01419BD92F80A A363A499291CBBC940DD62E41F10027AF002F8B0 ; do gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys \"$key\" || gpg --batch --keyserver keyserver.ubuntu.com --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 gpgconf --kill all \u0026\u0026 rm -rf \"$GNUPGHOME\" \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) V= \u0026\u0026 make install \u0026\u0026 apk del .build-deps-full \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt; fi \u0026\u0026 rm -f \"node-v$NODE_VERSION-linux-$ARCH-musl.tar.xz\" \u0026\u0026 find /usr/local/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name \"$OPENSSL_ARCH\" -exec rm -rf {} \\; \u0026\u0026 apk del .build-deps \u0026\u0026 node --version \u0026\u0026 npm --version", - "inBaseImage": true - } - }, - { - "name": "musl", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "musl-utils", - "version": "1.2.4_git20230717-r4", - "commit": "ca7f2ab5e88794e4e654b40776f8a92256f50639", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "scanelf", - "version": "1.3.7-r2", - "commit": "e65a4f2d0470e70d862ef2b5c412ecf2cb9ad0a6", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "ssl_client", - "version": "1.36.1-r15", - "commit": "d1b6f274f29076967826e0ecf6ebcaa5d360272f", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - }, - { - "name": "zlib", - "version": "1.3.1-r0", - "commit": "9406f6fc5fca057d990eb0d260d75839eeb34d83", - "ecosystem": "Alpine:v3.19", - "compareAs": "Alpine", - "imageOrigin": { - "layerID": "\u003cAny value\u003e", - "originCommand": "/bin/sh -c #(nop) ADD file:37a76ec18f9887751cd8473744917d08b7431fc4085097bb6a09d81b41775473 in / ", - "inBaseImage": true - } - } - ] - } - ], - "ImagePath": "fixtures/test-node_modules-yarn-full.tar" -} ---- diff --git a/internal/image/extractor.go b/internal/image/extractor.go deleted file mode 100644 index 5b8de170d6..0000000000 --- a/internal/image/extractor.go +++ /dev/null @@ -1,114 +0,0 @@ -package image - -import ( - "context" - "errors" - "fmt" - "io/fs" - "strings" - - "github.com/google/osv-scalibr/extractor" - "github.com/google/osv-scalibr/extractor/filesystem" - "github.com/google/osv-scalibr/extractor/filesystem/language/golang/gobinary" - "github.com/google/osv-scalibr/extractor/filesystem/os/apk" - "github.com/google/osv-scalibr/extractor/filesystem/os/dpkg" - "github.com/google/osv-scalibr/extractor/filesystem/simplefileapi" - "github.com/google/osv-scanner/internal/scalibrextract" - "github.com/google/osv-scanner/internal/scalibrextract/language/javascript/nodemodules" - "github.com/google/osv-scanner/pkg/lockfile" -) - -// artifactExtractors contains only extractors for artifacts that are important in -// the final layer of a container image -var artifactExtractors []filesystem.Extractor = []filesystem.Extractor{ - // TODO: Using nodemodules extractor to minimize changes of snapshots - // After annotations are added, we should switch to using packagejson. - // packagejson.New(packagejson.DefaultConfig()), - nodemodules.Extractor{}, - - apk.New(apk.DefaultConfig()), - gobinary.New(gobinary.DefaultConfig()), - // TODO: Add tests for debian containers - dpkg.New(dpkg.DefaultConfig()), -} - -func findArtifactExtractor(path string, fileInfo fs.FileInfo) []filesystem.Extractor { - // Use ShouldExtract to collect and return a slice of artifactExtractors - var extractors []filesystem.Extractor - for _, extractor := range artifactExtractors { - if extractor.FileRequired(simplefileapi.New(path, fileInfo)) { - extractors = append(extractors, extractor) - } - } - - return extractors -} - -// Note: Output is non deterministic -func extractArtifactDeps(extractPath string, layer *Layer) ([]*extractor.Inventory, error) { - pathFileInfo, err := layer.Stat(extractPath) - if err != nil { - return nil, fmt.Errorf("attempted to get FileInfo but failed: %w", err) - } - - scalibrPath := strings.TrimPrefix(extractPath, "/") - foundExtractors := findArtifactExtractor(scalibrPath, pathFileInfo) - if len(foundExtractors) == 0 { - return nil, fmt.Errorf("%w for %s", scalibrextract.ErrExtractorNotFound, extractPath) - } - - inventories := []*extractor.Inventory{} - var extractedAs string - for _, extractor := range foundExtractors { - // File has to be reopened per extractor as each extractor moves the read cursor - f, err := layer.Open(extractPath) - if err != nil { - return nil, fmt.Errorf("attempted to open file but failed: %w", err) - } - - scanInput := &filesystem.ScanInput{ - FS: layer, - Path: scalibrPath, - Root: "/", - Reader: f, - Info: pathFileInfo, - } - - newPackages, err := extractor.Extract(context.Background(), scanInput) - f.Close() - - if err != nil { - if errors.Is(err, lockfile.ErrIncompatibleFileFormat) { - continue - } - - return nil, fmt.Errorf("(extracting as %s) %w", extractor.Name(), err) - } - - for i := range newPackages { - newPackages[i].Extractor = extractor - } - - extractedAs = extractor.Name() - inventories = newPackages - // TODO(rexpan): Determine if this it's acceptable to have multiple extractors - // extract from the same file successfully - break - } - - if extractedAs == "" { - return nil, fmt.Errorf("%w for %s", scalibrextract.ErrExtractorNotFound, extractPath) - } - - // Perform any one-off translations here - for _, inv := range inventories { - // Scalibr uses go to indicate go compiler version - // We specifically cares about the stdlib version inside the package - // so convert the package name from go to stdlib - if inv.Ecosystem() == "Go" && inv.Name == "go" { - inv.Name = "stdlib" - } - } - - return inventories, nil -} diff --git a/internal/image/fixtures/test-node_modules-npm-empty.Dockerfile b/internal/image/fixtures/test-node_modules-npm-empty.Dockerfile index 2dbae77b8f..67ff3b79f7 100644 --- a/internal/image/fixtures/test-node_modules-npm-empty.Dockerfile +++ b/internal/image/fixtures/test-node_modules-npm-empty.Dockerfile @@ -1,6 +1,6 @@ ARG MANAGER_VERSION="10.2.4" -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 +FROM node:20-alpine@sha256:c0a3badbd8a0a760de903e00cedbca94588e609299820557e72cba2a53dbaa2c WORKDIR /prod/app diff --git a/internal/image/fixtures/test-node_modules-npm-full.Dockerfile b/internal/image/fixtures/test-node_modules-npm-full.Dockerfile index 1043f54004..96e136b5f7 100644 --- a/internal/image/fixtures/test-node_modules-npm-full.Dockerfile +++ b/internal/image/fixtures/test-node_modules-npm-full.Dockerfile @@ -1,6 +1,6 @@ ARG MANAGER_VERSION="10.2.4" -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 +FROM node:20-alpine@sha256:c0a3badbd8a0a760de903e00cedbca94588e609299820557e72cba2a53dbaa2c WORKDIR /prod/app diff --git a/internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile b/internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile index fd97227527..7a221ca7ea 100644 --- a/internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile +++ b/internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile @@ -1,6 +1,6 @@ ARG MANAGER_VERSION="8.15.4" -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 +FROM node:20-alpine@sha256:c0a3badbd8a0a760de903e00cedbca94588e609299820557e72cba2a53dbaa2c WORKDIR /prod/app diff --git a/internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile b/internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile index 666de1ef50..80e1ee6519 100644 --- a/internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile +++ b/internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile @@ -1,6 +1,6 @@ ARG MANAGER_VERSION="8.15.4" -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 +FROM node:20-alpine@sha256:c0a3badbd8a0a760de903e00cedbca94588e609299820557e72cba2a53dbaa2c WORKDIR /prod/app diff --git a/internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile b/internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile index b9743644d1..41f4c2f423 100644 --- a/internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile +++ b/internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile @@ -1,6 +1,6 @@ ARG MANAGER_VERSION="1.22.22" -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 +FROM node:20-alpine@sha256:c0a3badbd8a0a760de903e00cedbca94588e609299820557e72cba2a53dbaa2c WORKDIR /prod/app diff --git a/internal/image/fixtures/test-node_modules-yarn-full.Dockerfile b/internal/image/fixtures/test-node_modules-yarn-full.Dockerfile index c675c21ad3..99e9653f01 100644 --- a/internal/image/fixtures/test-node_modules-yarn-full.Dockerfile +++ b/internal/image/fixtures/test-node_modules-yarn-full.Dockerfile @@ -1,6 +1,6 @@ ARG MANAGER_VERSION="1.22.22" -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 +FROM node:20-alpine@sha256:c0a3badbd8a0a760de903e00cedbca94588e609299820557e72cba2a53dbaa2c WORKDIR /prod/app diff --git a/internal/image/guess_base_image.go b/internal/image/guess_base_image.go deleted file mode 100644 index 92b0575d2b..0000000000 --- a/internal/image/guess_base_image.go +++ /dev/null @@ -1,80 +0,0 @@ -package image - -import ( - "strings" - - v1 "github.com/google/go-containerregistry/pkg/v1" -) - -// Originally from https://github.com/aquasecurity/trivy/blob/1f5f34895823fae81bf521fc939bee743a50e304/pkg/fanal/image/image.go#L111 -// Modified to return non empty index - -// GuessBaseImageIndex tries to guess index of base layer. Index counting only non empty layers. -// -// e.g. In the following example, we should detect layers in debian:8. -// -// FROM debian:8 -// RUN apt-get update -// COPY mysecret / -// ENTRYPOINT ["entrypoint.sh"] -// CMD ["somecmd"] -// -// debian:8 may be like -// -// ADD file:5d673d25da3a14ce1f6cf66e4c7fd4f4b85a3759a9d93efb3fd9ff852b5b56e4 in / -// CMD ["/bin/sh"] -// -// In total, it would be like: -// -// ADD file:5d673d25da3a14ce1f6cf66e4c7fd4f4b85a3759a9d93efb3fd9ff852b5b56e4 in / -// CMD ["/bin/sh"] # empty layer (detected) -// RUN apt-get update -// COPY mysecret / -// ENTRYPOINT ["entrypoint.sh"] # empty layer (skipped) -// CMD ["somecmd"] # empty layer (skipped) -// -// This method tries to detect CMD in the second line and assume the first line is a base layer. -// 1. Iterate histories from the bottom. -// 2. Skip all the empty layers at the bottom. In the above example, "entrypoint.sh" and "somecmd" will be skipped -// 3. If it finds CMD, it assumes that it is the end of base layers. -// 4. It gets all the layers as base layers above the CMD found in #3. -func guessBaseImageIndex(histories []v1.History) int { - baseImageIndex := -1 - var foundNonEmpty bool - for i := len(histories) - 1; i >= 0; i-- { - h := histories[i] - - // Skip the last CMD, ENTRYPOINT, etc. - if !foundNonEmpty { - if h.EmptyLayer { - continue - } - foundNonEmpty = true - } - - if !h.EmptyLayer { - continue - } - - // Detect CMD instruction in base image - if strings.HasPrefix(h.CreatedBy, "/bin/sh -c #(nop) CMD") || - strings.HasPrefix(h.CreatedBy, "CMD") { // BuildKit - baseImageIndex = i - break - } - } - - if baseImageIndex == -1 { - return -1 - } - - nonEmptyIndex := 0 - for i := 0; i <= baseImageIndex; i++ { - if histories[i].EmptyLayer { - continue - } - nonEmptyIndex += 1 - } - - return nonEmptyIndex -} diff --git a/internal/image/image.go b/internal/image/image.go deleted file mode 100644 index 0be6f53bf2..0000000000 --- a/internal/image/image.go +++ /dev/null @@ -1,279 +0,0 @@ -package image - -import ( - "archive/tar" - "errors" - "fmt" - "io" - "io/fs" - "os" - "path" - "path/filepath" - "strings" - - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/tarball" - "github.com/google/osv-scanner/internal/image/pathtree" - "github.com/google/osv-scanner/pkg/lockfile" -) - -const whiteoutPrefix = ".wh." - -// 2 GB -const fileReadLimit = 2 * 1 << (10 * 3) -const dirPermission = 0700 -const filePermission = 0600 - -var ErrNoHistoryAvailable = errors.New("no history available") - -type ScanResults struct { - Lockfiles []lockfile.Lockfile - ImagePath string -} - -type Image struct { - // Final layer is the last element in the slice - layers []Layer - innerImage *v1.Image - extractDir string - baseImageIndex int - configFile *v1.ConfigFile - layerIDToIndex map[string]int -} - -// layerIDToCommand takes in a layer id (see imgLayer.id) and returns the history CreatedBy field -// of the corresponding layer -func (img *Image) layerIDToCommand(id string) (string, error) { - idxCount := img.layerIDToIndex[id] - var i int - // Match history to layer IDX by skipping empty layer history entries - for i = 0; idxCount >= 0; i++ { - if i >= len(img.configFile.History) { - return "", ErrNoHistoryAvailable - } - - if img.configFile.History[i].EmptyLayer { - continue - } - idxCount -= 1 - } - // -1 from i because when idxCount becomes -1 it still increments i by 1 - return img.configFile.History[i-1].CreatedBy, nil -} - -func (img *Image) LastLayer() *Layer { - return &img.layers[len(img.layers)-1] -} - -func (img *Image) Cleanup() error { - if img == nil { - return errors.New("image is nil") - } - - return os.RemoveAll(img.extractDir) -} - -func LoadImage(imagePath string) (*Image, error) { - image, err := tarball.ImageFromPath(imagePath, nil) - if err != nil { - return nil, err - } - - layers, err := image.Layers() - if err != nil { - return nil, err - } - - configFile, err := image.ConfigFile() - if err != nil { - return nil, fmt.Errorf("failed to load config file: %w", err) - } - - tempPath, err := os.MkdirTemp("", "osv-scanner-image-scanning-*") - if err != nil { - return nil, err - } - - outputImage := Image{ - extractDir: tempPath, - innerImage: &image, - layers: make([]Layer, len(layers)), - layerIDToIndex: make(map[string]int), - configFile: configFile, - baseImageIndex: guessBaseImageIndex(configFile.History), - } - - // Initiate the layers first - for i := range layers { - hash, err := layers[i].DiffID() - if err != nil { - // Return the partial image so that the temporary path folder can be cleaned up - return &outputImage, err - } - - outputImage.layers[i] = Layer{ - fileNodeTrie: pathtree.NewNode[FileNode](), - id: hash.Hex, - rootImage: &outputImage, - } - - outputImage.layerIDToIndex[hash.Hex] = i - } - - // Reverse loop through the layers to start from the latest layer first - // this allows us to skip all files already seen - for i := len(layers) - 1; i >= 0; i-- { - dirPath := filepath.Join(tempPath, outputImage.layers[i].id) - err = os.Mkdir(dirPath, dirPermission) - if err != nil { - return &outputImage, err - } - - layerReader, err := layers[i].Uncompressed() - if err != nil { - return &outputImage, err - } - - tarReader := tar.NewReader(layerReader) - - for { - header, err := tarReader.Next() - if errors.Is(err, io.EOF) { - break - } - if err != nil { - return &outputImage, fmt.Errorf("reading tar: %w", err) - } - // Some tools prepend everything with "./", so if we don't Clean the - // name, we may have duplicate entries, which angers tar-split. - // Using path instead of filepath to keep `/` and deterministic behavior - cleanedFilePath := path.Clean(header.Name) - // Prevent "Zip Slip" - if strings.HasPrefix(cleanedFilePath, "../") { - // TODO: Could this occur with a normal image? - // e.g. maybe a bad symbolic link? - // and should we warn the user that some files are ignored - continue - } - // force PAX format to remove Name/Linkname length limit of 100 characters - // required by USTAR and to not depend on internal tar package guess which - // prefers USTAR over PAX - header.Format = tar.FormatPAX - - basename := path.Base(cleanedFilePath) - dirname := path.Dir(cleanedFilePath) - tombstone := strings.HasPrefix(basename, whiteoutPrefix) - if tombstone { // TODO: Handle Opaque Whiteouts - basename = basename[len(whiteoutPrefix):] - } - - // check if we have seen value before - // if we're checking a directory, don't filepath.Join names - var virtualPath string - if header.Typeflag == tar.TypeDir { - virtualPath = "/" + cleanedFilePath - } else { - virtualPath = "/" + path.Join(dirname, basename) - } - - // where the file will be written to disk - // filepath.Clean first to convert to OS specific file path - // TODO: Escape invalid characters on windows that's valid on linux - absoluteDiskPath := filepath.Join(dirPath, filepath.Clean(cleanedFilePath)) - - var fileType fileType - // write out the file/dir to disk - switch header.Typeflag { - case tar.TypeDir: - if _, err := os.Stat(absoluteDiskPath); err != nil { - if err := os.MkdirAll(absoluteDiskPath, dirPermission); err != nil { - return &outputImage, err - } - } - fileType = Dir - - default: // Assume if it's not a directory, it's a normal file - // Write all files as read/writable by the current user, inaccessible by anyone else - // Actual permission bits are stored in FileNode - f, err := os.OpenFile(absoluteDiskPath, os.O_CREATE|os.O_RDWR, filePermission) - if err != nil { - return &outputImage, err - } - numBytes, err := io.Copy(f, io.LimitReader(tarReader, fileReadLimit)) - if numBytes >= fileReadLimit || errors.Is(err, io.EOF) { - f.Close() - return &outputImage, errors.New("file exceeds read limit (potential decompression bomb attack)") - } - if err != nil { - f.Close() - return &outputImage, fmt.Errorf("unable to copy file: %w", err) - } - fileType = RegularFile - f.Close() - } - - // Each outer loop, we add a layer to each relevant output flattenedLayers slice - // Because we are looping backwards in the outer loop (latest layer first) - // we ignore any files that's already in each flattenedLayer, as they would - // have been overwritten. - // - // This loop will add the file to all future layers if it doesn't already exist - // (i.e. hasn't been overwritten) - for ii := i; ii < len(layers); ii++ { - currentMap := &outputImage.layers[ii] - - if item := currentMap.fileNodeTrie.Get(virtualPath); item != nil { - // A newer version of the file already exists on a later map. - // Since we do not want to overwrite a later layer with information - // written in an earlier layer, skip this file. - continue - } - - // check for a whited out parent directory - if inWhiteoutDir(*currentMap, virtualPath) { - // The entire directory has been deleted, so no need to save this file - continue - } - - err := currentMap.fileNodeTrie.Insert(virtualPath, &FileNode{ - rootImage: &outputImage, - // Select the original layer of the file - originLayer: &outputImage.layers[i], - virtualPath: virtualPath, - fileType: fileType, - isWhiteout: tombstone, - permission: fs.FileMode(header.Mode), //nolint:gosec - }) - - if err != nil { - return &outputImage, fmt.Errorf("image tar has repeated files: %w", err) - } - } - } - - // Manually close at the end of the for loop - // We don't want to defer because then no layers will be closed until entire image is read - layerReader.Close() - } - - return &outputImage, nil -} - -func inWhiteoutDir(fileMap Layer, filePath string) bool { - for { - if filePath == "" { - break - } - dirname := path.Dir(filePath) - if filePath == dirname { - break - } - node := fileMap.fileNodeTrie.Get(dirname) - if node != nil && node.isWhiteout { - return true - } - filePath = dirname - } - - return false -} diff --git a/internal/image/image_test.go.disabled b/internal/image/image_test.go.disabled deleted file mode 100644 index 787f258cc7..0000000000 --- a/internal/image/image_test.go.disabled +++ /dev/null @@ -1,99 +0,0 @@ -// package image_test - -// import ( -// "errors" -// "os" -// "testing" - -// "github.com/google/osv-scanner/internal/image" -// "github.com/google/osv-scanner/internal/testutility" -// "github.com/google/osv-scanner/pkg/reporter" -// ) - -// func TestScanImage(t *testing.T) { -// t.Parallel() -// testutility.SkipIfNotAcceptanceTesting(t, "Not consistent on MacOS/Windows") - -// type args struct { -// imagePath string -// } -// tests := []struct { -// name string -// args args -// want testutility.Snapshot -// wantErr bool -// }{ -// { -// name: "Alpine 3.10 image tar with 3.18 version file", -// args: args{imagePath: "fixtures/test-alpine.tar"}, -// want: testutility.NewSnapshot(), -// wantErr: false, -// }, -// { -// name: "scanning node_modules using npm with no packages", -// args: args{imagePath: "fixtures/test-node_modules-npm-empty.tar"}, -// want: testutility.NewSnapshot(), -// wantErr: false, -// }, -// { -// name: "scanning node_modules using npm with some packages", -// args: args{imagePath: "fixtures/test-node_modules-npm-full.tar"}, -// want: testutility.NewSnapshot(), -// wantErr: false, -// }, -// { -// name: "scanning node_modules using yarn with no packages", -// args: args{imagePath: "fixtures/test-node_modules-yarn-empty.tar"}, -// want: testutility.NewSnapshot(), -// wantErr: false, -// }, -// { -// name: "scanning node_modules using yarn with some packages", -// args: args{imagePath: "fixtures/test-node_modules-yarn-full.tar"}, -// want: testutility.NewSnapshot(), -// wantErr: false, -// }, -// { -// name: "scanning node_modules using pnpm with no packages", -// args: args{imagePath: "fixtures/test-node_modules-pnpm-empty.tar"}, -// want: testutility.NewSnapshot(), -// wantErr: false, -// }, -// { -// name: "scanning node_modules using pnpm with some packages", -// args: args{imagePath: "fixtures/test-node_modules-pnpm-full.tar"}, -// want: testutility.NewSnapshot(), -// wantErr: false, -// }, -// { -// name: "scanning go binaries that's been overwritten for package tracing", -// args: args{imagePath: "fixtures/test-package-tracing.tar"}, -// want: testutility.NewSnapshot(), -// wantErr: false, -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// t.Parallel() - -// // point out that we need the images to be built and saved separately -// if _, err := os.Stat(tt.args.imagePath); errors.Is(err, os.ErrNotExist) { -// t.Fatalf("%s does not exist - have you run scripts/build_test_images.sh?", tt.args.imagePath) -// } - -// got, err := image.ScanImage(&reporter.VoidReporter{}, tt.args.imagePath) -// if (err != nil) != tt.wantErr { -// t.Errorf("ScanImage() error = %v, wantErr %v", err, tt.wantErr) -// return -// } - -// for _, lockfile := range got.Lockfiles { -// for _, pkg := range lockfile.Packages { -// pkg.ImageOrigin.LayerID = "" -// } -// } - -// tt.want.MatchJSON(t, got) -// }) -// } -// } diff --git a/internal/image/layer.go b/internal/image/layer.go deleted file mode 100644 index 8539285cc3..0000000000 --- a/internal/image/layer.go +++ /dev/null @@ -1,186 +0,0 @@ -package image - -import ( - "io/fs" - "os" - "strings" - "time" - - // Note that paths accessing the disk must use filepath, but all virtual paths should use path - "path" - "path/filepath" - - "github.com/google/osv-scanner/internal/image/pathtree" -) - -type fileType int - -const ( - RegularFile fileType = iota - Dir -) - -// FileNode represents a file on a specific layer, mapping the contents to an extracted file on disk -type FileNode struct { - // TODO: Determine the performance implications of having a pointer to base image in every fileNode - rootImage *Image - fileType fileType - isWhiteout bool - originLayer *Layer - virtualPath string - permission fs.FileMode -} - -var _ fs.DirEntry = &FileNode{} - -func (f *FileNode) IsDir() bool { - return f.fileType == Dir -} - -func (f *FileNode) Name() string { - return path.Base(f.virtualPath) -} - -func (f *FileNode) Type() fs.FileMode { - return f.permission -} - -func (f *FileNode) Info() (fs.FileInfo, error) { - return f.Stat() -} - -type FileNodeFileInfo struct { - baseFileInfo fs.FileInfo - fileNode *FileNode -} - -var _ fs.FileInfo = FileNodeFileInfo{} - -func (f FileNodeFileInfo) Name() string { - return path.Base(f.fileNode.virtualPath) -} - -func (f FileNodeFileInfo) Size() int64 { - return f.baseFileInfo.Size() -} - -func (f FileNodeFileInfo) Mode() fs.FileMode { - return f.fileNode.permission -} - -func (f FileNodeFileInfo) ModTime() time.Time { - return f.baseFileInfo.ModTime() -} - -func (f FileNodeFileInfo) IsDir() bool { - return f.fileNode.fileType == Dir -} - -func (f FileNodeFileInfo) Sys() any { - return nil -} - -// Stat returns the FileInfo structure describing file. -func (f *FileNode) Stat() (fs.FileInfo, error) { - baseFileInfo, err := os.Stat(f.absoluteDiskPath()) - if err != nil { - return nil, err - } - - return FileNodeFileInfo{ - baseFileInfo: baseFileInfo, - fileNode: f, - }, nil -} - -// Open returns a file handle for the file -func (f *FileNode) Open() (*os.File, error) { - if f.isWhiteout { - return nil, fs.ErrNotExist - } - - return os.Open(f.absoluteDiskPath()) -} - -func (f *FileNode) absoluteDiskPath() string { - return filepath.Join(f.rootImage.extractDir, f.originLayer.id, f.virtualPath) -} - -// Layer represents all the files on a layer -type Layer struct { - // id is the sha256 digest of the layer - id string - fileNodeTrie *pathtree.Node[FileNode] - rootImage *Image - // TODO: Use hashmap to speed up path lookups -} - -func (filemap Layer) Open(path string) (fs.File, error) { - node, err := filemap.getFileNode(path) - if err != nil { - return nil, err - } - - return node.Open() -} - -func (filemap Layer) Stat(path string) (fs.FileInfo, error) { - node, err := filemap.getFileNode(path) - if err != nil { - return nil, err - } - - return node.Stat() -} - -func (filemap Layer) ReadDir(path string) ([]fs.DirEntry, error) { - children := filemap.fileNodeTrie.GetChildren(path) - output := make([]fs.DirEntry, 0, len(children)) - for _, node := range children { - output = append(output, node) - } - - return output, nil -} - -var _ fs.FS = Layer{} -var _ fs.StatFS = Layer{} -var _ fs.ReadDirFS = Layer{} - -func (filemap Layer) getFileNode(nodePath string) (*FileNode, error) { - // We expect all paths queried to be absolute paths rooted at the container root - // However, scalibr uses paths without a prepending /, because the paths are relative to Root. - // Root will always be '/' for container scanning, so prepend with / if necessary. - if !strings.HasPrefix(nodePath, "/") { - nodePath = path.Join("/", nodePath) - } - - node := filemap.fileNodeTrie.Get(nodePath) - if node == nil { - return nil, fs.ErrNotExist - } - - return node, nil -} - -// AllFiles return all files that exist on the layer the FileMap is representing -func (filemap Layer) AllFiles() []*FileNode { - allFiles := []*FileNode{} - // No need to check error since we are not returning any errors - _ = filemap.fileNodeTrie.Walk(func(_ string, node *FileNode) error { - if node.fileType != RegularFile { // Only add regular files - return nil - } - - // TODO: Check if parent is an opaque whiteout - if node.isWhiteout { // Don't add whiteout files as they have been deleted - return nil - } - - allFiles = append(allFiles, node) - - return nil - }) - - return allFiles -} diff --git a/internal/image/pathtree/pathtree.go b/internal/image/pathtree/pathtree.go deleted file mode 100644 index d14666a5a1..0000000000 --- a/internal/image/pathtree/pathtree.go +++ /dev/null @@ -1,133 +0,0 @@ -// Package pathtree provides a tree structure for representing file paths. -// Each path segment is a node in the tree, enabling efficient storage -// and retrieval for building virtual file systems. -package pathtree - -import ( - "errors" - "fmt" - "strings" -) - -const divider string = "/" - -var ErrNodeAlreadyExists = errors.New("node already exists") - -// Root node represents the root directory / -type Node[V any] struct { - value *V - children map[string]*Node[V] -} - -func NewNode[V any]() *Node[V] { - return &Node[V]{ - children: make(map[string]*Node[V]), - } -} - -// Insert inserts a value into the tree at the given path. -// If a node already exists at the given path, an error is returned. -// -// If a file is inserted without also inserting the parent directory -// the parent directory entry will have a nil value. -func (node *Node[V]) Insert(path string, value *V) error { - path, err := cleanPath(path) - if err != nil { - return fmt.Errorf("Insert() error: %w", err) - } - - cursor := node - for _, segment := range strings.Split(path, divider) { - next, ok := cursor.children[segment] - // Create the segment if it doesn't exist - if !ok { - next = &Node[V]{ - value: nil, - children: make(map[string]*Node[V]), - } - cursor.children[segment] = next - } - cursor = next - } - - if cursor.value != nil { - return fmt.Errorf("%w: %v", ErrNodeAlreadyExists, divider+path) - } - - cursor.value = value - - return nil -} - -// Get retrieves the value at the given path. -// If no node exists at the given path, nil is returned. -func (node *Node[V]) Get(path string) *V { - path, _ = cleanPath(path) - - cursor := node - for _, segment := range strings.Split(path, divider) { - next, ok := cursor.children[segment] - if !ok { - return nil - } - cursor = next - } - - return cursor.value -} - -// Get retrieves all the direct children of this given path -func (node *Node[V]) GetChildren(path string) []*V { - path, _ = cleanPath(path) - - cursor := node - for _, segment := range strings.Split(path, divider) { - next, ok := cursor.children[segment] - if !ok { - return nil - } - cursor = next - } - - var children = make([]*V, 0, len(cursor.children)) - for _, child := range cursor.children { - // Some entries could be nil if a file is inserted without inserting the - // parent directories. - if child != nil { - children = append(children, child.value) - } - } - - return children -} - -// cleanPath returns a path for use in the tree -// additionally an error is returned if path is not formatted as expected -func cleanPath(inputPath string) (string, error) { - path, found := strings.CutPrefix(inputPath, divider) - if !found { - return "", fmt.Errorf("path %q is not an absolute path", inputPath) - } - path = strings.TrimSuffix(path, "/") - - return path, nil -} - -// Walk walks through all elements of this tree depths first, calling fn at every node -func (node *Node[V]) Walk(fn func(string, *V) error) error { - return node.walk("/", fn) -} - -func (node *Node[V]) walk(path string, fn func(string, *V) error) error { - for key, node := range node.children { - if err := fn(key, node.value); err != nil { - return err - } - err := node.walk(path+divider+key, fn) - if err != nil { - return err - } - } - - return nil -} diff --git a/internal/image/pathtree/pathtree_test.go b/internal/image/pathtree/pathtree_test.go deleted file mode 100644 index 556c97545a..0000000000 --- a/internal/image/pathtree/pathtree_test.go +++ /dev/null @@ -1,264 +0,0 @@ -package pathtree_test - -import ( - "strings" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/google/osv-scanner/internal/image/pathtree" -) - -type testVal struct { - string -} - -func assertNoError(t *testing.T, err error) { - t.Helper() - - if err != nil { - t.Errorf("%v", err) - } -} - -func testTree(t *testing.T) *pathtree.Node[testVal] { - t.Helper() - - tree := pathtree.NewNode[testVal]() - assertNoError(t, tree.Insert("/a", &testVal{"value1"})) - assertNoError(t, tree.Insert("/a/b", &testVal{"value2"})) - assertNoError(t, tree.Insert("/a/b/c", &testVal{"value3"})) - assertNoError(t, tree.Insert("/a/b/d", &testVal{"value4"})) - assertNoError(t, tree.Insert("/a/e", &testVal{"value5"})) - assertNoError(t, tree.Insert("/a/e/f", &testVal{"value6"})) - assertNoError(t, tree.Insert("/a/b/d/f", &testVal{"value7"})) - assertNoError(t, tree.Insert("/a/g", &testVal{"value8"})) - - return tree -} - -func TestNode_Insert_Error(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - tree *pathtree.Node[testVal] - key string - val *testVal - }{ - { - name: "duplicate node", - tree: func() *pathtree.Node[testVal] { - tree := pathtree.NewNode[testVal]() - _ = tree.Insert("/a", &testVal{"value1"}) - - return tree - }(), - key: "/a", - val: &testVal{"value2"}, - }, - { - name: "duplicate node in subtree", - tree: func() *pathtree.Node[testVal] { - tree := pathtree.NewNode[testVal]() - _ = tree.Insert("/a", &testVal{"value1"}) - _ = tree.Insert("/a/b", &testVal{"value2"}) - - return tree - }(), - key: "/a/b", - val: &testVal{"value3"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - err := tt.tree.Insert(tt.key, tt.val) - if err == nil { - t.Errorf("Node.Insert() expected error, got nil") - } - }) - } -} - -func TestNode_Get(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - tree *pathtree.Node[testVal] - key string - want *testVal - }{ - { - name: "empty tree", - tree: pathtree.NewNode[testVal](), - key: "/a", - want: nil, - }, - { - name: "single node", - tree: func() *pathtree.Node[testVal] { - tree := pathtree.NewNode[testVal]() - _ = tree.Insert("/a", &testVal{"value"}) - - return tree - }(), - key: "/a", - want: &testVal{"value"}, - }, - { - name: "non-existent node in single node tree", - tree: func() *pathtree.Node[testVal] { - tree := pathtree.NewNode[testVal]() - _ = tree.Insert("/a", &testVal{"value"}) - - return tree - }(), - key: "/b", - want: nil, - }, - { - name: "multiple nodes", - tree: testTree(t), - key: "/a/b/c", - want: &testVal{"value3"}, - }, - { - name: "non-existent node", - tree: testTree(t), - key: "/a/b/g", - want: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - got := tt.tree.Get(tt.key) - if diff := cmp.Diff(tt.want, got, cmp.AllowUnexported(testVal{})); diff != "" { - t.Errorf("Node.Get() (-want +got): %v", diff) - } - }) - } -} - -func TestNode_GetChildren(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - tree *pathtree.Node[testVal] - key string - want []*testVal - }{ - { - name: "empty tree", - tree: pathtree.NewNode[testVal](), - key: "/a", - want: nil, - }, - { - name: "single node no children", - tree: func() *pathtree.Node[testVal] { - tree := pathtree.NewNode[testVal]() - _ = tree.Insert("/a", &testVal{"value"}) - - return tree - }(), - key: "/a", - want: []*testVal{}, - }, - { - name: "multiple nodes with children", - tree: testTree(t), - key: "/a/b", - want: []*testVal{ - {"value3"}, - {"value4"}, - }, - }, - { - name: "non-existent node", - tree: testTree(t), - key: "/a/b/g", - want: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - got := tt.tree.GetChildren(tt.key) - if diff := cmp.Diff( - tt.want, - got, - cmp.AllowUnexported(testVal{}), - cmpopts.SortSlices(func(a, b *testVal) bool { - return strings.Compare(a.string, b.string) < 0 - })); diff != "" { - t.Errorf("Node.GetChildren() (-want +got): %v", diff) - } - }) - } -} - -func TestNode_Walk(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - tree *pathtree.Node[testVal] - want []string - }{ - { - name: "empty tree", - tree: pathtree.NewNode[testVal](), - want: []string{}, - }, - { - name: "single node", - tree: func() *pathtree.Node[testVal] { - tree := pathtree.NewNode[testVal]() - _ = tree.Insert("/a", &testVal{"value"}) - - return tree - }(), - want: []string{"value"}, - }, - { - name: "multiple nodes", - tree: testTree(t), - want: []string{ - "value1", - "value2", - "value3", - "value4", - "value5", - "value6", - "value7", - "value8", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - got := []string{} - err := tt.tree.Walk(func(_ string, node *testVal) error { - got = append(got, node.string) - return nil - }) - if err != nil { - t.Errorf("Node.Walk() error = %v", err) - } - if diff := cmp.Diff(tt.want, got, cmpopts.SortSlices(func(a, b string) bool { - return strings.Compare(a, b) < 0 - })); diff != "" { - t.Errorf("Node.Walk() (-want +got): %v", diff) - } - }) - } -} diff --git a/internal/image/scan.go b/internal/image/scan.go deleted file mode 100644 index cc98f5bbc7..0000000000 --- a/internal/image/scan.go +++ /dev/null @@ -1,256 +0,0 @@ -package image - -import ( - "cmp" - "errors" - "fmt" - "io/fs" - "log" - "path" - "slices" - "strings" - - "github.com/google/osv-scalibr/extractor" - "github.com/google/osv-scalibr/extractor/filesystem/os/dpkg" - "github.com/google/osv-scanner/internal/scalibrextract" - "github.com/google/osv-scanner/pkg/lockfile" - "github.com/google/osv-scanner/pkg/models" - "github.com/google/osv-scanner/pkg/reporter" - "golang.org/x/exp/maps" -) - -// ScanImage scans an exported docker image .tar file -func ScanImage(r reporter.Reporter, imagePath string) (ScanResults, error) { - img, err := LoadImage(imagePath) - if err != nil { - // Ignore errors on cleanup since the folder might not have been created anyway. - _ = img.Cleanup() - return ScanResults{}, fmt.Errorf("failed to load image %s: %w", imagePath, err) - } - - allFiles := img.LastLayer().AllFiles() - - scanResults := ScanResults{ - ImagePath: imagePath, - } - - inventories := []*extractor.Inventory{} - - for _, file := range allFiles { - if file.fileType != RegularFile { - continue - } - - // TODO: Currently osv-scalibr does not correctly annotate OS packages - // causing artifact extractors to double extract elements here. - // So let's skip all these directories for now. - // See (b/364536788) - // - // https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard - // > Secondary hierarchy for read-only user data; contains the majority of (multi-)user utilities and applications. - // > Should be shareable and read-only. - // - if strings.HasPrefix(file.virtualPath, "/usr/") { - continue - } - - extractedInventories, err := extractArtifactDeps(file.virtualPath, img.LastLayer()) - if err != nil { - if !errors.Is(err, scalibrextract.ErrExtractorNotFound) { - r.Errorf("Attempted to extract lockfile but failed: %s - %v\n", file.virtualPath, err) - } - - continue - } - inventories = append(inventories, extractedInventories...) - } - - // TODO: Remove the lockfile.Lockfile conversion - // Temporarily convert back to lockfile.Lockfiles to minimize snapshot changes - // This is done to verify the scanning behavior have not changed with this refactor - // and to minimize changes in the initial PR. - lockfiles := map[string]lockfile.Lockfile{} - for _, i := range inventories { - if len(i.Annotations) > 1 { - log.Printf("%v", i.Annotations) - } - lf, exists := lockfiles[path.Join("/", i.Locations[0])] - if !exists { - lf = lockfile.Lockfile{ - FilePath: path.Join("/", i.Locations[0]), - ParsedAs: i.Extractor.Name(), - } - } - - name := i.Name - version := i.Version - - // Debian packages may have a different source name than their package name. - // OSV.dev matches vulnerabilities by source name. - // Convert the given package information to its source information if it is specified. - if metadata, ok := i.Metadata.(*dpkg.Metadata); ok { - if metadata.SourceName != "" { - name = metadata.SourceName - } - if metadata.SourceVersion != "" { - version = metadata.SourceVersion - } - } - - pkg := lockfile.PackageDetails{ - Name: name, - Version: version, - Ecosystem: lockfile.Ecosystem(i.Ecosystem()), - CompareAs: lockfile.Ecosystem(strings.Split(i.Ecosystem(), ":")[0]), - } - if i.SourceCode != nil { - pkg.Commit = i.SourceCode.Commit - } - - lf.Packages = append(lf.Packages, pkg) - - lockfiles[path.Join("/", i.Locations[0])] = lf - } - - for _, l := range lockfiles { - slices.SortFunc(l.Packages, func(a, b lockfile.PackageDetails) int { - return cmp.Or( - strings.Compare(a.Name, b.Name), - strings.Compare(a.Version, b.Version), - ) - }) - } - - scanResults.Lockfiles = maps.Values(lockfiles) - slices.SortFunc(scanResults.Lockfiles, func(a, b lockfile.Lockfile) int { - return strings.Compare(a.FilePath, b.FilePath) - }) - - traceOrigin(img, &scanResults) - - // TODO: Reenable this sort when removing lockfile.Lockfile - // Sort to have deterministic output, and to match behavior of lockfile.extractDeps - // slices.SortFunc(scanResults.Inventories, func(a, b *extractor.Inventory) int { - // // TODO: Should we consider errors here? - // aPURL, _ := a.Extractor.ToPURL(a) - // bPURL, _ := b.Extractor.ToPURL(b) - - // return strings.Compare(aPURL.ToString(), bPURL.ToString()) - // }) - - err = img.Cleanup() - if err != nil { - err = fmt.Errorf("failed to cleanup: %w", img.Cleanup()) - } - - return scanResults, err -} - -// traceOrigin fills out the originLayerID for each package in ScanResults -func traceOrigin(img *Image, scannedLockfiles *ScanResults) { - // Trace package origins - for _, file := range scannedLockfiles.Lockfiles { - // Defined locally as this is the only place this is used. - type PDKey struct { - Name string - Version string - Commit string - Ecosystem string - } - - // TODO: Remove this function after fully migrating to extractor.Inventory - makePDKey := func(pd lockfile.PackageDetails) PDKey { - return PDKey{ - Name: pd.Name, - Version: pd.Version, - Commit: pd.Commit, - Ecosystem: string(pd.Ecosystem), - } - } - - makePDKey2 := func(pd *extractor.Inventory) PDKey { - var commit string - if pd.SourceCode != nil { - commit = pd.SourceCode.Commit - } - - return PDKey{ - Name: pd.Name, - Version: pd.Version, - Commit: commit, - Ecosystem: pd.Ecosystem(), - } - } - - // First get the latest file node - lastFileNode, err := img.layers[len(img.layers)-1].getFileNode(file.FilePath) - if err != nil { - log.Panicf("did not expect to fail getting file node we just scanned: %v", err) - } - // Get the layer index this file belongs to (the last layer it was changed on) - layerIdx := img.layerIDToIndex[lastFileNode.originLayer.id] - var prevLayerIdx int - - sourceLayerIdx := map[PDKey]int{} - for _, pkg := range file.Packages { - // Start with originating from the latest layer - // Then push back as we iterate through layers - sourceLayerIdx[makePDKey(pkg)] = layerIdx - } - - for { - // Scan the lockfile again every time it was changed - if layerIdx == 0 { - // This layer is the base layer, we cannot go further back - // All entries in sourceLayerIdx would have been set in the previous loop, or just above the loop - // So we can immediately exit here - break - } - - // Look at the layer before the current layer - oldFileNode, err := img.layers[layerIdx-1].getFileNode(file.FilePath) - if errors.Is(err, fs.ErrNotExist) || (err == nil && oldFileNode.isWhiteout) { - // Did not exist in the layer before - - // All entries in sourceLayerIdx would have been set in the previous loop, or just above the loop - // So we can immediately exit here - break - } - - if err != nil { - log.Panicf("did not expect a different error [%v] when getting file node", err) - } - - prevLayerIdx = layerIdx - // Set the layerIdx to the new file node layer - layerIdx = img.layerIDToIndex[oldFileNode.originLayer.id] - - oldDeps, err := extractArtifactDeps(file.FilePath, oldFileNode.originLayer) - if err != nil { - // Failed to parse an older version of file in image - // Behave as if the file does not exist - break - } - - // For each package in the old version, check if it existed in the newer layer, if so, the origin must be this layer or earlier. - for _, pkg := range oldDeps { - key := makePDKey2(pkg) - if val, ok := sourceLayerIdx[key]; ok && val == prevLayerIdx { - sourceLayerIdx[key] = layerIdx - } - } - } - - // Finally save the package IDs back into the ScanResults - for i, pkg := range file.Packages { - layerID := img.layers[sourceLayerIdx[makePDKey(pkg)]].id - // Ignore error as we can't do much about it - originCommand, _ := img.layerIDToCommand(layerID) - file.Packages[i].ImageOrigin = &models.ImageOriginDetails{ - LayerID: layerID, - OriginCommand: originCommand, - InBaseImage: img.layerIDToIndex[layerID] <= img.baseImageIndex, - } - } - } -} diff --git a/internal/image/testmain_test.go b/internal/image/testmain_test.go deleted file mode 100644 index 1f5d0323cf..0000000000 --- a/internal/image/testmain_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package image_test - -import ( - "os" - "testing" - - "github.com/google/osv-scanner/internal/testutility" -) - -func TestMain(m *testing.M) { - code := m.Run() - - testutility.CleanSnapshots(m) - - os.Exit(code) -} diff --git a/internal/imodels/imodels.go b/internal/imodels/imodels.go index bcca6c0b24..9ddb046fc7 100644 --- a/internal/imodels/imodels.go +++ b/internal/imodels/imodels.go @@ -59,6 +59,12 @@ func (pkg *PackageInfo) Name() string { } } + if metadata, ok := pkg.Inventory.Metadata.(*apk.Metadata); ok { + if metadata.OriginName != "" { + return metadata.OriginName + } + } + return pkg.Inventory.Name } @@ -164,22 +170,15 @@ func FromInventory(inventory *extractor.Inventory) PackageInfo { type PackageScanResult struct { PackageInfo PackageInfo // TODO: Use osvschema.Vulnerability instead - Vulnerabilities []*models.Vulnerability - Licenses []models.License - ImageOriginLayerID string + Vulnerabilities []*models.Vulnerability + Licenses []models.License + LayerDetails *extractor.LayerDetails // TODO(v2): // SourceAnalysis *SourceAnalysis // Any additional scan enrichment steps } -type ImageMetadata struct { - // TODO: - // OS - // BaseImage - // LayerMetadata []LayerMetadata -} - // SourceType categorizes packages based on the extractor that extracted // the "source", for use in the output. type SourceType int diff --git a/internal/imodels/results/scanresults.go b/internal/imodels/results/scanresults.go index eee2711e34..86f5a81318 100644 --- a/internal/imodels/results/scanresults.go +++ b/internal/imodels/results/scanresults.go @@ -3,6 +3,7 @@ package results import ( "github.com/google/osv-scanner/internal/config" "github.com/google/osv-scanner/internal/imodels" + "github.com/google/osv-scanner/pkg/models" ) // ScanResults represents the complete results of a scan. @@ -19,5 +20,5 @@ type ScanResults struct { ConfigManager config.Manager // For container scanning, metadata including layer information - ImageMetadata *imodels.ImageMetadata + ImageMetadata *models.ImageMetadata } diff --git a/internal/output/__snapshots__/machinejson_test.snap b/internal/output/__snapshots__/machinejson_test.snap index e07d8a010f..9c8f3a0082 100755 --- a/internal/output/__snapshots__/machinejson_test.snap +++ b/internal/output/__snapshots__/machinejson_test.snap @@ -877,7 +877,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false @@ -926,7 +926,7 @@ "OSV-2" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-2": { "called": true, "unimportant": false @@ -995,7 +995,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false @@ -1225,7 +1225,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": true, "unimportant": false @@ -1290,7 +1290,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false @@ -2276,7 +2276,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false @@ -2289,7 +2289,7 @@ "OSV-5" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-5": { "called": true, "unimportant": false @@ -2407,7 +2407,7 @@ "OSV-3" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-3": { "called": true, "unimportant": false @@ -2580,7 +2580,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": true, "unimportant": false @@ -2593,7 +2593,7 @@ "GHSA-123" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "GHSA-123": { "called": false, "unimportant": false @@ -2650,7 +2650,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": true, "unimportant": false @@ -2707,7 +2707,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false @@ -2887,7 +2887,7 @@ "OSV-1", "GHSA-123" ], - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false diff --git a/internal/output/output_result.go b/internal/output/output_result.go index 252417700a..95c8447bae 100644 --- a/internal/output/output_result.go +++ b/internal/output/output_result.go @@ -156,7 +156,7 @@ func BuildResults(vulnResult *models.VulnerabilityResults) Result { // which are already covered by OS-specific vulnerabilities. // This filtering should be handled by the container scanning process. // TODO(gongh@): Revisit this once container scanning can distinguish these cases. - if strings.Contains(packageSource.Source.String(), "/usr/lib/") { + if strings.HasPrefix(packageSource.Source.Path, "usr/lib/") { continue } @@ -289,10 +289,10 @@ func processPackage(vulnPkg models.PackageVulns) PackageResult { if vulnPkg.Package.ImageOrigin != nil { packageLayerDetail := PackageLayerDetail{ - LayerID: vulnPkg.Package.ImageOrigin.LayerID, - InBaseImage: vulnPkg.Package.ImageOrigin.InBaseImage, + LayerID: "", + InBaseImage: true, } - packageLayerDetail.LayerCommand, packageLayerDetail.LayerCommandDetailed = formatLayerCommand(vulnPkg.Package.ImageOrigin.OriginCommand) + packageLayerDetail.LayerCommand, packageLayerDetail.LayerCommandDetailed = formatLayerCommand("COMMAND") packageResult.LayerDetail = packageLayerDetail } diff --git a/internal/sourceanalysis/__snapshots__/go_test.snap b/internal/sourceanalysis/__snapshots__/go_test.snap index d9bf45c4d7..16866191d3 100755 --- a/internal/sourceanalysis/__snapshots__/go_test.snap +++ b/internal/sourceanalysis/__snapshots__/go_test.snap @@ -158,7 +158,7 @@ "GO-2021-0053" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "GO-2021-0053": { "called": false, "unimportant": false @@ -312,7 +312,7 @@ "GO-2023-1558" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "GO-2023-1558": { "called": true, "unimportant": false @@ -467,7 +467,7 @@ "GO-2023-1572" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "GO-2023-1572": { "called": false, "unimportant": false diff --git a/pkg/models/image.go b/pkg/models/image.go index d3bc134c77..381a6b51bd 100644 --- a/pkg/models/image.go +++ b/pkg/models/image.go @@ -1,7 +1,26 @@ package models +import "github.com/opencontainers/go-digest" + type ImageOriginDetails struct { - LayerID string `json:"layerID"` - OriginCommand string `json:"originCommand"` - InBaseImage bool `json:"inBaseImage"` + Index int `json:"index"` +} + +type ImageMetadata struct { + OS string `json:"os"` + LayerMetadata []LayerMetadata `json:"layer_metadata"` + BaseImages [][]BaseImageDetails `json:"base_images"` +} + +type BaseImageDetails struct { + Name string `json:"name"` + // TODO: Not yet filled in + Tags []string `json:"tags"` +} + +type LayerMetadata struct { + DiffID digest.Digest `json:"diff_id"` + Command string `json:"command"` + IsEmpty bool `json:"is_empty"` + BaseImageIndex int `json:"base_image_index"` } diff --git a/pkg/models/results.go b/pkg/models/results.go index 04a297d8bf..00273198d1 100644 --- a/pkg/models/results.go +++ b/pkg/models/results.go @@ -9,6 +9,7 @@ import ( type VulnerabilityResults struct { Results []PackageSource `json:"results"` ExperimentalAnalysisConfig ExperimentalAnalysisConfig `json:"experimental_config"` + ImageMetadata *ImageMetadata `json:"image_metadata,omitempty"` } // ExperimentalAnalysisConfig is an experimental type intended to contain the @@ -110,7 +111,7 @@ type GroupInfo struct { // Aliases include all aliases and IDs Aliases []string `json:"aliases"` // Map of Vulnerability IDs to AnalysisInfo - ExperimentalAnalysis map[string]AnalysisInfo `json:"experimentalAnalysis,omitempty"` + ExperimentalAnalysis map[string]AnalysisInfo `json:"experimental_analysis,omitempty"` MaxSeverity string `json:"max_severity"` } @@ -188,9 +189,10 @@ type AnalysisInfo struct { // Specific package information type PackageInfo struct { - Name string `json:"name"` - Version string `json:"version"` - Ecosystem string `json:"ecosystem"` - Commit string `json:"commit,omitempty"` - ImageOrigin *ImageOriginDetails `json:"imageOrigin,omitempty"` + Name string `json:"name"` + OSPackageName string `json:"os_package_name,omitempty"` + Version string `json:"version"` + Ecosystem string `json:"ecosystem"` + Commit string `json:"commit,omitempty"` + ImageOrigin *ImageOriginDetails `json:"image_origin_details,omitempty"` } diff --git a/pkg/osvscanner/internal/imagehelpers/imagehelpers.go b/pkg/osvscanner/internal/imagehelpers/imagehelpers.go new file mode 100644 index 0000000000..7a989294fa --- /dev/null +++ b/pkg/osvscanner/internal/imagehelpers/imagehelpers.go @@ -0,0 +1,137 @@ +package imagehelpers + +import ( + "bufio" + "context" + "errors" + "fmt" + "os" + "os/exec" + "strings" + + "github.com/google/osv-scalibr/artifact/image/layerscanning/image" + "github.com/google/osv-scalibr/extractor/filesystem/os/osrelease" + "github.com/google/osv-scanner/internal/clients/clientinterfaces" + "github.com/google/osv-scanner/pkg/models" + "github.com/google/osv-scanner/pkg/reporter" +) + +func BuildImageMetadata(img *image.Image, baseImageMatcher clientinterfaces.BaseImageMatcher) (*models.ImageMetadata, error) { + chainLayers, err := img.ChainLayers() + if err != nil { + // This is very unlikely, as if this would error we would have failed the initial scan + return nil, err + } + m, err := osrelease.GetOSRelease(chainLayers[len(chainLayers)-1].FS()) + OS := "Unknown" + if err == nil { + OS = m["PRETTY_NAME"] + } + + layerMetadata := []models.LayerMetadata{} + for _, cl := range chainLayers { + layerMetadata = append(layerMetadata, models.LayerMetadata{ + DiffID: cl.Layer().DiffID(), + Command: cl.Layer().Command(), + IsEmpty: cl.Layer().IsEmpty(), + }) + } + + var baseImages [][]models.BaseImageDetails + + if baseImageMatcher != nil { + baseImages, err = baseImageMatcher.MatchBaseImages(context.Background(), layerMetadata) + if err != nil { + return nil, fmt.Errorf("failed to query for container base images: %w", err) + } + } else { + baseImages = [][]models.BaseImageDetails{ + // The base image at index 0 is a placeholder representing your image, so always empty + // This is the case even if your image is a base image, in that case no layers point to index 0 + {}, + } + } + + imgMetadata := models.ImageMetadata{ + OS: OS, + LayerMetadata: layerMetadata, + BaseImages: baseImages, + } + + return &imgMetadata, nil +} + +func ExportDockerImage(r reporter.Reporter, dockerImageName string) (string, error) { + // Skip saving if the file is already a tar archive. + if strings.Contains(dockerImageName, ".tar") { + if _, err := os.Stat(dockerImageName); err == nil { + return dockerImageName, nil + } + } + + tempImageFile, err := os.CreateTemp("", "docker-image-*.tar") + if err != nil { + r.Errorf("Failed to create temporary file: %s\n", err) + return "", err + } + + err = tempImageFile.Close() + if err != nil { + return "", err + } + + // Check if image exists locally, if not, pull from the cloud. + r.Infof("Checking if docker image (%q) exists locally...\n", dockerImageName) + cmd := exec.Command("docker", "images", "-q", dockerImageName) + output, err := cmd.Output() + if err != nil || string(output) == "" { + r.Infof("Image not found locally, pulling docker image (%q)...\n", dockerImageName) + err = runCommandLogError(r, "docker", "pull", "-q", dockerImageName) + if err != nil { + return "", fmt.Errorf("failed to pull container image: %w", err) + } + } + + r.Infof("Saving docker image (%q) to temporary file...\n", dockerImageName) + err = runCommandLogError(r, "docker", "save", "-o", tempImageFile.Name(), dockerImageName) + if err != nil { + return "", err + } + + return tempImageFile.Name(), nil +} + +func runCommandLogError(r reporter.Reporter, name string, args ...string) error { + cmd := exec.Command(name, args...) + + // Get stderr for debugging when docker fails + stderr, err := cmd.StderrPipe() + if err != nil { + r.Errorf("Failed to get stderr: %s\n", err) + return err + } + + err = cmd.Start() + if err != nil { + r.Errorf("Failed to run docker command (%q): %s\n", cmd.String(), err) + return err + } + // This has to be captured before cmd.Wait() is called, as cmd.Wait() closes the stderr pipe. + var stderrLines []string + scanner := bufio.NewScanner(stderr) + for scanner.Scan() { + stderrLines = append(stderrLines, scanner.Text()) + } + + err = cmd.Wait() + if err != nil { + r.Errorf("Docker command exited with code (%q): %d\nSTDERR:\n", cmd.String(), cmd.ProcessState.ExitCode()) + for _, line := range stderrLines { + r.Errorf("> %s\n", line) + } + + return errors.New("failed to run docker command") + } + + return nil +} diff --git a/pkg/osvscanner/internal/scanners/artifacts.go b/pkg/osvscanner/internal/scanners/artifacts.go new file mode 100644 index 0000000000..c669121ddd --- /dev/null +++ b/pkg/osvscanner/internal/scanners/artifacts.go @@ -0,0 +1 @@ +package scanners diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index deffa61b35..8ffbcfc82e 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -4,9 +4,14 @@ import ( "context" "errors" "fmt" + "net/http" + "os" "time" + scalibr "github.com/google/osv-scalibr" + "github.com/google/osv-scalibr/artifact/image/layerscanning/image" "github.com/google/osv-scalibr/extractor" + "github.com/google/osv-scanner/internal/clients/clientimpl/baseimagematcher" "github.com/google/osv-scanner/internal/clients/clientimpl/licensematcher" "github.com/google/osv-scanner/internal/clients/clientimpl/localmatcher" "github.com/google/osv-scanner/internal/clients/clientimpl/osvmatcher" @@ -21,6 +26,8 @@ import ( "github.com/google/osv-scanner/internal/resolution/datasource" "github.com/google/osv-scanner/internal/version" "github.com/google/osv-scanner/pkg/models" + "github.com/google/osv-scanner/pkg/osvscanner/internal/imagehelpers" + "github.com/google/osv-scanner/pkg/osvscanner/internal/scanners" "github.com/google/osv-scanner/pkg/reporter" "github.com/ossf/osv-schema/bindings/go/osvschema" ) @@ -33,7 +40,7 @@ type ScannerActions struct { Recursive bool SkipGit bool NoIgnore bool - DockerImageName string + Image string ConfigOverridePath string CallAnalysisStates map[string]bool @@ -60,8 +67,9 @@ type TransitiveScanningActions struct { type ExternalAccessors struct { // Matchers - VulnMatcher clientinterfaces.VulnerabilityMatcher - LicenseMatcher clientinterfaces.LicenseMatcher + VulnMatcher clientinterfaces.VulnerabilityMatcher + LicenseMatcher clientinterfaces.LicenseMatcher + BaseImageMatcher clientinterfaces.BaseImageMatcher // Required for pomxmlnet Extractor MavenRegistryAPIClient *datasource.MavenRegistryAPIClient @@ -82,6 +90,7 @@ var ErrNoPackagesFound = errors.New("no packages found in scan") var ErrVulnerabilitiesFound = errors.New("vulnerabilities found") // ErrAPIFailed describes errors related to querying API endpoints. +// TODO(v2): Actually use this error var ErrAPIFailed = errors.New("API query failed") func initializeExternalAccessors(r reporter.Reporter, actions ScannerActions) (ExternalAccessors, error) { @@ -122,6 +131,14 @@ func initializeExternalAccessors(r reporter.Reporter, actions ScannerActions) (E } } + // --- Base Image Matcher --- + if actions.Image != "" || actions.ScanOCIImage != "" { + externalAccessors.BaseImageMatcher = &baseimagematcher.DepsDevBaseImageMatcher{ + Client: *http.DefaultClient, + Reporter: r, + } + } + // --- OSV.dev Client --- // We create a separate client from VulnMatcher to keep things clean. externalAccessors.OSVDevClient = osvdev.DefaultClient() @@ -238,11 +255,118 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe ) } - if len(results.Results) > 0 { - // Determine the correct error to return. + return results, determineReturnErr(results) +} + +func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityResults, error) { + if r == nil { + r = &reporter.VoidReporter{} + } + + scanResult := results.ScanResults{ + ConfigManager: config.Manager{ + DefaultConfig: config.Config{}, + ConfigMap: make(map[string]config.Config), + }, + } + + // --- Setup Accessors/Clients --- + accessors, err := initializeExternalAccessors(r, actions) + if err != nil { + return models.VulnerabilityResults{}, fmt.Errorf("failed to initialize accessors: %w", err) + } + + // --- Initialize Image To Scan --- + var img *image.Image + if actions.ScanOCIImage != "" { + img, err = image.FromTarball(actions.ScanOCIImage, image.DefaultConfig()) + r.Infof("Scanning image %q\n", actions.ScanOCIImage) + } else if actions.Image != "" { + path, exportErr := imagehelpers.ExportDockerImage(r, actions.Image) + if exportErr != nil { + return models.VulnerabilityResults{}, exportErr + } + + // If Image is a local tar file, then path == Image, and we shouldn't remove it + if path != actions.Image { + defer os.Remove(path) + } + img, err = image.FromTarball(path, image.DefaultConfig()) + r.Infof("Scanning image %q\n", actions.Image) + } + if err != nil { + return models.VulnerabilityResults{}, err + } + defer func() { + err := img.CleanUp() + if err != nil { + r.Errorf("Failed to clean up image: %s\n", err) + } + }() + + // --- Do Scalibr Scan --- + scanner := scalibr.New() + scalibrSR, err := scanner.ScanContainer(context.Background(), img, &scalibr.ScanConfig{ + FilesystemExtractors: scanners.BuildArtifactExtractors(), + }) + if err != nil { + return models.VulnerabilityResults{}, fmt.Errorf("failed to scan container image: %w", err) + } + + if len(scalibrSR.Inventories) == 0 { + return models.VulnerabilityResults{}, ErrNoPackagesFound + } - // TODO(v2): in the next breaking release of osv-scanner, consider - // returning a ScanError instead of an error. + // --- Save Scalibr Scan Results --- + scanResult.PackageScanResults = make([]imodels.PackageScanResult, len(scalibrSR.Inventories)) + for i, inv := range scalibrSR.Inventories { + scanResult.PackageScanResults[i].PackageInfo = imodels.FromInventory(inv) + scanResult.PackageScanResults[i].LayerDetails = inv.LayerDetails + } + + // --- Fill Image Metadata --- + scanResult.ImageMetadata, err = imagehelpers.BuildImageMetadata(img, accessors.BaseImageMatcher) + if err != nil { // Not getting image metadata is not fatal + r.Errorf("Failed to fully get image metadata: %v", err) + } + + // ----- Filtering ----- + filterUnscannablePackages(r, &scanResult) + + // --- Make Vulnerability Requests --- + if accessors.VulnMatcher != nil { + err = makeVulnRequestWithMatcher(r, scanResult.PackageScanResults, accessors.VulnMatcher) + if err != nil { + return models.VulnerabilityResults{}, err + } + } + + // --- Make License Requests --- + if accessors.LicenseMatcher != nil { + err = accessors.LicenseMatcher.MatchLicenses(context.Background(), scanResult.PackageScanResults) + if err != nil { + return models.VulnerabilityResults{}, err + } + } + + results := buildVulnerabilityResults(r, actions, &scanResult) + + filtered := filterResults(r, &results, &scanResult.ConfigManager, actions.ShowAllPackages) + if filtered > 0 { + r.Infof( + "Filtered %d %s from output\n", + filtered, + output.Form(filtered, "vulnerability", "vulnerabilities"), + ) + } + + return results, determineReturnErr(results) +} + +// determineReturnErr determines whether we found a "vulnerability" or not, +// and therefore whether we should return a ErrVulnerabilityFound error. +func determineReturnErr(results models.VulnerabilityResults) error { + if len(results.Results) > 0 { var vuln bool onlyUncalledVuln := true var licenseViolation bool @@ -258,17 +382,16 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe } } onlyUncalledVuln = onlyUncalledVuln && vuln - licenseViolation = licenseViolation && len(actions.ScanLicensesAllowlist) > 0 if (!vuln || onlyUncalledVuln) && !licenseViolation { // There is no error. - return results, nil + return nil } - return results, ErrVulnerabilitiesFound + return ErrVulnerabilitiesFound } - return results, nil + return nil } // TODO(V2): Add context diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go index 6005ec601a..9ab7deb20d 100644 --- a/pkg/osvscanner/vulnerability_result.go +++ b/pkg/osvscanner/vulnerability_result.go @@ -25,7 +25,8 @@ func buildVulnerabilityResults( scanResults *results.ScanResults, ) models.VulnerabilityResults { results := models.VulnerabilityResults{ - Results: []models.PackageSource{}, + Results: []models.PackageSource{}, + ImageMetadata: scanResults.ImageMetadata, } groupedBySource := map[models.SourceInfo][]models.PackageVulns{} for _, psr := range scanResults.PackageScanResults { @@ -40,13 +41,18 @@ func buildVulnerabilityResults( if p.Version() != "" && !p.Ecosystem().IsEmpty() { pkg.Package = models.PackageInfo{ - Name: p.Name(), - Version: p.Version(), - Ecosystem: p.Ecosystem().String(), - // ImageOrigin: p.ImageOrigin, + Name: p.Name(), + Version: p.Version(), + Ecosystem: p.Ecosystem().String(), + OSPackageName: p.OSPackageName(), } } + if psr.LayerDetails != nil { + pkg.Package.ImageOrigin = &models.ImageOriginDetails{ + Index: psr.LayerDetails.Index, + } + } pkg.DepGroups = p.DepGroups() configToUse := scanResults.ConfigManager.Get(r, p.Location())