From 171c3be54e9a5dbdc91838720af911b72a3c1678 Mon Sep 17 00:00:00 2001 From: Rex P Date: Tue, 31 Dec 2024 14:11:53 +1100 Subject: [PATCH 01/45] Use scalibr fully to do container scanning --- cmd/osv-scanner/scan/main.go | 12 +++- pkg/osvscanner/osvscanner.go | 107 +++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/cmd/osv-scanner/scan/main.go b/cmd/osv-scanner/scan/main.go index bf763f3b15..da2f673dba 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,7 +283,7 @@ 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"), @@ -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") != "" { + vulnResult, err = osvscanner.DoContainerScan(scannerAction, r) + } else { + vulnResult, err = osvscanner.DoScan(scannerAction, r) + } if err != nil && !errors.Is(err, osvscanner.VulnerabilitiesFoundErr) { return r, err diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 811e63272e..42c4d381ce 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -1,14 +1,21 @@ package osvscanner import ( + "context" "errors" "fmt" + "github.com/google/osv-scalibr/artifact/image/layerscanning/image" + "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-scanner/internal/config" "github.com/google/osv-scanner/internal/depsdev" "github.com/google/osv-scanner/internal/imodels" "github.com/google/osv-scanner/internal/imodels/results" "github.com/google/osv-scanner/internal/local" + "github.com/google/osv-scanner/internal/lockfilescalibr/language/javascript/nodemodules" "github.com/google/osv-scanner/internal/output" "github.com/google/osv-scanner/internal/semantic" "github.com/google/osv-scanner/internal/version" @@ -17,6 +24,8 @@ import ( "github.com/google/osv-scanner/pkg/reporter" "github.com/ossf/osv-schema/bindings/go/osvschema" + scalibr "github.com/google/osv-scalibr" + depsdevpb "deps.dev/api/v3" ) @@ -72,6 +81,104 @@ var OnlyUncalledVulnerabilitiesFoundErr = errors.New("only uncalled vulnerabilit // ErrAPIFailed describes errors related to querying API endpoints. var ErrAPIFailed = errors.New("API query failed") +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), + }, + } + + scanner := scalibr.New() + image, err := image.FromRemoteName(actions.DockerImageName, image.DefaultConfig()) + if err != nil { + r.Errorf("Failed to pull container image %q\n", actions.DockerImageName) + return models.VulnerabilityResults{}, err + } + + defer image.CleanUp() + + scalibrSR, err := scanner.ScanContainer(context.Background(), image, &scalibr.ScanConfig{ + FilesystemExtractors: []filesystem.Extractor{ + nodemodules.Extractor{}, + apk.New(apk.DefaultConfig()), + gobinary.New(gobinary.DefaultConfig()), + // TODO: Add tests for debian containers + dpkg.New(dpkg.DefaultConfig()), + }, + }) + + if err != nil { + return models.VulnerabilityResults{}, fmt.Errorf("failed to scan container image: %w", err) + } + + scanResult.PackageScanResults = make([]imodels.PackageScanResult, len(scalibrSR.Inventories)) + for i, inv := range scalibrSR.Inventories { + scanResult.PackageScanResults[i].PackageInfo = imodels.FromInventory(inv) + } + + filterUnscannablePackages(r, &scanResult) + + err = makeRequest(r, scanResult.PackageScanResults, actions.CompareOffline, actions.DownloadDatabases, actions.LocalDBPath) + if err != nil { + return models.VulnerabilityResults{}, err + } + + if len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary { + err = makeLicensesRequests(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"), + ) + } + + if len(results.Results) > 0 { + // Determine the correct error to return. + + // TODO(v2): in the next breaking release of osv-scanner, consider + // returning a ScanError instead of an error. + var vuln bool + onlyUncalledVuln := true + var licenseViolation bool + for _, vf := range results.Flatten() { + if vf.Vulnerability.ID != "" { + vuln = true + if vf.GroupInfo.IsCalled() { + onlyUncalledVuln = false + } + } + if len(vf.LicenseViolations) > 0 { + licenseViolation = true + } + } + onlyUncalledVuln = onlyUncalledVuln && vuln + licenseViolation = licenseViolation && len(actions.ScanLicensesAllowlist) > 0 + + if (!vuln || onlyUncalledVuln) && !licenseViolation { + // There is no error. + return results, nil + } + + return results, VulnerabilitiesFoundErr + } + + return results, nil +} + // Perform osv scanner action, with optional reporter to output information func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityResults, error) { if r == nil { From 6941ee3a16c745244bd0a537f8f766d5d10e3211 Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 2 Jan 2025 11:51:49 +1100 Subject: [PATCH 02/45] Fix tests and pipe through --- cmd/osv-scanner/__snapshots__/main_test.snap | 52 +++--- cmd/osv-scanner/main_test.go | 4 - cmd/osv-scanner/scan/main.go | 2 +- go.mod | 46 ++++- go.sum | 175 ++++++++++++++++++- internal/imodels/imodels.go | 8 +- pkg/osvscanner/osvscanner.go | 94 +++++++++- pkg/osvscanner/vulnerability_result.go | 7 + 8 files changed, 336 insertions(+), 52 deletions(-) diff --git a/cmd/osv-scanner/__snapshots__/main_test.snap b/cmd/osv-scanner/__snapshots__/main_test.snap index 3743845355..32d53045c1 100755 --- a/cmd/osv-scanner/__snapshots__/main_test.snap +++ b/cmd/osv-scanner/__snapshots__/main_test.snap @@ -930,7 +930,7 @@ 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 --- @@ -943,14 +943,14 @@ 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")... Saving docker image ("alpine:3.18.9") to temporary file... -Scanning image... +Scanning image "/tmp/docker-image-2671409146.tar" No issues found --- @@ -962,7 +962,7 @@ No issues found [TestRun_Docker/Real_empty_image - 1] Pulling docker image ("hello-world")... Saving docker image ("hello-world") to temporary file... -Scanning image... +Scanning image "/tmp/docker-image-1910286536.tar" --- @@ -974,7 +974,7 @@ No package sources found, --help for usage information. [TestRun_Docker/Real_empty_image_with_tag - 1] Pulling docker image ("hello-world:linux")... Saving docker image ("hello-world:linux") to temporary file... -Scanning image... +Scanning image "/tmp/docker-image-2242457421.tar" --- @@ -2672,14 +2672,13 @@ 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 +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. 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 | +---------+-------------------+---------------+------------+ @@ -2696,24 +2695,23 @@ 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_node_modules_using_npm_with_no_packages - 1] -Scanning image ../../internal/image/fixtures/test-node_modules-npm-empty.tar +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. 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 | +---------+-------------------+---------------+------------+ @@ -2730,14 +2728,13 @@ 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 +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. 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 | +----------+-------------------+------------------+------------+ @@ -2746,8 +2743,7 @@ npm +----------+-------------------+------------------+------------+ 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 | +---------+-------------------+---------------+------------+ @@ -2764,14 +2760,13 @@ 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 +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. 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 | +---------+-------------------+---------------+------------+ @@ -2788,14 +2783,13 @@ 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 +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. 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 | +---------+-------------------+---------------+------------+ @@ -2812,14 +2806,13 @@ 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 +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. 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 | +---------+-------------------+---------------+------------+ @@ -2836,14 +2829,13 @@ 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 +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. 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 | +---------+-------------------+---------------+------------+ diff --git a/cmd/osv-scanner/main_test.go b/cmd/osv-scanner/main_test.go index 404cb88a2b..d31e2676ad 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") @@ -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 da2f673dba..00d08ac824 100644 --- a/cmd/osv-scanner/scan/main.go +++ b/cmd/osv-scanner/scan/main.go @@ -315,7 +315,7 @@ func action(context *cli.Context, stdout, stderr io.Writer) (reporter.Reporter, } var vulnResult models.VulnerabilityResults - if context.String("docker") != "" { + if context.String("docker") != "" || context.String("experimental-oci-image") != "" { vulnResult, err = osvscanner.DoContainerScan(scannerAction, r) } else { vulnResult, err = osvscanner.DoScan(scannerAction, r) diff --git a/go.mod b/go.mod index 3d96158b54..8f4a1483cb 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/go-git/go-git/v5 v5.12.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.20241210165202-6da18027fec0 + github.com/google/osv-scalibr v0.1.6-0.20241230072043-5f1a98f2c3a9 github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd github.com/jedib0t/go-pretty/v6 v6.6.0 github.com/muesli/reflow v0.3.0 @@ -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.0.0 // 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,38 @@ require ( github.com/charmbracelet/x/ansi v0.4.2 // indirect github.com/charmbracelet/x/term v0.2.0 // 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.4 // 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.2.4 // 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/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 +101,54 @@ 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.2.2 // 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.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.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.26.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-20241007155032-5fefd90f89a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // 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 ) + +replace github.com/google/osv-scalibr => github.com/another-rex/osv-scalibr v0.0.0-20241231043518-477772e495f7 diff --git a/go.sum b/go.sum index 4fb5f68ad6..169065555f 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-20241010035105-b3ba03369df1 h1:qvrLinmQrkOLmguTE9FpRfC/e2iud/eVMWigXXTdrdA= @@ -8,6 +9,11 @@ deps.dev/util/resolve v0.0.0-20241218001045-3890182485f3 h1:9mtMNCV9XDN689ukjYBS deps.dev/util/resolve v0.0.0-20241218001045-3890182485f3/go.mod h1:XXi6yRYqhtxw5DvGX/mbG6fHSLn8OgoPowNd8EAxDgk= deps.dev/util/semver v0.0.0-20241010035105-b3ba03369df1 h1:t4P0dCCNIrV84B5d7kOIAzji+HrO303Nrw9BB4ktBy0= deps.dev/util/semver v0.0.0-20241010035105-b3ba03369df1/go.mod h1:jkcH+k02gWHBiZ7G4OnUOkSZ6WDq54Pt5DrOA8FN8Uo= +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.1 h1:yffaWOZsv77oTJa/SdVZYdgAgFioCeycBUKkqS2qzQM= @@ -15,6 +21,8 @@ github.com/CycloneDX/cyclonedx-go v0.9.1/go.mod h1:NE/EWvzELOFlG6+ljX/QeMlVt9VKc 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.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= @@ -28,6 +36,8 @@ github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 h1:6CO github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/another-rex/osv-scalibr v0.0.0-20241231043518-477772e495f7 h1:f1aZ2SjZgDclNl6MKf8j3Ro75kAR76c/izpAAnpUV6Y= +github.com/another-rex/osv-scalibr v0.0.0-20241231043518-477772e495f7/go.mod h1:S8mrRjoWESAOOTq25lJqzxiKR6tbWSFYG8SVb5EFLHk= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= @@ -42,29 +52,44 @@ github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd3 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/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +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.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs= github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw= -github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= -github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= -github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY= -github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/ansi v0.4.2 h1:0JM6Aj/g/KC154/gOP4vfxun0ff6itogDYk41kof+qk= github.com/charmbracelet/x/ansi v0.4.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= 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= @@ -81,16 +106,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 v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= 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= @@ -109,24 +142,55 @@ 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.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +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/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.20241210165202-6da18027fec0 h1:6B2j21HOF1Vdei7wyEnq8EwEA7ktRoNXc8og44YX22o= -github.com/google/osv-scalibr v0.1.6-0.20241210165202-6da18027fec0/go.mod h1:fvnB14pFjAupxDoCLUgdMg2rHu6v86BgKGQHzgTFrTg= +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.0 h1:wmZVuAcEkZRT+Aq1xXpE8IGat4vE5WXOMmBpbQ github.com/jedib0t/go-pretty/v6 v6.6.0/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E= 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-20241127234932-c44c7842979f h1:F7CMsEIwWsbYgt9tNLMOnVrqqz1WmxmwpRCLqNeJ1N0= github.com/ossf/osv-schema/bindings/go v0.0.0-20241127234932-c44c7842979f/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 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -260,6 +344,8 @@ 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.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= @@ -267,22 +353,52 @@ github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRla 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.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 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-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= +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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -291,18 +407,27 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/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.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -331,7 +456,13 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= @@ -339,15 +470,41 @@ golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0 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-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/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.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +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.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -364,6 +521,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= @@ -374,3 +535,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/imodels/imodels.go b/internal/imodels/imodels.go index 2453005a59..7173133685 100644 --- a/internal/imodels/imodels.go +++ b/internal/imodels/imodels.go @@ -136,9 +136,11 @@ 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 + // TODO(V2 models, only save layer ID): + LayerDetails *extractor.LayerDetails + // ImageOriginLayerID string // TODO(v2): // SourceAnalysis *SourceAnalysis diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 42c4d381ce..0ece57c6d7 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -1,9 +1,12 @@ package osvscanner import ( + "bufio" "context" "errors" "fmt" + "os" + "os/exec" "github.com/google/osv-scalibr/artifact/image/layerscanning/image" "github.com/google/osv-scalibr/extractor/filesystem" @@ -15,8 +18,8 @@ import ( "github.com/google/osv-scanner/internal/imodels" "github.com/google/osv-scanner/internal/imodels/results" "github.com/google/osv-scanner/internal/local" - "github.com/google/osv-scanner/internal/lockfilescalibr/language/javascript/nodemodules" "github.com/google/osv-scanner/internal/output" + "github.com/google/osv-scanner/internal/scalibrextract/language/javascript/nodemodules" "github.com/google/osv-scanner/internal/semantic" "github.com/google/osv-scanner/internal/version" "github.com/google/osv-scanner/pkg/models" @@ -94,15 +97,27 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner } scanner := scalibr.New() - image, err := image.FromRemoteName(actions.DockerImageName, image.DefaultConfig()) + + var img *image.Image + var err error + if actions.ScanOCIImage != "" { + img, err = image.FromTarball(actions.ScanOCIImage, image.DefaultConfig()) + r.Infof("Scanning image %q\n", actions.ScanOCIImage) + } else if actions.DockerImageName != "" { + path, exportErr := exportDockerImage(r, actions.DockerImageName) + if exportErr != nil { + return models.VulnerabilityResults{}, exportErr + } + defer os.Remove(path) + img, err = image.FromTarball(path, image.DefaultConfig()) + r.Infof("Scanning image %q\n", path) + } if err != nil { - r.Errorf("Failed to pull container image %q\n", actions.DockerImageName) return models.VulnerabilityResults{}, err } + defer img.CleanUp() - defer image.CleanUp() - - scalibrSR, err := scanner.ScanContainer(context.Background(), image, &scalibr.ScanConfig{ + scalibrSR, err := scanner.ScanContainer(context.Background(), img, &scalibr.ScanConfig{ FilesystemExtractors: []filesystem.Extractor{ nodemodules.Extractor{}, apk.New(apk.DefaultConfig()), @@ -116,9 +131,14 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner return models.VulnerabilityResults{}, fmt.Errorf("failed to scan container image: %w", err) } + if len(scalibrSR.Inventories) == 0 { + return models.VulnerabilityResults{}, NoPackagesFoundErr + } + 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 } filterUnscannablePackages(r, &scanResult) @@ -179,6 +199,68 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner return results, nil } +func exportDockerImage(r reporter.Reporter, dockerImageName string) (string, error) { + 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 + } + + r.Infof("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 +} + // Perform osv scanner action, with optional reporter to output information func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityResults, error) { if r == nil { diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go index 1963159cf5..8045c87a48 100644 --- a/pkg/osvscanner/vulnerability_result.go +++ b/pkg/osvscanner/vulnerability_result.go @@ -47,6 +47,13 @@ func buildVulnerabilityResults( } } + if psr.LayerDetails != nil { + pkg.Package.ImageOrigin = &models.ImageOriginDetails{ + LayerID: psr.LayerDetails.DiffID, + OriginCommand: psr.LayerDetails.Command, + InBaseImage: psr.LayerDetails.InBaseImage, + } + } pkg.DepGroups = p.DepGroups configToUse := scanResults.ConfigManager.Get(r, p.Location) From 11b3f883d9d41bcb59dc07a905a61c5e79467ffe Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 2 Jan 2025 14:22:38 +1100 Subject: [PATCH 03/45] Remove image package as it is no longer used --- internal/image/__snapshots__/image_test.snap | 1879 ----------------- internal/image/extractor.go | 114 - .../image/fixtures/alpine-3.18-alpine-release | 1 - .../image/fixtures/alpine-3.18-os-release | 7 - .../fixtures/package-tracing-fixture/go.mod | 5 - .../fixtures/package-tracing-fixture/go.sum | 2 - .../fixtures/package-tracing-fixture/main.go | 11 - .../package-tracing-fixture/osv-scanner.toml | 5 - .../image/fixtures/test-alpine.Dockerfile | 5 - .../test-node_modules-npm-empty.Dockerfile | 14 - .../test-node_modules-npm-full.Dockerfile | 18 - .../test-node_modules-pnpm-empty.Dockerfile | 14 - .../test-node_modules-pnpm-full.Dockerfile | 18 - .../test-node_modules-yarn-empty.Dockerfile | 14 - .../test-node_modules-yarn-full.Dockerfile | 18 - .../fixtures/test-package-tracing.Dockerfile | 44 - internal/image/guess_base_image.go | 80 - internal/image/image.go | 279 --- internal/image/image_test.go.disabled | 99 - internal/image/layer.go | 186 -- internal/image/pathtree/pathtree.go | 133 -- internal/image/pathtree/pathtree_test.go | 264 --- internal/image/scan.go | 256 --- internal/image/testmain_test.go | 16 - 24 files changed, 3482 deletions(-) delete mode 100755 internal/image/__snapshots__/image_test.snap delete mode 100644 internal/image/extractor.go delete mode 100644 internal/image/fixtures/alpine-3.18-alpine-release delete mode 100644 internal/image/fixtures/alpine-3.18-os-release delete mode 100644 internal/image/fixtures/package-tracing-fixture/go.mod delete mode 100644 internal/image/fixtures/package-tracing-fixture/go.sum delete mode 100644 internal/image/fixtures/package-tracing-fixture/main.go delete mode 100644 internal/image/fixtures/package-tracing-fixture/osv-scanner.toml delete mode 100644 internal/image/fixtures/test-alpine.Dockerfile delete mode 100644 internal/image/fixtures/test-node_modules-npm-empty.Dockerfile delete mode 100644 internal/image/fixtures/test-node_modules-npm-full.Dockerfile delete mode 100644 internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile delete mode 100644 internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile delete mode 100644 internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile delete mode 100644 internal/image/fixtures/test-node_modules-yarn-full.Dockerfile delete mode 100644 internal/image/fixtures/test-package-tracing.Dockerfile delete mode 100644 internal/image/guess_base_image.go delete mode 100644 internal/image/image.go delete mode 100644 internal/image/image_test.go.disabled delete mode 100644 internal/image/layer.go delete mode 100644 internal/image/pathtree/pathtree.go delete mode 100644 internal/image/pathtree/pathtree_test.go delete mode 100644 internal/image/scan.go delete mode 100644 internal/image/testmain_test.go 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/alpine-3.18-alpine-release b/internal/image/fixtures/alpine-3.18-alpine-release deleted file mode 100644 index d21858b119..0000000000 --- a/internal/image/fixtures/alpine-3.18-alpine-release +++ /dev/null @@ -1 +0,0 @@ -3.18.1 diff --git a/internal/image/fixtures/alpine-3.18-os-release b/internal/image/fixtures/alpine-3.18-os-release deleted file mode 100644 index ffb92a8cd4..0000000000 --- a/internal/image/fixtures/alpine-3.18-os-release +++ /dev/null @@ -1,7 +0,0 @@ -/ # cat /etc/os-release -NAME="Alpine Linux" -ID=alpine -VERSION_ID=3.18.1 -PRETTY_NAME="Alpine Linux v3.18" -HOME_URL="https://alpinelinux.org/" -BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues" diff --git a/internal/image/fixtures/package-tracing-fixture/go.mod b/internal/image/fixtures/package-tracing-fixture/go.mod deleted file mode 100644 index b5ceeb4c01..0000000000 --- a/internal/image/fixtures/package-tracing-fixture/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module ptf - -go 1.22.4 - -require github.com/BurntSushi/toml v1.4.0 diff --git a/internal/image/fixtures/package-tracing-fixture/go.sum b/internal/image/fixtures/package-tracing-fixture/go.sum deleted file mode 100644 index 8bc10f6628..0000000000 --- a/internal/image/fixtures/package-tracing-fixture/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= diff --git a/internal/image/fixtures/package-tracing-fixture/main.go b/internal/image/fixtures/package-tracing-fixture/main.go deleted file mode 100644 index 74eb5796a5..0000000000 --- a/internal/image/fixtures/package-tracing-fixture/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "os" - - "github.com/BurntSushi/toml" -) - -func main() { - toml.NewEncoder(os.Stdout) -} diff --git a/internal/image/fixtures/package-tracing-fixture/osv-scanner.toml b/internal/image/fixtures/package-tracing-fixture/osv-scanner.toml deleted file mode 100644 index abab9245b2..0000000000 --- a/internal/image/fixtures/package-tracing-fixture/osv-scanner.toml +++ /dev/null @@ -1,5 +0,0 @@ -[[PackageOverrides]] -name = "stdlib" -ecosystem = "Go" -ignore = true -reason = "This is an intentionally vulnerable test project" diff --git a/internal/image/fixtures/test-alpine.Dockerfile b/internal/image/fixtures/test-alpine.Dockerfile deleted file mode 100644 index d6aa79f1c8..0000000000 --- a/internal/image/fixtures/test-alpine.Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM alpine:3.10@sha256:451eee8bedcb2f029756dc3e9d73bab0e7943c1ac55cff3a4861c52a0fdd3e98 - -# Switch the version to 3.18 to show the advisories published for the latest alpine versions -COPY "alpine-3.18-alpine-release" "/etc/alpine-release" -COPY "alpine-3.18-os-release" "/etc/os-release" diff --git a/internal/image/fixtures/test-node_modules-npm-empty.Dockerfile b/internal/image/fixtures/test-node_modules-npm-empty.Dockerfile deleted file mode 100644 index 2dbae77b8f..0000000000 --- a/internal/image/fixtures/test-node_modules-npm-empty.Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -ARG MANAGER_VERSION="10.2.4" - -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 - -WORKDIR /prod/app - -# install the desired package manager -RUN npm i -g "npm@$MANAGER_VERSION" - -# initialize the package.json using the manager -RUN npm init -y - -# ensure that we finish fully installed -RUN npm install diff --git a/internal/image/fixtures/test-node_modules-npm-full.Dockerfile b/internal/image/fixtures/test-node_modules-npm-full.Dockerfile deleted file mode 100644 index 1043f54004..0000000000 --- a/internal/image/fixtures/test-node_modules-npm-full.Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -ARG MANAGER_VERSION="10.2.4" - -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 - -WORKDIR /prod/app - -# install the desired package manager -RUN npm i -g "npm@$MANAGER_VERSION" - -# initialize the package.json using the manager -RUN npm init -y - -# install a few dependencies at specific versions -RUN npm i mkdirp@0.5.0 -RUN npm i -d cryo@0.0.6 - -# ensure that we finish fully installed -RUN npm install diff --git a/internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile b/internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile deleted file mode 100644 index fd97227527..0000000000 --- a/internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -ARG MANAGER_VERSION="8.15.4" - -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 - -WORKDIR /prod/app - -# install the desired package manager -RUN npm i -g "pnpm@$MANAGER_VERSION" - -# initialize the package.json using the manager -RUN pnpm init - -# ensure that we finish fully installed -RUN pnpm install diff --git a/internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile b/internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile deleted file mode 100644 index 666de1ef50..0000000000 --- a/internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -ARG MANAGER_VERSION="8.15.4" - -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 - -WORKDIR /prod/app - -# install the desired package manager -RUN npm i -g "pnpm@$MANAGER_VERSION" - -# initialize the package.json using the manager -RUN pnpm init - -# install a few dependencies at specific versions -RUN pnpm add mkdirp@0.5.0 -RUN pnpm add -d cryo@0.0.6 - -# ensure that we finish fully installed -RUN pnpm install diff --git a/internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile b/internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile deleted file mode 100644 index b9743644d1..0000000000 --- a/internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -ARG MANAGER_VERSION="1.22.22" - -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 - -WORKDIR /prod/app - -# install the desired package manager -RUN npm i -g "yarn@$MANAGER_VERSION" --force - -# initialize the package.json using the manager -RUN yarn init -y - -# ensure that we finish fully installed -RUN yarn install diff --git a/internal/image/fixtures/test-node_modules-yarn-full.Dockerfile b/internal/image/fixtures/test-node_modules-yarn-full.Dockerfile deleted file mode 100644 index c675c21ad3..0000000000 --- a/internal/image/fixtures/test-node_modules-yarn-full.Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -ARG MANAGER_VERSION="1.22.22" - -FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 - -WORKDIR /prod/app - -# install the desired package manager -RUN npm i -g "yarn@$MANAGER_VERSION" --force - -# initialize the package.json using the manager -RUN yarn init -y - -# install a few dependencies at specific versions -RUN yarn add mkdirp@0.5.0 -RUN yarn add --dev cryo@0.0.6 - -# ensure that we finish fully installed -RUN yarn install diff --git a/internal/image/fixtures/test-package-tracing.Dockerfile b/internal/image/fixtures/test-package-tracing.Dockerfile deleted file mode 100644 index b0d3fdbca1..0000000000 --- a/internal/image/fixtures/test-package-tracing.Dockerfile +++ /dev/null @@ -1,44 +0,0 @@ -FROM golang:1.22.4-alpine3.20@sha256:ace6cc3fe58d0c7b12303c57afe6d6724851152df55e08057b43990b927ad5e8 AS build - -COPY package-tracing-fixture/ /work - -RUN cd /work && go get github.com/BurntSushi/toml@v1.4.0 && go mod tidy -RUN cd /work && go build . -RUN cp /work/ptf /work/ptf-1.4.0 - -RUN cd /work && go get github.com/BurntSushi/toml@v1.3.0 && go mod tidy -RUN cd /work && go build . -RUN cp /work/ptf /work/ptf-1.3.0 - -RUN cd /work && go get github.com/BurntSushi/toml@v1.2.0 && go mod tidy -RUN cd /work && go build . -RUN cp /work/ptf /work/ptf-1.2.0 - -# RUN go install github.com/google/osv-scanner/cmd/osv-scanner@v1.3.0 -# RUN cp /go/bin/osv-scanner /go/bin/osv-scanner-1.3.0 -# RUN go install github.com/google/osv-scanner/cmd/osv-scanner@v1.8.1 -# RUN cp /go/bin/osv-scanner /go/bin/osv-scanner-1.8.1 -# RUN go install github.com/google/osv-scanner/cmd/osv-scanner@v1.5.0 -# RUN cp /go/bin/osv-scanner /go/bin/osv-scanner-1.5.0 - -FROM alpine:3.20.1@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0 - -COPY --from=build /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ - -# This tests when a file that exists in the final layer doesn't exist in one intermediate layer -RUN mv /go/bin/ptf-1.3.0 /go/bin/ptf-1.3.0-moved -RUN cp /go/bin/ptf-1.3.0-moved /go/bin/ptf-1.3.0 - -# This tests when a file only exist in a intermediate layer -RUN cp /go/bin/ptf-1.3.0 /go/bin/ptf-1.3.0-copy -RUN rm /go/bin/ptf-1.3.0-copy - -# This tests when a less vulnerable file overwrites a more vulnerable file -# This tests when a less vulnerable file overwrites a more vulnerable file -RUN cp /go/bin/ptf-1.3.0 /go/bin/ptf-vulnerable -RUN cp /go/bin/ptf-1.4.0 /go/bin/ptf-vulnerable - -# This tests when a more vulnerable file overwrites a less vulnerable file -RUN cp /go/bin/ptf-1.4.0 /go/bin/more-vuln-overwrite-less-vuln -RUN cp /go/bin/ptf-1.2.0 /go/bin/more-vuln-overwrite-less-vuln - 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) -} From 14555f823e345deb198c1edc263ee95633d0c8f2 Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 2 Jan 2025 16:31:03 +1100 Subject: [PATCH 04/45] Pass layer information to output --- internal/imodels/imodels.go | 11 +---------- internal/imodels/results/scanresults.go | 3 ++- pkg/models/results.go | 13 +++++++++++++ pkg/osvscanner/osvscanner.go | 25 +++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/internal/imodels/imodels.go b/internal/imodels/imodels.go index 7173133685..ca6c64a395 100644 --- a/internal/imodels/imodels.go +++ b/internal/imodels/imodels.go @@ -138,22 +138,13 @@ type PackageScanResult struct { // TODO: Use osvschema.Vulnerability instead Vulnerabilities []models.Vulnerability Licenses []models.License - // TODO(V2 models, only save layer ID): - LayerDetails *extractor.LayerDetails - // ImageOriginLayerID string + 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/pkg/models/results.go b/pkg/models/results.go index 04a297d8bf..d9a7025c99 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 @@ -194,3 +195,15 @@ type PackageInfo struct { Commit string `json:"commit,omitempty"` ImageOrigin *ImageOriginDetails `json:"imageOrigin,omitempty"` } + +type ImageMetadata struct { + OS string `json:"os"` + BaseImages []string `json:"base_images"` + LayerMetadata []LayerMetadata `json:"layer_metadata"` +} + +type LayerMetadata struct { + DiffID string `json:"diff_id"` + Command string `json:"command"` + IsEmpty bool `json:"is_empty"` +} diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 0ece57c6d7..e25f9c658e 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -13,6 +13,7 @@ import ( "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/os/osrelease" "github.com/google/osv-scanner/internal/config" "github.com/google/osv-scanner/internal/depsdev" "github.com/google/osv-scanner/internal/imodels" @@ -141,6 +142,30 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner scanResult.PackageScanResults[i].LayerDetails = inv.LayerDetails } + // --- Fill Image Metadata --- + { + // Ignore error, as if this would error we would have failed the initial scan + chainLayers, _ := img.ChainLayers() + m, err := osrelease.GetOSRelease(chainLayers[len(chainLayers)-1].FS()) + OS := "Unknown" + if err != nil { + OS = m["OSID"] + } + + layerMetadata := []models.LayerMetadata{} + for _, cl := range chainLayers { + layerMetadata = append(layerMetadata, models.LayerMetadata{ + DiffID: cl.Layer().DiffID(), + Command: cl.Layer().Command(), + IsEmpty: cl.Layer().IsEmpty(), + }) + } + scanResult.ImageMetadata = &models.ImageMetadata{ + OS: OS, + LayerMetadata: layerMetadata, + } + } + filterUnscannablePackages(r, &scanResult) err = makeRequest(r, scanResult.PackageScanResults, actions.CompareOffline, actions.DownloadDatabases, actions.LocalDBPath) From 40cabbde97e6887ad3e19bda17f7e38343375b8e Mon Sep 17 00:00:00 2001 From: Rex P Date: Mon, 6 Jan 2025 11:08:35 +1100 Subject: [PATCH 05/45] Run go mod tidy --- go.mod | 46 +++++++++++++++-- go.sum | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 202 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 220f341676..0572807ca0 100644 --- a/go.mod +++ b/go.mod @@ -17,8 +17,7 @@ require ( github.com/go-git/go-billy/v5 v5.6.0 github.com/go-git/go-git/v5 v5.12.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.20241219225011-fd6877f0b783 + github.com/google/osv-scalibr v0.1.6-0.20250105222824-56e5c3bfb149 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 @@ -44,7 +43,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.0.0 // indirect github.com/alecthomas/chroma/v2 v2.14.0 // indirect github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 // indirect @@ -54,19 +56,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 +101,52 @@ 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.2.2 // 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 2947904abc..d0a21ecd57 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.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= @@ -42,6 +50,7 @@ github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd3 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/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +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= @@ -56,11 +65,29 @@ 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.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= 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= @@ -77,16 +104,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 v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= 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= @@ -105,30 +140,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.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= +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.20241219225011-fd6877f0b783 h1:YzLIdmgxXdnYO0oGnS+i0s7kC3uwlVBZe53YIfvtrh4= -github.com/google/osv-scalibr v0.1.6-0.20241219225011-fd6877f0b783/go.mod h1:S8mrRjoWESAOOTq25lJqzxiKR6tbWSFYG8SVb5EFLHk= +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/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= @@ -139,6 +203,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= @@ -167,6 +233,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= @@ -181,6 +257,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= @@ -197,6 +277,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= @@ -231,6 +314,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= @@ -263,6 +347,8 @@ 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.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= @@ -270,6 +356,12 @@ github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRla 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= @@ -280,22 +372,40 @@ 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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -304,18 +414,27 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/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.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -344,7 +463,13 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= @@ -352,15 +477,41 @@ 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= @@ -377,6 +528,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= @@ -387,3 +542,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= From 04beb876e399401034ff1ad58f867d1349ff1847 Mon Sep 17 00:00:00 2001 From: Rex P Date: Mon, 6 Jan 2025 11:17:05 +1100 Subject: [PATCH 06/45] Revert "Remove image package as it is no longer used" This reverts commit 11b3f883d9d41bcb59dc07a905a61c5e79467ffe. --- internal/image/__snapshots__/image_test.snap | 1879 +++++++++++++++++ internal/image/extractor.go | 114 + .../image/fixtures/alpine-3.18-alpine-release | 1 + .../image/fixtures/alpine-3.18-os-release | 7 + .../fixtures/package-tracing-fixture/go.mod | 5 + .../fixtures/package-tracing-fixture/go.sum | 2 + .../fixtures/package-tracing-fixture/main.go | 11 + .../package-tracing-fixture/osv-scanner.toml | 5 + .../image/fixtures/test-alpine.Dockerfile | 5 + .../test-node_modules-npm-empty.Dockerfile | 14 + .../test-node_modules-npm-full.Dockerfile | 18 + .../test-node_modules-pnpm-empty.Dockerfile | 14 + .../test-node_modules-pnpm-full.Dockerfile | 18 + .../test-node_modules-yarn-empty.Dockerfile | 14 + .../test-node_modules-yarn-full.Dockerfile | 18 + .../fixtures/test-package-tracing.Dockerfile | 44 + internal/image/guess_base_image.go | 80 + internal/image/image.go | 279 +++ internal/image/image_test.go.disabled | 99 + internal/image/layer.go | 186 ++ internal/image/pathtree/pathtree.go | 133 ++ internal/image/pathtree/pathtree_test.go | 264 +++ internal/image/scan.go | 256 +++ internal/image/testmain_test.go | 16 + 24 files changed, 3482 insertions(+) create mode 100755 internal/image/__snapshots__/image_test.snap create mode 100644 internal/image/extractor.go create mode 100644 internal/image/fixtures/alpine-3.18-alpine-release create mode 100644 internal/image/fixtures/alpine-3.18-os-release create mode 100644 internal/image/fixtures/package-tracing-fixture/go.mod create mode 100644 internal/image/fixtures/package-tracing-fixture/go.sum create mode 100644 internal/image/fixtures/package-tracing-fixture/main.go create mode 100644 internal/image/fixtures/package-tracing-fixture/osv-scanner.toml create mode 100644 internal/image/fixtures/test-alpine.Dockerfile create mode 100644 internal/image/fixtures/test-node_modules-npm-empty.Dockerfile create mode 100644 internal/image/fixtures/test-node_modules-npm-full.Dockerfile create mode 100644 internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile create mode 100644 internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile create mode 100644 internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile create mode 100644 internal/image/fixtures/test-node_modules-yarn-full.Dockerfile create mode 100644 internal/image/fixtures/test-package-tracing.Dockerfile create mode 100644 internal/image/guess_base_image.go create mode 100644 internal/image/image.go create mode 100644 internal/image/image_test.go.disabled create mode 100644 internal/image/layer.go create mode 100644 internal/image/pathtree/pathtree.go create mode 100644 internal/image/pathtree/pathtree_test.go create mode 100644 internal/image/scan.go create mode 100644 internal/image/testmain_test.go diff --git a/internal/image/__snapshots__/image_test.snap b/internal/image/__snapshots__/image_test.snap new file mode 100755 index 0000000000..58b8b54897 --- /dev/null +++ b/internal/image/__snapshots__/image_test.snap @@ -0,0 +1,1879 @@ + +[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 new file mode 100644 index 0000000000..5b8de170d6 --- /dev/null +++ b/internal/image/extractor.go @@ -0,0 +1,114 @@ +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/alpine-3.18-alpine-release b/internal/image/fixtures/alpine-3.18-alpine-release new file mode 100644 index 0000000000..d21858b119 --- /dev/null +++ b/internal/image/fixtures/alpine-3.18-alpine-release @@ -0,0 +1 @@ +3.18.1 diff --git a/internal/image/fixtures/alpine-3.18-os-release b/internal/image/fixtures/alpine-3.18-os-release new file mode 100644 index 0000000000..ffb92a8cd4 --- /dev/null +++ b/internal/image/fixtures/alpine-3.18-os-release @@ -0,0 +1,7 @@ +/ # cat /etc/os-release +NAME="Alpine Linux" +ID=alpine +VERSION_ID=3.18.1 +PRETTY_NAME="Alpine Linux v3.18" +HOME_URL="https://alpinelinux.org/" +BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues" diff --git a/internal/image/fixtures/package-tracing-fixture/go.mod b/internal/image/fixtures/package-tracing-fixture/go.mod new file mode 100644 index 0000000000..b5ceeb4c01 --- /dev/null +++ b/internal/image/fixtures/package-tracing-fixture/go.mod @@ -0,0 +1,5 @@ +module ptf + +go 1.22.4 + +require github.com/BurntSushi/toml v1.4.0 diff --git a/internal/image/fixtures/package-tracing-fixture/go.sum b/internal/image/fixtures/package-tracing-fixture/go.sum new file mode 100644 index 0000000000..8bc10f6628 --- /dev/null +++ b/internal/image/fixtures/package-tracing-fixture/go.sum @@ -0,0 +1,2 @@ +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= diff --git a/internal/image/fixtures/package-tracing-fixture/main.go b/internal/image/fixtures/package-tracing-fixture/main.go new file mode 100644 index 0000000000..74eb5796a5 --- /dev/null +++ b/internal/image/fixtures/package-tracing-fixture/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "os" + + "github.com/BurntSushi/toml" +) + +func main() { + toml.NewEncoder(os.Stdout) +} diff --git a/internal/image/fixtures/package-tracing-fixture/osv-scanner.toml b/internal/image/fixtures/package-tracing-fixture/osv-scanner.toml new file mode 100644 index 0000000000..abab9245b2 --- /dev/null +++ b/internal/image/fixtures/package-tracing-fixture/osv-scanner.toml @@ -0,0 +1,5 @@ +[[PackageOverrides]] +name = "stdlib" +ecosystem = "Go" +ignore = true +reason = "This is an intentionally vulnerable test project" diff --git a/internal/image/fixtures/test-alpine.Dockerfile b/internal/image/fixtures/test-alpine.Dockerfile new file mode 100644 index 0000000000..d6aa79f1c8 --- /dev/null +++ b/internal/image/fixtures/test-alpine.Dockerfile @@ -0,0 +1,5 @@ +FROM alpine:3.10@sha256:451eee8bedcb2f029756dc3e9d73bab0e7943c1ac55cff3a4861c52a0fdd3e98 + +# Switch the version to 3.18 to show the advisories published for the latest alpine versions +COPY "alpine-3.18-alpine-release" "/etc/alpine-release" +COPY "alpine-3.18-os-release" "/etc/os-release" diff --git a/internal/image/fixtures/test-node_modules-npm-empty.Dockerfile b/internal/image/fixtures/test-node_modules-npm-empty.Dockerfile new file mode 100644 index 0000000000..2dbae77b8f --- /dev/null +++ b/internal/image/fixtures/test-node_modules-npm-empty.Dockerfile @@ -0,0 +1,14 @@ +ARG MANAGER_VERSION="10.2.4" + +FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 + +WORKDIR /prod/app + +# install the desired package manager +RUN npm i -g "npm@$MANAGER_VERSION" + +# initialize the package.json using the manager +RUN npm init -y + +# ensure that we finish fully installed +RUN npm install diff --git a/internal/image/fixtures/test-node_modules-npm-full.Dockerfile b/internal/image/fixtures/test-node_modules-npm-full.Dockerfile new file mode 100644 index 0000000000..1043f54004 --- /dev/null +++ b/internal/image/fixtures/test-node_modules-npm-full.Dockerfile @@ -0,0 +1,18 @@ +ARG MANAGER_VERSION="10.2.4" + +FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 + +WORKDIR /prod/app + +# install the desired package manager +RUN npm i -g "npm@$MANAGER_VERSION" + +# initialize the package.json using the manager +RUN npm init -y + +# install a few dependencies at specific versions +RUN npm i mkdirp@0.5.0 +RUN npm i -d cryo@0.0.6 + +# ensure that we finish fully installed +RUN npm install diff --git a/internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile b/internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile new file mode 100644 index 0000000000..fd97227527 --- /dev/null +++ b/internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile @@ -0,0 +1,14 @@ +ARG MANAGER_VERSION="8.15.4" + +FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 + +WORKDIR /prod/app + +# install the desired package manager +RUN npm i -g "pnpm@$MANAGER_VERSION" + +# initialize the package.json using the manager +RUN pnpm init + +# ensure that we finish fully installed +RUN pnpm install diff --git a/internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile b/internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile new file mode 100644 index 0000000000..666de1ef50 --- /dev/null +++ b/internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile @@ -0,0 +1,18 @@ +ARG MANAGER_VERSION="8.15.4" + +FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 + +WORKDIR /prod/app + +# install the desired package manager +RUN npm i -g "pnpm@$MANAGER_VERSION" + +# initialize the package.json using the manager +RUN pnpm init + +# install a few dependencies at specific versions +RUN pnpm add mkdirp@0.5.0 +RUN pnpm add -d cryo@0.0.6 + +# ensure that we finish fully installed +RUN pnpm install diff --git a/internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile b/internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile new file mode 100644 index 0000000000..b9743644d1 --- /dev/null +++ b/internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile @@ -0,0 +1,14 @@ +ARG MANAGER_VERSION="1.22.22" + +FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 + +WORKDIR /prod/app + +# install the desired package manager +RUN npm i -g "yarn@$MANAGER_VERSION" --force + +# initialize the package.json using the manager +RUN yarn init -y + +# ensure that we finish fully installed +RUN yarn install diff --git a/internal/image/fixtures/test-node_modules-yarn-full.Dockerfile b/internal/image/fixtures/test-node_modules-yarn-full.Dockerfile new file mode 100644 index 0000000000..c675c21ad3 --- /dev/null +++ b/internal/image/fixtures/test-node_modules-yarn-full.Dockerfile @@ -0,0 +1,18 @@ +ARG MANAGER_VERSION="1.22.22" + +FROM node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 + +WORKDIR /prod/app + +# install the desired package manager +RUN npm i -g "yarn@$MANAGER_VERSION" --force + +# initialize the package.json using the manager +RUN yarn init -y + +# install a few dependencies at specific versions +RUN yarn add mkdirp@0.5.0 +RUN yarn add --dev cryo@0.0.6 + +# ensure that we finish fully installed +RUN yarn install diff --git a/internal/image/fixtures/test-package-tracing.Dockerfile b/internal/image/fixtures/test-package-tracing.Dockerfile new file mode 100644 index 0000000000..b0d3fdbca1 --- /dev/null +++ b/internal/image/fixtures/test-package-tracing.Dockerfile @@ -0,0 +1,44 @@ +FROM golang:1.22.4-alpine3.20@sha256:ace6cc3fe58d0c7b12303c57afe6d6724851152df55e08057b43990b927ad5e8 AS build + +COPY package-tracing-fixture/ /work + +RUN cd /work && go get github.com/BurntSushi/toml@v1.4.0 && go mod tidy +RUN cd /work && go build . +RUN cp /work/ptf /work/ptf-1.4.0 + +RUN cd /work && go get github.com/BurntSushi/toml@v1.3.0 && go mod tidy +RUN cd /work && go build . +RUN cp /work/ptf /work/ptf-1.3.0 + +RUN cd /work && go get github.com/BurntSushi/toml@v1.2.0 && go mod tidy +RUN cd /work && go build . +RUN cp /work/ptf /work/ptf-1.2.0 + +# RUN go install github.com/google/osv-scanner/cmd/osv-scanner@v1.3.0 +# RUN cp /go/bin/osv-scanner /go/bin/osv-scanner-1.3.0 +# RUN go install github.com/google/osv-scanner/cmd/osv-scanner@v1.8.1 +# RUN cp /go/bin/osv-scanner /go/bin/osv-scanner-1.8.1 +# RUN go install github.com/google/osv-scanner/cmd/osv-scanner@v1.5.0 +# RUN cp /go/bin/osv-scanner /go/bin/osv-scanner-1.5.0 + +FROM alpine:3.20.1@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0 + +COPY --from=build /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ + +# This tests when a file that exists in the final layer doesn't exist in one intermediate layer +RUN mv /go/bin/ptf-1.3.0 /go/bin/ptf-1.3.0-moved +RUN cp /go/bin/ptf-1.3.0-moved /go/bin/ptf-1.3.0 + +# This tests when a file only exist in a intermediate layer +RUN cp /go/bin/ptf-1.3.0 /go/bin/ptf-1.3.0-copy +RUN rm /go/bin/ptf-1.3.0-copy + +# This tests when a less vulnerable file overwrites a more vulnerable file +# This tests when a less vulnerable file overwrites a more vulnerable file +RUN cp /go/bin/ptf-1.3.0 /go/bin/ptf-vulnerable +RUN cp /go/bin/ptf-1.4.0 /go/bin/ptf-vulnerable + +# This tests when a more vulnerable file overwrites a less vulnerable file +RUN cp /go/bin/ptf-1.4.0 /go/bin/more-vuln-overwrite-less-vuln +RUN cp /go/bin/ptf-1.2.0 /go/bin/more-vuln-overwrite-less-vuln + diff --git a/internal/image/guess_base_image.go b/internal/image/guess_base_image.go new file mode 100644 index 0000000000..92b0575d2b --- /dev/null +++ b/internal/image/guess_base_image.go @@ -0,0 +1,80 @@ +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 new file mode 100644 index 0000000000..0be6f53bf2 --- /dev/null +++ b/internal/image/image.go @@ -0,0 +1,279 @@ +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 new file mode 100644 index 0000000000..787f258cc7 --- /dev/null +++ b/internal/image/image_test.go.disabled @@ -0,0 +1,99 @@ +// 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 new file mode 100644 index 0000000000..8539285cc3 --- /dev/null +++ b/internal/image/layer.go @@ -0,0 +1,186 @@ +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 new file mode 100644 index 0000000000..d14666a5a1 --- /dev/null +++ b/internal/image/pathtree/pathtree.go @@ -0,0 +1,133 @@ +// 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 new file mode 100644 index 0000000000..556c97545a --- /dev/null +++ b/internal/image/pathtree/pathtree_test.go @@ -0,0 +1,264 @@ +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 new file mode 100644 index 0000000000..cc98f5bbc7 --- /dev/null +++ b/internal/image/scan.go @@ -0,0 +1,256 @@ +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 new file mode 100644 index 0000000000..1f5d0323cf --- /dev/null +++ b/internal/image/testmain_test.go @@ -0,0 +1,16 @@ +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) +} From 314d5868c3e6da543451950dc6c04bbb414bbd59 Mon Sep 17 00:00:00 2001 From: Rex P Date: Mon, 6 Jan 2025 11:18:20 +1100 Subject: [PATCH 07/45] Remove just the go files --- internal/image/__snapshots__/image_test.snap | 1879 ------------------ internal/image/extractor.go | 114 -- internal/image/guess_base_image.go | 80 - internal/image/image.go | 279 --- internal/image/image_test.go.disabled | 99 - internal/image/layer.go | 186 -- internal/image/pathtree/pathtree.go | 133 -- internal/image/pathtree/pathtree_test.go | 264 --- internal/image/scan.go | 256 --- internal/image/testmain_test.go | 16 - 10 files changed, 3306 deletions(-) delete mode 100755 internal/image/__snapshots__/image_test.snap delete mode 100644 internal/image/extractor.go delete mode 100644 internal/image/guess_base_image.go delete mode 100644 internal/image/image.go delete mode 100644 internal/image/image_test.go.disabled delete mode 100644 internal/image/layer.go delete mode 100644 internal/image/pathtree/pathtree.go delete mode 100644 internal/image/pathtree/pathtree_test.go delete mode 100644 internal/image/scan.go delete mode 100644 internal/image/testmain_test.go 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/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) -} From 06c40a67575236a882a633b514b64ac349f3a4f6 Mon Sep 17 00:00:00 2001 From: Rex P Date: Mon, 6 Jan 2025 11:32:34 +1100 Subject: [PATCH 08/45] Revert docker hash to build the older version of alpine images --- internal/image/fixtures/test-node_modules-npm-empty.Dockerfile | 2 +- internal/image/fixtures/test-node_modules-npm-full.Dockerfile | 2 +- internal/image/fixtures/test-node_modules-pnpm-empty.Dockerfile | 2 +- internal/image/fixtures/test-node_modules-pnpm-full.Dockerfile | 2 +- internal/image/fixtures/test-node_modules-yarn-empty.Dockerfile | 2 +- internal/image/fixtures/test-node_modules-yarn-full.Dockerfile | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) 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 From 11674c5813fc1f9b09993e83d66ed6e4795c15f4 Mon Sep 17 00:00:00 2001 From: Rex P Date: Mon, 6 Jan 2025 11:34:13 +1100 Subject: [PATCH 09/45] Updating logging output to be consistent and make sense --- cmd/osv-scanner/__snapshots__/main_test.snap | 6 +++--- pkg/osvscanner/osvscanner.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/osv-scanner/__snapshots__/main_test.snap b/cmd/osv-scanner/__snapshots__/main_test.snap index 32d53045c1..9999555c93 100755 --- a/cmd/osv-scanner/__snapshots__/main_test.snap +++ b/cmd/osv-scanner/__snapshots__/main_test.snap @@ -950,7 +950,7 @@ failed to pull container image: failed to run docker command [TestRun_Docker/Real_Alpine_image - 1] Pulling docker image ("alpine:3.18.9")... Saving docker image ("alpine:3.18.9") to temporary file... -Scanning image "/tmp/docker-image-2671409146.tar" +Scanning image "alpine:3.18.9" No issues found --- @@ -962,7 +962,7 @@ No issues found [TestRun_Docker/Real_empty_image - 1] Pulling docker image ("hello-world")... Saving docker image ("hello-world") to temporary file... -Scanning image "/tmp/docker-image-1910286536.tar" +Scanning image "hello-world" --- @@ -974,7 +974,7 @@ No package sources found, --help for usage information. [TestRun_Docker/Real_empty_image_with_tag - 1] Pulling docker image ("hello-world:linux")... Saving docker image ("hello-world:linux") to temporary file... -Scanning image "/tmp/docker-image-2242457421.tar" +Scanning image "hello-world:linux" --- diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index e25f9c658e..02b8911570 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -111,7 +111,7 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner } defer os.Remove(path) img, err = image.FromTarball(path, image.DefaultConfig()) - r.Infof("Scanning image %q\n", path) + r.Infof("Scanning image %q\n", actions.DockerImageName) } if err != nil { return models.VulnerabilityResults{}, err From ef9b82673c9a6a15bdec4809b29c2d4f2a17ddd9 Mon Sep 17 00:00:00 2001 From: Rex P Date: Mon, 6 Jan 2025 11:52:34 +1100 Subject: [PATCH 10/45] Update structures --- internal/output/output_result.go | 2 +- pkg/models/image.go | 22 +++++++++++++++++++--- pkg/models/results.go | 11 ----------- pkg/osvscanner/osvscanner.go | 5 +++-- pkg/osvscanner/vulnerability_result.go | 2 +- 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/internal/output/output_result.go b/internal/output/output_result.go index 252417700a..93f291feb1 100644 --- a/internal/output/output_result.go +++ b/internal/output/output_result.go @@ -289,7 +289,7 @@ func processPackage(vulnPkg models.PackageVulns) PackageResult { if vulnPkg.Package.ImageOrigin != nil { packageLayerDetail := PackageLayerDetail{ - LayerID: vulnPkg.Package.ImageOrigin.LayerID, + LayerID: vulnPkg.Package.ImageOrigin.DiffID, InBaseImage: vulnPkg.Package.ImageOrigin.InBaseImage, } packageLayerDetail.LayerCommand, packageLayerDetail.LayerCommandDetailed = formatLayerCommand(vulnPkg.Package.ImageOrigin.OriginCommand) diff --git a/pkg/models/image.go b/pkg/models/image.go index d3bc134c77..56101334d0 100644 --- a/pkg/models/image.go +++ b/pkg/models/image.go @@ -1,7 +1,23 @@ package models type ImageOriginDetails struct { - LayerID string `json:"layerID"` - OriginCommand string `json:"originCommand"` - InBaseImage bool `json:"inBaseImage"` + DiffID string `json:"diff_id"` + + // TODO: Deprecated, use ImageMetadata to retrieve this info + OriginCommand string `json:"origin_command"` + InBaseImage bool `json:"in_base_image"` +} + +type ImageMetadata struct { + OS string `json:"os"` + LayerMetadata []LayerMetadata `json:"layer_metadata"` + BaseImageIndex int `json:"base_image_index"` +} + +type LayerMetadata struct { + DiffID string `json:"diff_id"` + Command string `json:"command"` + IsEmpty bool `json:"is_empty"` + // TODO: Not yet filled in + BaseImages []string `json:"base_images"` } diff --git a/pkg/models/results.go b/pkg/models/results.go index d9a7025c99..984d6c194b 100644 --- a/pkg/models/results.go +++ b/pkg/models/results.go @@ -196,14 +196,3 @@ type PackageInfo struct { ImageOrigin *ImageOriginDetails `json:"imageOrigin,omitempty"` } -type ImageMetadata struct { - OS string `json:"os"` - BaseImages []string `json:"base_images"` - LayerMetadata []LayerMetadata `json:"layer_metadata"` -} - -type LayerMetadata struct { - DiffID string `json:"diff_id"` - Command string `json:"command"` - IsEmpty bool `json:"is_empty"` -} diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 02b8911570..cec73ce469 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -161,8 +161,9 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner }) } scanResult.ImageMetadata = &models.ImageMetadata{ - OS: OS, - LayerMetadata: layerMetadata, + BaseImageIndex: img.BaseImageIndex, + OS: OS, + LayerMetadata: layerMetadata, } } diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go index 8045c87a48..e82bd5bb93 100644 --- a/pkg/osvscanner/vulnerability_result.go +++ b/pkg/osvscanner/vulnerability_result.go @@ -49,7 +49,7 @@ func buildVulnerabilityResults( if psr.LayerDetails != nil { pkg.Package.ImageOrigin = &models.ImageOriginDetails{ - LayerID: psr.LayerDetails.DiffID, + DiffID: psr.LayerDetails.DiffID, OriginCommand: psr.LayerDetails.Command, InBaseImage: psr.LayerDetails.InBaseImage, } From 2584ed4238b7dd1eaea428fdc262b3db4eeb94ae Mon Sep 17 00:00:00 2001 From: Rex P Date: Tue, 7 Jan 2025 12:01:36 +1100 Subject: [PATCH 11/45] go mod replace --- go.mod | 2 ++ go.sum | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 0572807ca0..d3eb559bf7 100644 --- a/go.mod +++ b/go.mod @@ -150,3 +150,5 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect www.velocidex.com/golang/regparser v0.0.0-20240404115756-2169ac0e3c09 // indirect ) + +replace github.com/google/osv-scalibr => github.com/another-rex/osv-scalibr v0.0.0-20250107005511-4051898dffce diff --git a/go.sum b/go.sum index d0a21ecd57..679e717c42 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,8 @@ github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 h1:6CO github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/another-rex/osv-scalibr v0.0.0-20250107005511-4051898dffce h1:prkXvk4qRbLBNK9iVQiqmCdtIobj8DLlJiTSIGWonrM= +github.com/another-rex/osv-scalibr v0.0.0-20250107005511-4051898dffce/go.mod h1:S8mrRjoWESAOOTq25lJqzxiKR6tbWSFYG8SVb5EFLHk= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= @@ -180,8 +182,6 @@ 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/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= From e50238d9a163d8c489c715ba5aff0214fcba592b Mon Sep 17 00:00:00 2001 From: Rex P Date: Tue, 7 Jan 2025 16:23:43 +1100 Subject: [PATCH 12/45] Merge new client matcher --- pkg/osvscanner/osvscanner.go | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index d5ed056d15..0eeb3681ef 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -7,14 +7,15 @@ import ( "fmt" "os" "os/exec" + "time" "github.com/google/osv-scalibr/artifact/image/layerscanning/image" + "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/os/osrelease" - "github.com/google/osv-scalibr/extractor" "github.com/google/osv-scanner/internal/clients/clientimpl/localmatcher" "github.com/google/osv-scanner/internal/clients/clientimpl/osvmatcher" "github.com/google/osv-scanner/internal/clients/clientinterfaces" @@ -24,8 +25,8 @@ import ( "github.com/google/osv-scanner/internal/imodels/results" "github.com/google/osv-scanner/internal/osvdev" "github.com/google/osv-scanner/internal/output" + "github.com/google/osv-scanner/internal/scalibrextract/language/javascript/nodemodules" "github.com/google/osv-scanner/pkg/models" - "github.com/google/osv-scanner/pkg/osv" "github.com/google/osv-scanner/pkg/reporter" "github.com/ossf/osv-schema/bindings/go/osvschema" @@ -168,11 +169,36 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner } } + // ----- Filtering ----- filterUnscannablePackages(r, &scanResult) - err = makeRequest(r, scanResult.PackageScanResults, actions.CompareOffline, actions.DownloadDatabases, actions.LocalDBPath) - if err != nil { - return models.VulnerabilityResults{}, err + // --- Make Vulnerability Requests --- + { + var matcher clientinterfaces.VulnerabilityMatcher + var err error + if actions.CompareOffline { + matcher, err = localmatcher.NewLocalMatcher(r, actions.LocalDBPath, actions.DownloadDatabases) + if err != nil { + return models.VulnerabilityResults{}, err + } + } else { + matcher = &osvmatcher.OSVMatcher{ + Client: *osvdev.DefaultClient(), + InitialQueryTimeout: 5 * time.Minute, + } + } + + err = makeRequestWithMatcher(r, scanResult.PackageScanResults, matcher) + if err != nil { + return models.VulnerabilityResults{}, err + } + } + + if len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary { + err = makeLicensesRequests(scanResult.PackageScanResults) + if err != nil { + return models.VulnerabilityResults{}, err + } } if len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary { From d165ceb6d82ad93e9ee0141d9cfd0ad6174617d3 Mon Sep 17 00:00:00 2001 From: Holly Gong <39108850+hogo6002@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:25:42 +1100 Subject: [PATCH 13/45] update image structs (#14) * update image structs * update json tag --- pkg/models/image.go | 15 +++++++++++---- pkg/osvscanner/osvscanner.go | 8 +++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/pkg/models/image.go b/pkg/models/image.go index 56101334d0..e31f52ae52 100644 --- a/pkg/models/image.go +++ b/pkg/models/image.go @@ -9,9 +9,16 @@ type ImageOriginDetails struct { } type ImageMetadata struct { - OS string `json:"os"` - LayerMetadata []LayerMetadata `json:"layer_metadata"` - BaseImageIndex int `json:"base_image_index"` + OS string `json:"os"` + LayerMetadata []LayerMetadata `json:"layer_metadata"` + // TODO: Not yet filled in + BaseImages [][]BaseImageDetails `json:"base_images"` +} + +type BaseImageDetails struct { + Name string `json:"name"` + // TODO: Not yet filled in + Tags []string `json:"tags"` } type LayerMetadata struct { @@ -19,5 +26,5 @@ type LayerMetadata struct { Command string `json:"command"` IsEmpty bool `json:"is_empty"` // TODO: Not yet filled in - BaseImages []string `json:"base_images"` + BaseImageIndex int `json:"base_image_index"` } diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 0eeb3681ef..9169080f95 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -162,10 +162,12 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner IsEmpty: cl.Layer().IsEmpty(), }) } + scanResult.ImageMetadata = &models.ImageMetadata{ - BaseImageIndex: img.BaseImageIndex, - OS: OS, - LayerMetadata: layerMetadata, + // TODO: Not yet filled in + BaseImages: [][]models.BaseImageDetails{}, + OS: OS, + LayerMetadata: layerMetadata, } } From 0e621c31d6044201e021146486e81102a6790985 Mon Sep 17 00:00:00 2001 From: Rex P Date: Tue, 7 Jan 2025 16:28:07 +1100 Subject: [PATCH 14/45] Add package scanning test for golang --- cmd/osv-scanner/__snapshots__/main_test.snap | 387 +++++++++++++++++++ cmd/osv-scanner/main_test.go | 5 + 2 files changed, 392 insertions(+) diff --git a/cmd/osv-scanner/__snapshots__/main_test.snap b/cmd/osv-scanner/__snapshots__/main_test.snap index 209457c1c0..c14eb65da4 100755 --- a/cmd/osv-scanner/__snapshots__/main_test.snap +++ b/cmd/osv-scanner/__snapshots__/main_test.snap @@ -2862,6 +2862,393 @@ You can also view the full vulnerability list in your terminal with: `osv-scanne --- +[TestRun_OCIImage/scanning_project_packages_using_go_binaries - 1] +{ + "results": [ + { + "source": { + "path": "go/bin/more-vuln-overwrite-less-vuln", + "type": "lockfile" + }, + "packages": [ + { + "package": { + "name": "github.com/BurntSushi/toml", + "version": "1.2.0", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "6cc8902daccff4aa4df2c8299135ab77a720a07085c14b5bf88479a9e8f4d4e3", + "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.2.0 /go/bin/more-vuln-overwrite-less-vuln # buildkit", + "in_base_image": false + } + } + }, + { + "package": { + "name": "go", + "version": "1.22.4", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "7ea1ef19e522424287f7047a77782b9c02aa47b9e66af3ad5221f9c1bbd6ea6c", + "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.4.0 /go/bin/more-vuln-overwrite-less-vuln # buildkit", + "in_base_image": false + } + } + } + ] + }, + { + "source": { + "path": "go/bin/ptf-1.2.0", + "type": "lockfile" + }, + "packages": [ + { + "package": { + "name": "github.com/BurntSushi/toml", + "version": "1.2.0", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "b36c3126f9d7222e6ea661df36c4b1826e361e9f56c728ed130fb7a6699527bf", + "origin_command": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", + "in_base_image": false + } + } + }, + { + "package": { + "name": "go", + "version": "1.22.4", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "b36c3126f9d7222e6ea661df36c4b1826e361e9f56c728ed130fb7a6699527bf", + "origin_command": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", + "in_base_image": false + } + } + } + ] + }, + { + "source": { + "path": "go/bin/ptf-1.3.0", + "type": "lockfile" + }, + "packages": [ + { + "package": { + "name": "github.com/BurntSushi/toml", + "version": "1.3.0", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "a2d02f92daabf8f33eb6254b0e20044351fc7b5ee812babdb86c133584db608f", + "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.3.0-moved /go/bin/ptf-1.3.0 # buildkit", + "in_base_image": false + } + } + }, + { + "package": { + "name": "go", + "version": "1.22.4", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "a2d02f92daabf8f33eb6254b0e20044351fc7b5ee812babdb86c133584db608f", + "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.3.0-moved /go/bin/ptf-1.3.0 # buildkit", + "in_base_image": false + } + } + } + ] + }, + { + "source": { + "path": "go/bin/ptf-1.3.0-moved", + "type": "lockfile" + }, + "packages": [ + { + "package": { + "name": "github.com/BurntSushi/toml", + "version": "1.3.0", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "28797ca2a20ce305c2b3323047bfe00ec13e4cf44f745cdb30b026a3348cfd62", + "origin_command": "RUN /bin/sh -c mv /go/bin/ptf-1.3.0 /go/bin/ptf-1.3.0-moved # buildkit", + "in_base_image": false + } + } + }, + { + "package": { + "name": "go", + "version": "1.22.4", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "28797ca2a20ce305c2b3323047bfe00ec13e4cf44f745cdb30b026a3348cfd62", + "origin_command": "RUN /bin/sh -c mv /go/bin/ptf-1.3.0 /go/bin/ptf-1.3.0-moved # buildkit", + "in_base_image": false + } + } + } + ] + }, + { + "source": { + "path": "go/bin/ptf-1.4.0", + "type": "lockfile" + }, + "packages": [ + { + "package": { + "name": "github.com/BurntSushi/toml", + "version": "1.4.0", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "b36c3126f9d7222e6ea661df36c4b1826e361e9f56c728ed130fb7a6699527bf", + "origin_command": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", + "in_base_image": false + } + } + }, + { + "package": { + "name": "go", + "version": "1.22.4", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "b36c3126f9d7222e6ea661df36c4b1826e361e9f56c728ed130fb7a6699527bf", + "origin_command": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", + "in_base_image": false + } + } + } + ] + }, + { + "source": { + "path": "go/bin/ptf-vulnerable", + "type": "lockfile" + }, + "packages": [ + { + "package": { + "name": "github.com/BurntSushi/toml", + "version": "1.4.0", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "392f16a6230e5b523709d4d6f572e6fd5a1c7ef4b0a0e6d26948807c1f43817b", + "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.4.0 /go/bin/ptf-vulnerable # buildkit", + "in_base_image": false + } + } + }, + { + "package": { + "name": "go", + "version": "1.22.4", + "ecosystem": "Go", + "imageOrigin": { + "diff_id": "6b295e7535ed4eefabe9f024e9e8c53c02705be23b27dcbc55857ea8dd087f9d", + "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.3.0 /go/bin/ptf-vulnerable # buildkit", + "in_base_image": false + } + } + } + ] + }, + { + "source": { + "path": "lib/apk/db/installed", + "type": "os" + }, + "packages": [ + { + "package": { + "name": "alpine-baselayout", + "version": "3.6.5-r0", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "alpine-baselayout-data", + "version": "3.6.5-r0", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "alpine-keys", + "version": "2.4-r1", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "apk-tools", + "version": "2.14.4-r0", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "busybox", + "version": "1.36.1-r29", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "busybox-binsh", + "version": "1.36.1-r29", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "ca-certificates-bundle", + "version": "20240226-r0", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "libcrypto3", + "version": "3.3.1-r0", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "libssl3", + "version": "3.3.1-r0", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "musl", + "version": "1.2.5-r0", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "musl-utils", + "version": "1.2.5-r0", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "scanelf", + "version": "1.3.7-r2", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "ssl_client", + "version": "1.36.1-r29", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + }, + { + "package": { + "name": "zlib", + "version": "1.3.1-r1", + "ecosystem": "Alpine:v3.20", + "imageOrigin": { + "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", + "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", + "in_base_image": false + } + } + } + ] + } + ], + "experimental_config": { + "licenses": { + "summary": false, + "allowlist": null + } + } +} + +--- + +[TestRun_OCIImage/scanning_project_packages_using_go_binaries - 2] +Scanning image "../../internal/image/fixtures/test-package-tracing.tar" + +--- + [TestRun_SubCommands/scan_with_a_flag - 1] Scanning dir ./fixtures/locks-one-with-nested Scanned /fixtures/locks-one-with-nested/nested/composer.lock file and found 1 package diff --git a/cmd/osv-scanner/main_test.go b/cmd/osv-scanner/main_test.go index d31e2676ad..7124dd6026 100644 --- a/cmd/osv-scanner/main_test.go +++ b/cmd/osv-scanner/main_test.go @@ -853,6 +853,11 @@ func TestRun_OCIImage(t *testing.T) { args: []string{"", "--experimental-oci-image", "../../internal/image/fixtures/test-node_modules-pnpm-full.tar"}, exit: 1, }, + { + name: "scanning project packages using go binaries", + args: []string{"", "--experimental-all-packages", "--format=json", "--experimental-oci-image", "../../internal/image/fixtures/test-package-tracing.tar"}, + exit: 1, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 464b773eb3b1cd372fbf3a677a1c3661256d8a01 Mon Sep 17 00:00:00 2001 From: Rex P Date: Tue, 7 Jan 2025 16:32:21 +1100 Subject: [PATCH 15/45] Fix stdlib --- cmd/osv-scanner/__snapshots__/main_test.snap | 429 +++---------------- cmd/osv-scanner/main_test.go | 2 +- internal/imodels/ecosystem/ecosystem.go | 6 + internal/imodels/imodels.go | 57 ++- 4 files changed, 94 insertions(+), 400 deletions(-) diff --git a/cmd/osv-scanner/__snapshots__/main_test.snap b/cmd/osv-scanner/__snapshots__/main_test.snap index c14eb65da4..9996ab24f7 100755 --- a/cmd/osv-scanner/__snapshots__/main_test.snap +++ b/cmd/osv-scanner/__snapshots__/main_test.snap @@ -2863,389 +2863,60 @@ You can also view the full vulnerability list in your terminal with: `osv-scanne --- [TestRun_OCIImage/scanning_project_packages_using_go_binaries - 1] -{ - "results": [ - { - "source": { - "path": "go/bin/more-vuln-overwrite-less-vuln", - "type": "lockfile" - }, - "packages": [ - { - "package": { - "name": "github.com/BurntSushi/toml", - "version": "1.2.0", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "6cc8902daccff4aa4df2c8299135ab77a720a07085c14b5bf88479a9e8f4d4e3", - "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.2.0 /go/bin/more-vuln-overwrite-less-vuln # buildkit", - "in_base_image": false - } - } - }, - { - "package": { - "name": "go", - "version": "1.22.4", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "7ea1ef19e522424287f7047a77782b9c02aa47b9e66af3ad5221f9c1bbd6ea6c", - "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.4.0 /go/bin/more-vuln-overwrite-less-vuln # buildkit", - "in_base_image": false - } - } - } - ] - }, - { - "source": { - "path": "go/bin/ptf-1.2.0", - "type": "lockfile" - }, - "packages": [ - { - "package": { - "name": "github.com/BurntSushi/toml", - "version": "1.2.0", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "b36c3126f9d7222e6ea661df36c4b1826e361e9f56c728ed130fb7a6699527bf", - "origin_command": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", - "in_base_image": false - } - } - }, - { - "package": { - "name": "go", - "version": "1.22.4", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "b36c3126f9d7222e6ea661df36c4b1826e361e9f56c728ed130fb7a6699527bf", - "origin_command": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", - "in_base_image": false - } - } - } - ] - }, - { - "source": { - "path": "go/bin/ptf-1.3.0", - "type": "lockfile" - }, - "packages": [ - { - "package": { - "name": "github.com/BurntSushi/toml", - "version": "1.3.0", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "a2d02f92daabf8f33eb6254b0e20044351fc7b5ee812babdb86c133584db608f", - "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.3.0-moved /go/bin/ptf-1.3.0 # buildkit", - "in_base_image": false - } - } - }, - { - "package": { - "name": "go", - "version": "1.22.4", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "a2d02f92daabf8f33eb6254b0e20044351fc7b5ee812babdb86c133584db608f", - "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.3.0-moved /go/bin/ptf-1.3.0 # buildkit", - "in_base_image": false - } - } - } - ] - }, - { - "source": { - "path": "go/bin/ptf-1.3.0-moved", - "type": "lockfile" - }, - "packages": [ - { - "package": { - "name": "github.com/BurntSushi/toml", - "version": "1.3.0", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "28797ca2a20ce305c2b3323047bfe00ec13e4cf44f745cdb30b026a3348cfd62", - "origin_command": "RUN /bin/sh -c mv /go/bin/ptf-1.3.0 /go/bin/ptf-1.3.0-moved # buildkit", - "in_base_image": false - } - } - }, - { - "package": { - "name": "go", - "version": "1.22.4", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "28797ca2a20ce305c2b3323047bfe00ec13e4cf44f745cdb30b026a3348cfd62", - "origin_command": "RUN /bin/sh -c mv /go/bin/ptf-1.3.0 /go/bin/ptf-1.3.0-moved # buildkit", - "in_base_image": false - } - } - } - ] - }, - { - "source": { - "path": "go/bin/ptf-1.4.0", - "type": "lockfile" - }, - "packages": [ - { - "package": { - "name": "github.com/BurntSushi/toml", - "version": "1.4.0", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "b36c3126f9d7222e6ea661df36c4b1826e361e9f56c728ed130fb7a6699527bf", - "origin_command": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", - "in_base_image": false - } - } - }, - { - "package": { - "name": "go", - "version": "1.22.4", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "b36c3126f9d7222e6ea661df36c4b1826e361e9f56c728ed130fb7a6699527bf", - "origin_command": "COPY /work/ptf-1.2.0 /work/ptf-1.3.0 /work/ptf-1.4.0 /go/bin/ # buildkit", - "in_base_image": false - } - } - } - ] - }, - { - "source": { - "path": "go/bin/ptf-vulnerable", - "type": "lockfile" - }, - "packages": [ - { - "package": { - "name": "github.com/BurntSushi/toml", - "version": "1.4.0", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "392f16a6230e5b523709d4d6f572e6fd5a1c7ef4b0a0e6d26948807c1f43817b", - "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.4.0 /go/bin/ptf-vulnerable # buildkit", - "in_base_image": false - } - } - }, - { - "package": { - "name": "go", - "version": "1.22.4", - "ecosystem": "Go", - "imageOrigin": { - "diff_id": "6b295e7535ed4eefabe9f024e9e8c53c02705be23b27dcbc55857ea8dd087f9d", - "origin_command": "RUN /bin/sh -c cp /go/bin/ptf-1.3.0 /go/bin/ptf-vulnerable # buildkit", - "in_base_image": false - } - } - } - ] - }, - { - "source": { - "path": "lib/apk/db/installed", - "type": "os" - }, - "packages": [ - { - "package": { - "name": "alpine-baselayout", - "version": "3.6.5-r0", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "alpine-baselayout-data", - "version": "3.6.5-r0", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "alpine-keys", - "version": "2.4-r1", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "apk-tools", - "version": "2.14.4-r0", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "busybox", - "version": "1.36.1-r29", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "busybox-binsh", - "version": "1.36.1-r29", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "ca-certificates-bundle", - "version": "20240226-r0", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "libcrypto3", - "version": "3.3.1-r0", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "libssl3", - "version": "3.3.1-r0", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "musl", - "version": "1.2.5-r0", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "musl-utils", - "version": "1.2.5-r0", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "scanelf", - "version": "1.3.7-r2", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "ssl_client", - "version": "1.36.1-r29", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - }, - { - "package": { - "name": "zlib", - "version": "1.3.1-r1", - "ecosystem": "Alpine:v3.20", - "imageOrigin": { - "diff_id": "94e5f06ff8e3d4441dc3cd8b090ff38dc911bfa8ebdb0dc28395bc98f82f983f", - "origin_command": "/bin/sh -c #(nop) ADD file:33ebe56b967747a97dcec01bc2559962bee8823686c9739d26be060381bbb3ca in / ", - "in_base_image": false - } - } - } - ] - } - ], - "experimental_config": { - "licenses": { - "summary": false, - "allowlist": null - } - } -} +Scanning image "../../internal/image/fixtures/test-package-tracing.tar" +Total 6 packages affected by 24 vulnerabilities (0 Critical, 0 High, 0 Medium, 0 Low, 24 Unknown) from 1 ecosystems. +24 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 | ++---------+-------------------+---------------+------------+ + +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_project_packages_using_go_binaries - 2] -Scanning image "../../internal/image/fixtures/test-package-tracing.tar" --- diff --git a/cmd/osv-scanner/main_test.go b/cmd/osv-scanner/main_test.go index 7124dd6026..46cf3500e0 100644 --- a/cmd/osv-scanner/main_test.go +++ b/cmd/osv-scanner/main_test.go @@ -855,7 +855,7 @@ func TestRun_OCIImage(t *testing.T) { }, { name: "scanning project packages using go binaries", - args: []string{"", "--experimental-all-packages", "--format=json", "--experimental-oci-image", "../../internal/image/fixtures/test-package-tracing.tar"}, + args: []string{"", "--experimental-oci-image", "../../internal/image/fixtures/test-package-tracing.tar"}, exit: 1, }, } diff --git a/internal/imodels/ecosystem/ecosystem.go b/internal/imodels/ecosystem/ecosystem.go index 8fe7d86e49..677ef70dc0 100644 --- a/internal/imodels/ecosystem/ecosystem.go +++ b/internal/imodels/ecosystem/ecosystem.go @@ -94,6 +94,12 @@ func MustParse(str string) Parsed { // Parse parses a string into a constants.Ecosystem and an optional suffix specified with a ":" func Parse(str string) (Parsed, error) { + // Special case to return an empty ecosystem if str is empty + // This is not considered an error. + if str == "" { + return Parsed{}, nil + } + ecosystem, suffix, _ := strings.Cut(str, ":") // Always return the full parsed value even if it might be invalid diff --git a/internal/imodels/imodels.go b/internal/imodels/imodels.go index d081bc8dcb..c8ee3dd604 100644 --- a/internal/imodels/imodels.go +++ b/internal/imodels/imodels.go @@ -12,6 +12,7 @@ import ( "github.com/google/osv-scanner/internal/imodels/ecosystem" "github.com/google/osv-scanner/internal/scalibrextract/vcs/gitrepo" "github.com/google/osv-scanner/pkg/models" + "github.com/ossf/osv-schema/bindings/go/osvschema" scalibrosv "github.com/google/osv-scalibr/extractor/filesystem/osv" ) @@ -68,49 +69,35 @@ func FromInventory(inventory *extractor.Inventory) PackageInfo { Location: inventory.Locations[0], AdditionalLocations: inventory.Locations[1:], OriginalInventory: inventory, - // TODO: SourceType } - // Ignore this error for now as we can't do too much about an unknown ecosystem + // Set Ecosystem eco, err := ecosystem.Parse(inventory.Ecosystem()) if err != nil { + // Ignore this error for now as we can't do too much about an unknown ecosystem // TODO(v2): Replace with slog log.Printf("Warning: %s\n", err.Error()) } - pkgInfo.Ecosystem = eco + // Set Commit and Repository if inventory.SourceCode != nil { pkgInfo.Commit = inventory.SourceCode.Commit pkgInfo.Repository = inventory.SourceCode.Repo } + // Set DepGroups if dg, ok := inventory.Metadata.(scalibrosv.DepGroups); ok { pkgInfo.DepGroups = dg.DepGroups() } + + // Set SourceType if inventory.Extractor != nil { extractorName := inventory.Extractor.Name() if _, ok := osExtractors[extractorName]; ok { pkgInfo.SourceType = SourceTypeOSPackage } else if _, ok := sbomExtractors[extractorName]; ok { pkgInfo.SourceType = SourceTypeSBOM - - // TODO (V2): SBOMs have a special case where we manually convert the PURL here - // instead while PURL to ESI conversion is not complete - purl := inventory.Extractor.ToPURL(inventory) - - if purl != nil { - // Error should never happen here since the PURL is from an already parsed purl - pi, _ := models.PURLToPackage(purl.String()) - pkgInfo.Name = pi.Name - pkgInfo.Version = pi.Version - parsed, err := ecosystem.Parse(pi.Ecosystem) - if err != nil { - // TODO: Replace with slog - log.Printf("Warning, found unexpected ecosystem in purl %q, likely will not return any results for this package.\n", purl.String()) - } - pkgInfo.Ecosystem = parsed - } } else if _, ok := gitExtractors[extractorName]; ok { pkgInfo.SourceType = SourceTypeGit } else { @@ -118,6 +105,36 @@ func FromInventory(inventory *extractor.Inventory) PackageInfo { } } + // --- General Patching --- + + // 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 eco.Ecosystem == osvschema.EcosystemGo && inventory.Name == "go" { + pkgInfo.Name = "stdlib" + } + + // TODO (V2): SBOMs have a special case where we manually convert the PURL here + // instead while PURL to ESI conversion is not complete + if pkgInfo.SourceType == SourceTypeSBOM { + purl := inventory.Extractor.ToPURL(inventory) + if purl != nil { + // Error should never happen here since the PURL is from an already parsed purl + pi, _ := models.PURLToPackage(purl.String()) + pkgInfo.Name = pi.Name + pkgInfo.Version = pi.Version + parsed, err := ecosystem.Parse(pi.Ecosystem) + if err != nil { + // TODO: Replace with slog + log.Printf("Warning, found unexpected ecosystem in purl %q, likely will not return any results for this package.\n", purl.String()) + } + pkgInfo.Ecosystem = parsed + } + } + + // --- Metadata Patching --- + + // Depending on the specific metadata types set fields in package info. if metadata, ok := inventory.Metadata.(*apk.Metadata); ok { pkgInfo.OSPackageName = metadata.PackageName } else if metadata, ok := inventory.Metadata.(*dpkg.Metadata); ok { From 50fcd89b435351dff2179db220823eb128b2af63 Mon Sep 17 00:00:00 2001 From: Rex P Date: Wed, 8 Jan 2025 11:59:31 +1100 Subject: [PATCH 16/45] License matcher --- .../licensematcher/licensematcher.go | 109 ++++++++++++++++++ .../clientinterfaces/licensematcher.go | 11 ++ internal/depsdev/license.go | 93 --------------- pkg/osvscanner/osvscanner.go | 40 ++----- 4 files changed, 132 insertions(+), 121 deletions(-) create mode 100644 internal/clients/clientimpl/licensematcher/licensematcher.go create mode 100644 internal/clients/clientinterfaces/licensematcher.go diff --git a/internal/clients/clientimpl/licensematcher/licensematcher.go b/internal/clients/clientimpl/licensematcher/licensematcher.go new file mode 100644 index 0000000000..f5ab531714 --- /dev/null +++ b/internal/clients/clientimpl/licensematcher/licensematcher.go @@ -0,0 +1,109 @@ +package licensematcher + +import ( + "context" + + depsdevpb "deps.dev/api/v3" + "github.com/google/osv-scanner/internal/depsdev" + "github.com/google/osv-scanner/internal/imodels" + "github.com/google/osv-scanner/internal/resolution/datasource" + "github.com/google/osv-scanner/pkg/models" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const ( + maxConcurrentRequests = 1000 +) + +// DepsDevLicenseMatcher implements the LicenseMatcher interface with a deps.dev client. +// It sends out requests for every package version and does not perform caching. +type DepsDevLicenseMatcher struct { + Client *datasource.DepsDevAPIClient +} + +func (matcher *DepsDevLicenseMatcher) MatchLicenses(ctx context.Context, packages []imodels.PackageScanResult) error { + queries := make([]*depsdevpb.GetVersionRequest, len(packages)) + + for i, psr := range packages { + pkg := psr.PackageInfo + system, ok := depsdev.System[psr.PackageInfo.Ecosystem.Ecosystem] + if !ok || pkg.Name == "" || pkg.Version == "" { + continue + } + queries[i] = versionQuery(system, pkg.Name, pkg.Version) + } + + licenses, err := matcher.makeVersionRequest(ctx, queries) + if err != nil { + return err + } + + for i, license := range licenses { + packages[i].Licenses = license + } + + return nil +} + +// makeVersionRequest calls the deps.dev GetVersion gRPC API endpoint for each +// query. It makes these requests concurrently, sharing the single HTTP/2 +// connection. The order in which the requests are specified should correspond +// to the order of licenses returned by this function. +func (matcher *DepsDevLicenseMatcher) makeVersionRequest(ctx context.Context, queries []*depsdevpb.GetVersionRequest) ([][]models.License, error) { + licenses := make([][]models.License, len(queries)) + g, ctx := errgroup.WithContext(ctx) + g.SetLimit(maxConcurrentRequests) + + for i := range queries { + if queries[i] == nil { + // This may be a private package. + licenses[i] = []models.License{models.License("UNKNOWN")} + continue + } + g.Go(func() error { + resp, err := matcher.Client.GetVersion(ctx, queries[i]) + if err != nil { + if status.Code(err) == codes.NotFound { + licenses[i] = append(licenses[i], "UNKNOWN") + return nil + } + + return err + } + ls := make([]models.License, len(resp.GetLicenses())) + for j, license := range resp.GetLicenses() { + ls[j] = models.License(license) + } + if len(ls) == 0 { + // The deps.dev API will return an + // empty slice if the license is + // unknown. + ls = []models.License{models.License("UNKNOWN")} + } + licenses[i] = ls + + return nil + }) + } + if err := g.Wait(); err != nil { + return nil, err + } + + return licenses, nil +} + +func versionQuery(system depsdevpb.System, name string, version string) *depsdevpb.GetVersionRequest { + if system == depsdevpb.System_GO { + version = "v" + version + } + + return &depsdevpb.GetVersionRequest{ + VersionKey: &depsdevpb.VersionKey{ + System: system, + Name: name, + Version: version, + }, + } +} diff --git a/internal/clients/clientinterfaces/licensematcher.go b/internal/clients/clientinterfaces/licensematcher.go new file mode 100644 index 0000000000..94f4bb2650 --- /dev/null +++ b/internal/clients/clientinterfaces/licensematcher.go @@ -0,0 +1,11 @@ +package clientinterfaces + +import ( + "context" + + "github.com/google/osv-scanner/internal/imodels" +) + +type LicenseMatcher interface { + MatchLicenses(ctx context.Context, psr []imodels.PackageScanResult) error +} diff --git a/internal/depsdev/license.go b/internal/depsdev/license.go index 2e3de4b4d7..18fcc98010 100644 --- a/internal/depsdev/license.go +++ b/internal/depsdev/license.go @@ -1,20 +1,9 @@ package depsdev import ( - "context" - "crypto/x509" - "fmt" - - "github.com/google/osv-scanner/pkg/models" - "github.com/google/osv-scanner/pkg/osv" "github.com/ossf/osv-schema/bindings/go/osvschema" depsdevpb "deps.dev/api/v3" - "golang.org/x/sync/errgroup" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/status" ) // DepsdevAPI is the URL to the deps.dev API. It is documented at @@ -30,85 +19,3 @@ var System = map[osvschema.Ecosystem]depsdevpb.System{ osvschema.EcosystemMaven: depsdevpb.System_MAVEN, osvschema.EcosystemPyPI: depsdevpb.System_PYPI, } - -// VersionQuery constructs a GetVersion request from the arguments. -func VersionQuery(system depsdevpb.System, name string, version string) *depsdevpb.GetVersionRequest { - if system == depsdevpb.System_GO { - version = "v" + version - } - - return &depsdevpb.GetVersionRequest{ - VersionKey: &depsdevpb.VersionKey{ - System: system, - Name: name, - Version: version, - }, - } -} - -// MakeVersionRequests wraps MakeVersionRequestsWithContext using context.Background. -func MakeVersionRequests(queries []*depsdevpb.GetVersionRequest) ([][]models.License, error) { - return MakeVersionRequestsWithContext(context.Background(), queries) -} - -// MakeVersionRequestsWithContext calls the deps.dev GetVersion gRPC API endpoint for each -// query. It makes these requests concurrently, sharing the single HTTP/2 -// connection. The order in which the requests are specified should correspond -// to the order of licenses returned by this function. -func MakeVersionRequestsWithContext(ctx context.Context, queries []*depsdevpb.GetVersionRequest) ([][]models.License, error) { - certPool, err := x509.SystemCertPool() - if err != nil { - return nil, fmt.Errorf("getting system cert pool: %w", err) - } - creds := credentials.NewClientTLSFromCert(certPool, "") - dialOpts := []grpc.DialOption{grpc.WithTransportCredentials(creds)} - - if osv.RequestUserAgent != "" { - dialOpts = append(dialOpts, grpc.WithUserAgent(osv.RequestUserAgent)) - } - - conn, err := grpc.NewClient(DepsdevAPI, dialOpts...) - if err != nil { - return nil, fmt.Errorf("dialing deps.dev gRPC API: %w", err) - } - client := depsdevpb.NewInsightsClient(conn) - - licenses := make([][]models.License, len(queries)) - g, ctx := errgroup.WithContext(ctx) - for i := range queries { - if queries[i] == nil { - // This may be a private package. - licenses[i] = []models.License{models.License("UNKNOWN")} - continue - } - g.Go(func() error { - resp, err := client.GetVersion(ctx, queries[i]) - if err != nil { - if status.Code(err) == codes.NotFound { - licenses[i] = append(licenses[i], "UNKNOWN") - return nil - } - - return err - } - ls := make([]models.License, len(resp.GetLicenses())) - for j, license := range resp.GetLicenses() { - ls[j] = models.License(license) - } - if len(ls) == 0 { - // The deps.dev API will return an - // empty slice if the license is - // unknown. - ls = []models.License{models.License("UNKNOWN")} - } - licenses[i] = ls - - return nil - }) - } - if err := g.Wait(); err != nil { - return nil, err - } - - return licenses, nil -} diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index b2846679a1..99b254c414 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -3,10 +3,10 @@ package osvscanner import ( "context" "errors" - "fmt" "time" "github.com/google/osv-scalibr/extractor" + "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" "github.com/google/osv-scanner/internal/clients/clientinterfaces" @@ -16,11 +16,10 @@ import ( "github.com/google/osv-scanner/internal/imodels/results" "github.com/google/osv-scanner/internal/osvdev" "github.com/google/osv-scanner/internal/output" + "github.com/google/osv-scanner/internal/resolution/datasource" "github.com/google/osv-scanner/pkg/models" "github.com/google/osv-scanner/pkg/reporter" "github.com/ossf/osv-schema/bindings/go/osvschema" - - depsdevpb "deps.dev/api/v3" ) type ScannerActions struct { @@ -146,8 +145,17 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe } } + // --- Make License Requests --- if len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary { - err = makeLicensesRequests(scanResult.PackageScanResults) + depsdevclient, err := datasource.NewDepsDevAPIClient(depsdev.DepsdevAPI) + if err != nil { + return models.VulnerabilityResults{}, err + } + var licenseMatcher clientinterfaces.LicenseMatcher = &licensematcher.DepsDevLicenseMatcher{ + Client: depsdevclient, + } + + err = licenseMatcher.MatchLicenses(context.Background(), scanResult.PackageScanResults) if err != nil { return models.VulnerabilityResults{}, err } @@ -209,7 +217,6 @@ func makeRequestWithMatcher( res, err := matcher.MatchVulnerabilities(context.Background(), invs) if err != nil { - // TODO: Handle error here r.Errorf("error when retrieving vulns: %v", err) if res == nil { return err @@ -223,29 +230,6 @@ func makeRequestWithMatcher( return nil } -// TODO(V2): Replace with client -func makeLicensesRequests(packages []imodels.PackageScanResult) error { - queries := make([]*depsdevpb.GetVersionRequest, len(packages)) - for i, psr := range packages { - pkg := psr.PackageInfo - system, ok := depsdev.System[psr.PackageInfo.Ecosystem().Ecosystem] - if !ok || pkg.Name() == "" || pkg.Version() == "" { - continue - } - queries[i] = depsdev.VersionQuery(system, pkg.Name(), pkg.Version()) - } - licenses, err := depsdev.MakeVersionRequests(queries) - if err != nil { - return fmt.Errorf("%w: deps.dev query failed: %w", ErrAPIFailed, err) - } - - for i, license := range licenses { - packages[i].Licenses = license - } - - return nil -} - // Overrides Go version using osv-scanner.toml func overrideGoVersion(r reporter.Reporter, scanResults *results.ScanResults) { for i, psr := range scanResults.PackageScanResults { From 18c31f294a8f038d42c374a4527a791cf11fa8d6 Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 9 Jan 2025 14:52:12 +1100 Subject: [PATCH 17/45] Setup user agent as a parameter --- cmd/osv-scanner/fix/main.go | 3 ++- cmd/osv-scanner/update/main.go | 3 ++- .../clients/clientimpl/licensematcher/licensematcher.go | 6 +++--- internal/depsdev/{license.go => depsdev.go} | 0 internal/osvdev/config.go | 2 +- internal/osvdev/osvdev.go | 4 +++- internal/resolution/client/depsdev_client.go | 4 ++-- internal/resolution/datasource/depsdev_api.go | 7 +++---- internal/version/version.go | 2 +- pkg/osvscanner/osvscanner.go | 3 ++- pkg/osvscanner/scan.go | 3 ++- pkg/osvscanner/vulnerability_result.go | 1 + scripts/generate_mock_resolution_universe/main.go | 3 ++- 13 files changed, 24 insertions(+), 17 deletions(-) rename internal/depsdev/{license.go => depsdev.go} (100%) diff --git a/cmd/osv-scanner/fix/main.go b/cmd/osv-scanner/fix/main.go index 0c7bed0448..0e5348042a 100644 --- a/cmd/osv-scanner/fix/main.go +++ b/cmd/osv-scanner/fix/main.go @@ -16,6 +16,7 @@ import ( "github.com/google/osv-scanner/internal/resolution/client" "github.com/google/osv-scanner/internal/resolution/lockfile" "github.com/google/osv-scanner/internal/resolution/manifest" + "github.com/google/osv-scanner/internal/version" "github.com/google/osv-scanner/pkg/reporter" "github.com/urfave/cli/v2" "golang.org/x/term" @@ -330,7 +331,7 @@ func action(ctx *cli.Context, stdout, stderr io.Writer) (reporter.Reporter, erro switch ctx.String("data-source") { case "deps.dev": - cl, err := client.NewDepsDevClient(depsdev.DepsdevAPI) + cl, err := client.NewDepsDevClient(depsdev.DepsdevAPI, "osv-scanner_fix/"+version.OSVVersion) if err != nil { return nil, err } diff --git a/cmd/osv-scanner/update/main.go b/cmd/osv-scanner/update/main.go index 5e74772740..d60cc9a81f 100644 --- a/cmd/osv-scanner/update/main.go +++ b/cmd/osv-scanner/update/main.go @@ -10,6 +10,7 @@ import ( "github.com/google/osv-scanner/internal/remediation/suggest" "github.com/google/osv-scanner/internal/resolution/client" "github.com/google/osv-scanner/internal/resolution/manifest" + "github.com/google/osv-scanner/internal/version" "github.com/google/osv-scanner/pkg/lockfile" "github.com/google/osv-scanner/pkg/reporter" "github.com/urfave/cli/v2" @@ -74,7 +75,7 @@ func action(ctx *cli.Context, stdout, stderr io.Writer) (reporter.Reporter, erro } var err error - options.Client.DependencyClient, err = client.NewDepsDevClient(depsdev.DepsdevAPI) + options.Client.DependencyClient, err = client.NewDepsDevClient(depsdev.DepsdevAPI, "osv-scanner_update/"+version.OSVVersion) if err != nil { return nil, err } diff --git a/internal/clients/clientimpl/licensematcher/licensematcher.go b/internal/clients/clientimpl/licensematcher/licensematcher.go index f5ab531714..aa83e22c55 100644 --- a/internal/clients/clientimpl/licensematcher/licensematcher.go +++ b/internal/clients/clientimpl/licensematcher/licensematcher.go @@ -28,11 +28,11 @@ func (matcher *DepsDevLicenseMatcher) MatchLicenses(ctx context.Context, package for i, psr := range packages { pkg := psr.PackageInfo - system, ok := depsdev.System[psr.PackageInfo.Ecosystem.Ecosystem] - if !ok || pkg.Name == "" || pkg.Version == "" { + system, ok := depsdev.System[psr.PackageInfo.Ecosystem().Ecosystem] + if !ok || pkg.Name() == "" || pkg.Version() == "" { continue } - queries[i] = versionQuery(system, pkg.Name, pkg.Version) + queries[i] = versionQuery(system, pkg.Name(), pkg.Version()) } licenses, err := matcher.makeVersionRequest(ctx, queries) diff --git a/internal/depsdev/license.go b/internal/depsdev/depsdev.go similarity index 100% rename from internal/depsdev/license.go rename to internal/depsdev/depsdev.go diff --git a/internal/osvdev/config.go b/internal/osvdev/config.go index 3d73fdaf09..5b7d7fc60b 100644 --- a/internal/osvdev/config.go +++ b/internal/osvdev/config.go @@ -18,7 +18,7 @@ func DefaultConfig() ClientConfig { JitterMultiplier: 2, BackoffDurationExponential: 2, BackoffDurationMultiplier: 1, - UserAgent: "osv-scanner/" + version.OSVVersion, + UserAgent: "osv-scanner_scan/" + version.OSVVersion, MaxConcurrentBatchRequests: 10, } } diff --git a/internal/osvdev/osvdev.go b/internal/osvdev/osvdev.go index 6d0074bf64..cfed5f92b8 100644 --- a/internal/osvdev/osvdev.go +++ b/internal/osvdev/osvdev.go @@ -26,6 +26,8 @@ const ( // MaxQueriesPerQueryBatchRequest is a limit set in osv.dev's API, so is not configurable MaxQueriesPerQueryBatchRequest = 1000 + + DefaultBaseURL = "https://api.osv.dev" ) type OSVClient struct { @@ -39,7 +41,7 @@ func DefaultClient() *OSVClient { return &OSVClient{ HTTPClient: http.DefaultClient, Config: DefaultConfig(), - BaseHostURL: "https://api.osv.dev", + BaseHostURL: DefaultBaseURL, } } diff --git a/internal/resolution/client/depsdev_client.go b/internal/resolution/client/depsdev_client.go index ffd0d91257..8a504b8b54 100644 --- a/internal/resolution/client/depsdev_client.go +++ b/internal/resolution/client/depsdev_client.go @@ -16,8 +16,8 @@ type DepsDevClient struct { c *datasource.DepsDevAPIClient } -func NewDepsDevClient(addr string) (*DepsDevClient, error) { - c, err := datasource.NewDepsDevAPIClient(addr) +func NewDepsDevClient(addr string, userAgent string) (*DepsDevClient, error) { + c, err := datasource.NewDepsDevAPIClient(addr, userAgent) if err != nil { return nil, err } diff --git a/internal/resolution/datasource/depsdev_api.go b/internal/resolution/datasource/depsdev_api.go index ecf0038709..01173375bf 100644 --- a/internal/resolution/datasource/depsdev_api.go +++ b/internal/resolution/datasource/depsdev_api.go @@ -8,7 +8,6 @@ import ( "time" pb "deps.dev/api/v3" - "github.com/google/osv-scanner/pkg/osv" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) @@ -52,7 +51,7 @@ func makeVersionKey(k *pb.VersionKey) versionKey { } } -func NewDepsDevAPIClient(addr string) (*DepsDevAPIClient, error) { +func NewDepsDevAPIClient(addr string, userAgent string) (*DepsDevAPIClient, error) { certPool, err := x509.SystemCertPool() if err != nil { return nil, fmt.Errorf("getting system cert pool: %w", err) @@ -60,8 +59,8 @@ func NewDepsDevAPIClient(addr string) (*DepsDevAPIClient, error) { creds := credentials.NewClientTLSFromCert(certPool, "") dialOpts := []grpc.DialOption{grpc.WithTransportCredentials(creds)} - if osv.RequestUserAgent != "" { - dialOpts = append(dialOpts, grpc.WithUserAgent(osv.RequestUserAgent)) + if userAgent != "" { + dialOpts = append(dialOpts, grpc.WithUserAgent(userAgent)) } conn, err := grpc.NewClient(addr, dialOpts...) diff --git a/internal/version/version.go b/internal/version/version.go index 995b31d6ea..6857939d1c 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -1,4 +1,4 @@ package version // OSVVersion is the current release version, you should update this variable when doing a release -var OSVVersion = "1.9.1" +const OSVVersion = "1.9.1" diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 99b254c414..c3a48557fd 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -17,6 +17,7 @@ import ( "github.com/google/osv-scanner/internal/osvdev" "github.com/google/osv-scanner/internal/output" "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/reporter" "github.com/ossf/osv-schema/bindings/go/osvschema" @@ -147,7 +148,7 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe // --- Make License Requests --- if len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary { - depsdevclient, err := datasource.NewDepsDevAPIClient(depsdev.DepsdevAPI) + depsdevclient, err := datasource.NewDepsDevAPIClient(depsdev.DepsdevAPI, "osv-scanner_scan/"+version.OSVVersion) if err != nil { return models.VulnerabilityResults{}, err } diff --git a/pkg/osvscanner/scan.go b/pkg/osvscanner/scan.go index 610d6301b0..cd6ea6f99f 100644 --- a/pkg/osvscanner/scan.go +++ b/pkg/osvscanner/scan.go @@ -9,6 +9,7 @@ import ( "github.com/google/osv-scanner/internal/resolution/datasource" "github.com/google/osv-scanner/internal/scalibrextract/ecosystemmock" "github.com/google/osv-scanner/internal/scalibrextract/language/java/pomxmlnet" + "github.com/google/osv-scanner/internal/version" "github.com/google/osv-scanner/pkg/osvscanner/internal/scanners" "github.com/google/osv-scanner/pkg/reporter" ) @@ -96,7 +97,7 @@ func createMavenExtractor(actions TransitiveScanningActions) (*pomxmlnet.Extract if actions.NativeDataSource { depClient, err = client.NewMavenRegistryClient(actions.MavenRegistry) } else { - depClient, err = client.NewDepsDevClient(depsdev.DepsdevAPI) + depClient, err = client.NewDepsDevClient(depsdev.DepsdevAPI, "osv-scanner_scan/"+version.OSVVersion) } if err != nil { return nil, err diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go index f3160ffd59..6005ec601a 100644 --- a/pkg/osvscanner/vulnerability_result.go +++ b/pkg/osvscanner/vulnerability_result.go @@ -129,6 +129,7 @@ func buildVulnerabilityResults( } } + // TODO(v2): Move source analysis out of here. for source, packages := range groupedBySource { sourceanalysis.Run(r, source, packages, actions.CallAnalysisStates) results.Results = append(results.Results, models.PackageSource{ diff --git a/scripts/generate_mock_resolution_universe/main.go b/scripts/generate_mock_resolution_universe/main.go index cd66b88e11..c926293fb0 100644 --- a/scripts/generate_mock_resolution_universe/main.go +++ b/scripts/generate_mock_resolution_universe/main.go @@ -31,6 +31,7 @@ import ( "github.com/google/osv-scanner/internal/resolution/lockfile" "github.com/google/osv-scanner/internal/resolution/manifest" "github.com/google/osv-scanner/internal/resolution/util" + "github.com/google/osv-scanner/internal/version" lf "github.com/google/osv-scanner/pkg/lockfile" "github.com/google/osv-scanner/pkg/models" "github.com/google/osv-scanner/pkg/osv" @@ -282,7 +283,7 @@ func typeString(t dep.Type) string { } func main() { - cl, err := client.NewDepsDevClient(depsdev.DepsdevAPI) + cl, err := client.NewDepsDevClient(depsdev.DepsdevAPI, "osv-scanner_generate_mock/"+version.OSVVersion) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) From 10a196d2795a8ecd57894abeee20161fc152b483 Mon Sep 17 00:00:00 2001 From: Rex P Date: Wed, 8 Jan 2025 15:14:40 +1100 Subject: [PATCH 18/45] Extract matchers and rename Error --- cmd/osv-reporter/main.go | 6 +-- cmd/osv-scanner/main.go | 4 +- cmd/osv-scanner/scan/main.go | 2 +- pkg/osvscanner/osvscanner.go | 92 ++++++++++++++++++++---------------- pkg/osvscanner/scan.go | 2 +- 5 files changed, 58 insertions(+), 48 deletions(-) diff --git a/cmd/osv-reporter/main.go b/cmd/osv-reporter/main.go index deb3125a7f..83e368f294 100644 --- a/cmd/osv-reporter/main.go +++ b/cmd/osv-reporter/main.go @@ -183,7 +183,7 @@ func run(args []string, stdout, stderr io.Writer) int { // if vulnerability exists it should return error if len(diffVulns.Results) > 0 && failOnVuln && anyIsCalled { - return osvscanner.VulnerabilitiesFoundErr + return osvscanner.ErrVulnerabilitiesFound } return nil @@ -194,11 +194,11 @@ func run(args []string, stdout, stderr io.Writer) int { if tableReporter == nil { tableReporter = reporter.NewTableReporter(stdout, stderr, reporter.InfoLevel, false, 0) } - if errors.Is(err, osvscanner.VulnerabilitiesFoundErr) { + if errors.Is(err, osvscanner.ErrVulnerabilitiesFound) { return 1 } - if errors.Is(err, osvscanner.NoPackagesFoundErr) { + if errors.Is(err, osvscanner.ErrNoPackagesFound) { tableReporter.Errorf("No package sources found, --help for usage information.\n") return 128 } diff --git a/cmd/osv-scanner/main.go b/cmd/osv-scanner/main.go index 3c891f338c..59484f5f97 100644 --- a/cmd/osv-scanner/main.go +++ b/cmd/osv-scanner/main.go @@ -66,9 +66,9 @@ func run(args []string, stdout, stderr io.Writer) int { r = reporter.NewTableReporter(stdout, stderr, reporter.InfoLevel, false, 0) } switch { - case errors.Is(err, osvscanner.VulnerabilitiesFoundErr): + case errors.Is(err, osvscanner.ErrVulnerabilitiesFound): return 1 - case errors.Is(err, osvscanner.NoPackagesFoundErr): + case errors.Is(err, osvscanner.ErrNoPackagesFound): r.Errorf("No package sources found, --help for usage information.\n") return 128 case errors.Is(err, osvscanner.ErrAPIFailed): diff --git a/cmd/osv-scanner/scan/main.go b/cmd/osv-scanner/scan/main.go index bf763f3b15..a9f73a4d93 100644 --- a/cmd/osv-scanner/scan/main.go +++ b/cmd/osv-scanner/scan/main.go @@ -313,7 +313,7 @@ func action(context *cli.Context, stdout, stderr io.Writer) (reporter.Reporter, }, }, r) - if err != nil && !errors.Is(err, osvscanner.VulnerabilitiesFoundErr) { + if err != nil && !errors.Is(err, osvscanner.ErrVulnerabilitiesFound) { return r, err } diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index c3a48557fd..b0e9765b50 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -3,6 +3,7 @@ package osvscanner import ( "context" "errors" + "fmt" "time" "github.com/google/osv-scalibr/extractor" @@ -56,25 +57,51 @@ type TransitiveScanningActions struct { MavenRegistry string } -// NoPackagesFoundErr for when no packages are found during a scan. -// -//nolint:errname,stylecheck,revive // Would require version major bump to change -var NoPackagesFoundErr = errors.New("no packages found in scan") +type Matchers struct { + VulnMatcher clientinterfaces.VulnerabilityMatcher + LicenseMatcher clientinterfaces.LicenseMatcher +} -// VulnerabilitiesFoundErr includes both vulnerabilities being found or license violations being found, -// however, will not be raised if only uncalled vulnerabilities are found. -// -//nolint:errname,stylecheck,revive // Would require version major bump to change -var VulnerabilitiesFoundErr = errors.New("vulnerabilities found") +// ErrNoPackagesFound for when no packages are found during a scan. +var ErrNoPackagesFound = errors.New("no packages found in scan") -// Deprecated: This error is no longer returned, check the results to determine if this is the case -// -//nolint:errname,stylecheck,revive // Would require version bump to change -var OnlyUncalledVulnerabilitiesFoundErr = errors.New("only uncalled vulnerabilities found") +// ErrVulnerabilitiesFound includes both vulnerabilities being found or license violations being found, +// however, will not be raised if only uncalled vulnerabilities are found. +var ErrVulnerabilitiesFound = errors.New("vulnerabilities found") // ErrAPIFailed describes errors related to querying API endpoints. var ErrAPIFailed = errors.New("API query failed") +func InitializeMatchers(r reporter.Reporter, actions ScannerActions) (Matchers, error) { + // Vulnerability Matcher + var vulnMatcher clientinterfaces.VulnerabilityMatcher + var err error + if actions.CompareOffline { + vulnMatcher, err = localmatcher.NewLocalMatcher(r, actions.LocalDBPath, actions.DownloadDatabases) + if err != nil { + return Matchers{}, err + } + } else { + vulnMatcher = &osvmatcher.OSVMatcher{ + Client: *osvdev.DefaultClient(), + InitialQueryTimeout: 5 * time.Minute, + } + } + + // License Matcher + depsdevclient, err := datasource.NewDepsDevAPIClient(depsdev.DepsdevAPI, "osv-scanner_scan/"+version.OSVVersion) if err != nil { + return Matchers{}, err + } + var licenseMatcher clientinterfaces.LicenseMatcher = &licensematcher.DepsDevLicenseMatcher{ + Client: depsdevclient, + } + + return Matchers{ + VulnMatcher: vulnMatcher, + LicenseMatcher: licenseMatcher, + }, nil +} + // Perform osv scanner action, with optional reporter to output information func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityResults, error) { if r == nil { @@ -124,39 +151,22 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe // ----- Custom Overrides ----- overrideGoVersion(r, &scanResult) - // --- Make Vulnerability Requests --- - { - var matcher clientinterfaces.VulnerabilityMatcher - var err error - if actions.CompareOffline { - matcher, err = localmatcher.NewLocalMatcher(r, actions.LocalDBPath, actions.DownloadDatabases) - if err != nil { - return models.VulnerabilityResults{}, err - } - } else { - matcher = &osvmatcher.OSVMatcher{ - Client: *osvdev.DefaultClient(), - InitialQueryTimeout: 5 * time.Minute, - } - } + matchers, err := InitializeMatchers(r, actions) + if err != nil { + return models.VulnerabilityResults{}, fmt.Errorf("failed to initialize matchers: %v", err) + } - err = makeRequestWithMatcher(r, scanResult.PackageScanResults, matcher) + // --- Make Vulnerability Requests --- + if matchers.VulnMatcher != nil { + err = makeVulnRequestWithMatcher(r, scanResult.PackageScanResults, matchers.VulnMatcher) if err != nil { return models.VulnerabilityResults{}, err } } // --- Make License Requests --- - if len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary { - depsdevclient, err := datasource.NewDepsDevAPIClient(depsdev.DepsdevAPI, "osv-scanner_scan/"+version.OSVVersion) - if err != nil { - return models.VulnerabilityResults{}, err - } - var licenseMatcher clientinterfaces.LicenseMatcher = &licensematcher.DepsDevLicenseMatcher{ - Client: depsdevclient, - } - - err = licenseMatcher.MatchLicenses(context.Background(), scanResult.PackageScanResults) + if matchers.LicenseMatcher != nil && len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary { + err = matchers.LicenseMatcher.MatchLicenses(context.Background(), scanResult.PackageScanResults) if err != nil { return models.VulnerabilityResults{}, err } @@ -200,14 +210,14 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe return results, nil } - return results, VulnerabilitiesFoundErr + return results, ErrVulnerabilitiesFound } return results, nil } // TODO(V2): Add context -func makeRequestWithMatcher( +func makeVulnRequestWithMatcher( r reporter.Reporter, packages []imodels.PackageScanResult, matcher clientinterfaces.VulnerabilityMatcher) error { diff --git a/pkg/osvscanner/scan.go b/pkg/osvscanner/scan.go index cd6ea6f99f..302612d316 100644 --- a/pkg/osvscanner/scan.go +++ b/pkg/osvscanner/scan.go @@ -61,7 +61,7 @@ func scan(r reporter.Reporter, actions ScannerActions) ([]imodels.PackageScanRes } if len(scannedInventories) == 0 { - return nil, NoPackagesFoundErr + return nil, ErrNoPackagesFound } // Convert to imodels.PackageScanResult for use in the rest of osv-scanner From 17bbc0f8efb368f882901ea51a88c8cbcb323f8d Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 9 Jan 2025 15:15:46 +1100 Subject: [PATCH 19/45] Remove osv package from many places --- cmd/osv-scanner/main.go | 3 -- .../clientimpl/localmatcher/localmatcher.go | 7 +++- .../clients/clientimpl/localmatcher/zip.go | 9 +++-- .../clientimpl/localmatcher/zip_test.go | 25 ++++++------ pkg/osvscanner/osvscanner.go | 5 ++- .../vulnerability_result_internal_test.go | 38 ++++++++----------- 6 files changed, 43 insertions(+), 44 deletions(-) diff --git a/cmd/osv-scanner/main.go b/cmd/osv-scanner/main.go index 59484f5f97..379aa09a3d 100644 --- a/cmd/osv-scanner/main.go +++ b/cmd/osv-scanner/main.go @@ -10,7 +10,6 @@ import ( "github.com/google/osv-scanner/cmd/osv-scanner/scan" "github.com/google/osv-scanner/cmd/osv-scanner/update" "github.com/google/osv-scanner/internal/version" - "github.com/google/osv-scanner/pkg/osv" "github.com/google/osv-scanner/pkg/osvscanner" "github.com/google/osv-scanner/pkg/reporter" @@ -30,8 +29,6 @@ func run(args []string, stdout, stderr io.Writer) int { r.Infof("osv-scanner version: %s\ncommit: %s\nbuilt at: %s\n", ctx.App.Version, commit, date) } - osv.RequestUserAgent = "osv-scanner/" + version.OSVVersion - app := &cli.App{ Name: "osv-scanner", Version: version.OSVVersion, diff --git a/internal/clients/clientimpl/localmatcher/localmatcher.go b/internal/clients/clientimpl/localmatcher/localmatcher.go index 727f7b0026..c460658274 100644 --- a/internal/clients/clientimpl/localmatcher/localmatcher.go +++ b/internal/clients/clientimpl/localmatcher/localmatcher.go @@ -26,11 +26,13 @@ type LocalMatcher struct { downloadDB bool // failedDBs keeps track of the errors when getting databases for each ecosystem failedDBs map[osvschema.Ecosystem]error + // userAgent sets the user agent requests for db zips are made with + userAgent string // TODO(v2 logging): Remove this reporter r reporter.Reporter } -func NewLocalMatcher(r reporter.Reporter, localDBPath string, downloadDB bool) (*LocalMatcher, error) { +func NewLocalMatcher(r reporter.Reporter, localDBPath string, userAgent string, downloadDB bool) (*LocalMatcher, error) { dbBasePath, err := setupLocalDBDirectory(localDBPath) if err != nil { return nil, fmt.Errorf("could not create %s: %w", dbBasePath, err) @@ -41,6 +43,7 @@ func NewLocalMatcher(r reporter.Reporter, localDBPath string, downloadDB bool) ( dbs: make(map[osvschema.Ecosystem]*ZipDB), downloadDB: downloadDB, r: r, + userAgent: userAgent, failedDBs: make(map[osvschema.Ecosystem]error), }, nil } @@ -97,7 +100,7 @@ func (matcher *LocalMatcher) loadDBFromCache(ctx context.Context, ecosystem ecos return nil, matcher.failedDBs[ecosystem.Ecosystem] } - db, err := NewZippedDB(ctx, matcher.dbBasePath, string(ecosystem.Ecosystem), fmt.Sprintf("%s/%s/all.zip", zippedDBRemoteHost, ecosystem.Ecosystem), !matcher.downloadDB) + db, err := NewZippedDB(ctx, matcher.dbBasePath, string(ecosystem.Ecosystem), fmt.Sprintf("%s/%s/all.zip", zippedDBRemoteHost, ecosystem.Ecosystem), matcher.userAgent, !matcher.downloadDB) if err != nil { matcher.failedDBs[ecosystem.Ecosystem] = err diff --git a/internal/clients/clientimpl/localmatcher/zip.go b/internal/clients/clientimpl/localmatcher/zip.go index 0e7be23005..1558fd5788 100644 --- a/internal/clients/clientimpl/localmatcher/zip.go +++ b/internal/clients/clientimpl/localmatcher/zip.go @@ -20,7 +20,6 @@ import ( "github.com/google/osv-scanner/internal/utility/vulns" "github.com/google/osv-scanner/pkg/lockfile" "github.com/google/osv-scanner/pkg/models" - "github.com/google/osv-scanner/pkg/osv" ) type ZipDB struct { @@ -34,6 +33,8 @@ type ZipDB struct { StoredAt string // the vulnerabilities that are loaded into this database vulnerabilities []models.Vulnerability + // User agent to query with + UserAgent string } var ErrOfflineDatabaseNotFound = errors.New("no offline version of the OSV database is available") @@ -105,8 +106,8 @@ func (db *ZipDB) fetchZip(ctx context.Context) ([]byte, error) { return nil, fmt.Errorf("could not retrieve OSV database archive: %w", err) } - if osv.RequestUserAgent != "" { - req.Header.Set("User-Agent", osv.RequestUserAgent) + if db.UserAgent != "" { + req.Header.Set("User-Agent", db.UserAgent) } resp, err := http.DefaultClient.Do(req) @@ -203,7 +204,7 @@ func (db *ZipDB) load(ctx context.Context) error { return nil } -func NewZippedDB(ctx context.Context, dbBasePath, name, url string, offline bool) (*ZipDB, error) { +func NewZippedDB(ctx context.Context, dbBasePath, name, url, userAgent string, offline bool) (*ZipDB, error) { db := &ZipDB{ Name: name, ArchiveURL: url, diff --git a/internal/clients/clientimpl/localmatcher/zip_test.go b/internal/clients/clientimpl/localmatcher/zip_test.go index 63525a23f9..50e42b488d 100644 --- a/internal/clients/clientimpl/localmatcher/zip_test.go +++ b/internal/clients/clientimpl/localmatcher/zip_test.go @@ -19,9 +19,12 @@ import ( "github.com/google/osv-scanner/internal/clients/clientimpl/localmatcher" "github.com/google/osv-scanner/internal/testutility" + "github.com/google/osv-scanner/internal/version" "github.com/google/osv-scanner/pkg/models" ) +const userAgent = "osv-scanner_test/" + version.OSVVersion + func expectDBToHaveOSVs( t *testing.T, db interface { @@ -146,7 +149,7 @@ func TestNewZippedDB_Offline_WithoutCache(t *testing.T) { t.Errorf("a server request was made when running offline") }) - _, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, true) + _, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, userAgent, true) if !errors.Is(err, localmatcher.ErrOfflineDatabaseNotFound) { t.Errorf("expected \"%v\" error but got \"%v\"", localmatcher.ErrOfflineDatabaseNotFound, err) @@ -178,7 +181,7 @@ func TestNewZippedDB_Offline_WithCache(t *testing.T) { "GHSA-5.json": {ID: "GHSA-5"}, })) - db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, true) + db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, userAgent, true) if err != nil { t.Fatalf("unexpected error \"%v\"", err) @@ -196,7 +199,7 @@ func TestNewZippedDB_BadZip(t *testing.T) { _, _ = w.Write([]byte("this is not a zip")) }) - _, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, false) + _, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, userAgent, false) if err == nil { t.Errorf("expected an error but did not get one") @@ -208,7 +211,7 @@ func TestNewZippedDB_UnsupportedProtocol(t *testing.T) { testDir := testutility.CreateTestDir(t) - _, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", "file://hello-world", false) + _, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", "file://hello-world", userAgent, false) if err == nil { t.Errorf("expected an error but did not get one") @@ -238,7 +241,7 @@ func TestNewZippedDB_Online_WithoutCache(t *testing.T) { }) }) - db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, false) + db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, userAgent, false) if err != nil { t.Fatalf("unexpected error \"%v\"", err) @@ -270,7 +273,7 @@ func TestNewZippedDB_Online_WithoutCacheAndNoHashHeader(t *testing.T) { })) }) - db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, false) + db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, userAgent, false) if err != nil { t.Fatalf("unexpected error \"%v\"", err) @@ -308,7 +311,7 @@ func TestNewZippedDB_Online_WithSameCache(t *testing.T) { cacheWrite(t, determineStoredAtPath(testDir, "my-db"), cache) - db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, false) + db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, userAgent, false) if err != nil { t.Fatalf("unexpected error \"%v\"", err) @@ -346,7 +349,7 @@ func TestNewZippedDB_Online_WithDifferentCache(t *testing.T) { "GHSA-3.json": {ID: "GHSA-3"}, })) - db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, false) + db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, userAgent, false) if err != nil { t.Fatalf("unexpected error \"%v\"", err) @@ -376,7 +379,7 @@ func TestNewZippedDB_Online_WithCacheButNoHashHeader(t *testing.T) { "GHSA-3.json": {ID: "GHSA-3"}, })) - _, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, false) + _, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, userAgent, false) if err == nil { t.Errorf("expected an error but did not get one") @@ -404,7 +407,7 @@ func TestNewZippedDB_Online_WithBadCache(t *testing.T) { cacheWriteBad(t, determineStoredAtPath(testDir, "my-db"), "this is not json!") - db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, false) + db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, userAgent, false) if err != nil { t.Fatalf("unexpected error \"%v\"", err) @@ -430,7 +433,7 @@ func TestNewZippedDB_FileChecks(t *testing.T) { }) }) - db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, false) + db, err := localmatcher.NewZippedDB(context.Background(), testDir, "my-db", ts.URL, userAgent, false) if err != nil { t.Fatalf("unexpected error \"%v\"", err) diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index b0e9765b50..262848b143 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -77,7 +77,7 @@ func InitializeMatchers(r reporter.Reporter, actions ScannerActions) (Matchers, var vulnMatcher clientinterfaces.VulnerabilityMatcher var err error if actions.CompareOffline { - vulnMatcher, err = localmatcher.NewLocalMatcher(r, actions.LocalDBPath, actions.DownloadDatabases) + vulnMatcher, err = localmatcher.NewLocalMatcher(r, actions.LocalDBPath, "osv-scanner_scan/"+version.OSVVersion, actions.DownloadDatabases) if err != nil { return Matchers{}, err } @@ -89,7 +89,8 @@ func InitializeMatchers(r reporter.Reporter, actions ScannerActions) (Matchers, } // License Matcher - depsdevclient, err := datasource.NewDepsDevAPIClient(depsdev.DepsdevAPI, "osv-scanner_scan/"+version.OSVVersion) if err != nil { + depsdevclient, err := datasource.NewDepsDevAPIClient(depsdev.DepsdevAPI, "osv-scanner_scan/"+version.OSVVersion) + if err != nil { return Matchers{}, err } var licenseMatcher clientinterfaces.LicenseMatcher = &licensematcher.DepsDevLicenseMatcher{ diff --git a/pkg/osvscanner/vulnerability_result_internal_test.go b/pkg/osvscanner/vulnerability_result_internal_test.go index d390ba95bf..dcd79fb088 100644 --- a/pkg/osvscanner/vulnerability_result_internal_test.go +++ b/pkg/osvscanner/vulnerability_result_internal_test.go @@ -10,7 +10,6 @@ import ( "github.com/google/osv-scanner/internal/scalibrextract/ecosystemmock" "github.com/google/osv-scanner/internal/testutility" "github.com/google/osv-scanner/pkg/models" - "github.com/google/osv-scanner/pkg/osv" "github.com/google/osv-scanner/pkg/reporter" ) @@ -47,22 +46,21 @@ func Test_assembleResult(t *testing.T) { }, }, } - vulnsResp := &osv.HydratedBatchedResponse{ - Results: []osv.Response{ - {Vulns: models.Vulnerabilities([]models.Vulnerability{ - { - ID: "GHSA-123", - Aliases: []string{"CVE-123"}, - }, { - ID: "CVE-123", - }, - })}, - {Vulns: models.Vulnerabilities{}}, - {Vulns: models.Vulnerabilities([]models.Vulnerability{ - { - ID: "GHSA-456", - }, - })}, + vulnsResp := [][]*models.Vulnerability{ + { + { + ID: "GHSA-123", + Aliases: []string{"CVE-123"}, + }, + { + ID: "CVE-123", + }, + }, + {}, + { + { + ID: "GHSA-456", + }, }, } @@ -86,13 +84,9 @@ func Test_assembleResult(t *testing.T) { } licensesResp = makeLicensesResp() for i := range packages { - vulnPointers := []*models.Vulnerability{} - for _, vuln := range vulnsResp.Results[i].Vulns { - vulnPointers = append(vulnPointers, &vuln) - } scanResults.PackageScanResults = append(scanResults.PackageScanResults, imodels.PackageScanResult{ PackageInfo: packages[i], - Vulnerabilities: vulnPointers, + Vulnerabilities: vulnsResp[i], Licenses: licensesResp[i], }) } From 0e07cb13674e2dbd3ed28941cb38151463167043 Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 9 Jan 2025 15:47:22 +1100 Subject: [PATCH 20/45] Use osvdev client in vendoredExtractor --- .../filesystem/vendored/vendored.go | 23 ++++++++++--------- .../filesystem/vendored/vendored_test.go | 5 +++- pkg/osvscanner/internal/scanners/walker.go | 7 +++++- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/internal/scalibrextract/filesystem/vendored/vendored.go b/internal/scalibrextract/filesystem/vendored/vendored.go index 1cd1a5fbe5..9ba40d6694 100644 --- a/internal/scalibrextract/filesystem/vendored/vendored.go +++ b/internal/scalibrextract/filesystem/vendored/vendored.go @@ -20,7 +20,7 @@ import ( scalibrfs "github.com/google/osv-scalibr/fs" "github.com/google/osv-scalibr/plugin" "github.com/google/osv-scalibr/purl" - "github.com/google/osv-scanner/pkg/osv" + "github.com/google/osv-scanner/internal/osvdev" ) var ( @@ -58,8 +58,7 @@ type Extractor struct { // ScanGitDir determines whether a vendored library with a git directory is scanned or not, // this is used to avoid duplicate results, once from git scanning, once from vendoredDir scanning ScanGitDir bool - // TODO(v2): Client rework - // determineVersionsClient + OSVClient *osvdev.OSVClient } var _ filesystem.Extractor = Extractor{} @@ -96,12 +95,10 @@ func (e Extractor) FileRequired(fapi filesystem.FileAPI) bool { // Extract determines the most likely package version from the directory and returns them as // commit hash inventory entries -func (e Extractor) Extract(_ context.Context, input *filesystem.ScanInput) ([]*extractor.Inventory, error) { +func (e Extractor) Extract(ctx context.Context, input *filesystem.ScanInput) ([]*extractor.Inventory, error) { var packages []*extractor.Inventory - // r.Infof("Scanning potential vendored dir: %s\n", libPath) - // TODO: make this a goroutine to parallelize this operation - results, err := queryDetermineVersions(input.Path, input.FS, e.ScanGitDir) + results, err := e.queryDetermineVersions(ctx, input.Path, input.FS, e.ScanGitDir) if err != nil { return nil, err } @@ -130,8 +127,8 @@ func (e Extractor) Ecosystem(_ *extractor.Inventory) string { return "" } -func queryDetermineVersions(repoDir string, fsys scalibrfs.FS, scanGitDir bool) (*osv.DetermineVersionResponse, error) { - var hashes []osv.DetermineVersionHash +func (e Extractor) queryDetermineVersions(ctx context.Context, repoDir string, fsys scalibrfs.FS, scanGitDir bool) (*osvdev.DetermineVersionResponse, error) { + var hashes []osvdev.DetermineVersionHash err := fs.WalkDir(fsys, repoDir, func(p string, d fs.DirEntry, _ error) error { if d.IsDir() { @@ -165,7 +162,7 @@ func queryDetermineVersions(repoDir string, fsys scalibrfs.FS, scanGitDir bool) return err } hash := md5.Sum(buf.Bytes()) //nolint:gosec - hashes = append(hashes, osv.DetermineVersionHash{ + hashes = append(hashes, osvdev.DetermineVersionHash{ Path: strings.ReplaceAll(p, repoDir, ""), Hash: hash[:], }) @@ -180,7 +177,11 @@ func queryDetermineVersions(repoDir string, fsys scalibrfs.FS, scanGitDir bool) return nil, fmt.Errorf("failed during hashing: %w", err) } - result, err := osv.MakeDetermineVersionRequest(filepath.Base(repoDir), hashes) + result, err := e.OSVClient.ExperimentalDetermineVersion(ctx, &osvdev.DetermineVersionsRequest{ + Name: filepath.Base(repoDir), + FileHashes: hashes, + }) + if err != nil { return nil, fmt.Errorf("failed to determine versions: %w", err) } diff --git a/internal/scalibrextract/filesystem/vendored/vendored_test.go b/internal/scalibrextract/filesystem/vendored/vendored_test.go index f61ca5b341..922c565f59 100644 --- a/internal/scalibrextract/filesystem/vendored/vendored_test.go +++ b/internal/scalibrextract/filesystem/vendored/vendored_test.go @@ -14,6 +14,7 @@ import ( "github.com/google/osv-scalibr/extractor/filesystem/simplefileapi" "github.com/google/osv-scalibr/testing/extracttest" "github.com/google/osv-scalibr/testing/fakefs" + "github.com/google/osv-scanner/internal/osvdev" "github.com/google/osv-scanner/internal/scalibrextract/filesystem/vendored" "github.com/google/osv-scanner/internal/testutility" ) @@ -127,7 +128,9 @@ func TestExtractor_Extract(t *testing.T) { for _, tt := range tests { t.Run(tt.Name, func(t *testing.T) { t.Parallel() - extr := vendored.Extractor{} + extr := vendored.Extractor{ + OSVClient: osvdev.DefaultClient(), + } scanInput := extracttest.GenerateScanInputMock(t, tt.InputConfig) defer extracttest.CloseTestScanInput(t, scanInput) diff --git a/pkg/osvscanner/internal/scanners/walker.go b/pkg/osvscanner/internal/scanners/walker.go index 028c1480ec..4ae43d653c 100644 --- a/pkg/osvscanner/internal/scanners/walker.go +++ b/pkg/osvscanner/internal/scanners/walker.go @@ -12,6 +12,7 @@ import ( "github.com/google/osv-scalibr/extractor/filesystem" "github.com/google/osv-scalibr/extractor/filesystem/language/java/pomxml" "github.com/google/osv-scanner/internal/customgitignore" + "github.com/google/osv-scanner/internal/osvdev" "github.com/google/osv-scanner/internal/output" "github.com/google/osv-scanner/internal/scalibrextract" "github.com/google/osv-scanner/internal/scalibrextract/filesystem/vendored" @@ -47,7 +48,11 @@ func ScanDir(r reporter.Reporter, dir string, skipGit bool, recursive bool, useG relevantExtractors = append(relevantExtractors, lockfileExtractors...) relevantExtractors = append(relevantExtractors, SBOMExtractors...) // Only scan git directories if we are skipping the git extractor - relevantExtractors = append(relevantExtractors, vendored.Extractor{ScanGitDir: skipGit}) + // TODO: If in offline mode, don't create a vendoredExtractor + relevantExtractors = append(relevantExtractors, vendored.Extractor{ + ScanGitDir: skipGit, + OSVClient: osvdev.DefaultClient(), + }) if pomExtractor != nil { relevantExtractors = append(relevantExtractors, pomExtractor) } else { From a75c674792592e6a0a7262ed9ea02e7af28a4e4c Mon Sep 17 00:00:00 2001 From: Rex P Date: Mon, 13 Jan 2025 14:52:59 +1100 Subject: [PATCH 21/45] Fix compilation issue --- internal/imodels/imodels.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/internal/imodels/imodels.go b/internal/imodels/imodels.go index bcca6c0b24..754cb870c8 100644 --- a/internal/imodels/imodels.go +++ b/internal/imodels/imodels.go @@ -164,22 +164,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 From 02fc3501f7fcb895de9be49b8ef8d041b132677b Mon Sep 17 00:00:00 2001 From: Rex P Date: Mon, 13 Jan 2025 15:51:58 +1100 Subject: [PATCH 22/45] Temporarily merge in the symbolic link changes --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d3eb559bf7..39d403c99f 100644 --- a/go.mod +++ b/go.mod @@ -151,4 +151,4 @@ require ( www.velocidex.com/golang/regparser v0.0.0-20240404115756-2169ac0e3c09 // indirect ) -replace github.com/google/osv-scalibr => github.com/another-rex/osv-scalibr v0.0.0-20250107005511-4051898dffce +replace github.com/google/osv-scalibr => github.com/another-rex/osv-scalibr v0.0.0-20250113043556-cd04d9064a2d diff --git a/go.sum b/go.sum index 679e717c42..83ed16969d 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,8 @@ github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 h1:6CO github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/another-rex/osv-scalibr v0.0.0-20250107005511-4051898dffce h1:prkXvk4qRbLBNK9iVQiqmCdtIobj8DLlJiTSIGWonrM= -github.com/another-rex/osv-scalibr v0.0.0-20250107005511-4051898dffce/go.mod h1:S8mrRjoWESAOOTq25lJqzxiKR6tbWSFYG8SVb5EFLHk= +github.com/another-rex/osv-scalibr v0.0.0-20250113043556-cd04d9064a2d h1:6Wr5mKNnMJoSYDt0KxNVfiiv3SW5XgV2nii3KtT2Yhc= +github.com/another-rex/osv-scalibr v0.0.0-20250113043556-cd04d9064a2d/go.mod h1:S8mrRjoWESAOOTq25lJqzxiKR6tbWSFYG8SVb5EFLHk= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= From 47552bbc87be17f1be34d1160eae1352e9213613 Mon Sep 17 00:00:00 2001 From: Holly Gong Date: Mon, 13 Jan 2025 16:35:02 +1100 Subject: [PATCH 23/45] enhance --docker flag --- pkg/osvscanner/osvscanner.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index fb3311480b..959e7f47a7 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "os/exec" + "strings" "time" "github.com/google/osv-scalibr/artifact/image/layerscanning/image" @@ -255,6 +256,13 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner } 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) @@ -266,10 +274,15 @@ func exportDockerImage(r reporter.Reporter, dockerImageName string) (string, err return "", err } - r.Infof("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) + // Check if image exists locally, if not, pull from the cloud. + cmd := exec.Command("docker", "images", "-q", dockerImageName) + output, err := cmd.Output() + if err != nil || string(output) == "" { + r.Infof("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) From d4f6eec9d364a6a313a663dd3c1ea3aaa9c4e181 Mon Sep 17 00:00:00 2001 From: Rex P Date: Mon, 13 Jan 2025 17:02:32 +1100 Subject: [PATCH 24/45] Hacked together calling deps.dev base image endpoint --- pkg/osvscanner/osvscanner.go | 95 +++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index fb3311480b..75d68f23c1 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -3,10 +3,13 @@ package osvscanner import ( "bufio" "context" + "encoding/json" "errors" "fmt" + "net/http" "os" "os/exec" + "slices" "time" "github.com/google/osv-scalibr/artifact/image/layerscanning/image" @@ -16,6 +19,7 @@ import ( "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/os/osrelease" + "github.com/google/osv-scalibr/log" "github.com/google/osv-scanner/internal/clients/clientimpl/localmatcher" "github.com/google/osv-scanner/internal/clients/clientimpl/osvmatcher" "github.com/google/osv-scanner/internal/clients/clientinterfaces" @@ -28,6 +32,7 @@ import ( "github.com/google/osv-scanner/internal/scalibrextract/language/javascript/nodemodules" "github.com/google/osv-scanner/pkg/models" "github.com/google/osv-scanner/pkg/reporter" + "github.com/opencontainers/go-digest" "github.com/ossf/osv-schema/bindings/go/osvschema" scalibr "github.com/google/osv-scalibr" @@ -146,6 +151,12 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner // --- Fill Image Metadata --- { + // depsdevClient, err := datasource.NewDepsDevAlphaAPIClient(depsdev.DepsdevAPI) + if err != nil { + return models.VulnerabilityResults{}, err + + } + // Ignore error, as if this would error we would have failed the initial scan chainLayers, _ := img.ChainLayers() m, err := osrelease.GetOSRelease(chainLayers[len(chainLayers)-1].FS()) @@ -161,14 +172,96 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner Command: cl.Layer().Command(), IsEmpty: cl.Layer().IsEmpty(), }) + } scanResult.ImageMetadata = &models.ImageMetadata{ // TODO: Not yet filled in - BaseImages: [][]models.BaseImageDetails{}, + 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 + {}, + }, OS: OS, LayerMetadata: layerMetadata, } + + var runningDigest digest.Digest + chainIDs := []digest.Digest{} + + for i, cl := range chainLayers { + var diffDigest digest.Digest + if cl.Layer().DiffID() == "" { + chainIDs = append(chainIDs, "") + continue + } + + diffDigest = digest.NewDigestFromEncoded(digest.SHA256, cl.Layer().DiffID()) + + if i == 0 { + runningDigest = diffDigest + } else { + runningDigest = digest.FromBytes([]byte(runningDigest + " " + diffDigest)) + } + + chainIDs = append(chainIDs, runningDigest) + } + + currentBaseImageIndex := 0 + client := http.DefaultClient + for i, cid := range slices.Backward(chainIDs) { + if cid == "" { + scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex + continue + } + + resp, err := client.Get("https://api.deps.dev/v3alpha/querycontainerimages/" + cid.String()) + if err != nil { + log.Errorf("API DEPS DEV ERROR: %s", err) + continue + } + + if resp.StatusCode == http.StatusNotFound { + scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex + continue + } + + if resp.StatusCode != http.StatusOK { + log.Errorf("API DEPS DEV ERROR: %s", resp.Status) + continue + } + + d := json.NewDecoder(resp.Body) + + type baseImageEntry struct { + Repository string `json:"repository"` + } + type baseImageResults struct { + Results []baseImageEntry `json:"results"` + } + + var results baseImageResults + err = d.Decode(&results) + if err != nil { + log.Errorf("API DEPS DEV ERROR: %s", err) + continue + } + + baseImagePossibilities := []models.BaseImageDetails{} + for _, r := range results.Results { + baseImagePossibilities = append(baseImagePossibilities, models.BaseImageDetails{ + Name: r.Repository, + }) + } + + slices.SortFunc(baseImagePossibilities, func(a, b models.BaseImageDetails) int { + return len(a.Name) - len(b.Name) + }) + + scanResult.ImageMetadata.BaseImages = append(scanResult.ImageMetadata.BaseImages, baseImagePossibilities) + currentBaseImageIndex += 1 + scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex + } } // ----- Filtering ----- From 6f71d15d4804f7e67b505e9530d6f515e7f38b24 Mon Sep 17 00:00:00 2001 From: Holly Gong Date: Mon, 13 Jan 2025 17:06:11 +1100 Subject: [PATCH 25/45] update usr/lib --- internal/output/output_result.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/output/output_result.go b/internal/output/output_result.go index 93f291feb1..978e8240fa 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 } From f240e52156e00bee25ae54cbae35ed86ac90bc99 Mon Sep 17 00:00:00 2001 From: Holly Gong Date: Tue, 14 Jan 2025 11:09:33 +1100 Subject: [PATCH 26/45] add logs --- pkg/osvscanner/osvscanner.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 959e7f47a7..e221ff61d8 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -275,10 +275,11 @@ func exportDockerImage(r reporter.Reporter, dockerImageName string) (string, 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("Pulling docker image (%q)...\n", dockerImageName) + 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) From d37eb736904ed99743c6f731e24d89b2c422c58c Mon Sep 17 00:00:00 2001 From: Rex P Date: Tue, 14 Jan 2025 11:36:19 +1100 Subject: [PATCH 27/45] A weird backfill attempt --- go.mod | 4 ++-- go.sum | 4 ++-- pkg/osvscanner/osvscanner.go | 34 ++++++++++++++++++++++++++-------- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 39d403c99f..f4f05f12f8 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( 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 @@ -110,7 +111,6 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.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 @@ -151,4 +151,4 @@ require ( www.velocidex.com/golang/regparser v0.0.0-20240404115756-2169ac0e3c09 // indirect ) -replace github.com/google/osv-scalibr => github.com/another-rex/osv-scalibr v0.0.0-20250113043556-cd04d9064a2d +replace github.com/google/osv-scalibr => github.com/another-rex/osv-scalibr v0.0.0-20250113235228-f11c4402ba7a diff --git a/go.sum b/go.sum index 83ed16969d..d9c6f8eea4 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,8 @@ github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 h1:6CO github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/another-rex/osv-scalibr v0.0.0-20250113043556-cd04d9064a2d h1:6Wr5mKNnMJoSYDt0KxNVfiiv3SW5XgV2nii3KtT2Yhc= -github.com/another-rex/osv-scalibr v0.0.0-20250113043556-cd04d9064a2d/go.mod h1:S8mrRjoWESAOOTq25lJqzxiKR6tbWSFYG8SVb5EFLHk= +github.com/another-rex/osv-scalibr v0.0.0-20250113235228-f11c4402ba7a h1:hqA5nPtTLkQJJ9laZIbFak5EJhYg0qKnT3w6Wjxt8o4= +github.com/another-rex/osv-scalibr v0.0.0-20250113235228-f11c4402ba7a/go.mod h1:S8mrRjoWESAOOTq25lJqzxiKR6tbWSFYG8SVb5EFLHk= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 75d68f23c1..0de0cf1508 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -10,6 +10,7 @@ import ( "os" "os/exec" "slices" + "strings" "time" "github.com/google/osv-scalibr/artifact/image/layerscanning/image" @@ -151,12 +152,6 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner // --- Fill Image Metadata --- { - // depsdevClient, err := datasource.NewDepsDevAlphaAPIClient(depsdev.DepsdevAPI) - if err != nil { - return models.VulnerabilityResults{}, err - - } - // Ignore error, as if this would error we would have failed the initial scan chainLayers, _ := img.ChainLayers() m, err := osrelease.GetOSRelease(chainLayers[len(chainLayers)-1].FS()) @@ -176,7 +171,6 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner } scanResult.ImageMetadata = &models.ImageMetadata{ - // TODO: Not yet filled in 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 @@ -247,6 +241,7 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner continue } + // Found some base images! baseImagePossibilities := []models.BaseImageDetails{} for _, r := range results.Results { baseImagePossibilities = append(baseImagePossibilities, models.BaseImageDetails{ @@ -260,7 +255,30 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner scanResult.ImageMetadata.BaseImages = append(scanResult.ImageMetadata.BaseImages, baseImagePossibilities) currentBaseImageIndex += 1 - scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex + // scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex + + // Backfill with heuristic + + possibleFinalBaseImageCommands := []string{ + "/bin/sh -c #(nop) CMD", + "CMD", + "/bin/sh -c #(nop) ENTRYPOINT", + "ENTRYPOINT", + } + BackfillLoop: + for i2 := i; i2 < len(scanResult.ImageMetadata.LayerMetadata); i2++ { + if !scanResult.ImageMetadata.LayerMetadata[i2].IsEmpty { + break + } + buildCommand := scanResult.ImageMetadata.LayerMetadata[i2].Command + scanResult.ImageMetadata.LayerMetadata[i2].BaseImageIndex = currentBaseImageIndex + for _, prefix := range possibleFinalBaseImageCommands { + if strings.HasPrefix(buildCommand, prefix) { + break BackfillLoop + } + } + } + } } From adf130994519da7d0d75fcf8e86107d9c0657563 Mon Sep 17 00:00:00 2001 From: Rex P Date: Wed, 15 Jan 2025 10:36:39 +1100 Subject: [PATCH 28/45] Origin image bug fix --- pkg/osvscanner/osvscanner.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index efc3c6ef3a..6e2c9cc983 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -183,7 +183,7 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner var runningDigest digest.Digest chainIDs := []digest.Digest{} - for i, cl := range chainLayers { + for _, cl := range chainLayers { var diffDigest digest.Digest if cl.Layer().DiffID() == "" { chainIDs = append(chainIDs, "") @@ -192,7 +192,7 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner diffDigest = digest.NewDigestFromEncoded(digest.SHA256, cl.Layer().DiffID()) - if i == 0 { + if runningDigest == "" { runningDigest = diffDigest } else { runningDigest = digest.FromBytes([]byte(runningDigest + " " + diffDigest)) From e656319b750d38ae948dbf1a91695a3ff0a70612 Mon Sep 17 00:00:00 2001 From: Rex P Date: Wed, 15 Jan 2025 11:02:41 +1100 Subject: [PATCH 29/45] OS fix and index as key --- pkg/models/image.go | 23 +++++++++-------------- pkg/models/results.go | 3 +-- pkg/osvscanner/osvscanner.go | 18 ++++++++++-------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/pkg/models/image.go b/pkg/models/image.go index e31f52ae52..a91ef8df10 100644 --- a/pkg/models/image.go +++ b/pkg/models/image.go @@ -1,18 +1,13 @@ package models type ImageOriginDetails struct { - DiffID string `json:"diff_id"` - - // TODO: Deprecated, use ImageMetadata to retrieve this info - OriginCommand string `json:"origin_command"` - InBaseImage bool `json:"in_base_image"` + Index int } type ImageMetadata struct { - OS string `json:"os"` - LayerMetadata []LayerMetadata `json:"layer_metadata"` - // TODO: Not yet filled in - BaseImages [][]BaseImageDetails `json:"base_images"` + OS string `json:"os"` + LayerMetadata []LayerMetadata `json:"layer_metadata"` + BaseImages [][]BaseImageDetails `json:"base_images"` } type BaseImageDetails struct { @@ -22,9 +17,9 @@ type BaseImageDetails struct { } type LayerMetadata struct { - DiffID string `json:"diff_id"` - Command string `json:"command"` - IsEmpty bool `json:"is_empty"` - // TODO: Not yet filled in - BaseImageIndex int `json:"base_image_index"` + Index int `json:"index"` + DiffID string `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 984d6c194b..91c1b682c4 100644 --- a/pkg/models/results.go +++ b/pkg/models/results.go @@ -193,6 +193,5 @@ type PackageInfo struct { Version string `json:"version"` Ecosystem string `json:"ecosystem"` Commit string `json:"commit,omitempty"` - ImageOrigin *ImageOriginDetails `json:"imageOrigin,omitempty"` + ImageOrigin *ImageOriginDetails `json:"image_origin_details"` } - diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 6e2c9cc983..ccb9d9fce4 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -126,6 +126,14 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner } defer img.CleanUp() + // Ignore error, as if this would error we would have failed the initial scan + chainLayers, _ := img.ChainLayers() + m, err := osrelease.GetOSRelease(chainLayers[len(chainLayers)-1].FS()) + OS := "Unknown" + if err == nil { + OS = m["OSID"] + } + scalibrSR, err := scanner.ScanContainer(context.Background(), img, &scalibr.ScanConfig{ FilesystemExtractors: []filesystem.Extractor{ nodemodules.Extractor{}, @@ -152,17 +160,11 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner // --- Fill Image Metadata --- { - // Ignore error, as if this would error we would have failed the initial scan - chainLayers, _ := img.ChainLayers() - m, err := osrelease.GetOSRelease(chainLayers[len(chainLayers)-1].FS()) - OS := "Unknown" - if err != nil { - OS = m["OSID"] - } layerMetadata := []models.LayerMetadata{} - for _, cl := range chainLayers { + for i, cl := range chainLayers { layerMetadata = append(layerMetadata, models.LayerMetadata{ + Index: i, DiffID: cl.Layer().DiffID(), Command: cl.Layer().Command(), IsEmpty: cl.Layer().IsEmpty(), From b7ae0abe51bedc42278dae2b352bd32441d7d216 Mon Sep 17 00:00:00 2001 From: Rex P Date: Wed, 15 Jan 2025 14:23:02 +1100 Subject: [PATCH 30/45] Better layout of extractor and client definitions. --- cmd/osv-scanner/main_test.go | 5 + pkg/osvscanner/internal/scanners/artifacts.go | 1 + .../internal/scanners/extractorbuilder.go | 127 ++++++++++++++++++ pkg/osvscanner/internal/scanners/lockfile.go | 83 +++++------- pkg/osvscanner/internal/scanners/sbom.go | 45 ------- pkg/osvscanner/internal/scanners/walker.go | 28 +--- pkg/osvscanner/osvscanner.go | 102 +++++++++----- pkg/osvscanner/scan.go | 83 +++--------- 8 files changed, 257 insertions(+), 217 deletions(-) create mode 100644 pkg/osvscanner/internal/scanners/artifacts.go create mode 100644 pkg/osvscanner/internal/scanners/extractorbuilder.go delete mode 100644 pkg/osvscanner/internal/scanners/sbom.go diff --git a/cmd/osv-scanner/main_test.go b/cmd/osv-scanner/main_test.go index 404cb88a2b..681e2f1afb 100644 --- a/cmd/osv-scanner/main_test.go +++ b/cmd/osv-scanner/main_test.go @@ -857,6 +857,11 @@ func TestRun_OCIImage(t *testing.T) { args: []string{"", "--experimental-oci-image", "../../internal/image/fixtures/test-node_modules-pnpm-full.tar"}, exit: 1, }, + { + name: "scanning image with go binary", + args: []string{"", "--experimental-oci-image", "../../internal/image/fixtures/test-package-tracing.tar"}, + exit: 1, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { 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/internal/scanners/extractorbuilder.go b/pkg/osvscanner/internal/scanners/extractorbuilder.go new file mode 100644 index 0000000000..0daf69efa5 --- /dev/null +++ b/pkg/osvscanner/internal/scanners/extractorbuilder.go @@ -0,0 +1,127 @@ +package scanners + +import ( + "github.com/google/osv-scalibr/extractor/filesystem" + "github.com/google/osv-scalibr/extractor/filesystem/language/cpp/conanlock" + "github.com/google/osv-scalibr/extractor/filesystem/language/dart/pubspec" + "github.com/google/osv-scalibr/extractor/filesystem/language/dotnet/packageslockjson" + "github.com/google/osv-scalibr/extractor/filesystem/language/erlang/mixlock" + "github.com/google/osv-scalibr/extractor/filesystem/language/golang/gomod" + "github.com/google/osv-scalibr/extractor/filesystem/language/java/gradlelockfile" + "github.com/google/osv-scalibr/extractor/filesystem/language/java/gradleverificationmetadataxml" + "github.com/google/osv-scalibr/extractor/filesystem/language/java/pomxml" + "github.com/google/osv-scalibr/extractor/filesystem/language/javascript/packagelockjson" + "github.com/google/osv-scalibr/extractor/filesystem/language/javascript/pnpmlock" + "github.com/google/osv-scalibr/extractor/filesystem/language/javascript/yarnlock" + "github.com/google/osv-scalibr/extractor/filesystem/language/php/composerlock" + "github.com/google/osv-scalibr/extractor/filesystem/language/python/pdmlock" + "github.com/google/osv-scalibr/extractor/filesystem/language/python/pipfilelock" + "github.com/google/osv-scalibr/extractor/filesystem/language/python/poetrylock" + "github.com/google/osv-scalibr/extractor/filesystem/language/python/requirements" + "github.com/google/osv-scalibr/extractor/filesystem/language/r/renvlock" + "github.com/google/osv-scalibr/extractor/filesystem/language/ruby/gemfilelock" + "github.com/google/osv-scalibr/extractor/filesystem/language/rust/cargolock" + "github.com/google/osv-scalibr/extractor/filesystem/sbom/cdx" + "github.com/google/osv-scalibr/extractor/filesystem/sbom/spdx" + "github.com/google/osv-scanner/internal/osvdev" + "github.com/google/osv-scanner/internal/resolution/client" + "github.com/google/osv-scanner/internal/resolution/datasource" + "github.com/google/osv-scanner/internal/scalibrextract/filesystem/vendored" + "github.com/google/osv-scanner/internal/scalibrextract/language/java/pomxmlnet" + "github.com/google/osv-scanner/internal/scalibrextract/vcs/gitrepo" + "github.com/ossf/osv-schema/bindings/go/osvschema" +) + +var sbomExtractors = []filesystem.Extractor{ + spdx.Extractor{}, + cdx.Extractor{}, +} + +var lockfileExtractors = []filesystem.Extractor{ + conanlock.Extractor{}, + packageslockjson.Extractor{}, + mixlock.Extractor{}, + pubspec.Extractor{}, + gomod.Extractor{}, + gradlelockfile.Extractor{}, + gradleverificationmetadataxml.Extractor{}, + packagelockjson.Extractor{}, + pnpmlock.Extractor{}, + yarnlock.Extractor{}, + composerlock.Extractor{}, + pipfilelock.Extractor{}, + pdmlock.Extractor{}, + poetrylock.Extractor{}, + requirements.Extractor{}, + renvlock.Extractor{}, + gemfilelock.Extractor{}, + cargolock.Extractor{}, +} + +type ExtractorArgs struct { +} + +// BuildLockfileExtractors returns all relevant extractors for lockfile scanning given the required clients +// All clients can be nil, and if nil the extractors requiring those clients will not be returned. +func BuildLockfileExtractors(dependencyClients map[osvschema.Ecosystem]client.DependencyClient, mavenAPIClient *datasource.MavenRegistryAPIClient) []filesystem.Extractor { + extractorsToUse := lockfileExtractors + + if dependencyClients[osvschema.EcosystemMaven] != nil && mavenAPIClient != nil { + extractorsToUse = append(extractorsToUse, pomxmlnet.Extractor{ + DependencyClient: dependencyClients[osvschema.EcosystemMaven], + MavenRegistryAPIClient: mavenAPIClient, + }) + } else { + extractorsToUse = append(extractorsToUse, pomxml.Extractor{}) + } + + return extractorsToUse +} + +// BuildSBOMExtractors returns extractors relevant to SBOM extraction +func BuildSBOMExtractors() []filesystem.Extractor { + return sbomExtractors +} + +// BuildWalkerExtractors returns all relevant extractors for directory scanning given the required clients +// All clients can be nil, and if nil the extractors requiring those clients will not be returned. +func BuildWalkerExtractors( + skipGit bool, + osvdevClient *osvdev.OSVClient, + dependencyClients map[osvschema.Ecosystem]client.DependencyClient, + mavenAPIClient *datasource.MavenRegistryAPIClient) []filesystem.Extractor { + + relevantExtractors := []filesystem.Extractor{} + + if !skipGit { + relevantExtractors = append(relevantExtractors, gitrepo.Extractor{}) + } + relevantExtractors = append(relevantExtractors, lockfileExtractors...) + relevantExtractors = append(relevantExtractors, sbomExtractors...) + + if osvdevClient != nil { + relevantExtractors = append(relevantExtractors, vendored.Extractor{ + ScanGitDir: skipGit, + OSVClient: osvdevClient, + }) + } + + if dependencyClients[osvschema.EcosystemMaven] != nil && mavenAPIClient != nil { + relevantExtractors = append(relevantExtractors, pomxmlnet.Extractor{ + DependencyClient: dependencyClients[osvschema.EcosystemMaven], + MavenRegistryAPIClient: mavenAPIClient, + }) + } else { + relevantExtractors = append(relevantExtractors, pomxml.Extractor{}) + } + + return relevantExtractors +} + +// BuildArtifactExtractors returns all relevant extractors for artifact scanning given the required clients +// All clients can be nil, and if nil the extractors requiring those clients will not be returned. +func BuildArtifactExtractors() []filesystem.Extractor { + extractorsToUse := lockfileExtractors + + return extractorsToUse +} diff --git a/pkg/osvscanner/internal/scanners/lockfile.go b/pkg/osvscanner/internal/scanners/lockfile.go index 4e2bec44fb..7848ac9a6a 100644 --- a/pkg/osvscanner/internal/scanners/lockfile.go +++ b/pkg/osvscanner/internal/scanners/lockfile.go @@ -8,25 +8,6 @@ import ( "github.com/google/osv-scalibr/extractor" "github.com/google/osv-scalibr/extractor/filesystem" - "github.com/google/osv-scalibr/extractor/filesystem/language/cpp/conanlock" - "github.com/google/osv-scalibr/extractor/filesystem/language/dart/pubspec" - "github.com/google/osv-scalibr/extractor/filesystem/language/dotnet/packageslockjson" - "github.com/google/osv-scalibr/extractor/filesystem/language/erlang/mixlock" - "github.com/google/osv-scalibr/extractor/filesystem/language/golang/gomod" - "github.com/google/osv-scalibr/extractor/filesystem/language/java/gradlelockfile" - "github.com/google/osv-scalibr/extractor/filesystem/language/java/gradleverificationmetadataxml" - "github.com/google/osv-scalibr/extractor/filesystem/language/java/pomxml" - "github.com/google/osv-scalibr/extractor/filesystem/language/javascript/packagelockjson" - "github.com/google/osv-scalibr/extractor/filesystem/language/javascript/pnpmlock" - "github.com/google/osv-scalibr/extractor/filesystem/language/javascript/yarnlock" - "github.com/google/osv-scalibr/extractor/filesystem/language/php/composerlock" - "github.com/google/osv-scalibr/extractor/filesystem/language/python/pdmlock" - "github.com/google/osv-scalibr/extractor/filesystem/language/python/pipfilelock" - "github.com/google/osv-scalibr/extractor/filesystem/language/python/poetrylock" - "github.com/google/osv-scalibr/extractor/filesystem/language/python/requirements" - "github.com/google/osv-scalibr/extractor/filesystem/language/r/renvlock" - "github.com/google/osv-scalibr/extractor/filesystem/language/ruby/gemfilelock" - "github.com/google/osv-scalibr/extractor/filesystem/language/rust/cargolock" "github.com/google/osv-scalibr/extractor/filesystem/os/apk" "github.com/google/osv-scalibr/extractor/filesystem/os/dpkg" "github.com/google/osv-scanner/internal/output" @@ -35,27 +16,6 @@ import ( "github.com/google/osv-scanner/pkg/reporter" ) -var lockfileExtractors = []filesystem.Extractor{ - conanlock.Extractor{}, - packageslockjson.Extractor{}, - mixlock.Extractor{}, - pubspec.Extractor{}, - gomod.Extractor{}, - gradlelockfile.Extractor{}, - gradleverificationmetadataxml.Extractor{}, - packagelockjson.Extractor{}, - pnpmlock.Extractor{}, - yarnlock.Extractor{}, - composerlock.Extractor{}, - pipfilelock.Extractor{}, - pdmlock.Extractor{}, - poetrylock.Extractor{}, - requirements.Extractor{}, - renvlock.Extractor{}, - gemfilelock.Extractor{}, - cargolock.Extractor{}, -} - var lockfileExtractorMapping = map[string]string{ "pubspec.lock": "dart/pubspec", "pnpm-lock.yaml": "javascript/pnpmlock", @@ -81,28 +41,47 @@ var lockfileExtractorMapping = map[string]string{ "Gemfile.lock": "ruby/gemfilelock", } -// ScanLockfile will load, identify, and parse the lockfile path passed in, and add the dependencies specified +// ScanSingleFile is similar to ScanSingleFileWithMapping, just without supporting the :/path/to/lockfile prefix identifier +func ScanSingleFile(r reporter.Reporter, path string, extractorsToUse []filesystem.Extractor) ([]*extractor.Inventory, error) { + // TODO: Update the logging output to stop referring to SBOMs + path, err := filepath.Abs(path) + if err != nil { + r.Errorf("Failed to resolved path %q with error: %s\n", path, err) + return nil, err + } + + invs, err := scalibrextract.ExtractWithExtractors(context.Background(), path, extractorsToUse) + if err != nil { + r.Infof("Failed to parse SBOM %q with error: %s\n", path, err) + return nil, err + } + + pkgCount := len(invs) + if pkgCount > 0 { + r.Infof( + "Scanned %s file and found %d %s\n", + path, + pkgCount, + output.Form(pkgCount, "package", "packages"), + ) + } + + return invs, nil +} + +// ScanSingleFileWithMapping will load, identify, and parse the lockfile path passed in, and add the dependencies specified // within to `query` -// -// TODO(V2 Models): pomExtractor is temporary until V2 Models -func ScanLockfile(r reporter.Reporter, scanArg string, pomExtractor filesystem.Extractor) ([]*extractor.Inventory, error) { +func ScanSingleFileWithMapping(r reporter.Reporter, scanPath string, extractorsToUse []filesystem.Extractor) ([]*extractor.Inventory, error) { var err error var inventories []*extractor.Inventory - parseAs, path := parseLockfilePath(scanArg) + parseAs, path := parseLockfilePath(scanPath) path, err = filepath.Abs(path) if err != nil { r.Errorf("Failed to resolved path %q with error: %s\n", path, err) return nil, err } - extractorsToUse := lockfileExtractors - - if pomExtractor != nil { - extractorsToUse = append(extractorsToUse, pomExtractor) - } else { - extractorsToUse = append(extractorsToUse, pomxml.Extractor{}) - } // special case for the APK and DPKG parsers because they have a very generic name while // living at a specific location, so they are not included in the map of parsers diff --git a/pkg/osvscanner/internal/scanners/sbom.go b/pkg/osvscanner/internal/scanners/sbom.go deleted file mode 100644 index 8af2b3eb95..0000000000 --- a/pkg/osvscanner/internal/scanners/sbom.go +++ /dev/null @@ -1,45 +0,0 @@ -package scanners - -import ( - "context" - "path/filepath" - - "github.com/google/osv-scalibr/extractor" - "github.com/google/osv-scalibr/extractor/filesystem" - "github.com/google/osv-scalibr/extractor/filesystem/sbom/cdx" - "github.com/google/osv-scalibr/extractor/filesystem/sbom/spdx" - "github.com/google/osv-scanner/internal/output" - "github.com/google/osv-scanner/internal/scalibrextract" - "github.com/google/osv-scanner/pkg/reporter" -) - -var SBOMExtractors = []filesystem.Extractor{ - spdx.Extractor{}, - cdx.Extractor{}, -} - -func ScanSBOM(r reporter.Reporter, path string) ([]*extractor.Inventory, error) { - path, err := filepath.Abs(path) - if err != nil { - r.Errorf("Failed to resolved path %q with error: %s\n", path, err) - return nil, err - } - - invs, err := scalibrextract.ExtractWithExtractors(context.Background(), path, SBOMExtractors) - if err != nil { - r.Infof("Failed to parse SBOM %q with error: %s\n", path, err) - return nil, err - } - - pkgCount := len(invs) - if pkgCount > 0 { - r.Infof( - "Scanned %s file and found %d %s\n", - path, - pkgCount, - output.Form(pkgCount, "package", "packages"), - ) - } - - return invs, nil -} diff --git a/pkg/osvscanner/internal/scanners/walker.go b/pkg/osvscanner/internal/scanners/walker.go index 4ae43d653c..c7b2830685 100644 --- a/pkg/osvscanner/internal/scanners/walker.go +++ b/pkg/osvscanner/internal/scanners/walker.go @@ -10,13 +10,9 @@ import ( "github.com/go-git/go-git/v5/plumbing/format/gitignore" "github.com/google/osv-scalibr/extractor" "github.com/google/osv-scalibr/extractor/filesystem" - "github.com/google/osv-scalibr/extractor/filesystem/language/java/pomxml" "github.com/google/osv-scanner/internal/customgitignore" - "github.com/google/osv-scanner/internal/osvdev" "github.com/google/osv-scanner/internal/output" "github.com/google/osv-scanner/internal/scalibrextract" - "github.com/google/osv-scanner/internal/scalibrextract/filesystem/vendored" - "github.com/google/osv-scanner/internal/scalibrextract/vcs/gitrepo" "github.com/google/osv-scanner/pkg/reporter" ) @@ -27,7 +23,7 @@ import ( // - Any git repositories with scanGit // // TODO(V2 Models): pomExtractor is temporary until V2 Models -func ScanDir(r reporter.Reporter, dir string, skipGit bool, recursive bool, useGitIgnore bool, pomExtractor filesystem.Extractor) ([]*extractor.Inventory, error) { +func ScanDir(r reporter.Reporter, dir string, skipGit bool, recursive bool, useGitIgnore bool, extractorsToUse []filesystem.Extractor) ([]*extractor.Inventory, error) { var ignoreMatcher *gitIgnoreMatcher if useGitIgnore { var err error @@ -40,26 +36,6 @@ func ScanDir(r reporter.Reporter, dir string, skipGit bool, recursive bool, useG root := true - // Setup scan config - relevantExtractors := []filesystem.Extractor{} - if !skipGit { - relevantExtractors = append(relevantExtractors, gitrepo.Extractor{}) - } - relevantExtractors = append(relevantExtractors, lockfileExtractors...) - relevantExtractors = append(relevantExtractors, SBOMExtractors...) - // Only scan git directories if we are skipping the git extractor - // TODO: If in offline mode, don't create a vendoredExtractor - relevantExtractors = append(relevantExtractors, vendored.Extractor{ - ScanGitDir: skipGit, - OSVClient: osvdev.DefaultClient(), - }) - if pomExtractor != nil { - relevantExtractors = append(relevantExtractors, pomExtractor) - } else { - // Use the offline pomxml extractor if networking is unavailable - relevantExtractors = append(relevantExtractors, pomxml.Extractor{}) - } - var scannedInventories []*extractor.Inventory err := filepath.WalkDir(dir, func(path string, info os.DirEntry, err error) error { @@ -92,7 +68,7 @@ func ScanDir(r reporter.Reporter, dir string, skipGit bool, recursive bool, useG } // -------- Perform scanning -------- - inventories, err := scalibrextract.ExtractWithExtractors(context.Background(), path, relevantExtractors) + inventories, err := scalibrextract.ExtractWithExtractors(context.Background(), path, extractorsToUse) if err != nil && !errors.Is(err, scalibrextract.ErrExtractorNotFound) { r.Errorf("Error during extraction: %s\n", err) } diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 262848b143..577352371b 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -17,6 +17,7 @@ import ( "github.com/google/osv-scanner/internal/imodels/results" "github.com/google/osv-scanner/internal/osvdev" "github.com/google/osv-scanner/internal/output" + "github.com/google/osv-scanner/internal/resolution/client" "github.com/google/osv-scanner/internal/resolution/datasource" "github.com/google/osv-scanner/internal/version" "github.com/google/osv-scanner/pkg/models" @@ -57,9 +58,12 @@ type TransitiveScanningActions struct { MavenRegistry string } -type Matchers struct { - VulnMatcher clientinterfaces.VulnerabilityMatcher - LicenseMatcher clientinterfaces.LicenseMatcher +type ExternalAccessors struct { + VulnMatcher clientinterfaces.VulnerabilityMatcher + LicenseMatcher clientinterfaces.LicenseMatcher + MavenRegistryAPIClient *datasource.MavenRegistryAPIClient + OSVDevClient *osvdev.OSVClient + DependencyClients map[osvschema.Ecosystem]client.DependencyClient } // ErrNoPackagesFound for when no packages are found during a scan. @@ -72,35 +76,64 @@ var ErrVulnerabilitiesFound = errors.New("vulnerabilities found") // ErrAPIFailed describes errors related to querying API endpoints. var ErrAPIFailed = errors.New("API query failed") -func InitializeMatchers(r reporter.Reporter, actions ScannerActions) (Matchers, error) { - // Vulnerability Matcher - var vulnMatcher clientinterfaces.VulnerabilityMatcher +func InitializeExternalAccessors(r reporter.Reporter, actions ScannerActions) (ExternalAccessors, error) { + externalAccessors := ExternalAccessors{ + DependencyClients: map[osvschema.Ecosystem]client.DependencyClient{}, + } + + // --- Vulnerability Matcher --- var err error if actions.CompareOffline { - vulnMatcher, err = localmatcher.NewLocalMatcher(r, actions.LocalDBPath, "osv-scanner_scan/"+version.OSVVersion, actions.DownloadDatabases) + externalAccessors.VulnMatcher, err = localmatcher.NewLocalMatcher(r, actions.LocalDBPath, "osv-scanner_scan/"+version.OSVVersion, actions.DownloadDatabases) if err != nil { - return Matchers{}, err - } - } else { - vulnMatcher = &osvmatcher.OSVMatcher{ - Client: *osvdev.DefaultClient(), - InitialQueryTimeout: 5 * time.Minute, + return ExternalAccessors{}, err } + + return externalAccessors, nil + } + + // Not offline, so create accessors that require network access + externalAccessors.VulnMatcher = &osvmatcher.OSVMatcher{ + Client: *osvdev.DefaultClient(), + InitialQueryTimeout: 5 * time.Minute, + } + + // --- License Matcher --- + depsdevapiclient, err := datasource.NewDepsDevAPIClient(depsdev.DepsdevAPI, "osv-scanner_scan/"+version.OSVVersion) + if err != nil { + return ExternalAccessors{}, err + } + + externalAccessors.LicenseMatcher = &licensematcher.DepsDevLicenseMatcher{ + Client: depsdevapiclient, + } + + if actions.TransitiveScanningActions.Disabled { + return externalAccessors, nil } - // License Matcher - depsdevclient, err := datasource.NewDepsDevAPIClient(depsdev.DepsdevAPI, "osv-scanner_scan/"+version.OSVVersion) + externalAccessors.MavenRegistryAPIClient, err = datasource.NewMavenRegistryAPIClient(datasource.MavenRegistry{ + URL: actions.TransitiveScanningActions.MavenRegistry, + ReleasesEnabled: true, + }) + if err != nil { - return Matchers{}, err + return ExternalAccessors{}, err } - var licenseMatcher clientinterfaces.LicenseMatcher = &licensematcher.DepsDevLicenseMatcher{ - Client: depsdevclient, + + if !actions.TransitiveScanningActions.NativeDataSource { + depsDevAPIClient, _ := client.NewDepsDevClient(depsdev.DepsdevAPI, "osv-scanner_scan/"+version.OSVVersion) + externalAccessors.DependencyClients[osvschema.EcosystemMaven] = depsDevAPIClient + } else { + externalAccessors.DependencyClients[osvschema.EcosystemMaven], err = client.NewMavenRegistryClient(actions.TransitiveScanningActions.MavenRegistry) + if err != nil { + return ExternalAccessors{}, err + } } - return Matchers{ - VulnMatcher: vulnMatcher, - LicenseMatcher: licenseMatcher, - }, nil + externalAccessors.OSVDevClient = osvdev.DefaultClient() + + return externalAccessors, nil } // Perform osv scanner action, with optional reporter to output information @@ -109,7 +142,8 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe r = &reporter.VoidReporter{} } - // TODO(v2): Move the logic of the offline flag moving other flags into here. + // --- Sanity check flags ---- + // TODO(v2): Move the logic of the offline flag changing other flags into here from the main.go/scan.go if actions.CompareOffline { actions.SkipGit = true @@ -129,6 +163,7 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe }, } + // --- Setup Config --- if actions.ConfigOverridePath != "" { err := scanResult.ConfigManager.UseOverride(r, actions.ConfigOverridePath) if err != nil { @@ -137,8 +172,14 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe } } + // --- Setup Accessors/Clients --- + accessors, err := InitializeExternalAccessors(r, actions) + if err != nil { + return models.VulnerabilityResults{}, fmt.Errorf("failed to initialize accessors: %v", err) + } + // ----- Perform Scanning ----- - packages, err := scan(r, actions) + packages, err := scan(r, accessors, actions) if err != nil { return models.VulnerabilityResults{}, err } @@ -152,22 +193,17 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe // ----- Custom Overrides ----- overrideGoVersion(r, &scanResult) - matchers, err := InitializeMatchers(r, actions) - if err != nil { - return models.VulnerabilityResults{}, fmt.Errorf("failed to initialize matchers: %v", err) - } - // --- Make Vulnerability Requests --- - if matchers.VulnMatcher != nil { - err = makeVulnRequestWithMatcher(r, scanResult.PackageScanResults, matchers.VulnMatcher) + if accessors.VulnMatcher != nil { + err = makeVulnRequestWithMatcher(r, scanResult.PackageScanResults, accessors.VulnMatcher) if err != nil { return models.VulnerabilityResults{}, err } } // --- Make License Requests --- - if matchers.LicenseMatcher != nil && len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary { - err = matchers.LicenseMatcher.MatchLicenses(context.Background(), scanResult.PackageScanResults) + if accessors.LicenseMatcher != nil && len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary { + err = accessors.LicenseMatcher.MatchLicenses(context.Background(), scanResult.PackageScanResults) if err != nil { return models.VulnerabilityResults{}, err } diff --git a/pkg/osvscanner/scan.go b/pkg/osvscanner/scan.go index 302612d316..94b33586e0 100644 --- a/pkg/osvscanner/scan.go +++ b/pkg/osvscanner/scan.go @@ -2,37 +2,20 @@ package osvscanner import ( "github.com/google/osv-scalibr/extractor" - "github.com/google/osv-scalibr/extractor/filesystem" - "github.com/google/osv-scanner/internal/depsdev" "github.com/google/osv-scanner/internal/imodels" - "github.com/google/osv-scanner/internal/resolution/client" - "github.com/google/osv-scanner/internal/resolution/datasource" "github.com/google/osv-scanner/internal/scalibrextract/ecosystemmock" - "github.com/google/osv-scanner/internal/scalibrextract/language/java/pomxmlnet" - "github.com/google/osv-scanner/internal/version" "github.com/google/osv-scanner/pkg/osvscanner/internal/scanners" "github.com/google/osv-scanner/pkg/reporter" ) // scan essentially converts ScannerActions into PackageScanResult by performing the extractions -func scan(r reporter.Reporter, actions ScannerActions) ([]imodels.PackageScanResult, error) { +func scan(r reporter.Reporter, accessors ExternalAccessors, actions ScannerActions) ([]imodels.PackageScanResult, error) { var scannedInventories []*extractor.Inventory - // TODO(V2 Models): Temporarily initialize pom here to reduce PR size - // Eventually, we want to move TransitiveScanningActions into its own models package to avoid - // cyclic imports - var pomExtractor filesystem.Extractor - if !actions.TransitiveScanningActions.Disabled { - var err error - pomExtractor, err = createMavenExtractor(actions.TransitiveScanningActions) - if err != nil { - return nil, err - } - } - // --- Lockfiles --- + lockfileExtractors := scanners.BuildLockfileExtractors(accessors.DependencyClients, accessors.MavenRegistryAPIClient) for _, lockfileElem := range actions.LockfilePaths { - invs, err := scanners.ScanLockfile(r, lockfileElem, pomExtractor) + invs, err := scanners.ScanSingleFileWithMapping(r, lockfileElem, lockfileExtractors) if err != nil { return nil, err } @@ -41,8 +24,9 @@ func scan(r reporter.Reporter, actions ScannerActions) ([]imodels.PackageScanRes } // --- SBOMs --- + sbomExtractors := scanners.BuildSBOMExtractors() for _, sbomPath := range actions.SBOMPaths { - invs, err := scanners.ScanSBOM(r, sbomPath) + invs, err := scanners.ScanSingleFile(r, sbomPath, sbomExtractors) if err != nil { return nil, err } @@ -51,15 +35,31 @@ func scan(r reporter.Reporter, actions ScannerActions) ([]imodels.PackageScanRes } // --- Directories --- + dirExtractors := scanners.BuildWalkerExtractors( + actions.SkipGit, + accessors.OSVDevClient, + accessors.DependencyClients, + accessors.MavenRegistryAPIClient, + ) for _, dir := range actions.DirectoryPaths { r.Infof("Scanning dir %s\n", dir) - pkgs, err := scanners.ScanDir(r, dir, actions.SkipGit, actions.Recursive, !actions.NoIgnore, pomExtractor) + pkgs, err := scanners.ScanDir(r, dir, actions.SkipGit, actions.Recursive, !actions.NoIgnore, dirExtractors) if err != nil { return nil, err } scannedInventories = append(scannedInventories, pkgs...) } + // Add on additional direct dependencies passed straight from ScannerActions: + for _, commit := range actions.GitCommits { + inv := &extractor.Inventory{ + SourceCode: &extractor.SourceCodeIdentifier{Commit: commit}, + Extractor: ecosystemmock.Extractor{}, // Empty ecosystem + } + + scannedInventories = append(scannedInventories, inv) + } + if len(scannedInventories) == 0 { return nil, ErrNoPackagesFound } @@ -74,44 +74,5 @@ func scan(r reporter.Reporter, actions ScannerActions) ([]imodels.PackageScanRes }) } - // Add on additional direct dependencies passed straight from ScannerActions: - for _, commit := range actions.GitCommits { - pi := imodels.PackageInfo{ - Inventory: &extractor.Inventory{ - SourceCode: &extractor.SourceCodeIdentifier{Commit: commit}, - Extractor: ecosystemmock.Extractor{}, // Empty ecosystem - }, - } - - packages = append(packages, imodels.PackageScanResult{ - PackageInfo: pi, - }) - } - return packages, nil } - -func createMavenExtractor(actions TransitiveScanningActions) (*pomxmlnet.Extractor, error) { - var depClient client.DependencyClient - var err error - if actions.NativeDataSource { - depClient, err = client.NewMavenRegistryClient(actions.MavenRegistry) - } else { - depClient, err = client.NewDepsDevClient(depsdev.DepsdevAPI, "osv-scanner_scan/"+version.OSVVersion) - } - if err != nil { - return nil, err - } - - mavenClient, err := datasource.NewMavenRegistryAPIClient(datasource.MavenRegistry{URL: actions.MavenRegistry, ReleasesEnabled: true}) - if err != nil { - return nil, err - } - - extractor := pomxmlnet.Extractor{ - DependencyClient: depClient, - MavenRegistryAPIClient: mavenClient, - } - - return &extractor, nil -} From aaba95bd33f84ddb5f7ac26a54c26e98f630d687 Mon Sep 17 00:00:00 2001 From: Rex P Date: Wed, 15 Jan 2025 14:29:50 +1100 Subject: [PATCH 31/45] Add artifact extractors --- .../internal/scanners/extractorbuilder.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pkg/osvscanner/internal/scanners/extractorbuilder.go b/pkg/osvscanner/internal/scanners/extractorbuilder.go index 0daf69efa5..faed1d96d9 100644 --- a/pkg/osvscanner/internal/scanners/extractorbuilder.go +++ b/pkg/osvscanner/internal/scanners/extractorbuilder.go @@ -6,6 +6,7 @@ import ( "github.com/google/osv-scalibr/extractor/filesystem/language/dart/pubspec" "github.com/google/osv-scalibr/extractor/filesystem/language/dotnet/packageslockjson" "github.com/google/osv-scalibr/extractor/filesystem/language/erlang/mixlock" + "github.com/google/osv-scalibr/extractor/filesystem/language/golang/gobinary" "github.com/google/osv-scalibr/extractor/filesystem/language/golang/gomod" "github.com/google/osv-scalibr/extractor/filesystem/language/java/gradlelockfile" "github.com/google/osv-scalibr/extractor/filesystem/language/java/gradleverificationmetadataxml" @@ -21,6 +22,8 @@ import ( "github.com/google/osv-scalibr/extractor/filesystem/language/r/renvlock" "github.com/google/osv-scalibr/extractor/filesystem/language/ruby/gemfilelock" "github.com/google/osv-scalibr/extractor/filesystem/language/rust/cargolock" + "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/sbom/cdx" "github.com/google/osv-scalibr/extractor/filesystem/sbom/spdx" "github.com/google/osv-scanner/internal/osvdev" @@ -28,6 +31,7 @@ import ( "github.com/google/osv-scanner/internal/resolution/datasource" "github.com/google/osv-scanner/internal/scalibrextract/filesystem/vendored" "github.com/google/osv-scanner/internal/scalibrextract/language/java/pomxmlnet" + "github.com/google/osv-scanner/internal/scalibrextract/language/javascript/nodemodules" "github.com/google/osv-scanner/internal/scalibrextract/vcs/gitrepo" "github.com/ossf/osv-schema/bindings/go/osvschema" ) @@ -121,7 +125,13 @@ func BuildWalkerExtractors( // BuildArtifactExtractors returns all relevant extractors for artifact scanning given the required clients // All clients can be nil, and if nil the extractors requiring those clients will not be returned. func BuildArtifactExtractors() []filesystem.Extractor { - extractorsToUse := lockfileExtractors - + extractorsToUse := []filesystem.Extractor{ + nodemodules.Extractor{}, + apk.New(apk.DefaultConfig()), + gobinary.New(gobinary.DefaultConfig()), + // TODO: Add tests for debian containers + dpkg.New(dpkg.DefaultConfig()), + } + return extractorsToUse } From f455024592bd21545e61abdb0124d46cd425c5b6 Mon Sep 17 00:00:00 2001 From: Rex P Date: Wed, 15 Jan 2025 14:38:09 +1100 Subject: [PATCH 32/45] Move where licensematcher is initialized --- pkg/osvscanner/osvscanner.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 577352371b..591b051828 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -104,8 +104,10 @@ func InitializeExternalAccessors(r reporter.Reporter, actions ScannerActions) (E return ExternalAccessors{}, err } - externalAccessors.LicenseMatcher = &licensematcher.DepsDevLicenseMatcher{ - Client: depsdevapiclient, + if len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary { + externalAccessors.LicenseMatcher = &licensematcher.DepsDevLicenseMatcher{ + Client: depsdevapiclient, + } } if actions.TransitiveScanningActions.Disabled { @@ -202,7 +204,7 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe } // --- Make License Requests --- - if accessors.LicenseMatcher != nil && len(actions.ScanLicensesAllowlist) > 0 || actions.ScanLicensesSummary { + if accessors.LicenseMatcher != nil { err = accessors.LicenseMatcher.MatchLicenses(context.Background(), scanResult.PackageScanResults) if err != nil { return models.VulnerabilityResults{}, err From 10928594ea9b050720fa3614c6884f09cf20770e Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 16 Jan 2025 10:09:29 +1100 Subject: [PATCH 33/45] Properly merge main in --- go.mod | 46 +++- go.sum | 163 ++++++++++++ pkg/osvscanner/osvscanner.go | 351 +++++++++++++++++++++++++ pkg/osvscanner/vulnerability_result.go | 4 +- 4 files changed, 558 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 95021fbf03..a2040b9f30 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/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..4508abe5a4 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,19 +138,41 @@ 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= @@ -123,10 +180,17 @@ github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l 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/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/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index deffa61b35..449594ad45 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -1,12 +1,27 @@ package osvscanner import ( + "bufio" "context" + "encoding/json" "errors" "fmt" + "net/http" + "os" + "os/exec" + "slices" + "strings" "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-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/os/osrelease" + "github.com/google/osv-scalibr/log" "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" @@ -19,9 +34,11 @@ import ( "github.com/google/osv-scanner/internal/output" "github.com/google/osv-scanner/internal/resolution/client" "github.com/google/osv-scanner/internal/resolution/datasource" + "github.com/google/osv-scanner/internal/scalibrextract/language/javascript/nodemodules" "github.com/google/osv-scanner/internal/version" "github.com/google/osv-scanner/pkg/models" "github.com/google/osv-scanner/pkg/reporter" + "github.com/opencontainers/go-digest" "github.com/ossf/osv-schema/bindings/go/osvschema" ) @@ -271,6 +288,340 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe return results, nil } +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: %v", err) + } + + scanner := scalibr.New() + + 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.DockerImageName != "" { + path, exportErr := exportDockerImage(r, actions.DockerImageName) + if exportErr != nil { + return models.VulnerabilityResults{}, exportErr + } + defer os.Remove(path) + img, err = image.FromTarball(path, image.DefaultConfig()) + r.Infof("Scanning image %q\n", actions.DockerImageName) + } + if err != nil { + return models.VulnerabilityResults{}, err + } + defer img.CleanUp() + + // Ignore error, as if this would error we would have failed the initial scan + chainLayers, _ := img.ChainLayers() + m, err := osrelease.GetOSRelease(chainLayers[len(chainLayers)-1].FS()) + OS := "Unknown" + if err == nil { + OS = m["OSID"] + } + + scalibrSR, err := scanner.ScanContainer(context.Background(), img, &scalibr.ScanConfig{ + FilesystemExtractors: []filesystem.Extractor{ + nodemodules.Extractor{}, + apk.New(apk.DefaultConfig()), + gobinary.New(gobinary.DefaultConfig()), + // TODO: Add tests for debian containers + dpkg.New(dpkg.DefaultConfig()), + }, + }) + + if err != nil { + return models.VulnerabilityResults{}, fmt.Errorf("failed to scan container image: %w", err) + } + + if len(scalibrSR.Inventories) == 0 { + return models.VulnerabilityResults{}, ErrNoPackagesFound + } + + 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 --- + { + + layerMetadata := []models.LayerMetadata{} + for i, cl := range chainLayers { + layerMetadata = append(layerMetadata, models.LayerMetadata{ + Index: i, + DiffID: cl.Layer().DiffID(), + Command: cl.Layer().Command(), + IsEmpty: cl.Layer().IsEmpty(), + }) + + } + + scanResult.ImageMetadata = &models.ImageMetadata{ + 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 + {}, + }, + OS: OS, + LayerMetadata: layerMetadata, + } + + var runningDigest digest.Digest + chainIDs := []digest.Digest{} + + for _, cl := range chainLayers { + var diffDigest digest.Digest + if cl.Layer().DiffID() == "" { + chainIDs = append(chainIDs, "") + continue + } + + diffDigest = digest.NewDigestFromEncoded(digest.SHA256, cl.Layer().DiffID()) + + if runningDigest == "" { + runningDigest = diffDigest + } else { + runningDigest = digest.FromBytes([]byte(runningDigest + " " + diffDigest)) + } + + chainIDs = append(chainIDs, runningDigest) + } + + currentBaseImageIndex := 0 + client := http.DefaultClient + for i, cid := range slices.Backward(chainIDs) { + if cid == "" { + scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex + continue + } + + resp, err := client.Get("https://api.deps.dev/v3alpha/querycontainerimages/" + cid.String()) + if err != nil { + log.Errorf("API DEPS DEV ERROR: %s", err) + continue + } + + if resp.StatusCode == http.StatusNotFound { + scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex + continue + } + + if resp.StatusCode != http.StatusOK { + log.Errorf("API DEPS DEV ERROR: %s", resp.Status) + continue + } + + d := json.NewDecoder(resp.Body) + + type baseImageEntry struct { + Repository string `json:"repository"` + } + type baseImageResults struct { + Results []baseImageEntry `json:"results"` + } + + var results baseImageResults + err = d.Decode(&results) + if err != nil { + log.Errorf("API DEPS DEV ERROR: %s", err) + continue + } + + // Found some base images! + baseImagePossibilities := []models.BaseImageDetails{} + for _, r := range results.Results { + baseImagePossibilities = append(baseImagePossibilities, models.BaseImageDetails{ + Name: r.Repository, + }) + } + + slices.SortFunc(baseImagePossibilities, func(a, b models.BaseImageDetails) int { + return len(a.Name) - len(b.Name) + }) + + scanResult.ImageMetadata.BaseImages = append(scanResult.ImageMetadata.BaseImages, baseImagePossibilities) + currentBaseImageIndex += 1 + // scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex + + // Backfill with heuristic + + possibleFinalBaseImageCommands := []string{ + "/bin/sh -c #(nop) CMD", + "CMD", + "/bin/sh -c #(nop) ENTRYPOINT", + "ENTRYPOINT", + } + BackfillLoop: + for i2 := i; i2 < len(scanResult.ImageMetadata.LayerMetadata); i2++ { + if !scanResult.ImageMetadata.LayerMetadata[i2].IsEmpty { + break + } + buildCommand := scanResult.ImageMetadata.LayerMetadata[i2].Command + scanResult.ImageMetadata.LayerMetadata[i2].BaseImageIndex = currentBaseImageIndex + for _, prefix := range possibleFinalBaseImageCommands { + if strings.HasPrefix(buildCommand, prefix) { + break BackfillLoop + } + } + } + + } + } + + // ----- 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"), + ) + } + + if len(results.Results) > 0 { + // Determine the correct error to return. + + // TODO(v2): in the next breaking release of osv-scanner, consider + // returning a ScanError instead of an error. + var vuln bool + onlyUncalledVuln := true + var licenseViolation bool + for _, vf := range results.Flatten() { + if vf.Vulnerability.ID != "" { + vuln = true + if vf.GroupInfo.IsCalled() { + onlyUncalledVuln = false + } + } + if len(vf.LicenseViolations) > 0 { + licenseViolation = true + } + } + onlyUncalledVuln = onlyUncalledVuln && vuln + licenseViolation = licenseViolation && len(actions.ScanLicensesAllowlist) > 0 + + if (!vuln || onlyUncalledVuln) && !licenseViolation { + // There is no error. + return results, nil + } + + return results, ErrVulnerabilitiesFound + } + + return results, 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 +} + // TODO(V2): Add context func makeVulnRequestWithMatcher( r reporter.Reporter, diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go index 49d71d0066..31d23aed88 100644 --- a/pkg/osvscanner/vulnerability_result.go +++ b/pkg/osvscanner/vulnerability_result.go @@ -49,9 +49,7 @@ func buildVulnerabilityResults( if psr.LayerDetails != nil { pkg.Package.ImageOrigin = &models.ImageOriginDetails{ - DiffID: psr.LayerDetails.DiffID, - OriginCommand: psr.LayerDetails.Command, - InBaseImage: psr.LayerDetails.InBaseImage, + Index: psr.LayerDetails.Index, } } pkg.DepGroups = p.DepGroups() From 4b1a3396cb6116ad1a02b315c348a04ed9e79af0 Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 16 Jan 2025 10:20:29 +1100 Subject: [PATCH 34/45] Fix bug with backfill --- pkg/osvscanner/osvscanner.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 449594ad45..ba0f763af2 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -360,7 +360,6 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner // --- Fill Image Metadata --- { - layerMetadata := []models.LayerMetadata{} for i, cl := range chainLayers { layerMetadata = append(layerMetadata, models.LayerMetadata{ @@ -457,7 +456,7 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner scanResult.ImageMetadata.BaseImages = append(scanResult.ImageMetadata.BaseImages, baseImagePossibilities) currentBaseImageIndex += 1 - // scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex + scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex // Backfill with heuristic From bd1066c59d676febe680e62b70e196579aef80a9 Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 16 Jan 2025 10:45:11 +1100 Subject: [PATCH 35/45] Update go.mod to latest scalibr version, apply backfill. --- go.mod | 2 +- go.sum | 2 + pkg/osvscanner/osvscanner.go | 97 +++++++++++++----------------------- 3 files changed, 38 insertions(+), 63 deletions(-) diff --git a/go.mod b/go.mod index a2040b9f30..686afb96c1 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ 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/osv-scalibr v0.1.6-0.20250105222824-56e5c3bfb149 + github.com/google/osv-scalibr v0.1.6-0.20250115231949-9a7ebb469850 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 diff --git a/go.sum b/go.sum index 4508abe5a4..0d1b2919e2 100644 --- a/go.sum +++ b/go.sum @@ -180,6 +180,8 @@ github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l 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.20250115231949-9a7ebb469850 h1:vxAtv63JdaXRYTrIsGSByr9gH4XFV2+UYlSV8QOrIv4= +github.com/google/osv-scalibr v0.1.6-0.20250115231949-9a7ebb469850/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= diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index ba0f763af2..2d9c8918d7 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -255,37 +255,7 @@ func DoScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityRe ) } - if len(results.Results) > 0 { - // Determine the correct error to return. - - // TODO(v2): in the next breaking release of osv-scanner, consider - // returning a ScanError instead of an error. - var vuln bool - onlyUncalledVuln := true - var licenseViolation bool - for _, vf := range results.Flatten() { - if vf.Vulnerability.ID != "" { - vuln = true - if vf.GroupInfo.IsCalled() { - onlyUncalledVuln = false - } - } - if len(vf.LicenseViolations) > 0 { - licenseViolation = true - } - } - onlyUncalledVuln = onlyUncalledVuln && vuln - licenseViolation = licenseViolation && len(actions.ScanLicensesAllowlist) > 0 - - if (!vuln || onlyUncalledVuln) && !licenseViolation { - // There is no error. - return results, nil - } - - return results, ErrVulnerabilitiesFound - } - - return results, nil + return results, determineReturnErr(results) } func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.VulnerabilityResults, error) { @@ -513,37 +483,7 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner ) } - if len(results.Results) > 0 { - // Determine the correct error to return. - - // TODO(v2): in the next breaking release of osv-scanner, consider - // returning a ScanError instead of an error. - var vuln bool - onlyUncalledVuln := true - var licenseViolation bool - for _, vf := range results.Flatten() { - if vf.Vulnerability.ID != "" { - vuln = true - if vf.GroupInfo.IsCalled() { - onlyUncalledVuln = false - } - } - if len(vf.LicenseViolations) > 0 { - licenseViolation = true - } - } - onlyUncalledVuln = onlyUncalledVuln && vuln - licenseViolation = licenseViolation && len(actions.ScanLicensesAllowlist) > 0 - - if (!vuln || onlyUncalledVuln) && !licenseViolation { - // There is no error. - return results, nil - } - - return results, ErrVulnerabilitiesFound - } - - return results, nil + return results, determineReturnErr(results) } func exportDockerImage(r reporter.Reporter, dockerImageName string) (string, error) { @@ -621,6 +561,39 @@ func runCommandLogError(r reporter.Reporter, name string, args ...string) error return nil } +func determineReturnErr(results models.VulnerabilityResults) error { + if len(results.Results) > 0 { + // Determine the correct error to return. + + // TODO(v2): in the next breaking release of osv-scanner, consider + // returning a ScanError instead of an error. + var vuln bool + onlyUncalledVuln := true + var licenseViolation bool + for _, vf := range results.Flatten() { + if vf.Vulnerability.ID != "" { + vuln = true + if vf.GroupInfo.IsCalled() { + onlyUncalledVuln = false + } + } + if len(vf.LicenseViolations) > 0 { + licenseViolation = true + } + } + onlyUncalledVuln = onlyUncalledVuln && vuln + + if (!vuln || onlyUncalledVuln) && !licenseViolation { + // There is no error. + return nil + } + + return ErrVulnerabilitiesFound + } + + return nil +} + // TODO(V2): Add context func makeVulnRequestWithMatcher( r reporter.Reporter, From 27957644f79bea04930852e86bd664c2fae32836 Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 16 Jan 2025 10:53:55 +1100 Subject: [PATCH 36/45] Move docker image initialization out to it's own package. --- pkg/models/image.go | 12 +- .../internal/imagehelpers/imagehelpers.go | 87 +++++++++++ pkg/osvscanner/osvscanner.go | 138 ++++-------------- 3 files changed, 120 insertions(+), 117 deletions(-) create mode 100644 pkg/osvscanner/internal/imagehelpers/imagehelpers.go diff --git a/pkg/models/image.go b/pkg/models/image.go index a91ef8df10..d2fe6def25 100644 --- a/pkg/models/image.go +++ b/pkg/models/image.go @@ -1,5 +1,7 @@ package models +import "github.com/opencontainers/go-digest" + type ImageOriginDetails struct { Index int } @@ -17,9 +19,9 @@ type BaseImageDetails struct { } type LayerMetadata struct { - Index int `json:"index"` - DiffID string `json:"diff_id"` - Command string `json:"command"` - IsEmpty bool `json:"is_empty"` - BaseImageIndex int `json:"base_image_index"` + Index int `json:"index"` + 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/osvscanner/internal/imagehelpers/imagehelpers.go b/pkg/osvscanner/internal/imagehelpers/imagehelpers.go new file mode 100644 index 0000000000..bd12dd35b6 --- /dev/null +++ b/pkg/osvscanner/internal/imagehelpers/imagehelpers.go @@ -0,0 +1,87 @@ +package imagehelpers + +import ( + "bufio" + "errors" + "fmt" + "os" + "os/exec" + "strings" + + "github.com/google/osv-scanner/pkg/reporter" +) + +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/osvscanner.go b/pkg/osvscanner/osvscanner.go index 2d9c8918d7..2b3e33da90 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -1,14 +1,12 @@ package osvscanner import ( - "bufio" "context" "encoding/json" "errors" "fmt" "net/http" "os" - "os/exec" "slices" "strings" "time" @@ -16,10 +14,6 @@ import ( 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-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/os/osrelease" "github.com/google/osv-scalibr/log" "github.com/google/osv-scanner/internal/clients/clientimpl/licensematcher" @@ -34,9 +28,10 @@ import ( "github.com/google/osv-scanner/internal/output" "github.com/google/osv-scanner/internal/resolution/client" "github.com/google/osv-scanner/internal/resolution/datasource" - "github.com/google/osv-scanner/internal/scalibrextract/language/javascript/nodemodules" "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/opencontainers/go-digest" "github.com/ossf/osv-schema/bindings/go/osvschema" @@ -276,14 +271,13 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner return models.VulnerabilityResults{}, fmt.Errorf("failed to initialize accessors: %v", err) } - scanner := scalibr.New() - + // --- 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.DockerImageName != "" { - path, exportErr := exportDockerImage(r, actions.DockerImageName) + path, exportErr := imagehelpers.ExportDockerImage(r, actions.DockerImageName) if exportErr != nil { return models.VulnerabilityResults{}, exportErr } @@ -296,22 +290,9 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner } defer img.CleanUp() - // Ignore error, as if this would error we would have failed the initial scan - chainLayers, _ := img.ChainLayers() - m, err := osrelease.GetOSRelease(chainLayers[len(chainLayers)-1].FS()) - OS := "Unknown" - if err == nil { - OS = m["OSID"] - } - + scanner := scalibr.New() scalibrSR, err := scanner.ScanContainer(context.Background(), img, &scalibr.ScanConfig{ - FilesystemExtractors: []filesystem.Extractor{ - nodemodules.Extractor{}, - apk.New(apk.DefaultConfig()), - gobinary.New(gobinary.DefaultConfig()), - // TODO: Add tests for debian containers - dpkg.New(dpkg.DefaultConfig()), - }, + FilesystemExtractors: scanners.BuildArtifactExtractors(), }) if err != nil { @@ -322,14 +303,25 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner return models.VulnerabilityResults{}, ErrNoPackagesFound } - 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 --- { + chainLayers, err := img.ChainLayers() + if err != nil { + // This is very unlikely, as if this would error we would have failed the initial scan + return models.VulnerabilityResults{}, err + } + m, err := osrelease.GetOSRelease(chainLayers[len(chainLayers)-1].FS()) + OS := "Unknown" + if err == nil { + OS = m["OSID"] + } + + 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 + } + layerMetadata := []models.LayerMetadata{} for i, cl := range chainLayers { layerMetadata = append(layerMetadata, models.LayerMetadata{ @@ -338,7 +330,6 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner Command: cl.Layer().Command(), IsEmpty: cl.Layer().IsEmpty(), }) - } scanResult.ImageMetadata = &models.ImageMetadata{ @@ -361,7 +352,7 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner continue } - diffDigest = digest.NewDigestFromEncoded(digest.SHA256, cl.Layer().DiffID()) + diffDigest = cl.Layer().DiffID() if runningDigest == "" { runningDigest = diffDigest @@ -486,87 +477,10 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner return results, determineReturnErr(results) } -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 -} - +// 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 { - // Determine the correct error to return. - - // TODO(v2): in the next breaking release of osv-scanner, consider - // returning a ScanError instead of an error. var vuln bool onlyUncalledVuln := true var licenseViolation bool From dc16ad0dc440a7491d8f1ce7019d089012586f36 Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 16 Jan 2025 11:18:07 +1100 Subject: [PATCH 37/45] Copy over a bunch of changes --- cmd/osv-scanner/scan/main.go | 2 +- internal/imodels/imodels.go | 6 ++++++ pkg/models/image.go | 1 - pkg/models/results.go | 11 ++++++----- pkg/osvscanner/osvscanner.go | 17 ++++++++++------- pkg/osvscanner/vulnerability_result.go | 11 ++++++----- 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/cmd/osv-scanner/scan/main.go b/cmd/osv-scanner/scan/main.go index 269a62a573..abbd8e34d4 100644 --- a/cmd/osv-scanner/scan/main.go +++ b/cmd/osv-scanner/scan/main.go @@ -286,7 +286,7 @@ func action(context *cli.Context, stdout, stderr io.Writer) (reporter.Reporter, 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"), diff --git a/internal/imodels/imodels.go b/internal/imodels/imodels.go index 754cb870c8..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 } diff --git a/pkg/models/image.go b/pkg/models/image.go index d2fe6def25..dcf467b8bd 100644 --- a/pkg/models/image.go +++ b/pkg/models/image.go @@ -19,7 +19,6 @@ type BaseImageDetails struct { } type LayerMetadata struct { - Index int `json:"index"` DiffID digest.Digest `json:"diff_id"` Command string `json:"command"` IsEmpty bool `json:"is_empty"` diff --git a/pkg/models/results.go b/pkg/models/results.go index 91c1b682c4..f6d21ab776 100644 --- a/pkg/models/results.go +++ b/pkg/models/results.go @@ -189,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:"image_origin_details"` + 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/osvscanner.go b/pkg/osvscanner/osvscanner.go index 2b3e33da90..90c549bbd4 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -45,7 +45,7 @@ type ScannerActions struct { Recursive bool SkipGit bool NoIgnore bool - DockerImageName string + Image string ConfigOverridePath string CallAnalysisStates map[string]bool @@ -276,14 +276,18 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner if actions.ScanOCIImage != "" { img, err = image.FromTarball(actions.ScanOCIImage, image.DefaultConfig()) r.Infof("Scanning image %q\n", actions.ScanOCIImage) - } else if actions.DockerImageName != "" { - path, exportErr := imagehelpers.ExportDockerImage(r, actions.DockerImageName) + } else if actions.Image != "" { + path, exportErr := imagehelpers.ExportDockerImage(r, actions.Image) if exportErr != nil { return models.VulnerabilityResults{}, exportErr } - defer os.Remove(path) + + // 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.DockerImageName) + r.Infof("Scanning image %q\n", actions.Image) } if err != nil { return models.VulnerabilityResults{}, err @@ -323,9 +327,8 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner } layerMetadata := []models.LayerMetadata{} - for i, cl := range chainLayers { + for _, cl := range chainLayers { layerMetadata = append(layerMetadata, models.LayerMetadata{ - Index: i, DiffID: cl.Layer().DiffID(), Command: cl.Layer().Command(), IsEmpty: cl.Layer().IsEmpty(), diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go index 31d23aed88..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,10 +41,10 @@ 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(), } } From ea75bb04f0c210d0131fe8816d376e506690c8b2 Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 16 Jan 2025 11:23:55 +1100 Subject: [PATCH 38/45] Update OSPackageName snapshots --- cmd/osv-scanner/__snapshots__/main_test.snap | 201 +++++++++++------- .../vulnerability_result_diff_test.snap | 11 +- .../__snapshots__/machinejson_test.snap | 88 ++++++++ .../output/__snapshots__/result_test.snap | 3 + internal/output/output_result.go | 6 +- .../sourceanalysis/__snapshots__/go_test.snap | 6 + .../__snapshots__/filter_internal_test.snap | 11 + .../vulnerability_result_internal_test.snap | 15 ++ 8 files changed, 257 insertions(+), 84 deletions(-) diff --git a/cmd/osv-scanner/__snapshots__/main_test.snap b/cmd/osv-scanner/__snapshots__/main_test.snap index 306ec9971c..a3d2e62cf3 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")... --- @@ -946,7 +947,8 @@ 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")... --- @@ -959,10 +961,23 @@ 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 "alpine:3.18.9" -No issues found +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,7 +986,7 @@ 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 "hello-world" @@ -983,7 +998,7 @@ 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 "hello-world:linux" @@ -1186,6 +1201,7 @@ Warning: `scan` exists as both a subcommand of OSV-Scanner and as a file on the { "package": { "name": "babel", + "OSPackageName": "", "version": "6.23.0", "ecosystem": "npm" }, @@ -1196,6 +1212,7 @@ Warning: `scan` exists as both a subcommand of OSV-Scanner and as a file on the { "package": { "name": "human-signals", + "OSPackageName": "", "version": "5.0.0", "ecosystem": "npm" }, @@ -1206,6 +1223,7 @@ Warning: `scan` exists as both a subcommand of OSV-Scanner and as a file on the { "package": { "name": "ms", + "OSPackageName": "", "version": "2.1.3", "ecosystem": "npm" }, @@ -1216,6 +1234,7 @@ Warning: `scan` exists as both a subcommand of OSV-Scanner and as a file on the { "package": { "name": "type-fest", + "OSPackageName": "", "version": "4.26.1", "ecosystem": "npm" }, @@ -1293,6 +1312,7 @@ license MIT WITH (Bison-exception-2.2 AND somethingelse) for package npm/ms/2.1. { "package": { "name": "babel", + "OSPackageName": "", "version": "6.23.0", "ecosystem": "npm" }, @@ -1303,6 +1323,7 @@ license MIT WITH (Bison-exception-2.2 AND somethingelse) for package npm/ms/2.1. { "package": { "name": "human-signals", + "OSPackageName": "", "version": "5.0.0", "ecosystem": "npm" }, @@ -1313,6 +1334,7 @@ license MIT WITH (Bison-exception-2.2 AND somethingelse) for package npm/ms/2.1. { "package": { "name": "ms", + "OSPackageName": "", "version": "2.1.3", "ecosystem": "npm" }, @@ -1323,6 +1345,7 @@ license MIT WITH (Bison-exception-2.2 AND somethingelse) for package npm/ms/2.1. { "package": { "name": "type-fest", + "OSPackageName": "", "version": "4.26.1", "ecosystem": "npm" }, @@ -1461,6 +1484,7 @@ overriding license for package Packagist/league/flysystem/1.0.8 with 0BSD { "package": { "name": "babel", + "OSPackageName": "", "version": "6.23.0", "ecosystem": "npm" }, @@ -1471,6 +1495,7 @@ overriding license for package Packagist/league/flysystem/1.0.8 with 0BSD { "package": { "name": "human-signals", + "OSPackageName": "", "version": "5.0.0", "ecosystem": "npm" }, @@ -1484,6 +1509,7 @@ overriding license for package Packagist/league/flysystem/1.0.8 with 0BSD { "package": { "name": "ms", + "OSPackageName": "", "version": "2.1.3", "ecosystem": "npm" }, @@ -1494,6 +1520,7 @@ overriding license for package Packagist/league/flysystem/1.0.8 with 0BSD { "package": { "name": "type-fest", + "OSPackageName": "", "version": "4.26.1", "ecosystem": "npm" }, @@ -1534,6 +1561,7 @@ Scanned /fixtures/locks-licenses/package-lock.json file and found 4 pac { "package": { "name": "human-signals", + "OSPackageName": "", "version": "5.0.0", "ecosystem": "npm" }, @@ -2692,8 +2720,8 @@ 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. +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 +----------------------------------------------------------+ @@ -2701,6 +2729,7 @@ Alpine:v3.18 +---------+-------------------+---------------+------------+ | PACKAGE | INSTALLED VERSION | FIX AVAILABLE | VULN COUNT | +---------+-------------------+---------------+------------+ +| openssl | 1.1.1k-r0 | Fix Available | 38 | | zlib | 1.2.11-r1 | Fix Available | 2 | +---------+-------------------+---------------+------------+ @@ -2723,10 +2752,76 @@ failed to load image from tarball with path "./fixtures/oci-image/no-file-here.t --- +[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. +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 +----------------------------------------------------------+ @@ -2735,6 +2830,7 @@ Alpine:v3.19 | 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`. @@ -2748,8 +2844,8 @@ 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. +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 +--------------------------------------------------------------+ @@ -2758,7 +2854,7 @@ npm | 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 +----------------------------------------------------------+ @@ -2767,6 +2863,7 @@ Alpine:v3.19 | 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`. @@ -2780,8 +2877,8 @@ 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. +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 +----------------------------------------------------------+ @@ -2790,6 +2887,7 @@ Alpine:v3.19 | 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`. @@ -2803,8 +2901,8 @@ 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. +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 +----------------------------------------------------------+ @@ -2813,6 +2911,7 @@ Alpine:v3.19 | 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`. @@ -2826,8 +2925,8 @@ 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. +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 +----------------------------------------------------------+ @@ -2836,6 +2935,7 @@ Alpine:v3.19 | 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`. @@ -2849,8 +2949,8 @@ 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. +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 +----------------------------------------------------------+ @@ -2859,6 +2959,7 @@ Alpine:v3.19 | 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`. @@ -2870,64 +2971,6 @@ You can also view the full vulnerability list in your terminal with: `osv-scanne --- -[TestRun_OCIImage/scanning_project_packages_using_go_binaries - 1] -Scanning image "../../internal/image/fixtures/test-package-tracing.tar" -Total 6 packages affected by 24 vulnerabilities (0 Critical, 0 High, 0 Medium, 0 Low, 24 Unknown) from 1 ecosystems. -24 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 | -+---------+-------------------+---------------+------------+ - -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_project_packages_using_go_binaries - 2] - ---- - [TestRun_SubCommands/scan_with_a_flag - 1] Scanning dir ./fixtures/locks-one-with-nested Scanned /fixtures/locks-one-with-nested/nested/composer.lock file and found 1 package diff --git a/internal/ci/__snapshots__/vulnerability_result_diff_test.snap b/internal/ci/__snapshots__/vulnerability_result_diff_test.snap index bb4a93ff9b..7a3f1fbf61 100755 --- a/internal/ci/__snapshots__/vulnerability_result_diff_test.snap +++ b/internal/ci/__snapshots__/vulnerability_result_diff_test.snap @@ -41,6 +41,7 @@ { "package": { "name": "github.com/gogo/protobuf", + "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -137,6 +138,7 @@ { "package": { "name": "github.com/gogo/protobuf", + "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -273,6 +275,7 @@ { "package": { "name": "github.com/gogo/protobuf", + "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -381,7 +384,7 @@ "CVE-2021-3121", "GHSA-c3h9-896r-86jm" ], - "max_severity": "" + "max_severity": "8.6" } ] } @@ -433,6 +436,7 @@ { "package": { "name": "github.com/gogo/protobuf", + "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -556,6 +560,7 @@ { "package": { "name": "regex", + "OSPackageName": "", "version": "1.5.1", "ecosystem": "crates.io" }, @@ -778,6 +783,7 @@ { "package": { "name": "github.com/gogo/protobuf", + "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -886,7 +892,7 @@ "CVE-2021-3121", "GHSA-c3h9-896r-86jm" ], - "max_severity": "" + "max_severity": "8.6" } ] } @@ -901,6 +907,7 @@ { "package": { "name": "regex", + "OSPackageName": "", "version": "1.5.1", "ecosystem": "crates.io" }, diff --git a/internal/output/__snapshots__/machinejson_test.snap b/internal/output/__snapshots__/machinejson_test.snap index e07d8a010f..c299e3ba74 100755 --- a/internal/output/__snapshots__/machinejson_test.snap +++ b/internal/output/__snapshots__/machinejson_test.snap @@ -11,6 +11,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -29,6 +30,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -39,6 +41,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -57,6 +60,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -67,6 +71,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -101,6 +106,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -122,6 +128,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -135,6 +142,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -153,6 +161,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -163,6 +172,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -200,6 +210,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -222,6 +233,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -235,6 +247,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -253,6 +266,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -263,6 +277,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -298,6 +313,7 @@ { "package": { "name": "author1/mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "Packagist" }, @@ -319,6 +335,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -332,6 +349,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -350,6 +368,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.3.5", "ecosystem": "NuGet" }, @@ -360,6 +379,7 @@ { "package": { "name": "author1/mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "Packagist" }, @@ -400,6 +420,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -425,6 +446,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -442,6 +464,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -460,6 +483,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -470,6 +494,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -585,6 +610,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -619,6 +645,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" } @@ -650,6 +677,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -684,6 +712,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -723,6 +752,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -760,6 +790,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -800,6 +831,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -821,6 +853,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "5.9.0", "ecosystem": "npm" }, @@ -855,6 +888,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -904,6 +938,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -942,6 +977,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -960,6 +996,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -973,6 +1010,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1038,6 +1076,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1081,6 +1120,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -1113,6 +1153,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -1131,6 +1172,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -1144,6 +1186,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1203,6 +1246,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1268,6 +1312,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1333,6 +1378,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1392,6 +1438,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1432,6 +1479,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "5.9.0", "ecosystem": "npm" }, @@ -1469,6 +1517,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1520,6 +1569,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.2", "ecosystem": "npm" }, @@ -1557,6 +1607,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -1589,6 +1640,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -1661,6 +1713,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1708,6 +1761,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.2", "ecosystem": "npm" }, @@ -1745,6 +1799,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -1774,6 +1829,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -1843,6 +1899,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" } @@ -1858,6 +1915,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" } @@ -1865,6 +1923,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" } @@ -1880,6 +1939,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" } @@ -1887,6 +1947,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" } @@ -1916,6 +1977,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1953,6 +2015,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -1982,6 +2045,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" } @@ -1997,6 +2061,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" } @@ -2004,6 +2069,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2055,6 +2121,7 @@ { "package": { "name": "author1/mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "Packagist" }, @@ -2102,6 +2169,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.2", "ecosystem": "npm" }, @@ -2139,6 +2207,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "NuGet" }, @@ -2171,6 +2240,7 @@ { "package": { "name": "author3/mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "Packagist" }, @@ -2243,6 +2313,7 @@ { "package": { "name": "author1/mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "Packagist" }, @@ -2302,6 +2373,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.2", "ecosystem": "npm" }, @@ -2339,6 +2411,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "3.2.5", "ecosystem": "NuGet" }, @@ -2371,6 +2444,7 @@ { "package": { "name": "author3/mine3", + "OSPackageName": "", "version": "0.4.1", "ecosystem": "Packagist" }, @@ -2518,6 +2592,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" } @@ -2547,6 +2622,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2628,6 +2704,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2685,6 +2762,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2742,6 +2820,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2793,6 +2872,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2847,6 +2927,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2922,6 +3003,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2991,6 +3073,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -3014,6 +3097,7 @@ { "package": { "name": "mine3", + "OSPackageName": "", "version": "0.10.2-rc", "ecosystem": "npm" }, @@ -3058,6 +3142,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -3095,6 +3180,7 @@ { "package": { "name": "mine2", + "OSPackageName": "", "version": "5.9.0", "ecosystem": "npm" } @@ -3124,6 +3210,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -3161,6 +3248,7 @@ { "package": { "name": "mine1", + "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, diff --git a/internal/output/__snapshots__/result_test.snap b/internal/output/__snapshots__/result_test.snap index 65e879e494..5af4e10ac5 100755 --- a/internal/output/__snapshots__/result_test.snap +++ b/internal/output/__snapshots__/result_test.snap @@ -29,6 +29,7 @@ { "Package": { "name": "regex", + "OSPackageName": "", "version": "1.5.1", "ecosystem": "crates.io" }, @@ -234,6 +235,7 @@ { "Package": { "name": "github.com/gogo/protobuf", + "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -314,6 +316,7 @@ { "Package": { "name": "regex", + "OSPackageName": "", "version": "1.5.1", "ecosystem": "crates.io" }, diff --git a/internal/output/output_result.go b/internal/output/output_result.go index 978e8240fa..95c8447bae 100644 --- a/internal/output/output_result.go +++ b/internal/output/output_result.go @@ -289,10 +289,10 @@ func processPackage(vulnPkg models.PackageVulns) PackageResult { if vulnPkg.Package.ImageOrigin != nil { packageLayerDetail := PackageLayerDetail{ - LayerID: vulnPkg.Package.ImageOrigin.DiffID, - 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..4cab6560c5 100755 --- a/internal/sourceanalysis/__snapshots__/go_test.snap +++ b/internal/sourceanalysis/__snapshots__/go_test.snap @@ -4,6 +4,7 @@ { "package": { "name": "github.com/gogo/protobuf", + "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -171,6 +172,7 @@ { "package": { "name": "github.com/ipfs/go-bitfield", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "Go" }, @@ -325,6 +327,7 @@ { "package": { "name": "golang.org/x/image", + "OSPackageName": "", "version": "0.4.0", "ecosystem": "Go" }, @@ -485,6 +488,7 @@ { "package": { "name": "github.com/gogo/protobuf", + "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -635,6 +639,7 @@ { "package": { "name": "github.com/ipfs/go-bitfield", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "Go" }, @@ -772,6 +777,7 @@ { "package": { "name": "golang.org/x/image", + "OSPackageName": "", "version": "0.4.0", "ecosystem": "Go" }, diff --git a/pkg/osvscanner/__snapshots__/filter_internal_test.snap b/pkg/osvscanner/__snapshots__/filter_internal_test.snap index 5b63a8a962..90ab8f84b3 100755 --- a/pkg/osvscanner/__snapshots__/filter_internal_test.snap +++ b/pkg/osvscanner/__snapshots__/filter_internal_test.snap @@ -23,6 +23,7 @@ { "package": { "name": "remove_dir_all", + "OSPackageName": "", "version": "0.5.3", "ecosystem": "crates.io" }, @@ -174,6 +175,7 @@ { "package": { "name": "time", + "OSPackageName": "", "version": "0.1.45", "ecosystem": "crates.io" }, @@ -431,6 +433,7 @@ { "package": { "name": "golang.org/x/net", + "OSPackageName": "", "version": "0.1.0", "ecosystem": "Go" }, @@ -927,6 +930,7 @@ { "package": { "name": "ascii", + "OSPackageName": "", "version": "0.8.7", "ecosystem": "crates.io" }, @@ -1073,6 +1077,7 @@ { "package": { "name": "remove_dir_all", + "OSPackageName": "", "version": "0.5.3", "ecosystem": "crates.io" }, @@ -1224,6 +1229,7 @@ { "package": { "name": "time", + "OSPackageName": "", "version": "0.1.45", "ecosystem": "crates.io" }, @@ -1494,6 +1500,7 @@ { "package": { "name": "unixodbc", + "OSPackageName": "", "version": "2.3.11-2", "ecosystem": "Debian:10" }, @@ -1553,6 +1560,7 @@ { "package": { "name": "chromium", + "OSPackageName": "", "version": "73.0.3683.75-1", "ecosystem": "Debian:10" }, @@ -1659,6 +1667,7 @@ { "package": { "name": "golang.org/x/net", + "OSPackageName": "", "version": "0.1.0", "ecosystem": "Go" }, @@ -1876,6 +1885,7 @@ { "package": { "name": "ascii", + "OSPackageName": "", "version": "0.8.7", "ecosystem": "crates.io" }, @@ -1954,6 +1964,7 @@ { "package": { "name": "time", + "OSPackageName": "", "version": "0.1.45", "ecosystem": "crates.io" }, diff --git a/pkg/osvscanner/__snapshots__/vulnerability_result_internal_test.snap b/pkg/osvscanner/__snapshots__/vulnerability_result_internal_test.snap index 9d7d7001d5..b08eff2e56 100755 --- a/pkg/osvscanner/__snapshots__/vulnerability_result_internal_test.snap +++ b/pkg/osvscanner/__snapshots__/vulnerability_result_internal_test.snap @@ -11,6 +11,7 @@ { "package": { "name": "pkg-1", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -52,6 +53,7 @@ { "package": { "name": "pkg-3", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -97,6 +99,7 @@ { "package": { "name": "pkg-1", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -130,6 +133,7 @@ { "package": { "name": "pkg-2", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" } @@ -145,6 +149,7 @@ { "package": { "name": "pkg-3", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -190,6 +195,7 @@ { "package": { "name": "pkg-1", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -235,6 +241,7 @@ { "package": { "name": "pkg-3", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -289,6 +296,7 @@ { "package": { "name": "pkg-1", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -326,6 +334,7 @@ { "package": { "name": "pkg-2", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -344,6 +353,7 @@ { "package": { "name": "pkg-3", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -398,6 +408,7 @@ { "package": { "name": "pkg-1", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -443,6 +454,7 @@ { "package": { "name": "pkg-3", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -494,6 +506,7 @@ { "package": { "name": "pkg-1", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -531,6 +544,7 @@ { "package": { "name": "pkg-2", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -549,6 +563,7 @@ { "package": { "name": "pkg-3", + "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, From 3d40e79ab87236d10df93909f754a8d9c5ba3a9b Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 16 Jan 2025 13:17:08 +1100 Subject: [PATCH 39/45] Update to latest scalibr to fix diffID issue --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 686afb96c1..1fc0bffbe6 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ 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/osv-scalibr v0.1.6-0.20250115231949-9a7ebb469850 + 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 diff --git a/go.sum b/go.sum index 0d1b2919e2..566678aeb5 100644 --- a/go.sum +++ b/go.sum @@ -178,10 +178,8 @@ 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.20250115231949-9a7ebb469850 h1:vxAtv63JdaXRYTrIsGSByr9gH4XFV2+UYlSV8QOrIv4= -github.com/google/osv-scalibr v0.1.6-0.20250115231949-9a7ebb469850/go.mod h1:cwPTti0kznoGCTRgQEb/4XUjzbuEXKSgq0mcXxITXQM= +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= From 8172875f9e32e70bde4fb13c6f2398904a9cb381 Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 16 Jan 2025 15:58:32 +1100 Subject: [PATCH 40/45] Add BaseImageMatcher --- .../baseimagematcher/baseimagematcher.go | 163 ++++++++++++++++++ .../clientinterfaces/baseimagematcher.go | 11 ++ pkg/osvscanner/osvscanner.go | 129 +++----------- 3 files changed, 194 insertions(+), 109 deletions(-) create mode 100644 internal/clients/clientimpl/baseimagematcher/baseimagematcher.go create mode 100644 internal/clients/clientinterfaces/baseimagematcher.go diff --git a/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go b/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go new file mode 100644 index 0000000000..6d6bf1974a --- /dev/null +++ b/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go @@ -0,0 +1,163 @@ +package baseimagematcher + +import ( + "context" + "encoding/json" + "net/http" + "slices" + "strings" + + "github.com/google/osv-scalibr/log" + "github.com/google/osv-scanner/pkg/models" + "github.com/opencontainers/go-digest" +) + +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 +} + +func (matcher *DepsDevBaseImageMatcher) MatchBaseImages(ctx context.Context, layerMetadata []models.LayerMetadata) ([][]models.BaseImageDetails, error) { + var runningDigest digest.Digest + 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 + {}, + } + + chainIDs := []digest.Digest{} + + for _, l := range layerMetadata { + if l.DiffID == "" { + chainIDs = append(chainIDs, "") + continue + } + + if runningDigest == "" { + runningDigest = l.DiffID + } else { + runningDigest = digest.FromBytes([]byte(runningDigest + " " + l.DiffID)) + } + + chainIDs = append(chainIDs, runningDigest) + } + + currentBaseImageIndex := 0 + for i, cid := range slices.Backward(chainIDs) { + if cid == "" { + layerMetadata[i].BaseImageIndex = currentBaseImageIndex + continue + } + + resp, err := matcher.Client.Get("https://api.deps.dev/v3alpha/querycontainerimages/" + cid.String()) + if err != nil { + log.Errorf("API DEPS DEV ERROR: %s", err) + continue + } + + if resp.StatusCode == http.StatusNotFound { + layerMetadata[i].BaseImageIndex = currentBaseImageIndex + continue + } + + if resp.StatusCode != http.StatusOK { + log.Errorf("API DEPS DEV ERROR: %s", resp.Status) + continue + } + + d := json.NewDecoder(resp.Body) + + type baseImageEntry struct { + Repository string `json:"repository"` + } + type baseImageResults struct { + Results []baseImageEntry `json:"results"` + } + + var results baseImageResults + err = d.Decode(&results) + if err != nil { + log.Errorf("API DEPS DEV ERROR: %s", err) + continue + } + + // Found some base images! + baseImagePossibilities := []models.BaseImageDetails{} + for _, r := range results.Results { + baseImagePossibilities = append(baseImagePossibilities, models.BaseImageDetails{ + Name: r.Repository, + }) + } + + slices.SortFunc(baseImagePossibilities, func(a, b models.BaseImageDetails) int { + return len(a.Name) - len(b.Name) + }) + + baseImages = append(baseImages, baseImagePossibilities) + currentBaseImageIndex += 1 + layerMetadata[i].BaseImageIndex = currentBaseImageIndex + + // Backfill with heuristic + + 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 { + break + } + buildCommand := layerMetadata[i2].Command + layerMetadata[i2].BaseImageIndex = currentBaseImageIndex + for _, prefix := range possibleFinalBaseImageCommands { + if strings.HasPrefix(buildCommand, prefix) { + break BackfillLoop + } + } + } + } + + return baseImages, nil + + // vulnerabilities := make([][]*models.Vulnerability, len(batchResp.Results)) + // g, ctx := errgroup.WithContext(ctx) + // g.SetLimit(maxConcurrentRequests) + + // for batchIdx, resp := range batchResp.Results { + // vulnerabilities[batchIdx] = make([]*models.Vulnerability, len(resp.Vulns)) + // for resultIdx, vuln := range resp.Vulns { + // g.Go(func() error { + // // exit early if another hydration request has already failed + // // results are thrown away later, so avoid needless work + // if ctx.Err() != nil { + // return nil //nolint:nilerr // this value doesn't matter to errgroup.Wait() + // } + // vuln, err := matcher.Client.GetVulnByID(ctx, vuln.ID) + // if err != nil { + // return err + // } + // vulnerabilities[batchIdx][resultIdx] = vuln + + // return nil + // }) + // } + // } + + // if err := g.Wait(); err != nil { + // return nil, err + // } + + // if deadlineExceeded { + // return vulnerabilities, context.DeadlineExceeded + // } + + // return vulnerabilities, 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/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 90c549bbd4..3c94f13fd8 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -2,20 +2,17 @@ package osvscanner import ( "context" - "encoding/json" "errors" "fmt" "net/http" "os" - "slices" - "strings" "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-scalibr/extractor/filesystem/os/osrelease" - "github.com/google/osv-scalibr/log" + "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" @@ -33,7 +30,6 @@ import ( "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/opencontainers/go-digest" "github.com/ossf/osv-schema/bindings/go/osvschema" ) @@ -72,8 +68,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 @@ -134,6 +131,13 @@ func initializeExternalAccessors(r reporter.Reporter, actions ScannerActions) (E } } + // --- Base Image Matcher --- + if actions.Image != "" || actions.ScanOCIImage != "" { + externalAccessors.BaseImageMatcher = &baseimagematcher.DepsDevBaseImageMatcher{ + Client: *http.DefaultClient, + } + } + // --- OSV.dev Client --- // We create a separate client from VulnMatcher to keep things clean. externalAccessors.OSVDevClient = osvdev.DefaultClient() @@ -317,7 +321,7 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner m, err := osrelease.GetOSRelease(chainLayers[len(chainLayers)-1].FS()) OS := "Unknown" if err == nil { - OS = m["OSID"] + OS = m["PRETTY_NAME"] } scanResult.PackageScanResults = make([]imodels.PackageScanResult, len(scalibrSR.Inventories)) @@ -336,114 +340,21 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner } scanResult.ImageMetadata = &models.ImageMetadata{ - 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 - {}, - }, OS: OS, LayerMetadata: layerMetadata, } - var runningDigest digest.Digest - chainIDs := []digest.Digest{} - - for _, cl := range chainLayers { - var diffDigest digest.Digest - if cl.Layer().DiffID() == "" { - chainIDs = append(chainIDs, "") - continue - } - - diffDigest = cl.Layer().DiffID() - - if runningDigest == "" { - runningDigest = diffDigest - } else { - runningDigest = digest.FromBytes([]byte(runningDigest + " " + diffDigest)) - } - - chainIDs = append(chainIDs, runningDigest) - } - - currentBaseImageIndex := 0 - client := http.DefaultClient - for i, cid := range slices.Backward(chainIDs) { - if cid == "" { - scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex - continue - } - - resp, err := client.Get("https://api.deps.dev/v3alpha/querycontainerimages/" + cid.String()) - if err != nil { - log.Errorf("API DEPS DEV ERROR: %s", err) - continue - } - - if resp.StatusCode == http.StatusNotFound { - scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex - continue - } - - if resp.StatusCode != http.StatusOK { - log.Errorf("API DEPS DEV ERROR: %s", resp.Status) - continue - } - - d := json.NewDecoder(resp.Body) - - type baseImageEntry struct { - Repository string `json:"repository"` - } - type baseImageResults struct { - Results []baseImageEntry `json:"results"` - } - - var results baseImageResults - err = d.Decode(&results) + if accessors.BaseImageMatcher != nil { + scanResult.ImageMetadata.BaseImages, err = accessors.BaseImageMatcher.MatchBaseImages(context.Background(), layerMetadata) if err != nil { - log.Errorf("API DEPS DEV ERROR: %s", err) - continue - } - - // Found some base images! - baseImagePossibilities := []models.BaseImageDetails{} - for _, r := range results.Results { - baseImagePossibilities = append(baseImagePossibilities, models.BaseImageDetails{ - Name: r.Repository, - }) + return models.VulnerabilityResults{}, fmt.Errorf("failed to query for container base images: %w", err) } - - slices.SortFunc(baseImagePossibilities, func(a, b models.BaseImageDetails) int { - return len(a.Name) - len(b.Name) - }) - - scanResult.ImageMetadata.BaseImages = append(scanResult.ImageMetadata.BaseImages, baseImagePossibilities) - currentBaseImageIndex += 1 - scanResult.ImageMetadata.LayerMetadata[i].BaseImageIndex = currentBaseImageIndex - - // Backfill with heuristic - - possibleFinalBaseImageCommands := []string{ - "/bin/sh -c #(nop) CMD", - "CMD", - "/bin/sh -c #(nop) ENTRYPOINT", - "ENTRYPOINT", - } - BackfillLoop: - for i2 := i; i2 < len(scanResult.ImageMetadata.LayerMetadata); i2++ { - if !scanResult.ImageMetadata.LayerMetadata[i2].IsEmpty { - break - } - buildCommand := scanResult.ImageMetadata.LayerMetadata[i2].Command - scanResult.ImageMetadata.LayerMetadata[i2].BaseImageIndex = currentBaseImageIndex - for _, prefix := range possibleFinalBaseImageCommands { - if strings.HasPrefix(buildCommand, prefix) { - break BackfillLoop - } - } + } else { + scanResult.ImageMetadata.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 + {}, } - } } From 9ba5968d7d2cbd0fbc3bfdb397e863719fe31847 Mon Sep 17 00:00:00 2001 From: Rex P Date: Fri, 17 Jan 2025 12:47:22 +1100 Subject: [PATCH 41/45] Parallel queries --- .../baseimagematcher/baseimagematcher.go | 176 +++++++++--------- 1 file changed, 83 insertions(+), 93 deletions(-) diff --git a/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go b/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go index 6d6bf1974a..f36e7fb58f 100644 --- a/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go +++ b/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go @@ -7,9 +7,10 @@ import ( "slices" "strings" - "github.com/google/osv-scalibr/log" "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 ( @@ -20,21 +21,17 @@ const ( // It sends out requests for every package version and does not perform caching. type DepsDevBaseImageMatcher struct { Client http.Client + r reporter.Reporter } func (matcher *DepsDevBaseImageMatcher) MatchBaseImages(ctx context.Context, layerMetadata []models.LayerMetadata) ([][]models.BaseImageDetails, error) { - var runningDigest digest.Digest - 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 - {}, - } - - chainIDs := []digest.Digest{} + baseImagesMap := make([][]models.BaseImageDetails, len(layerMetadata)) + g, ctx := errgroup.WithContext(ctx) + g.SetLimit(maxConcurrentRequests) - for _, l := range layerMetadata { + var runningDigest digest.Digest + for i, l := range layerMetadata { if l.DiffID == "" { - chainIDs = append(chainIDs, "") continue } @@ -44,66 +41,89 @@ func (matcher *DepsDevBaseImageMatcher) MatchBaseImages(ctx context.Context, lay runningDigest = digest.FromBytes([]byte(runningDigest + " " + l.DiffID)) } - chainIDs = append(chainIDs, runningDigest) - } - - currentBaseImageIndex := 0 - for i, cid := range slices.Backward(chainIDs) { - if cid == "" { - layerMetadata[i].BaseImageIndex = currentBaseImageIndex - continue - } - - resp, err := matcher.Client.Get("https://api.deps.dev/v3alpha/querycontainerimages/" + cid.String()) - if err != nil { - log.Errorf("API DEPS DEV ERROR: %s", err) - continue - } + chainID := runningDigest + g.Go(func() error { + if ctx.Err() != nil { + return nil // this value doesn't matter to errgroup.Wait(), it will be ctx.Err() + } - if resp.StatusCode == http.StatusNotFound { - layerMetadata[i].BaseImageIndex = currentBaseImageIndex - continue - } + resp, err := matcher.Client.Get("https://api.deps.dev/v3alpha/querycontainerimages/" + chainID.String()) + if err != nil { + matcher.r.Errorf("deps.dev API error: %s\n", err) + return nil + } - if resp.StatusCode != http.StatusOK { - log.Errorf("API DEPS DEV ERROR: %s", resp.Status) - continue - } + if resp.StatusCode == http.StatusNotFound { + return nil + } - d := json.NewDecoder(resp.Body) + if resp.StatusCode != http.StatusOK { + matcher.r.Errorf("deps.dev API error: %s\n", resp.Status) + return nil + } - type baseImageEntry struct { - Repository string `json:"repository"` - } - type baseImageResults struct { - Results []baseImageEntry `json:"results"` - } + var results struct { + Results []struct { + Repository string `json:"repository"` + } `json:"results"` + } - var results baseImageResults - err = d.Decode(&results) - if err != nil { - log.Errorf("API DEPS DEV ERROR: %s", err) - continue - } + d := json.NewDecoder(resp.Body) + err = d.Decode(&results) + if err != nil { + matcher.r.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, + // 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 - slices.SortFunc(baseImagePossibilities, func(a, b models.BaseImageDetails) int { - return len(a.Name) - len(b.Name) + return nil }) + } + + if err := g.Wait(); err != nil { + return nil, context.DeadlineExceeded + } - baseImages = append(baseImages, baseImagePossibilities) + 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 - + // 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", @@ -113,10 +133,14 @@ func (matcher *DepsDevBaseImageMatcher) MatchBaseImages(ctx context.Context, lay 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 @@ -125,39 +149,5 @@ func (matcher *DepsDevBaseImageMatcher) MatchBaseImages(ctx context.Context, lay } } - return baseImages, nil - - // vulnerabilities := make([][]*models.Vulnerability, len(batchResp.Results)) - // g, ctx := errgroup.WithContext(ctx) - // g.SetLimit(maxConcurrentRequests) - - // for batchIdx, resp := range batchResp.Results { - // vulnerabilities[batchIdx] = make([]*models.Vulnerability, len(resp.Vulns)) - // for resultIdx, vuln := range resp.Vulns { - // g.Go(func() error { - // // exit early if another hydration request has already failed - // // results are thrown away later, so avoid needless work - // if ctx.Err() != nil { - // return nil //nolint:nilerr // this value doesn't matter to errgroup.Wait() - // } - // vuln, err := matcher.Client.GetVulnByID(ctx, vuln.ID) - // if err != nil { - // return err - // } - // vulnerabilities[batchIdx][resultIdx] = vuln - - // return nil - // }) - // } - // } - - // if err := g.Wait(); err != nil { - // return nil, err - // } - - // if deadlineExceeded { - // return vulnerabilities, context.DeadlineExceeded - // } - - // return vulnerabilities, nil + return allBaseImages, nil } From 7bd89befaffec06382655c36fa757b3f899619ca Mon Sep 17 00:00:00 2001 From: Rex P Date: Fri, 17 Jan 2025 12:58:33 +1100 Subject: [PATCH 42/45] Cleanup and move filling image metadata to imagehelpers --- .../internal/imagehelpers/imagehelpers.go | 50 ++++++++++++++++ pkg/osvscanner/osvscanner.go | 58 ++++--------------- 2 files changed, 62 insertions(+), 46 deletions(-) diff --git a/pkg/osvscanner/internal/imagehelpers/imagehelpers.go b/pkg/osvscanner/internal/imagehelpers/imagehelpers.go index bd12dd35b6..a566eef742 100644 --- a/pkg/osvscanner/internal/imagehelpers/imagehelpers.go +++ b/pkg/osvscanner/internal/imagehelpers/imagehelpers.go @@ -2,15 +2,65 @@ 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(r reporter.Reporter, 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") { diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 3c94f13fd8..6fa1577728 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -11,7 +11,6 @@ import ( 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-scalibr/extractor/filesystem/os/osrelease" "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" @@ -91,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) { @@ -298,11 +298,11 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner } defer img.CleanUp() + // --- 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) } @@ -311,51 +311,17 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner return models.VulnerabilityResults{}, ErrNoPackagesFound } - // --- Fill Image Metadata --- - { - chainLayers, err := img.ChainLayers() - if err != nil { - // This is very unlikely, as if this would error we would have failed the initial scan - return models.VulnerabilityResults{}, err - } - m, err := osrelease.GetOSRelease(chainLayers[len(chainLayers)-1].FS()) - OS := "Unknown" - if err == nil { - OS = m["PRETTY_NAME"] - } - - 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 - } - - layerMetadata := []models.LayerMetadata{} - for _, cl := range chainLayers { - layerMetadata = append(layerMetadata, models.LayerMetadata{ - DiffID: cl.Layer().DiffID(), - Command: cl.Layer().Command(), - IsEmpty: cl.Layer().IsEmpty(), - }) - } - - scanResult.ImageMetadata = &models.ImageMetadata{ - OS: OS, - LayerMetadata: layerMetadata, - } + // --- 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 + } - if accessors.BaseImageMatcher != nil { - scanResult.ImageMetadata.BaseImages, err = accessors.BaseImageMatcher.MatchBaseImages(context.Background(), layerMetadata) - if err != nil { - return models.VulnerabilityResults{}, fmt.Errorf("failed to query for container base images: %w", err) - } - } else { - scanResult.ImageMetadata.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 - {}, - } - } + // --- Fill Image Metadata --- + scanResult.ImageMetadata, err = imagehelpers.BuildImageMetadata(r, img, accessors.BaseImageMatcher) + if err != nil { // Not getting image metadata is not fatal + r.Errorf("Failed to fully get image metadata: %v", err) } // ----- Filtering ----- From 8f850dc722a03943901500db84e75982dfd8f3a7 Mon Sep 17 00:00:00 2001 From: Rex P Date: Fri, 17 Jan 2025 13:15:56 +1100 Subject: [PATCH 43/45] Update snapshots because new vuln got found --- cmd/osv-scanner/__snapshots__/main_test.snap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/osv-scanner/__snapshots__/main_test.snap b/cmd/osv-scanner/__snapshots__/main_test.snap index a3d2e62cf3..72fd9d46b2 100755 --- a/cmd/osv-scanner/__snapshots__/main_test.snap +++ b/cmd/osv-scanner/__snapshots__/main_test.snap @@ -2029,6 +2029,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 | @@ -2227,6 +2228,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 | From 0f286eba84cf19dc4e3ab624e52ce3e262af48f7 Mon Sep 17 00:00:00 2001 From: Rex P Date: Fri, 17 Jan 2025 13:25:03 +1100 Subject: [PATCH 44/45] Resolve lints --- .../baseimagematcher/baseimagematcher.go | 21 ++++++++++++------- pkg/models/image.go | 2 +- pkg/models/results.go | 2 +- .../internal/imagehelpers/imagehelpers.go | 2 +- pkg/osvscanner/osvscanner.go | 14 +++++++++---- 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go b/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go index f36e7fb58f..9c5645bd22 100644 --- a/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go +++ b/internal/clients/clientimpl/baseimagematcher/baseimagematcher.go @@ -20,8 +20,8 @@ const ( // 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 - r reporter.Reporter + Client http.Client + Reporter reporter.Reporter } func (matcher *DepsDevBaseImageMatcher) MatchBaseImages(ctx context.Context, layerMetadata []models.LayerMetadata) ([][]models.BaseImageDetails, error) { @@ -44,21 +44,28 @@ func (matcher *DepsDevBaseImageMatcher) MatchBaseImages(ctx context.Context, lay chainID := runningDigest g.Go(func() error { if ctx.Err() != nil { - return nil // this value doesn't matter to errgroup.Wait(), it will be ctx.Err() + return ctx.Err() // this value doesn't matter to errgroup.Wait(), it will be ctx.Err() } - resp, err := matcher.Client.Get("https://api.deps.dev/v3alpha/querycontainerimages/" + chainID.String()) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.deps.dev/v3alpha/querycontainerimages/"+chainID.String(), nil) if err != nil { - matcher.r.Errorf("deps.dev API error: %s\n", err) + 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.r.Errorf("deps.dev API error: %s\n", resp.Status) + matcher.Reporter.Errorf("deps.dev API error: %s\n", resp.Status) return nil } @@ -71,7 +78,7 @@ func (matcher *DepsDevBaseImageMatcher) MatchBaseImages(ctx context.Context, lay d := json.NewDecoder(resp.Body) err = d.Decode(&results) if err != nil { - matcher.r.Errorf("Unexpected return type from deps.dev base image endpoint: %s", err) + matcher.Reporter.Errorf("Unexpected return type from deps.dev base image endpoint: %s", err) return nil } diff --git a/pkg/models/image.go b/pkg/models/image.go index dcf467b8bd..381a6b51bd 100644 --- a/pkg/models/image.go +++ b/pkg/models/image.go @@ -3,7 +3,7 @@ package models import "github.com/opencontainers/go-digest" type ImageOriginDetails struct { - Index int + Index int `json:"index"` } type ImageMetadata struct { diff --git a/pkg/models/results.go b/pkg/models/results.go index f6d21ab776..d5cefa3d9e 100644 --- a/pkg/models/results.go +++ b/pkg/models/results.go @@ -190,7 +190,7 @@ type AnalysisInfo struct { // Specific package information type PackageInfo struct { Name string `json:"name"` - OSPackageName string `json:os_package_name,omitempty` + OSPackageName string `json:"os_package_name,omitempty"` Version string `json:"version"` Ecosystem string `json:"ecosystem"` Commit string `json:"commit,omitempty"` diff --git a/pkg/osvscanner/internal/imagehelpers/imagehelpers.go b/pkg/osvscanner/internal/imagehelpers/imagehelpers.go index a566eef742..7a989294fa 100644 --- a/pkg/osvscanner/internal/imagehelpers/imagehelpers.go +++ b/pkg/osvscanner/internal/imagehelpers/imagehelpers.go @@ -16,7 +16,7 @@ import ( "github.com/google/osv-scanner/pkg/reporter" ) -func BuildImageMetadata(r reporter.Reporter, img *image.Image, baseImageMatcher clientinterfaces.BaseImageMatcher) (*models.ImageMetadata, error) { +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 diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index 6fa1577728..8ffbcfc82e 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -134,7 +134,8 @@ func initializeExternalAccessors(r reporter.Reporter, actions ScannerActions) (E // --- Base Image Matcher --- if actions.Image != "" || actions.ScanOCIImage != "" { externalAccessors.BaseImageMatcher = &baseimagematcher.DepsDevBaseImageMatcher{ - Client: *http.DefaultClient, + Client: *http.DefaultClient, + Reporter: r, } } @@ -272,7 +273,7 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner // --- Setup Accessors/Clients --- accessors, err := initializeExternalAccessors(r, actions) if err != nil { - return models.VulnerabilityResults{}, fmt.Errorf("failed to initialize accessors: %v", err) + return models.VulnerabilityResults{}, fmt.Errorf("failed to initialize accessors: %w", err) } // --- Initialize Image To Scan --- @@ -296,7 +297,12 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner if err != nil { return models.VulnerabilityResults{}, err } - defer img.CleanUp() + defer func() { + err := img.CleanUp() + if err != nil { + r.Errorf("Failed to clean up image: %s\n", err) + } + }() // --- Do Scalibr Scan --- scanner := scalibr.New() @@ -319,7 +325,7 @@ func DoContainerScan(actions ScannerActions, r reporter.Reporter) (models.Vulner } // --- Fill Image Metadata --- - scanResult.ImageMetadata, err = imagehelpers.BuildImageMetadata(r, img, accessors.BaseImageMatcher) + 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) } From 1e78081675c55eec7f72b1b17b94aff43e121844 Mon Sep 17 00:00:00 2001 From: Rex P Date: Fri, 17 Jan 2025 16:19:54 +1100 Subject: [PATCH 45/45] Update snapshots with omitempty --- cmd/osv-scanner/__snapshots__/main_test.snap | 13 -- cmd/osv-scanner/main_test.go | 2 +- .../vulnerability_result_diff_test.snap | 7 -- .../__snapshots__/machinejson_test.snap | 114 ++---------------- .../output/__snapshots__/result_test.snap | 3 - .../sourceanalysis/__snapshots__/go_test.snap | 12 +- pkg/models/results.go | 2 +- .../__snapshots__/filter_internal_test.snap | 11 -- .../vulnerability_result_internal_test.snap | 15 --- 9 files changed, 18 insertions(+), 161 deletions(-) diff --git a/cmd/osv-scanner/__snapshots__/main_test.snap b/cmd/osv-scanner/__snapshots__/main_test.snap index 72fd9d46b2..e638bf62e3 100755 --- a/cmd/osv-scanner/__snapshots__/main_test.snap +++ b/cmd/osv-scanner/__snapshots__/main_test.snap @@ -1201,7 +1201,6 @@ Warning: `scan` exists as both a subcommand of OSV-Scanner and as a file on the { "package": { "name": "babel", - "OSPackageName": "", "version": "6.23.0", "ecosystem": "npm" }, @@ -1212,7 +1211,6 @@ Warning: `scan` exists as both a subcommand of OSV-Scanner and as a file on the { "package": { "name": "human-signals", - "OSPackageName": "", "version": "5.0.0", "ecosystem": "npm" }, @@ -1223,7 +1221,6 @@ Warning: `scan` exists as both a subcommand of OSV-Scanner and as a file on the { "package": { "name": "ms", - "OSPackageName": "", "version": "2.1.3", "ecosystem": "npm" }, @@ -1234,7 +1231,6 @@ Warning: `scan` exists as both a subcommand of OSV-Scanner and as a file on the { "package": { "name": "type-fest", - "OSPackageName": "", "version": "4.26.1", "ecosystem": "npm" }, @@ -1312,7 +1308,6 @@ license MIT WITH (Bison-exception-2.2 AND somethingelse) for package npm/ms/2.1. { "package": { "name": "babel", - "OSPackageName": "", "version": "6.23.0", "ecosystem": "npm" }, @@ -1323,7 +1318,6 @@ license MIT WITH (Bison-exception-2.2 AND somethingelse) for package npm/ms/2.1. { "package": { "name": "human-signals", - "OSPackageName": "", "version": "5.0.0", "ecosystem": "npm" }, @@ -1334,7 +1328,6 @@ license MIT WITH (Bison-exception-2.2 AND somethingelse) for package npm/ms/2.1. { "package": { "name": "ms", - "OSPackageName": "", "version": "2.1.3", "ecosystem": "npm" }, @@ -1345,7 +1338,6 @@ license MIT WITH (Bison-exception-2.2 AND somethingelse) for package npm/ms/2.1. { "package": { "name": "type-fest", - "OSPackageName": "", "version": "4.26.1", "ecosystem": "npm" }, @@ -1484,7 +1476,6 @@ overriding license for package Packagist/league/flysystem/1.0.8 with 0BSD { "package": { "name": "babel", - "OSPackageName": "", "version": "6.23.0", "ecosystem": "npm" }, @@ -1495,7 +1486,6 @@ overriding license for package Packagist/league/flysystem/1.0.8 with 0BSD { "package": { "name": "human-signals", - "OSPackageName": "", "version": "5.0.0", "ecosystem": "npm" }, @@ -1509,7 +1499,6 @@ overriding license for package Packagist/league/flysystem/1.0.8 with 0BSD { "package": { "name": "ms", - "OSPackageName": "", "version": "2.1.3", "ecosystem": "npm" }, @@ -1520,7 +1509,6 @@ overriding license for package Packagist/league/flysystem/1.0.8 with 0BSD { "package": { "name": "type-fest", - "OSPackageName": "", "version": "4.26.1", "ecosystem": "npm" }, @@ -1561,7 +1549,6 @@ Scanned /fixtures/locks-licenses/package-lock.json file and found 4 pac { "package": { "name": "human-signals", - "OSPackageName": "", "version": "5.0.0", "ecosystem": "npm" }, diff --git a/cmd/osv-scanner/main_test.go b/cmd/osv-scanner/main_test.go index 58e670c686..565c8d2298 100644 --- a/cmd/osv-scanner/main_test.go +++ b/cmd/osv-scanner/main_test.go @@ -792,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 { diff --git a/internal/ci/__snapshots__/vulnerability_result_diff_test.snap b/internal/ci/__snapshots__/vulnerability_result_diff_test.snap index 7a3f1fbf61..ab68d01c97 100755 --- a/internal/ci/__snapshots__/vulnerability_result_diff_test.snap +++ b/internal/ci/__snapshots__/vulnerability_result_diff_test.snap @@ -41,7 +41,6 @@ { "package": { "name": "github.com/gogo/protobuf", - "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -138,7 +137,6 @@ { "package": { "name": "github.com/gogo/protobuf", - "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -275,7 +273,6 @@ { "package": { "name": "github.com/gogo/protobuf", - "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -436,7 +433,6 @@ { "package": { "name": "github.com/gogo/protobuf", - "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -560,7 +556,6 @@ { "package": { "name": "regex", - "OSPackageName": "", "version": "1.5.1", "ecosystem": "crates.io" }, @@ -783,7 +778,6 @@ { "package": { "name": "github.com/gogo/protobuf", - "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -907,7 +901,6 @@ { "package": { "name": "regex", - "OSPackageName": "", "version": "1.5.1", "ecosystem": "crates.io" }, diff --git a/internal/output/__snapshots__/machinejson_test.snap b/internal/output/__snapshots__/machinejson_test.snap index c299e3ba74..9c8f3a0082 100755 --- a/internal/output/__snapshots__/machinejson_test.snap +++ b/internal/output/__snapshots__/machinejson_test.snap @@ -11,7 +11,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -30,7 +29,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -41,7 +39,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -60,7 +57,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -71,7 +67,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -106,7 +101,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -128,7 +122,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -142,7 +135,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -161,7 +153,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -172,7 +163,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -210,7 +200,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -233,7 +222,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -247,7 +235,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -266,7 +253,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -277,7 +263,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -313,7 +298,6 @@ { "package": { "name": "author1/mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "Packagist" }, @@ -335,7 +319,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -349,7 +332,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -368,7 +350,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.3.5", "ecosystem": "NuGet" }, @@ -379,7 +360,6 @@ { "package": { "name": "author1/mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "Packagist" }, @@ -420,7 +400,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -446,7 +425,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -464,7 +442,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -483,7 +460,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -494,7 +470,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -610,7 +585,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -645,7 +619,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" } @@ -677,7 +650,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -712,7 +684,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -752,7 +723,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -790,7 +760,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -831,7 +800,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -853,7 +821,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "5.9.0", "ecosystem": "npm" }, @@ -888,7 +855,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -911,7 +877,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false @@ -938,7 +904,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -961,7 +926,7 @@ "OSV-2" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-2": { "called": true, "unimportant": false @@ -977,7 +942,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -996,7 +960,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -1010,7 +973,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1033,7 +995,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false @@ -1076,7 +1038,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1120,7 +1081,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -1153,7 +1113,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -1172,7 +1131,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" }, @@ -1186,7 +1144,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1246,7 +1203,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1269,7 +1225,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": true, "unimportant": false @@ -1312,7 +1268,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1335,7 +1290,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false @@ -1378,7 +1333,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1438,7 +1392,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1479,7 +1432,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "5.9.0", "ecosystem": "npm" }, @@ -1517,7 +1469,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1569,7 +1520,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.2", "ecosystem": "npm" }, @@ -1607,7 +1557,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -1640,7 +1589,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -1713,7 +1661,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -1761,7 +1708,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.2", "ecosystem": "npm" }, @@ -1799,7 +1745,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -1829,7 +1774,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" }, @@ -1899,7 +1843,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" } @@ -1915,7 +1858,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" } @@ -1923,7 +1865,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" } @@ -1939,7 +1880,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" } @@ -1947,7 +1887,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" } @@ -1977,7 +1916,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2015,7 +1953,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "npm" }, @@ -2045,7 +1982,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "npm" } @@ -2061,7 +1997,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.3.5", "ecosystem": "npm" } @@ -2069,7 +2004,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2121,7 +2055,6 @@ { "package": { "name": "author1/mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "Packagist" }, @@ -2169,7 +2102,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.2", "ecosystem": "npm" }, @@ -2207,7 +2139,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "NuGet" }, @@ -2240,7 +2171,6 @@ { "package": { "name": "author3/mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "Packagist" }, @@ -2313,7 +2243,6 @@ { "package": { "name": "author1/mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "Packagist" }, @@ -2347,7 +2276,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false @@ -2360,7 +2289,7 @@ "OSV-5" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-5": { "called": true, "unimportant": false @@ -2373,7 +2302,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.2", "ecosystem": "npm" }, @@ -2411,7 +2339,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "3.2.5", "ecosystem": "NuGet" }, @@ -2444,7 +2371,6 @@ { "package": { "name": "author3/mine3", - "OSPackageName": "", "version": "0.4.1", "ecosystem": "Packagist" }, @@ -2481,7 +2407,7 @@ "OSV-3" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-3": { "called": true, "unimportant": false @@ -2592,7 +2518,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" } @@ -2622,7 +2547,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2656,7 +2580,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": true, "unimportant": false @@ -2669,7 +2593,7 @@ "GHSA-123" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "GHSA-123": { "called": false, "unimportant": false @@ -2704,7 +2628,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2727,7 +2650,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": true, "unimportant": false @@ -2762,7 +2685,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2785,7 +2707,7 @@ "OSV-1" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false @@ -2820,7 +2742,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2872,7 +2793,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2927,7 +2847,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -2968,7 +2887,7 @@ "OSV-1", "GHSA-123" ], - "experimentalAnalysis": { + "experimental_analysis": { "OSV-1": { "called": false, "unimportant": false @@ -3003,7 +2922,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -3073,7 +2991,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -3097,7 +3014,6 @@ { "package": { "name": "mine3", - "OSPackageName": "", "version": "0.10.2-rc", "ecosystem": "npm" }, @@ -3142,7 +3058,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -3180,7 +3095,6 @@ { "package": { "name": "mine2", - "OSPackageName": "", "version": "5.9.0", "ecosystem": "npm" } @@ -3210,7 +3124,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, @@ -3248,7 +3161,6 @@ { "package": { "name": "mine1", - "OSPackageName": "", "version": "1.2.3", "ecosystem": "npm" }, diff --git a/internal/output/__snapshots__/result_test.snap b/internal/output/__snapshots__/result_test.snap index 5af4e10ac5..65e879e494 100755 --- a/internal/output/__snapshots__/result_test.snap +++ b/internal/output/__snapshots__/result_test.snap @@ -29,7 +29,6 @@ { "Package": { "name": "regex", - "OSPackageName": "", "version": "1.5.1", "ecosystem": "crates.io" }, @@ -235,7 +234,6 @@ { "Package": { "name": "github.com/gogo/protobuf", - "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -316,7 +314,6 @@ { "Package": { "name": "regex", - "OSPackageName": "", "version": "1.5.1", "ecosystem": "crates.io" }, diff --git a/internal/sourceanalysis/__snapshots__/go_test.snap b/internal/sourceanalysis/__snapshots__/go_test.snap index 4cab6560c5..16866191d3 100755 --- a/internal/sourceanalysis/__snapshots__/go_test.snap +++ b/internal/sourceanalysis/__snapshots__/go_test.snap @@ -4,7 +4,6 @@ { "package": { "name": "github.com/gogo/protobuf", - "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -159,7 +158,7 @@ "GO-2021-0053" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "GO-2021-0053": { "called": false, "unimportant": false @@ -172,7 +171,6 @@ { "package": { "name": "github.com/ipfs/go-bitfield", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "Go" }, @@ -314,7 +312,7 @@ "GO-2023-1558" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "GO-2023-1558": { "called": true, "unimportant": false @@ -327,7 +325,6 @@ { "package": { "name": "golang.org/x/image", - "OSPackageName": "", "version": "0.4.0", "ecosystem": "Go" }, @@ -470,7 +467,7 @@ "GO-2023-1572" ], "aliases": null, - "experimentalAnalysis": { + "experimental_analysis": { "GO-2023-1572": { "called": false, "unimportant": false @@ -488,7 +485,6 @@ { "package": { "name": "github.com/gogo/protobuf", - "OSPackageName": "", "version": "1.3.1", "ecosystem": "Go" }, @@ -639,7 +635,6 @@ { "package": { "name": "github.com/ipfs/go-bitfield", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "Go" }, @@ -777,7 +772,6 @@ { "package": { "name": "golang.org/x/image", - "OSPackageName": "", "version": "0.4.0", "ecosystem": "Go" }, diff --git a/pkg/models/results.go b/pkg/models/results.go index d5cefa3d9e..00273198d1 100644 --- a/pkg/models/results.go +++ b/pkg/models/results.go @@ -111,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"` } diff --git a/pkg/osvscanner/__snapshots__/filter_internal_test.snap b/pkg/osvscanner/__snapshots__/filter_internal_test.snap index 90ab8f84b3..5b63a8a962 100755 --- a/pkg/osvscanner/__snapshots__/filter_internal_test.snap +++ b/pkg/osvscanner/__snapshots__/filter_internal_test.snap @@ -23,7 +23,6 @@ { "package": { "name": "remove_dir_all", - "OSPackageName": "", "version": "0.5.3", "ecosystem": "crates.io" }, @@ -175,7 +174,6 @@ { "package": { "name": "time", - "OSPackageName": "", "version": "0.1.45", "ecosystem": "crates.io" }, @@ -433,7 +431,6 @@ { "package": { "name": "golang.org/x/net", - "OSPackageName": "", "version": "0.1.0", "ecosystem": "Go" }, @@ -930,7 +927,6 @@ { "package": { "name": "ascii", - "OSPackageName": "", "version": "0.8.7", "ecosystem": "crates.io" }, @@ -1077,7 +1073,6 @@ { "package": { "name": "remove_dir_all", - "OSPackageName": "", "version": "0.5.3", "ecosystem": "crates.io" }, @@ -1229,7 +1224,6 @@ { "package": { "name": "time", - "OSPackageName": "", "version": "0.1.45", "ecosystem": "crates.io" }, @@ -1500,7 +1494,6 @@ { "package": { "name": "unixodbc", - "OSPackageName": "", "version": "2.3.11-2", "ecosystem": "Debian:10" }, @@ -1560,7 +1553,6 @@ { "package": { "name": "chromium", - "OSPackageName": "", "version": "73.0.3683.75-1", "ecosystem": "Debian:10" }, @@ -1667,7 +1659,6 @@ { "package": { "name": "golang.org/x/net", - "OSPackageName": "", "version": "0.1.0", "ecosystem": "Go" }, @@ -1885,7 +1876,6 @@ { "package": { "name": "ascii", - "OSPackageName": "", "version": "0.8.7", "ecosystem": "crates.io" }, @@ -1964,7 +1954,6 @@ { "package": { "name": "time", - "OSPackageName": "", "version": "0.1.45", "ecosystem": "crates.io" }, diff --git a/pkg/osvscanner/__snapshots__/vulnerability_result_internal_test.snap b/pkg/osvscanner/__snapshots__/vulnerability_result_internal_test.snap index b08eff2e56..9d7d7001d5 100755 --- a/pkg/osvscanner/__snapshots__/vulnerability_result_internal_test.snap +++ b/pkg/osvscanner/__snapshots__/vulnerability_result_internal_test.snap @@ -11,7 +11,6 @@ { "package": { "name": "pkg-1", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -53,7 +52,6 @@ { "package": { "name": "pkg-3", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -99,7 +97,6 @@ { "package": { "name": "pkg-1", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -133,7 +130,6 @@ { "package": { "name": "pkg-2", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" } @@ -149,7 +145,6 @@ { "package": { "name": "pkg-3", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -195,7 +190,6 @@ { "package": { "name": "pkg-1", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -241,7 +235,6 @@ { "package": { "name": "pkg-3", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -296,7 +289,6 @@ { "package": { "name": "pkg-1", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -334,7 +326,6 @@ { "package": { "name": "pkg-2", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -353,7 +344,6 @@ { "package": { "name": "pkg-3", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -408,7 +398,6 @@ { "package": { "name": "pkg-1", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -454,7 +443,6 @@ { "package": { "name": "pkg-3", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -506,7 +494,6 @@ { "package": { "name": "pkg-1", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -544,7 +531,6 @@ { "package": { "name": "pkg-2", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" }, @@ -563,7 +549,6 @@ { "package": { "name": "pkg-3", - "OSPackageName": "", "version": "1.0.0", "ecosystem": "npm" },