From 509f1a1a5baad016d0f33a69cb433aa082888109 Mon Sep 17 00:00:00 2001 From: King Star Date: Thu, 1 Oct 2026 03:34:29 +0800 Subject: [PATCH 01/14] fix(java): filter Tomcat server CPE for embedded EL (#5322) Signed-off-by: King Star <54024410+jstar0@users.noreply.github.com> Co-authored-by: Will Murphy --- .../cataloger/internal/cpegenerate/filter.go | 16 +++++++++ .../internal/cpegenerate/generate_test.go | 33 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/syft/pkg/cataloger/internal/cpegenerate/filter.go b/syft/pkg/cataloger/internal/cpegenerate/filter.go index 8423c320b..2fffe67a8 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/filter.go +++ b/syft/pkg/cataloger/internal/cpegenerate/filter.go @@ -14,6 +14,7 @@ type filterFn func(cpe cpe.Attributes, p pkg.Package) bool var cpeFilters = []filterFn{ disallowJiraClientServerMismatch, + disallowTomcatServerCPEForEmbeddedEL, disallowJenkinsServerCPEForPluginPackage, disallowJenkinsCPEsNotAssociatedWithJenkins, disallowNonParseableCPEs, @@ -71,3 +72,18 @@ func disallowJiraClientServerMismatch(c cpe.Attributes, p pkg.Package) bool { } return false } + +// disallowTomcatServerCPEForEmbeddedEL prevents the server-level Tomcat CPE from being +// assigned to the EL-only embedded artifact. Keep its artifact-specific CPE candidates. +func disallowTomcatServerCPEForEmbeddedEL(c cpe.Attributes, p pkg.Package) bool { + if c.Vendor != "apache" || c.Product != "tomcat" { + return false + } + + metadata, ok := p.Metadata.(pkg.JavaArchive) + if !ok || metadata.PomProperties == nil { + return false + } + + return metadata.PomProperties.GroupID == "org.apache.tomcat.embed" && metadata.PomProperties.ArtifactID == "tomcat-embed-el" +} diff --git a/syft/pkg/cataloger/internal/cpegenerate/generate_test.go b/syft/pkg/cataloger/internal/cpegenerate/generate_test.go index 9a463b6ad..ae03d205b 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/generate_test.go +++ b/syft/pkg/cataloger/internal/cpegenerate/generate_test.go @@ -963,6 +963,39 @@ func TestGeneratePackageCPEs(t *testing.T) { } } +func TestTomcatEmbeddedCPEs(t *testing.T) { + packageFor := func(artifactID string) pkg.Package { + return pkg.Package{ + Name: artifactID, + Version: "11.0.20", + Language: pkg.Java, + Type: pkg.JavaPkg, + Metadata: pkg.JavaArchive{ + PomProperties: &pkg.JavaPomProperties{ + GroupID: "org.apache.tomcat.embed", + ArtifactID: artifactID, + }, + }, + } + } + + hasCPE := func(cpes []cpe.CPE, vendor, product string) bool { + for _, generated := range cpes { + if generated.Attributes.Vendor == vendor && generated.Attributes.Product == product { + return true + } + } + return false + } + + elCPEs := FromPackageAttributes(packageFor("tomcat-embed-el")) + assert.False(t, hasCPE(elCPEs, "apache", "tomcat"), "EL-only module should not inherit the server CPE") + assert.True(t, hasCPE(elCPEs, "apache", "tomcat-embed-el"), "keep the artifact-specific CPE candidate") + + coreCPEs := FromPackageAttributes(packageFor("tomcat-embed-core")) + assert.True(t, hasCPE(coreCPEs, "apache", "tomcat"), "core module should retain the Tomcat server CPE") +} + func TestCandidateProducts(t *testing.T) { tests := []struct { name string From f76ae8e57c2a62cd621595d5f21ec521840d4148 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 30 Sep 2026 15:45:46 -0400 Subject: [PATCH 02/14] feat: report the perl interpreter as a cpan package (#5132) The `perl-binary` classifier now emits `pkg:cpan/perl@` instead of `pkg:generic/perl@`. `perl` is itself a CPAN distribution and carries advisories under that name, so typing it as `generic` left every one of them unreachable. This changes an existing purl. Anything keyed on `pkg:generic/perl` (allowlists, policy, SBOM diffs) needs updating. Builds on the cpan cataloging branch, which adds the package type this uses. Signed-off-by: Alex Goodman --- internal/capabilities/generate/merge.go | 15 ++++- syft/pkg/cataloger/binary/capabilities.yaml | 4 +- .../binary/classifier_cataloger_test.go | 64 +++++++++++++++++-- syft/pkg/cataloger/binary/classifiers.go | 4 +- .../cataloger/internal/binutils/classifier.go | 11 ++++ .../internal/binutils/classifier_package.go | 2 +- .../internal/binutils/classifier_test.go | 30 +++++++++ 7 files changed, 119 insertions(+), 11 deletions(-) diff --git a/internal/capabilities/generate/merge.go b/internal/capabilities/generate/merge.go index b5d3b9b02..678c7eb29 100644 --- a/internal/capabilities/generate/merge.go +++ b/internal/capabilities/generate/merge.go @@ -502,6 +502,19 @@ func convertToJSONSchemaTypesFromMetadata(metadataTypes []string) []string { return result } +// packageTypeConstName renders a package type using its go constant name, which is what the detector +// "type" field reports (e.g. "BinaryPkg"). +// a switch rather than a derivation, since pkg.Type values don't reliably map back to their constant +// names (e.g. "java-archive" is JavaPkg); add an arm when a classifier declares a new type. +func packageTypeConstName(t pkg.Type) string { + switch t { + case pkg.CpanPkg: + return "CpanPkg" + default: + return "BinaryPkg" + } +} + // EnrichWithBinaryClassifier enriches an entry with binary classifier detectors if it's the binary-classifier-cataloger func (e *EnrichmentData) EnrichWithBinaryClassifier(catalogerName string, entry *capabilities.CatalogerEntry) { // special handling for binary-classifier-cataloger: auto-generate one detector per classifier @@ -523,7 +536,7 @@ func (e *EnrichmentData) EnrichWithBinaryClassifier(catalogerName string, entry Name: classifier.Package, PURL: purlStr, CPEs: cpeStrings, - Type: "BinaryPkg", + Type: packageTypeConstName(classifier.PackageType()), }, } diff --git a/syft/pkg/cataloger/binary/capabilities.yaml b/syft/pkg/cataloger/binary/capabilities.yaml index e8d68b254..25fb3d7b8 100644 --- a/syft/pkg/cataloger/binary/capabilities.yaml +++ b/syft/pkg/cataloger/binary/capabilities.yaml @@ -155,10 +155,10 @@ catalogers: packages: - class: perl-binary name: perl - purl: pkg:generic/perl + purl: pkg:cpan/perl cpes: - cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:* - type: BinaryPkg + type: CpanPkg - method: glob criteria: - '**/composer*' diff --git a/syft/pkg/cataloger/binary/classifier_cataloger_test.go b/syft/pkg/cataloger/binary/classifier_cataloger_test.go index 54cd04f3b..52768213c 100644 --- a/syft/pkg/cataloger/binary/classifier_cataloger_test.go +++ b/syft/pkg/cataloger/binary/classifier_cataloger_test.go @@ -460,8 +460,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) { expected: pkg.Package{ Name: "perl", Version: "5.12.5", - Type: "binary", - PURL: "pkg:generic/perl@5.12.5", + Type: "cpan", + PURL: "pkg:cpan/perl@5.12.5", Locations: locations("perl"), Metadata: metadata("perl-binary"), }, @@ -473,8 +473,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) { expected: pkg.Package{ Name: "perl", Version: "5.20.0", - Type: "binary", - PURL: "pkg:generic/perl@5.20.0", + Type: "cpan", + PURL: "pkg:cpan/perl@5.20.0", Locations: locations("perl"), Metadata: metadata("perl-binary"), }, @@ -486,8 +486,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) { expected: pkg.Package{ Name: "perl", Version: "5.37.8", - Type: "binary", - PURL: "pkg:generic/perl@5.37.8", + Type: "cpan", + PURL: "pkg:cpan/perl@5.37.8", Locations: locations("perl"), Metadata: metadata("perl-binary"), }, @@ -3187,6 +3187,58 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) { } } +// Test_Cataloger_PackageTypes covers what assertPackagesAreEqual deliberately ignores: the package type and CPEs. +// The perl interpreter is reported as cpan so it lines up with the rest of the CPAN ecosystem, everything else +// keeps the default binary type. +func Test_Cataloger_PackageTypes(t *testing.T) { + tests := []struct { + logicalFixture string + wantType pkg.Type + wantPURL string + wantCPEs []string + }{ + { + logicalFixture: "perl/5.20.0/linux-amd64", + wantType: pkg.CpanPkg, + wantPURL: "pkg:cpan/perl@5.20.0", + wantCPEs: []string{"cpe:2.3:a:perl:perl:5.20.0:*:*:*:*:*:*:*"}, + }, + { + logicalFixture: "haproxy/1.5.14/linux-amd64", + wantType: pkg.BinaryPkg, + wantPURL: "pkg:generic/haproxy@1.5.14", + wantCPEs: []string{"cpe:2.3:a:haproxy:haproxy:1.5.14:*:*:*:*:*:*:*"}, + }, + } + + for _, test := range tests { + t.Run(test.logicalFixture, func(t *testing.T) { + c := NewClassifierCataloger(DefaultClassifierCatalogerConfig()) + + path := testutil.SnippetOrBinary(t, test.logicalFixture, *mustUseOriginalBinaries) + + src, err := directorysource.NewFromPath(path) + require.NoError(t, err) + + resolver, err := src.FileResolver(source.SquashedScope) + require.NoError(t, err) + + packages, _, err := c.Catalog(context.Background(), resolver) + require.NoError(t, err) + require.Len(t, packages, 1) + + var cpes []string + for _, c := range packages[0].CPEs { + cpes = append(cpes, c.Attributes.BindToFmtString()) + } + + assert.Equal(t, test.wantType, packages[0].Type) + assert.Equal(t, test.wantPURL, packages[0].PURL) + assert.Equal(t, test.wantCPEs, cpes) + }) + } +} + func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) { c := NewClassifierCataloger(DefaultClassifierCatalogerConfig()) diff --git a/syft/pkg/cataloger/binary/classifiers.go b/syft/pkg/cataloger/binary/classifiers.go index 26929ae2a..4e4afb478 100644 --- a/syft/pkg/cataloger/binary/classifiers.go +++ b/syft/pkg/cataloger/binary/classifiers.go @@ -5,6 +5,7 @@ import ( "github.com/anchore/packageurl-go" "github.com/anchore/syft/syft/cpe" + "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/binutils" ) @@ -251,8 +252,9 @@ func DefaultClassifiers() []binutils.Classifier { EvidenceMatcher: m.FileContentsVersionMatcher( `(?m)\/usr\/local\/lib\/perl\d\/(?P[0-9]+\.[0-9]+\.[0-9]+)`), Package: "perl", - PURL: mustPURL("pkg:generic/perl@version"), + PURL: mustPURL("pkg:cpan/perl@version"), CPEs: singleCPE("cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource), + Type: pkg.CpanPkg, }, { Class: "php-composer-binary", diff --git a/syft/pkg/cataloger/internal/binutils/classifier.go b/syft/pkg/cataloger/internal/binutils/classifier.go index b2c0ff5e6..05b62897e 100644 --- a/syft/pkg/cataloger/internal/binutils/classifier.go +++ b/syft/pkg/cataloger/internal/binutils/classifier.go @@ -50,6 +50,17 @@ type Classifier struct { // CPEs are the specific CPEs we want to include for this binary with updated version information CPEs []cpe.CPE `json:"cpes"` + + // Type optionally overrides the package type reported for matches; when unset pkg.BinaryPkg is used + Type pkg.Type `json:"-"` +} + +// PackageType is the package type reported for matches of this classifier, defaulting to pkg.BinaryPkg. +func (cfg Classifier) PackageType() pkg.Type { + if cfg.Type == "" { + return pkg.BinaryPkg + } + return cfg.Type } func (cfg Classifier) MarshalJSON() ([]byte, error) { diff --git a/syft/pkg/cataloger/internal/binutils/classifier_package.go b/syft/pkg/cataloger/internal/binutils/classifier_package.go index 42831ddae..e1c4393df 100644 --- a/syft/pkg/cataloger/internal/binutils/classifier_package.go +++ b/syft/pkg/cataloger/internal/binutils/classifier_package.go @@ -35,7 +35,7 @@ func NewClassifierPackage(classifier Classifier, location file.Location, matchMe Locations: file.NewLocationSet( location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ), - Type: pkg.BinaryPkg, + Type: classifier.PackageType(), CPEs: cpes, FoundBy: catalogerName, Metadata: pkg.BinarySignature{ diff --git a/syft/pkg/cataloger/internal/binutils/classifier_test.go b/syft/pkg/cataloger/internal/binutils/classifier_test.go index c9d1c9e31..5bae45683 100644 --- a/syft/pkg/cataloger/internal/binutils/classifier_test.go +++ b/syft/pkg/cataloger/internal/binutils/classifier_test.go @@ -12,6 +12,7 @@ import ( "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/internal/unionreader" + "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source/directorysource" ) @@ -102,6 +103,35 @@ func Test_ClassifierCPEs(t *testing.T) { } } +func Test_ClassifierPackageType(t *testing.T) { + tests := []struct { + name string + classifier Classifier + want pkg.Type + }{ + { + name: "defaults to binary when unset", + classifier: Classifier{Class: "some-binary", Package: "some-app"}, + want: pkg.BinaryPkg, + }, + { + name: "honors an explicit type", + classifier: Classifier{Class: "perl-binary", Package: "perl", Type: pkg.CpanPkg}, + want: pkg.CpanPkg, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + assert.Equal(t, test.want, test.classifier.PackageType()) + + p := NewClassifierPackage(test.classifier, file.NewLocation("/usr/bin/app"), map[string]string{"version": "1.2.3"}, "cataloger-name") + require.NotNil(t, p) + assert.Equal(t, test.want, p.Type) + }) + } +} + func TestClassifier_MarshalJSON(t *testing.T) { tests := []struct { From ae649b09cfb529e9e18651ff6943ed9575db0905 Mon Sep 17 00:00:00 2001 From: anchore-oss-update-bot Date: Thu, 1 Oct 2026 11:11:11 -0400 Subject: [PATCH 03/14] chore(deps): update anchore dependencies (#5304) Signed-off-by: anchore-oss-update-bot Co-authored-by: anchore-oss-update-bot --- go.mod | 82 +++++++++++++-------- go.sum | 220 ++++++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 210 insertions(+), 92 deletions(-) diff --git a/go.mod b/go.mod index 09b8346f3..aa6ed1670 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/anchore/go-sync v0.1.2 github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b github.com/anchore/packageurl-go v0.2.0 - github.com/anchore/stereoscope v0.3.2 + github.com/anchore/stereoscope v0.3.3 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be github.com/aquasecurity/go-pep440-version v0.0.1 github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef @@ -115,6 +115,7 @@ require ( cloud.google.com/go/iam v1.12.0 // indirect cloud.google.com/go/monitoring v1.30.0 // indirect cloud.google.com/go/storage v1.65.1 // indirect + cyphar.com/go-pathrs v0.2.5 // indirect dario.cat/mergo v1.0.2 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 // indirect @@ -122,9 +123,10 @@ require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 // indirect - github.com/Microsoft/hcsshim v0.15.0-rc.1 // indirect + github.com/Microsoft/hcsshim v0.15.0-rc.4 // indirect github.com/ProtonMail/go-crypto v1.4.1 // indirect github.com/STARRY-S/zip v0.2.3 // indirect + github.com/VividCortex/ewma v1.2.0 // indirect github.com/agext/levenshtein v1.2.1 // indirect github.com/anchore/go-lzo v0.1.1 // indirect github.com/anchore/go-struct-converter v0.2.0-rc2 // indirect @@ -169,20 +171,25 @@ require ( github.com/cloudflare/circl v1.6.3 // indirect github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect github.com/containerd/cgroups/v3 v3.1.3 // indirect - github.com/containerd/containerd/api v1.11.1 // indirect - github.com/containerd/containerd/v2 v2.3.6 // indirect + github.com/containerd/containerd/api v1.12.0 // indirect + github.com/containerd/containerd/v2 v2.4.1 // indirect github.com/containerd/continuity v0.5.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/fifo v1.1.0 // indirect - github.com/containerd/log v0.1.0 // indirect + github.com/containerd/log v0.2.0 // indirect + github.com/containerd/log/otel v0.1.0 // indirect github.com/containerd/platforms v1.0.0-rc.5 // indirect github.com/containerd/plugin v1.1.0 // indirect - github.com/containerd/ttrpc v1.2.8 // indirect - github.com/containerd/typeurl/v2 v2.2.3 // indirect - github.com/cyphar/filepath-securejoin v0.6.1 // indirect - github.com/docker/cli v29.8.0+incompatible // indirect - github.com/docker/docker-credential-helpers v0.9.5 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect + github.com/containerd/ttrpc v1.2.9 // indirect + github.com/containerd/typeurl/v2 v2.3.0 // indirect + github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect + github.com/containers/ocicrypt v1.3.2 // indirect + github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect + github.com/cyphar/filepath-securejoin v0.7.0 // indirect + github.com/docker/cli v29.8.1+incompatible // indirect + github.com/docker/docker-credential-helpers v0.9.8 // indirect github.com/docker/go-connections v0.8.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect @@ -192,17 +199,17 @@ require ( github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fatih/color v1.19.0 // indirect github.com/felixge/fgprof v0.9.5 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/felixge/httpsnoop v1.1.0 // indirect + github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/gabriel-vasile/mimetype v1.4.15 // indirect github.com/gkampitakis/ciinfo v0.3.4 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-jose/go-jose/v4 v4.1.4 // indirect + github.com/go-jose/go-jose/v4 v4.1.5 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-restruct/restruct v1.2.0-alpha // indirect - github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/google/go-intervals v0.0.2 // indirect github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.20 // indirect @@ -226,10 +233,13 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.24 // indirect github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect - github.com/mattn/go-runewidth v0.0.21 // indirect + github.com/mattn/go-runewidth v0.0.27 // indirect + github.com/mattn/go-sqlite3 v1.14.48 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/miekg/pkcs11 v1.1.2 // indirect github.com/mikelolasagasti/xz v1.0.1 // indirect github.com/minio/minlz v1.0.1 // indirect + github.com/mistifyio/go-zfs/v4 v4.0.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect @@ -238,10 +248,10 @@ require ( github.com/moby/locker v1.0.1 // indirect github.com/moby/moby/api v1.56.0 // indirect github.com/moby/moby/client v0.6.0 // indirect - github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/capability v0.4.0 // indirect github.com/moby/sys/signal v0.7.1 // indirect - github.com/moby/sys/user v0.4.0 // indirect - github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/sys/user v0.4.1 // indirect + github.com/moby/sys/userns v0.2.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect @@ -254,6 +264,7 @@ require ( github.com/olekukonko/ll v0.1.6 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/runtime-spec v1.3.0 // indirect + github.com/opencontainers/selinux v1.15.1 // indirect github.com/pborman/indent v1.2.1 // indirect github.com/pelletier/go-toml/v2 v2.4.3 // indirect github.com/pierrec/lz4/v4 v4.1.26 // indirect @@ -264,46 +275,59 @@ require ( github.com/pkg/xattr v0.4.12 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect + github.com/proglottis/gpgme v0.1.6 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/sahilm/fuzzy v0.1.1 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.11.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/sirupsen/logrus v1.9.4 // indirect + github.com/sigstore/fulcio v1.8.7 // indirect + github.com/sigstore/protobuf-specs v0.5.1 // indirect + github.com/sigstore/sigstore v1.10.8 // indirect + github.com/sirupsen/logrus v1.10.2 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d // indirect + github.com/smallstep/pkcs7 v0.2.1 // indirect github.com/sorairolake/lzip-go v0.3.8 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper v1.21.0 // indirect github.com/spiffe/go-spiffe/v2 v2.7.0 // indirect + github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect github.com/stretchr/objx v0.5.3 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/sylabs/sif/v2 v2.24.1 // indirect github.com/sylabs/squashfs v1.0.6 // indirect + github.com/tchap/go-patricia/v2 v2.3.3 // indirect github.com/therootcompany/xz v1.0.1 // indirect github.com/tidwall/gjson v1.19.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect + github.com/vbatts/tar-split v0.12.3 // indirect + github.com/vbauerster/cupwriter v0.0.4 // indirect + github.com/vbauerster/mpb/v8 v8.14.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect github.com/zclconf/go-cty v1.19.0 // indirect - go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect - go.opentelemetry.io/otel v1.45.0 // indirect - go.opentelemetry.io/otel/metric v1.45.0 // indirect - go.opentelemetry.io/otel/sdk v1.45.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.45.0 // indirect - go.opentelemetry.io/otel/trace v1.45.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 // indirect + go.opentelemetry.io/otel v1.46.0 // indirect + go.opentelemetry.io/otel/metric v1.46.0 // indirect + go.opentelemetry.io/otel/sdk v1.46.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.46.0 // indirect + go.opentelemetry.io/otel/trace v1.46.0 // indirect + go.podman.io/image/v5 v5.41.2 // indirect + go.podman.io/storage v1.64.1 // indirect go.yaml.in/yaml/v4 v4.0.0-rc.2 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/crypto v0.57.0 // indirect @@ -316,8 +340,8 @@ require ( gonum.org/v1/gonum v0.17.0 // indirect google.golang.org/api v0.294.0 // indirect google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect google.golang.org/grpc v1.83.2 // indirect google.golang.org/protobuf v1.36.12 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index b66b9b51a..77524bcf6 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ cloud.google.com/go/storage v1.65.1 h1:LRRpBJUTf+OXDPX9jZUKZ3mSLIsz3htG+qUpeNZov cloud.google.com/go/storage v1.65.1/go.mod h1:UsS9OgFg/XHOSYakQ8ZtLWWeyGkk1WnmD/GsGfN0BHM= cloud.google.com/go/trace v1.16.0 h1:GmQovzFc5F0CNfl0VLgL64aoTtu7xsM0YajW2GlG9+E= cloud.google.com/go/trace v1.16.0/go.mod h1:r+bdAn16dKLSV1G2D5v3e58IlQlizfxWrUfjx7kM7X0= +cyphar.com/go-pathrs v0.2.5 h1:SnX9FBvnoyn3lUs1dkMgZ52bAETpirNu3FTRh5HlRik= +cyphar.com/go-pathrs v0.2.5/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -99,8 +101,8 @@ github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSC github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 h1:0kQAzHq8vLs7Pptv+7TxjdETLf/nIqJpIB4oC6Ba4vY= github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0= -github.com/Microsoft/hcsshim v0.15.0-rc.1 h1:FbbwtQmiD+BVHynGkx5S65JkLyhkEiiTP8nrpmg2SZw= -github.com/Microsoft/hcsshim v0.15.0-rc.1/go.mod h1:HWvvUPIy9HF6LotILj1G4VyS065rcLQ6tqj6tMUdOfI= +github.com/Microsoft/hcsshim v0.15.0-rc.4 h1:aZFX4LH0S20Lgjq0wG61StIClj7im4yzrxIClkaR8Z8= +github.com/Microsoft/hcsshim v0.15.0-rc.4/go.mod h1:BA9CBztgu4h/6Jsvo1O1M4qjWw09PoYpaEYgexPE578= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= @@ -108,6 +110,8 @@ github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67 github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4= github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk= +github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= +github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE= @@ -146,8 +150,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.2.0 h1:CkrM4RMUwrEGAiE1OVlxaZNzWj0TuHRey7o4T/EAErk= github.com/anchore/packageurl-go v0.2.0/go.mod h1:2JCgOQMIsqZ7TmliXG4PnUthPJAKE3mWQbsW2XHjAOE= -github.com/anchore/stereoscope v0.3.2 h1:A2FDMSU56m3rErO1e0CtWAVcDS8tc2iX4UXTw1hyPnI= -github.com/anchore/stereoscope v0.3.2/go.mod h1:/IUDVDka+4gufc8kDw+JO3gFl9PkpCjJ2r3bd9UWouM= +github.com/anchore/stereoscope v0.3.3 h1:sur8G2THAOnOrSXDjIc1SXzQwco8hjFBtGFq1jFvNVA= +github.com/anchore/stereoscope v0.3.3/go.mod h1:LxL4iOFtB8XMbA1MykUUTkmtQmDuYdbr4+TkBkCfljU= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= @@ -294,10 +298,10 @@ github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6dyLYXQ= github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw= -github.com/containerd/containerd/api v1.11.1 h1:h8nfoDW9+fNsC/9TwiAHj8B1GzXKtR4eFtkhi/X5RLU= -github.com/containerd/containerd/api v1.11.1/go.mod h1:CaQFRu+N1MtbgL6JDOJLUB1hCKESU1lD6MuTJhgtdlw= -github.com/containerd/containerd/v2 v2.3.6 h1:huoqZXaDW1x1kgPvFC4rMwxv92vUJnqgq7I5b1rSddM= -github.com/containerd/containerd/v2 v2.3.6/go.mod h1:qaUSWKk9EchqbudSbhv1MxI8dXKGYHLpZtMQdkEkVZ4= +github.com/containerd/containerd/api v1.12.0 h1:kuQm82SbDrCuO4n7hf2L8zsBtZLuympyq5X/VotfX2A= +github.com/containerd/containerd/api v1.12.0/go.mod h1:EBcSzoi9Vl18cdODaXUCskf3D2NT8lsSXeZJnU5jIUc= +github.com/containerd/containerd/v2 v2.4.1 h1:DUx/ZJN7cEu0WuzHClDB+68H/bqMEH5pWoEjf0ae4hc= +github.com/containerd/containerd/v2 v2.4.1/go.mod h1:vgLdtvl3prFk1d3ZVqQcsDD5Q9IKM+vAIdU54U85w+I= github.com/containerd/continuity v0.5.0 h1:7a85HZpCSs+1Zps0Ee3DPSuAWY+0SJM1JNM51nlEVDg= github.com/containerd/continuity v0.5.0/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -306,23 +310,33 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= 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/log v0.2.0 h1:BewD/umNgVnoczglOpX8eRMyEy5t5iPlu5AIpnWDONc= +github.com/containerd/log v0.2.0/go.mod h1:/M7L7CXKcPTfNC74XzaK+5H5KbO5+4lJVpuVI6vRLoM= +github.com/containerd/log/otel v0.1.0 h1:Az5rFFo0+c4v2yC8ROR63sWsZpKl/vhtUnUhqLSpE6U= +github.com/containerd/log/otel v0.1.0/go.mod h1:65C5iYF2xIQByCyxzoO2KKKK1+/tGxD4XqhdlrTtvzE= github.com/containerd/platforms v1.0.0-rc.5 h1:vXd569rDrz8LeMXzAnBsy6LADV5YtsD8oyaRarxdmSU= github.com/containerd/platforms v1.0.0-rc.5/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A= github.com/containerd/plugin v1.1.0 h1:O+7lczNJVMy8rz0YNx3xGB8tTf5qY4i5abF041Ew19U= github.com/containerd/plugin v1.1.0/go.mod h1:qBTum+A8lJ6lO44A19Eo7y1OlcLj4OWFH1DA/vnHmcc= -github.com/containerd/ttrpc v1.2.8 h1:xbVu6D4qF2jihdh9rDVOKqUMiFBQk6YctTdo1zk087Y= -github.com/containerd/ttrpc v1.2.8/go.mod h1:wyZW2K79t4Hfcxl+GUvkZqRBzJlqFFvgEeeWXa42tyE= -github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= -github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= +github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw= +github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY= +github.com/containerd/ttrpc v1.2.9 h1:ha0ak962T0s3CA/RoZ6S6xiWZQF24GrBaEpiGX1uihg= +github.com/containerd/ttrpc v1.2.9/go.mod h1:jjtQRwXm4DL3KsHKW8vDiUOV6wO0hi6IPhmJhxU7aEs= +github.com/containerd/typeurl/v2 v2.3.0 h1:HZHPhRWo5XMy3QGQoPrUzbW/2ckwjfweHmOwlkIrPAQ= +github.com/containerd/typeurl/v2 v2.3.0/go.mod h1:Qk+PAdUYArVj41TnGi6rJ+48RF0PkcTc4i/taoBcK0w= +github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= +github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= +github.com/containers/ocicrypt v1.3.2 h1:MuqHSfiPpGzoAQdgDSX85FgixSgIm9mSDXTLTgugY5E= +github.com/containers/ocicrypt v1.3.2/go.mod h1:ntBZabYG0rlvstB/1rK/ba2laaU5EHE0pD5ioHoPTq4= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= -github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= +github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q= +github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= +github.com/cyphar/filepath-securejoin v0.7.0 h1:s0Y3ITPy6sQn5xt54DuYvTF8hu134ooYLUb58DX/HjE= +github.com/cyphar/filepath-securejoin v0.7.0/go.mod h1:ymLGms/u3BYaviIiuKFnUx8EkQEZeK6cInNoAPJA3o4= github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -339,10 +353,12 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= -github.com/docker/cli v29.8.0+incompatible h1:ih0c2jq/nN7QfES8zIfwSzIhRScjs4ehER+kZ60aeSk= -github.com/docker/cli v29.8.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= -github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/cli v29.8.1+incompatible h1:qYL1bCp6cRw2SB1xmLlIOPyV171dilw9W2Jew38vy9c= +github.com/docker/cli v29.8.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker-credential-helpers v0.9.8 h1:bIREROb7So6PRlq6KTtdS9MPEjC29OQRkFNlvK2OX8Q= +github.com/docker/docker-credential-helpers v0.9.8/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-connections v0.8.1 h1:JibmG5hULs5qXSr/cp/w3Pw5fZuStt4MOHMUExb29/M= github.com/docker/go-connections v0.8.1/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -381,8 +397,8 @@ github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMD github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= 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/erofs/go-erofs v0.3.0 h1:o/W5ABAA3sHYl97WL93dacKEfeDpJhdFf3c2snAti7I= -github.com/erofs/go-erofs v0.3.0/go.mod h1:XkSeN9MHszGd4+3gcEjadJLYHCQpWzJ7/8yznzMuzJs= +github.com/erofs/go-erofs v0.3.1 h1:Sux82Jq9yvyYhIoLgSHDp741p/+370HsOj9dAh1+VVs= +github.com/erofs/go-erofs v0.3.1/go.mod h1:XkSeN9MHszGd4+3gcEjadJLYHCQpWzJ7/8yznzMuzJs= github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c/go.mod h1:QGzNH9ujQ2ZUr/CjDGZGWeDAVStrWNjHeEcjJL96Nuk= github.com/facebookincubator/nvdtools v0.1.5 h1:jbmDT1nd6+k+rlvKhnkgMokrCAzHoASWE5LtHbX2qFQ= github.com/facebookincubator/nvdtools v0.1.5/go.mod h1:Kh55SAWnjckS96TBSrXI99KrEKH4iB0OJby3N8GRJO4= @@ -396,13 +412,13 @@ github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0C github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc= +github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= +github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI= github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -427,8 +443,8 @@ github.com/go-git/go-git/v5 v5.19.2/go.mod h1:QqCBE1EFN5ddFmrliLQ3/ntRCUjZU3EJuw github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= -github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-jose/go-jose/v4 v4.1.5 h1:RjgjO2LOtWOJKUC5wpwY9LR3B3vwVAz6JS2YHfYU6eA= +github.com/go-jose/go-jose/v4 v4.1.5/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -453,7 +469,6 @@ github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxUW+NltUg= github.com/gohugoio/hashstructure v0.6.0/go.mod h1:lapVLk9XidheHG1IQ4ZSbyYrXcaILU1ZEP/+vno5rBQ= @@ -507,10 +522,13 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.22.1 h1:RZuuSYhTvlDvtsK+NkutoCZ//C0X2ebLK8X8l3ULs84= github.com/google/go-containerregistry v0.22.1/go.mod h1:bJR35SK8XgisYmhg/FMQ/5RK0S/XrOAqLBV5/LR2XE0= +github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM= +github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs= github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY= @@ -543,6 +561,7 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/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/googleapis/enterprise-certificate-proxy v0.3.20 h1:t/xL64VUoN69MuMRQuJETqYGOw4Z9mSRJK9epIEtwFk= @@ -558,6 +577,8 @@ github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gookit/color v1.6.1 h1:KoTnDxJPRgrL0SoX0f8rCFg2zI0t4E3GZZBMo2nN8LU= github.com/gookit/color v1.6.1/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gpustack/gguf-parser-go v0.26.3 h1:F63PlUPIW56HGU5k4Crv0JAG9ojtz/TuwzgMYlb82Ec= github.com/gpustack/gguf-parser-go v0.26.3/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -692,8 +713,10 @@ github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsRe github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw= github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w= -github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-runewidth v0.0.27 h1:Feg/Oou5zI/wnpgDF6omIU0OokC9GxLC/WRknhVlIR0= +github.com/mattn/go-runewidth v0.0.27/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8= +github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs= +github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -702,10 +725,14 @@ github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPp github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/pkcs11 v1.1.2 h1:/VxmeAX5qU6Q3EwafypogwWbYryHFmF2RpkJmw3m4MQ= +github.com/miekg/pkcs11 v1.1.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0= github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc= github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A= github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec= +github.com/mistifyio/go-zfs/v4 v4.0.0 h1:sU0+5dX45tdDK5xNZ3HBi95nxUc48FS92qbIZEvpAg4= +github.com/mistifyio/go-zfs/v4 v4.0.0/go.mod h1:weotFtXTHvBwhr9Mv96KYnDkTPBOHFUbm9cBmQpesL0= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -727,16 +754,16 @@ github.com/moby/moby/api v1.56.0 h1:GQzua3NA599ASSIICx0iFgiJeO9YkdDARvQsm23ZZuQ= github.com/moby/moby/api v1.56.0/go.mod h1:sZ+THbVWkjOmBPPfbnzdD/G1LuIexWhqlSHHPTDQ1Uk= github.com/moby/moby/client v0.6.0 h1:AJjEB21QPbXSXjDsZorFBoDZPhMrfbpaPLgSMAW9Bgs= github.com/moby/moby/client v0.6.0/go.mod h1:OCo00wNRyA3m4lmJ228W3JbyCN4ZNNYjpOXiJydBdcQ= +github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk= +github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= -github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= -github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/sys/user v0.4.1 h1:RgjRlaDKi/Xmyrz4t8lyzXT6v2ooFeO/7xtchmhVWE0= +github.com/moby/sys/user v0.4.1/go.mod h1:E9QsW5WRe1kUAf7kW8hXKwu1uhsZEAdPLYHYSDudF4Y= +github.com/moby/sys/userns v0.2.1 h1:4OvdM7BcPkASbuouHsbW3aeMJSFlYDldBRnXVZhaRk8= +github.com/moby/sys/userns v0.2.1/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -776,6 +803,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.15.1 h1:ERxeh5caJvCzNAKdI8WQbJmB1LDTn4BuaAg8wihLBpA= +github.com/opencontainers/selinux v1.15.1/go.mod h1:LenyElirjUHszfxrjuFqC85HIeXZKumHcKMQtnaDlQQ= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -814,6 +843,8 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/proglottis/gpgme v0.1.6 h1:8WpQ8VWggLdxkuTnW+sZ1r1t92XBNd8GZNDhQ4Rz+98= +github.com/proglottis/gpgme v0.1.6/go.mod h1:5LoXMgpE4bttgwwdv9bLs/vwqv3qV7F4glEEZ7mRKrM= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= @@ -826,8 +857,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= -github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= +github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY= github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= @@ -853,6 +884,8 @@ github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7 github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rYg= github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg= github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI= github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ= @@ -860,21 +893,31 @@ github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd7 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8rc= github.com/sebdah/goldie/v2 v2.8.0/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= +github.com/secure-systems-lab/go-securesystemslib v0.11.0 h1:iuCR9kcMFD4QurdKrGvPLoKZLv9YvwPYVr0473BdtFs= +github.com/secure-systems-lab/go-securesystemslib v0.11.0/go.mod h1:+PMOTjUGwHj2vcZ+TFKlb1tXRbrdWE1LYDT5i9JC80Q= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/sigstore/fulcio v1.8.7 h1:d7QJQxukgXarqPCT9uDNeyzt1Bbt/biw58/VfOnwI/o= +github.com/sigstore/fulcio v1.8.7/go.mod h1:7fX+QyigZxRGKTgHvTfOfxwwniCDxK02PVloHy5/vg4= +github.com/sigstore/protobuf-specs v0.5.1 h1:/5OPaNuolRJmQfeZLayJGFXMpsRJEdgC6ah1/+7Px7U= +github.com/sigstore/protobuf-specs v0.5.1/go.mod h1:DRBzpFuE+LnvQMN10/dU6nBeKwVLGEQ6o2FovN2Rats= +github.com/sigstore/sigstore v1.10.8 h1:1Mgkxvkw4AXMfIP1DOjc6kw0GkUgA8pGVpveN/EfOq4= +github.com/sigstore/sigstore v1.10.8/go.mod h1:f9+B/4iaYimvUkySyb2mvc73n3RLqNn24grHZM/ET8M= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= -github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo= +github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q= github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d h1:3VwvTjiRPA7cqtgOWddEL+JrcijMlXUmj99c/6YyZoY= github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d/go.mod h1:tAG61zBM1DYRaGIPloumExGvScf08oHuo0kFoOqdbT0= +github.com/smallstep/pkcs7 v0.2.1 h1:6Kfzr/QizdIuB6LSv8y1LJdZ3aPSfTNhTLqAx9CTLfA= +github.com/smallstep/pkcs7 v0.2.1/go.mod h1:RcXHsMfL+BzH8tRhmrF1NkkpebKpq3JEM66cOFxanf0= github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik= github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= @@ -904,6 +947,8 @@ github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/spiffe/go-spiffe/v2 v2.7.0 h1:uXe1MflJoHw58wAUvxVlcM7WpKtijWG7I1UidcGh6g4= github.com/spiffe/go-spiffe/v2 v2.7.0/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U= +github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw= +github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -931,6 +976,8 @@ github.com/sylabs/squashfs v1.0.6 h1:PvJcDzxr+vIm2kH56mEMbaOzvGu79gK7P7IX+R7BDZI github.com/sylabs/squashfs v1.0.6/go.mod h1:DlDeUawVXLWAsSRa085Eo0ZenGzAB32JdAUFaB0LZfE= github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd h1:Rf9uhF1+VJ7ZHqxrG8pJ6YacmHvVCmByDmGbAWCc/gA= github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo= +github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc= +github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/terminalstatic/go-xsd-validate v0.1.8 h1:UVrTCy1j3DhwaYTTUF+QYO/Nan13S0tf+Jwi+p45Bf0= github.com/terminalstatic/go-xsd-validate v0.1.8/go.mod h1:1kb47fi2c6onlf+B7UrrQ9VYraOhcYwFm3iG+J6F4Zo= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= @@ -951,6 +998,12 @@ github.com/ulikunitz/xz v0.5.16 h1:ld6NyySjx5lowVKwJvMRLnW5nxKX/xnpSiFYZ/Lxur0= github.com/ulikunitz/xz v0.5.16/go.mod h1:H9Rt/W6/Qj27PGauhQc6nfCDy7vHpzsOThBSaYDoEhw= github.com/vbatts/go-mtree v0.7.0 h1:ytmOc3MTRidZiBi9VBCyZ2BHe4fZS47L5v7BVXDWW4E= github.com/vbatts/go-mtree v0.7.0/go.mod h1:EjdpFC+LZy1TXbRGNa1MKKgjQ+7ew3foMFJK8o4/TdY= +github.com/vbatts/tar-split v0.12.3 h1:Cd46rkGXI3Td4yrVNwU8ripbxFaQbmesqhjBUUYAJSw= +github.com/vbatts/tar-split v0.12.3/go.mod h1:sQOc6OlqGCr7HkGx/IDBeKiTIvqhmj8KffNhEXG4Nq0= +github.com/vbauerster/cupwriter v0.0.4 h1:9sBPe0uXWLZuWQU5lqVbhyFlxX6c09asST/YfatFAys= +github.com/vbauerster/cupwriter v0.0.4/go.mod h1:IFyzS6Xis5dnBH/rdAhrnuzg3c+KkUqEN6yE8lhJlDw= +github.com/vbauerster/mpb/v8 v8.14.0 h1:55SR80dptMfASxIG/oCEkBXgBhxeSu4GrVsjl16oKmA= +github.com/vbauerster/mpb/v8 v8.14.0/go.mod h1:HgpQPKfcWe3kbuGGPmi+jatHreMase5C3Fp5dpdAy0Q= github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0= github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o= github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA= @@ -972,6 +1025,8 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -994,31 +1049,33 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/detectors/gcp v1.44.0 h1:NmLfL734pJhM0JKaYd2Y28+nY9dPRWYAAbxhRCrKXPw= go.opentelemetry.io/contrib/detectors/gcp v1.44.0/go.mod h1:tNAsgd8avTGke1+MndXlU5Cru4PQ9Ai/cCNWQv/ZJ/s= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= -go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= -go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0 h1:B2h3uqicet1CT2N5TOFhS+Gq++9i0/CLmaxvhmhtP5s= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0/go.mod h1:dylvB+ZiiwMvsDij9O84Uy7SijLgHMX4mbkncds+4Sw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU= +go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= +go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0 h1:hqxVTu/GtBF+vJ8d1fzW7fRxZFvgoDjWcxwwCaFDYpU= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0/go.mod h1:z5fVEF4X5v0ESvlJqBrrFlBVoj5EQuefZpzsu7R+x5Q= -go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= -go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= -go.opentelemetry.io/otel/metric/x v0.67.0 h1:PcicCNZFkZ4bXfSooXdo3WN7RBOVOtjVdo1wD358Uns= -go.opentelemetry.io/otel/metric/x v0.67.0/go.mod h1:FBjCWZe6wgcqxcMtjdGiClDKXb2YxxXii0CXftE4QtI= -go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw= -go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA= -go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o= -go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA= -go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= -go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= +go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= +go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o= +go.opentelemetry.io/otel/metric/x v0.68.0 h1:TA/cBT23D3MnxYPwHL7YFOdYGdx0A0v+s7Mzotpd1dU= +go.opentelemetry.io/otel/metric/x v0.68.0/go.mod h1:agudOmvWhwUTjgibWDzxD2PoWYnpw5Ht5jISYOD2Hd4= +go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI= +go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM= +go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE= +go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4= +go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= +go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.podman.io/image/v5 v5.41.2 h1:YnVaFrGLPWSBmipKfNvlOsGcBtk5bUxlPxQ+A6CS/uo= +go.podman.io/image/v5 v5.41.2/go.mod h1:T/bzxEFpdpAzyHabPmcq5K+my3wBsht22sFkFjJSKT4= +go.podman.io/storage v1.64.1 h1:DqbG8scW/bD3xHiSRzV7O5fzn46qVC+SwmUN6C6Fr5w= +go.podman.io/storage v1.64.1/go.mod h1:ft0DzCRMZs1yn0rszmLYXEEwgd7IIc66JRwSRPUDwwc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -1043,6 +1100,10 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1084,6 +1145,10 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c= golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1129,7 +1194,12 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues= golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1163,6 +1233,11 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1239,11 +1314,22 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE= golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1256,6 +1342,11 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1318,6 +1409,9 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.50.0 h1:c2ifzfcuY7L90lZ2aKd8S4K2NpASF08SZx9ZuJkHmSU= golang.org/x/tools v0.50.0/go.mod h1:7ulVMw3831Mwi5EZD6RomGyffr4VFjuNYXf2BbCEAV0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1434,10 +1528,10 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d h1:C9v1o0/4quuhOAfmRXA2j+we0PqZIp8traLdeogF3Ms= google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d/go.mod h1:Wz2wFJntZFmLGo7pLDXZ3wYk5hyc0Mb+SkHhDDXT+lU= -google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d h1:QwnJwPte4XXAkhPu26LTDIahnsMSUV0kK8HkxbC+Pc4= -google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d/go.mod h1:WRrQ7/7N19PypuT0fxLOL5Lq0waoiRri4FbtHDEKrGE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 h1:cYNAzI2sUwhmCcoj9TxvihSrqsxt6uIkj3rDRhSDmW4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA= +google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 h1:izFU9hz7aeLI/Mi1J0991ae+xcwRLr7hTqWnB/9aIIU= +google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5/go.mod h1:3LhxRw4YYkf+ylAfgaY9JlVLFKhokkCV8duhLLe7+t0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 h1:1VUiZAXyC+zmiFYi+WLtBzr68Cj8wOofHjjrA/kkizc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= From 29d2de72c8601b831780d46d3c265f7ef76266ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 15:35:21 +0000 Subject: [PATCH 04/14] chore(deps): bump github.com/gohugoio/hashstructure from 0.6.0 to 1.1.0 (#5277) Bumps [github.com/gohugoio/hashstructure](https://github.com/gohugoio/hashstructure) from 0.6.0 to 1.1.0. - [Release notes](https://github.com/gohugoio/hashstructure/releases) - [Commits](https://github.com/gohugoio/hashstructure/compare/v0.6.0...v1.1.0) --- updated-dependencies: - dependency-name: github.com/gohugoio/hashstructure dependency-version: 1.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index aa6ed1670..78194365b 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( github.com/go-test/deep v1.1.1 github.com/go-viper/mapstructure/v2 v2.5.0 github.com/goccy/go-yaml v1.19.2 - github.com/gohugoio/hashstructure v0.6.0 + github.com/gohugoio/hashstructure v1.1.0 github.com/google/go-cmp v0.7.0 github.com/google/go-containerregistry v0.22.1 github.com/google/licensecheck v0.3.1 diff --git a/go.sum b/go.sum index 77524bcf6..7b4ec387a 100644 --- a/go.sum +++ b/go.sum @@ -470,8 +470,8 @@ github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7Lk github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxUW+NltUg= -github.com/gohugoio/hashstructure v0.6.0/go.mod h1:lapVLk9XidheHG1IQ4ZSbyYrXcaILU1ZEP/+vno5rBQ= +github.com/gohugoio/hashstructure v1.1.0 h1:38yUfZBca6qXSbUpteLhjDGLNskclHaguFBYpjaRjf4= +github.com/gohugoio/hashstructure v1.1.0/go.mod h1:Pz8dcwjZs6FBKWu9x/ZIChrTHIM175zfUJK0KLvC1z8= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= From c00be184b5c74f3f195a58b1f39993e6be4a5028 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 1 Oct 2026 13:40:34 -0400 Subject: [PATCH 05/14] fix: revert hashstructure to v0.6.0 to keep artifact IDs stable (#5364) * fix: revert hashstructure to v0.6.0 to keep artifact IDs stable hashstructure v1 changed `SlicesAsSets` hashing (every slice now gets a finalizing hash pass, and duplicate elements are deduplicated), which changed nearly every artifact ID syft produces. - pin `github.com/gohugoio/hashstructure` back to v0.6.0 and have dependabot ignore major bumps - make the aggregate `Unit tests` job fail when a dependency fails; previously it was skipped, which satisfies the required check and let #5277 auto-merge with failing unit tests Signed-off-by: Alex Goodman * ci: fail unit test gate on any failed, cancelled, or skipped dependency Signed-off-by: Alex Goodman * ci: require every unit test dependency to succeed Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- .github/dependabot.yml | 3 +++ .github/workflows/validations.yaml | 13 ++++++++++++- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d52390045..638268f38 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -21,6 +21,9 @@ updates: - dependency-name: "github.com/aquasecurity/go-version" - dependency-name: "github.com/knqyf263/go-apk-version" - dependency-name: "github.com/knqyf263/go-deb-version" + # v1 changed SlicesAsSets hashing, which changes every artifact ID syft produces + - dependency-name: "github.com/gohugoio/hashstructure" + update-types: ["version-update:semver-major"] - package-ecosystem: "github-actions" directories: diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 95092a476..0186c5ace 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -51,8 +51,19 @@ jobs: name: "Unit tests" runs-on: ubuntu-latest needs: [Unit-Test-Linux, Unit-Test-Windows] + # a skipped job satisfies a required check, so this job must always run and fail explicitly + # unless every dependency succeeded (otherwise a failing PR can still be auto-merged) + if: ${{ always() }} steps: - - run: echo All unit tests passed! + - name: Require all unit test jobs to succeed + env: + NEEDS: ${{ toJSON(needs) }} + run: | + echo "$NEEDS" | jq -e 'length > 0 and all(.[]; .result == "success")' > /dev/null || { + echo "$NEEDS" | jq -r 'to_entries[] | "\(.key): \(.value.result)"' + exit 1 + } + echo All unit tests passed! Unit-Test-Linux: name: "Unit tests (Linux)" diff --git a/go.mod b/go.mod index 78194365b..aa6ed1670 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( github.com/go-test/deep v1.1.1 github.com/go-viper/mapstructure/v2 v2.5.0 github.com/goccy/go-yaml v1.19.2 - github.com/gohugoio/hashstructure v1.1.0 + github.com/gohugoio/hashstructure v0.6.0 github.com/google/go-cmp v0.7.0 github.com/google/go-containerregistry v0.22.1 github.com/google/licensecheck v0.3.1 diff --git a/go.sum b/go.sum index 7b4ec387a..77524bcf6 100644 --- a/go.sum +++ b/go.sum @@ -470,8 +470,8 @@ github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7Lk github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/gohugoio/hashstructure v1.1.0 h1:38yUfZBca6qXSbUpteLhjDGLNskclHaguFBYpjaRjf4= -github.com/gohugoio/hashstructure v1.1.0/go.mod h1:Pz8dcwjZs6FBKWu9x/ZIChrTHIM175zfUJK0KLvC1z8= +github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxUW+NltUg= +github.com/gohugoio/hashstructure v0.6.0/go.mod h1:lapVLk9XidheHG1IQ4ZSbyYrXcaILU1ZEP/+vno5rBQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= From d654c00bd6942cf11e6fe133fe4ae7baae0192d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 18:00:41 +0000 Subject: [PATCH 06/14] chore(deps): bump github.com/bmatcuk/doublestar/v4 from 4.10.0 to 4.10.2 (#5365) Bumps [github.com/bmatcuk/doublestar/v4](https://github.com/bmatcuk/doublestar) from 4.10.0 to 4.10.2. - [Release notes](https://github.com/bmatcuk/doublestar/releases) - [Commits](https://github.com/bmatcuk/doublestar/compare/v4.10.0...v4.10.2) --- updated-dependencies: - dependency-name: github.com/bmatcuk/doublestar/v4 dependency-version: 4.10.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index aa6ed1670..2c57fd886 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/aquasecurity/go-pep440-version v0.0.1 github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb - github.com/bmatcuk/doublestar/v4 v4.10.0 + github.com/bmatcuk/doublestar/v4 v4.10.2 github.com/cespare/xxhash/v2 v2.3.0 github.com/charmbracelet/bubbles v1.0.0 github.com/charmbracelet/bubbletea v1.3.10 diff --git a/go.sum b/go.sum index 77524bcf6..99908987a 100644 --- a/go.sum +++ b/go.sum @@ -229,8 +229,8 @@ github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef h1:TSFnfbbu2oAO github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= -github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= -github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bmatcuk/doublestar/v4 v4.10.2 h1:eF7W7HWKg3z9NrWV9pTLnNeoXaqq3Tq9DNKXVMfoCnw= +github.com/bmatcuk/doublestar/v4 v4.10.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4= From db7e445c540127116cb2260d6391e6cf61c84e7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 18:03:47 +0000 Subject: [PATCH 07/14] chore(deps): bump github.com/ulikunitz/xz from 0.5.16 to 0.5.17 (#5366) Bumps [github.com/ulikunitz/xz](https://github.com/ulikunitz/xz) from 0.5.16 to 0.5.17. - [Commits](https://github.com/ulikunitz/xz/compare/v0.5.16...v0.5.17) --- updated-dependencies: - dependency-name: github.com/ulikunitz/xz dependency-version: 0.5.17 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2c57fd886..253dc7d7d 100644 --- a/go.mod +++ b/go.mod @@ -83,7 +83,7 @@ require ( github.com/spf13/afero v1.15.0 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.12.1 - github.com/ulikunitz/xz v0.5.16 + github.com/ulikunitz/xz v0.5.17 github.com/vbatts/go-mtree v0.7.0 github.com/vifraa/gopom v1.0.0 github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 diff --git a/go.sum b/go.sum index 99908987a..ebc5d20cf 100644 --- a/go.sum +++ b/go.sum @@ -994,8 +994,8 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ulikunitz/xz v0.5.16 h1:ld6NyySjx5lowVKwJvMRLnW5nxKX/xnpSiFYZ/Lxur0= -github.com/ulikunitz/xz v0.5.16/go.mod h1:H9Rt/W6/Qj27PGauhQc6nfCDy7vHpzsOThBSaYDoEhw= +github.com/ulikunitz/xz v0.5.17 h1:flR0y/x1hgM8EGV1AW3Xll6T413G0glV8UfBwR617V4= +github.com/ulikunitz/xz v0.5.17/go.mod h1:H9Rt/W6/Qj27PGauhQc6nfCDy7vHpzsOThBSaYDoEhw= github.com/vbatts/go-mtree v0.7.0 h1:ytmOc3MTRidZiBi9VBCyZ2BHe4fZS47L5v7BVXDWW4E= github.com/vbatts/go-mtree v0.7.0/go.mod h1:EjdpFC+LZy1TXbRGNa1MKKgjQ+7ew3foMFJK8o4/TdY= github.com/vbatts/tar-split v0.12.3 h1:Cd46rkGXI3Td4yrVNwU8ripbxFaQbmesqhjBUUYAJSw= From ef356eccea955328b2e00c1fdccce39093f47623 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 1 Oct 2026 15:00:37 -0400 Subject: [PATCH 08/14] fix: sign release checksums with a sigstore bundle (#5362) * fix: sign release checksums with a sigstore bundle The cosign update rejects the legacy `--output-certificate` / `--output-signature` flags alongside the new bundle format, which broke signing during the v1.53.0 release. - checksums are now signed into a single `checksums.txt.sigstore.json` bundle (signature, certificate, and tlog proof) - `install.sh -v` verifies with `--bundle` for releases >= v1.53.0 and keeps the `.sig` / `.pem` flow for older releases Signed-off-by: Alex Goodman * chore: smooth the cutover to sigstore bundle verification - `install.sh -v` now hints that bundle verification needs cosign v2.5.0 or newer when verification of a bundle-signed release fails - install tests keep exercising the legacy `.sig` / `.pem` path against v1.52.0 once the latest release is bundle-signed - touch the install test environment Dockerfiles so the cached images rebuild with a current cosign Signed-off-by: Alex Goodman * fix: only hint at the cosign minimum when cosign is actually too old - `install.sh -v` used to print the cosign v2.5.0 note on any failed bundle verification, including a missing bundle or a bad signature. It now only prints it when cosign reports it couldn't read the bundle (`bundle does not contain cert`), which is how v2.2.4 through v2.4.1 fail on a sigstore bundle. - `verify_sign` is now covered with a stub cosign, so the bundle path is exercised before any bundle-signed release is latest Signed-off-by: Alex Goodman * fix: stop the install when a release asset is missing or fails its checksum - `install.sh` now stops when the checksums file, the signature bundle, or the asset can't be downloaded, instead of handing an empty or error-page file to the next step - a sha256 mismatch between the asset and the checksums file now stops the install. Before this the mismatch was logged and the asset was installed anyway, so `-v` only ever vouched for `checksums.txt`, not the binary Signed-off-by: Alex Goodman * chore: pin cosign in the install test images The install test images pulled whatever cosign was latest, unverified. They now install cosign v3.1.3 checked against its published sha256, so the install tests always run with a cosign that can verify sigstore bundles (releases >= v1.53.0 need v2.5.0 or newer). Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman Co-authored-by: Keith Zantow --- .goreleaser.yaml | 9 +- install.sh | 58 +++++++++---- test/install/2_download_release_asset_test.sh | 6 ++ .../4_prep_signature_verification_test.sh | 58 +++++++++++++ .../install/5_download_asset_failures_test.sh | 84 +++++++++++++++++++ .../environments/Dockerfile-alpine-3.6 | 8 +- .../environments/Dockerfile-busybox-1.36 | 15 ++-- .../environments/Dockerfile-ubuntu-20.04 | 10 ++- test/install/test_harness.sh | 25 ++++++ 9 files changed, 241 insertions(+), 32 deletions(-) create mode 100755 test/install/5_download_asset_failures_test.sh diff --git a/.goreleaser.yaml b/.goreleaser.yaml index d5bb7de18..a06e04762 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -167,16 +167,13 @@ sboms: - "--output" - "json=$document" +# the signature, certificate, and transparency log proof all land in a single sigstore bundle. signs: - cmd: .tool/cosign - signature: "${artifact}.sig" - certificate: "${artifact}.pem" + signature: "${artifact}.sigstore.json" args: - "sign-blob" - - "--use-signing-config=false" - - "--oidc-issuer=https://token.actions.githubusercontent.com" - - "--output-certificate=${certificate}" - - "--output-signature=${signature}" + - "--bundle=${signature}" - "${artifact}" - "--yes" artifacts: checksum diff --git a/install.sh b/install.sh index bf9222d06..7fb08d0d4 100755 --- a/install.sh +++ b/install.sh @@ -19,6 +19,8 @@ VERIFY_SIGN=false VERIFY_SIGN_SUPPORTED_VERSION=v0.104.0 # this is the earliest tag in the repo where the -v flag was introduced to this install.sh script VERIFY_SIGN_FLAG_VERSION=v1.6.0 +# releases at or after this version are signed with a sigstore bundle (.sigstore.json); earlier releases ship a separate .sig and .pem +VERIFY_SIGN_BUNDLE_VERSION=v1.53.0 # do not change the name of this parameter (this must always be backwards compatible) DOWNLOAD_TAG_INSTALL_SCRIPT=${DOWNLOAD_TAG_INSTALL_SCRIPT:-true} @@ -386,8 +388,8 @@ download_github_release_checksums_files() ( complete_url=$(github_release_asset_url "${download_url}" "${name}" "${version}" "${filename}") output_path="${output_dir}/${complete_filename}" - http_download "${output_path}" "${complete_url}" "" - asset_file_exists "${output_path}" + http_download "${output_path}" "${complete_url}" "" || return 1 + asset_file_exists "${output_path}" || return 1 log_trace "download_github_release_checksums_files() returned '${output_path}' for file '${complete_filename}'" @@ -585,28 +587,31 @@ download_and_install_asset() ( install_asset "${asset_filepath}" "${install_path}" "${binary}" ) -# verify_sign [checksums-file-path] [certificate-reference] [signature-reference] [version] +# verify_sign [checksums-file-path] [verification-material-flags...] # # attempts verify the signature of the checksums file from the release workflow in Github Actions run against the main branch. +# the verification material is either "--bundle " or (for older releases) "--certificate --signature ". # verify_sign() { checksums_file=$1 - cert_reference=$2 - sig_reference=$3 + shift - log_trace "verifying artifact $1" + log_trace "verifying artifact ${checksums_file}" log_file=$(mktemp) ${COSIGN_BINARY} \ verify-blob "$checksums_file" \ - --certificate "$cert_reference" \ - --signature "$sig_reference" \ + "$@" \ --certificate-identity "https://github.com/${OWNER}/${REPO}/.github/workflows/release.yaml@refs/heads/main" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" > "${log_file}" 2>&1 if [ $? -ne 0 ]; then log_err "$(cat "${log_file}")" + # cosign older than v2.4.2 can't read the bundle's verification material and reports a missing cert + if [ "$1" = "--bundle" ] && grep -q "bundle does not contain cert" "${log_file}"; then + log_err "note: releases >= ${VERIFY_SIGN_BUNDLE_VERSION} are verified with a sigstore bundle, which requires cosign v2.5.0 or newer (check '${COSIGN_BINARY} version')" + fi rm -f "${log_file}" return 1 fi @@ -630,7 +635,10 @@ download_asset() ( log_trace "download_asset(url=${download_url}, destination=${destination}, name=${name}, os=${os}, arch=${arch}, version=${version}, format=${format})" - checksums_filepath=$(download_github_release_checksums "${download_url}" "${name}" "${version}" "${destination}") + if ! checksums_filepath=$(download_github_release_checksums "${download_url}" "${name}" "${version}" "${destination}"); then + log_err "unable to download checksums file" + return 1 + fi log_trace "checksums content:\n$(cat ${checksums_filepath})" @@ -642,13 +650,25 @@ download_asset() ( fi if [ "$VERIFY_SIGN" = true ]; then - checksum_sig_file_url=$(github_release_checksums_sig_url "${download_url}" "${name}" "${version}") - log_trace "checksums signature url: ${checksum_sig_file_url}" + if compare_semver "${version}" "${VERIFY_SIGN_BUNDLE_VERSION}"; then + if ! checksums_bundle_filepath=$(download_github_release_checksums_files "${download_url}" "${name}" "${version}" "${destination}" "checksums.txt.sigstore.json"); then + log_err "unable to download checksums signature bundle" + return 1 + fi + log_trace "checksums bundle: ${checksums_bundle_filepath}" - checksums_cert_file_url=$(github_release_checksums_cert_url "${download_url}" "${name}" "${version}") - log_trace "checksums certificate url: ${checksums_cert_file_url}" + set -- --bundle "${checksums_bundle_filepath}" + else + checksum_sig_file_url=$(github_release_checksums_sig_url "${download_url}" "${name}" "${version}") + log_trace "checksums signature url: ${checksum_sig_file_url}" - if ! verify_sign "${checksums_filepath}" "${checksums_cert_file_url}" "${checksum_sig_file_url}"; then + checksums_cert_file_url=$(github_release_checksums_cert_url "${download_url}" "${name}" "${version}") + log_trace "checksums certificate url: ${checksums_cert_file_url}" + + set -- --certificate "${checksums_cert_file_url}" --signature "${checksum_sig_file_url}" + fi + + if ! verify_sign "${checksums_filepath}" "$@"; then log_err "signature verification failed" return 1 fi @@ -657,9 +677,15 @@ download_asset() ( asset_url="${download_url}/${asset_filename}" asset_filepath="${destination}/${asset_filename}" - http_download "${asset_filepath}" "${asset_url}" "" + if ! http_download "${asset_filepath}" "${asset_url}" ""; then + log_err "unable to download asset '${asset_url}'" + return 1 + fi - hash_sha256_verify "${asset_filepath}" "${checksums_filepath}" + # this is what ties the asset to the (optionally signature verified) checksums file, so a mismatch must stop the install + if ! hash_sha256_verify "${asset_filepath}" "${checksums_filepath}"; then + return 1 + fi log_trace "download_asset_by_checksums_file() returned '${asset_filepath}'" diff --git a/test/install/2_download_release_asset_test.sh b/test/install/2_download_release_asset_test.sh index 003477149..4ea173e15 100755 --- a/test/install/2_download_release_asset_test.sh +++ b/test/install/2_download_release_asset_test.sh @@ -43,3 +43,9 @@ run_test_case test_download_release_asset "${release}" "linux" "amd64" "deb" "ap run_test_case test_download_release_asset "${release}" "linux" "arm64" "tar.gz" "application/gzip" run_test_case test_download_release_asset "${release}" "linux" "arm64" "rpm" "application/x-rpm" run_test_case test_download_release_asset "${release}" "linux" "arm64" "deb" "application/vnd.debian.binary-package" + +# releases before VERIFY_SIGN_BUNDLE_VERSION are signed with a separate .sig and .pem instead of a sigstore bundle, +# so keep exercising that path against a fixed release +legacy_release="v1.52.0" + +run_test_case test_download_release_asset "${legacy_release}" "linux" "amd64" "tar.gz" "application/gzip" diff --git a/test/install/4_prep_signature_verification_test.sh b/test/install/4_prep_signature_verification_test.sh index 401364ac0..545aaf4b9 100755 --- a/test/install/4_prep_signature_verification_test.sh +++ b/test/install/4_prep_signature_verification_test.sh @@ -87,3 +87,61 @@ test_prep_signature_verification() { } run_test_case test_prep_signature_verification + +# ensure the verification material is passed through to cosign and the old-cosign hint only shows when warranted +test_verify_sign() { + # verify_sign [checksums-file-path] [verification-material-flags...] + + OG_COSIGN_BINARY=${COSIGN_BINARY} + + tmpdir=$(mktemp -d) + args_file="${tmpdir}/args" + hint="requires cosign v2.5.0 or newer" + + # stub cosign that records its arguments, then prints STUB_OUTPUT and exits with STUB_EXIT + COSIGN_BINARY="${tmpdir}/cosign" + cat > "${COSIGN_BINARY}" < "${args_file}" +echo "\${STUB_OUTPUT}" +exit "\${STUB_EXIT}" +STUB + chmod +x "${COSIGN_BINARY}" + + identity="--certificate-identity https://github.com/${OWNER}/${REPO}/.github/workflows/release.yaml@refs/heads/main --certificate-oidc-issuer https://token.actions.githubusercontent.com" + + # bundle verification succeeds + STUB_OUTPUT="Verified OK" STUB_EXIT=0 verify_sign "checksums.txt" --bundle "checksums.txt.sigstore.json" >/dev/null 2>&1 + assertEquals "0" "$?" "bundle verification should succeed" + assertEquals "verify-blob checksums.txt --bundle checksums.txt.sigstore.json ${identity}" "$(cat "${args_file}")" "unexpected cosign args for bundle" + + # legacy verification succeeds + STUB_OUTPUT="Verified OK" STUB_EXIT=0 verify_sign "checksums.txt" --certificate "c.pem" --signature "c.sig" >/dev/null 2>&1 + assertEquals "0" "$?" "legacy verification should succeed" + assertEquals "verify-blob checksums.txt --certificate c.pem --signature c.sig ${identity}" "$(cat "${args_file}")" "unexpected cosign args for legacy" + + # cosign too old to read the bundle: fail with the hint (this is what cosign v2.2.4 through v2.4.1 print) + output=$(STUB_OUTPUT="Error: bundle does not contain cert for verification, please provide public key" STUB_EXIT=1 verify_sign "checksums.txt" --bundle "b.json" 2>&1) + assertEquals "1" "$?" "old cosign should fail verification" + assertContains "${output}" "${hint}" "old cosign should get the version hint" + + # bad signature (e.g. tampered checksums): fail without the hint + output=$(STUB_OUTPUT="Error: invalid signature when validating ASN.1 encoded signature" STUB_EXIT=1 verify_sign "checksums.txt" --bundle "b.json" 2>&1) + assertEquals "1" "$?" "bad signature should fail verification" + assertNotContains "${output}" "${hint}" "bad signature should not get the version hint" + + # unparseable bundle (e.g. a 404 body): fail without the hint + output=$(STUB_OUTPUT="Error: invalid character 'N' looking for beginning of value" STUB_EXIT=1 verify_sign "checksums.txt" --bundle "b.json" 2>&1) + assertEquals "1" "$?" "unparseable bundle should fail verification" + assertNotContains "${output}" "${hint}" "unparseable bundle should not get the version hint" + + # legacy failure never gets the bundle hint, even with the same output + output=$(STUB_OUTPUT="Error: bundle does not contain cert for verification, please provide public key" STUB_EXIT=1 verify_sign "checksums.txt" --certificate "c.pem" --signature "c.sig" 2>&1) + assertEquals "1" "$?" "legacy failure should fail verification" + assertNotContains "${output}" "${hint}" "legacy failure should not get the version hint" + + COSIGN_BINARY=${OG_COSIGN_BINARY} + rm -rf -- "${tmpdir}" +} + +run_test_case test_verify_sign diff --git a/test/install/5_download_asset_failures_test.sh b/test/install/5_download_asset_failures_test.sh new file mode 100755 index 000000000..edc51234f --- /dev/null +++ b/test/install/5_download_asset_failures_test.sh @@ -0,0 +1,84 @@ +. test_harness.sh + +# serve release assets from a local fixture dir instead of the network (a missing file is a failed download) +http_download() { + src="${FIXTURE_DIR}/${2##*/}" + [ -f "${src}" ] || return 1 + cp "${src}" "$1" +} + +# ensure every missing or mismatched piece of a release stops download_asset before anything is installed +test_download_asset_failures() { + # download_asset [release-url-prefix] [download-path] [name] [os] [arch] [version] [format] + + # we are expecting error messages, which is confusing to look at in passing tests... disable logging for now + log_set_priority -1 + + OG_COSIGN_BINARY=${COSIGN_BINARY} + + tmpdir=$(mktemp -d) + FIXTURE_DIR="${tmpdir}/release" + dest="${tmpdir}/dest" + args_file="${tmpdir}/cosign-args" + mkdir -p "${FIXTURE_DIR}" "${dest}" + + # stub cosign that records it was called and always verifies + COSIGN_BINARY="${tmpdir}/cosign" + printf '#!/bin/sh\necho "$@" > "%s"\n' "${args_file}" > "${COSIGN_BINARY}" + chmod +x "${COSIGN_BINARY}" + + asset="syft_1.54.0_linux_amd64.tar.gz" + echo "the real asset" > "${FIXTURE_DIR}/${asset}" + echo "$(hash_sha256 "${FIXTURE_DIR}/${asset}") ${asset}" > "${FIXTURE_DIR}/syft_1.54.0_checksums.txt" + echo "{}" > "${FIXTURE_DIR}/syft_1.54.0_checksums.txt.sigstore.json" + + run() { + rm -rf "${dest:?}"/* "${args_file}" + download_asset "https://example.invalid/v1.54.0" "${dest}" "syft" "linux" "amd64" "1.54.0" "tar.gz" + } + + # happy path, verified and unverified + VERIFY_SIGN=false + asset_path=$(run) + assertEquals "0" "$?" "complete release should download" + assertEquals "${dest}/${asset}" "${asset_path}" "unexpected asset path" + + VERIFY_SIGN=true + run >/dev/null + assertEquals "0" "$?" "complete release should verify" + assertContains "$(cat "${args_file}")" "--bundle ${dest}/syft_1.54.0_checksums.txt.sigstore.json" "bundle should be passed to cosign" + + # missing bundle: fail before cosign is ever called + mv "${FIXTURE_DIR}/syft_1.54.0_checksums.txt.sigstore.json" "${tmpdir}/bundle" + run >/dev/null + assertEquals "1" "$?" "missing bundle should fail" + assertFilesDoesNotExist "${args_file}" "cosign should not be called without a bundle" + mv "${tmpdir}/bundle" "${FIXTURE_DIR}/syft_1.54.0_checksums.txt.sigstore.json" + + for VERIFY_SIGN in true false; do + # asset does not match the checksums file + echo "a tampered asset" > "${FIXTURE_DIR}/${asset}" + run >/dev/null + assertEquals "1" "$?" "checksum mismatch should fail (verify=${VERIFY_SIGN})" + echo "the real asset" > "${FIXTURE_DIR}/${asset}" + + # missing asset + mv "${FIXTURE_DIR}/${asset}" "${tmpdir}/asset" + run >/dev/null + assertEquals "1" "$?" "missing asset should fail (verify=${VERIFY_SIGN})" + mv "${tmpdir}/asset" "${FIXTURE_DIR}/${asset}" + + # missing checksums file + mv "${FIXTURE_DIR}/syft_1.54.0_checksums.txt" "${tmpdir}/checksums" + run >/dev/null + assertEquals "1" "$?" "missing checksums should fail (verify=${VERIFY_SIGN})" + mv "${tmpdir}/checksums" "${FIXTURE_DIR}/syft_1.54.0_checksums.txt" + done + + COSIGN_BINARY=${OG_COSIGN_BINARY} + rm -rf -- "${tmpdir}" + + log_set_priority 0 +} + +run_test_case test_download_asset_failures diff --git a/test/install/environments/Dockerfile-alpine-3.6 b/test/install/environments/Dockerfile-alpine-3.6 index 69df678a6..31cfbae30 100644 --- a/test/install/environments/Dockerfile-alpine-3.6 +++ b/test/install/environments/Dockerfile-alpine-3.6 @@ -1,5 +1,9 @@ FROM alpine:3.6 RUN apk update && apk add python3 wget curl unzip make ca-certificates jq -RUN curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64" && \ - mv cosign-linux-amd64 /usr/local/bin/cosign && \ +# cosign is pinned (and checksum verified) so the install tests always run with a cosign that can verify +# sigstore bundles (releases >= v1.53.0 need v2.5.0 or newer) +ARG COSIGN_VERSION=v3.1.3 +ARG COSIGN_SHA256=4629c757b7618056f8ddd7e2625ae9fdd94c0372a65049520bc7d9df9efc7f71 +RUN curl -sSfL -o /usr/local/bin/cosign "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64" && \ + echo "${COSIGN_SHA256} /usr/local/bin/cosign" | sha256sum -c - && \ chmod +x /usr/local/bin/cosign diff --git a/test/install/environments/Dockerfile-busybox-1.36 b/test/install/environments/Dockerfile-busybox-1.36 index e2cf2d722..2f9747f90 100644 --- a/test/install/environments/Dockerfile-busybox-1.36 +++ b/test/install/environments/Dockerfile-busybox-1.36 @@ -5,17 +5,22 @@ RUN apk update && apk add ca-certificates # so let docker pull the image that matches the hosts architecture first and then pull the correct asset FROM busybox:1.36.1-musl +# cosign is pinned (and checksum verified) so the install tests always run with a cosign that can verify +# sigstore bundles (releases >= v1.53.0 need v2.5.0 or newer) +ARG COSIGN_VERSION=v3.1.3 +ARG COSIGN_SHA256_AMD64=4629c757b7618056f8ddd7e2625ae9fdd94c0372a65049520bc7d9df9efc7f71 +ARG COSIGN_SHA256_ARM64=c5d324e091826b0d7a78eb16fef316450b4eb9aaec045611c08ba06f5e73220a RUN ARCH=$(uname -m) && \ if [ "$ARCH" = "x86_64" ]; then \ - COSIGN_ARCH="amd64"; \ + COSIGN_ARCH="amd64"; COSIGN_SHA256="${COSIGN_SHA256_AMD64}"; \ elif [ "$ARCH" = "aarch64" ]; then \ - COSIGN_ARCH="arm64"; \ + COSIGN_ARCH="arm64"; COSIGN_SHA256="${COSIGN_SHA256_ARM64}"; \ else \ echo "Unsupported architecture: $ARCH" && exit 1; \ fi && \ - echo "Downloading cosign for $COSIGN_ARCH" && \ - wget https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-${COSIGN_ARCH} && \ - mv cosign-linux-${COSIGN_ARCH} /bin/cosign && \ + echo "Downloading cosign ${COSIGN_VERSION} for $COSIGN_ARCH" && \ + wget -O /bin/cosign https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-${COSIGN_ARCH} && \ + echo "${COSIGN_SHA256} /bin/cosign" | sha256sum -c - && \ chmod +x /bin/cosign COPY --from=certs /etc/ssl/certs /etc/ssl/certs diff --git a/test/install/environments/Dockerfile-ubuntu-20.04 b/test/install/environments/Dockerfile-ubuntu-20.04 index 87c09e66f..967610232 100644 --- a/test/install/environments/Dockerfile-ubuntu-20.04 +++ b/test/install/environments/Dockerfile-ubuntu-20.04 @@ -1,5 +1,9 @@ FROM --platform=linux/amd64 ubuntu:20.04@sha256:33a5cc25d22c45900796a1aca487ad7a7cb09f09ea00b779e3b2026b4fc2faba RUN apt update -y && apt install make python3 curl unzip jq -y -RUN LATEST_VERSION=$(curl https://api.github.com/repos/sigstore/cosign/releases/latest | grep tag_name | cut -d : -f2 | tr -d "v\", ") && \ - curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign_${LATEST_VERSION}_amd64.deb" && \ - dpkg -i cosign_${LATEST_VERSION}_amd64.deb +# cosign is pinned (and checksum verified) so the install tests always run with a cosign that can verify +# sigstore bundles (releases >= v1.53.0 need v2.5.0 or newer) +ARG COSIGN_VERSION=v3.1.3 +ARG COSIGN_SHA256=4629c757b7618056f8ddd7e2625ae9fdd94c0372a65049520bc7d9df9efc7f71 +RUN curl -sSfL -o /usr/local/bin/cosign "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64" && \ + echo "${COSIGN_SHA256} /usr/local/bin/cosign" | sha256sum -c - && \ + chmod +x /usr/local/bin/cosign diff --git a/test/install/test_harness.sh b/test/install/test_harness.sh index 8db34de36..e38b8b092 100644 --- a/test/install/test_harness.sh +++ b/test/install/test_harness.sh @@ -56,6 +56,31 @@ assertFileExists() { fi } +assertContains() { + haystack=$1 + needle=$2 + msg=$3 + case "$haystack" in + *"$needle"*) ;; + *) + echo "assertContains failed: '$needle' not found in '$haystack' $msg" + exit 2 + ;; + esac +} + +assertNotContains() { + haystack=$1 + needle=$2 + msg=$3 + case "$haystack" in + *"$needle"*) + echo "assertNotContains failed: '$needle' found in '$haystack' $msg" + exit 2 + ;; + esac +} + assertFilesEqual() { want=$1 got=$2 From cc326e45a6213360266dda4b30cc68095946d676 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 1 Oct 2026 15:00:50 -0400 Subject: [PATCH 09/14] fix: preserve quoted args when deferring to tagged install script (#5363) The installer word-split the original arguments when piping into a tagged release's `install.sh`, so `-b '/tmp/syft install path'` arrived as three words and the trailing word was treated as a release tag. - argument parsing moved into `parse_install_args` so `main` keeps its original args - tagged script is invoked with `"$@"` instead of the unquoted `PROGRAM_ARGS` Port of anchore/grype#3738 Signed-off-by: Alex Goodman --- install.sh | 11 +++--- test/install/5_release_script_args_test.sh | 41 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) create mode 100755 test/install/5_release_script_args_test.sh diff --git a/install.sh b/install.sh index 7fb08d0d4..88303e4f2 100755 --- a/install.sh +++ b/install.sh @@ -8,7 +8,6 @@ REPO="${PROJECT_NAME}" GITHUB_DOWNLOAD_PREFIX=https://github.com/${OWNER}/${REPO}/releases/download INSTALL_SH_BASE_URL=https://get.anchore.io/${PROJECT_NAME} LEGACY_INSTALL_SH_BASE_URL=https://raw.githubusercontent.com/${OWNER}/${PROJECT_NAME} -PROGRAM_ARGS=$@ # signature verification options @@ -791,7 +790,7 @@ prep_signature_verification() { fi } -main() ( +parse_install_args() { # parse arguments # note: never change default install directory (this must always be backwards compatible) @@ -831,6 +830,11 @@ EOF set +u tag=$1 + set -u +} + +main() ( + parse_install_args "$@" if [ -z "${tag}" ]; then log_info "checking github for the current release tag" @@ -838,7 +842,6 @@ EOF else log_info "checking github for release tag='${tag}'" fi - set -u if ! tag=$(get_release_tag "${OWNER}" "${REPO}" "${tag}"); then log_err "unable to find tag='${tag}'" @@ -869,7 +872,7 @@ EOF log_warn "failed to fetch from ${INSTALL_SH_BASE_URL}, trying fallback URL" install_script=$(http_copy "${LEGACY_INSTALL_SH_BASE_URL}/${tag}/install.sh" "") fi - echo "${install_script}" | sh -s -- ${PROGRAM_ARGS} + echo "${install_script}" | sh -s -- "$@" exit $? fi diff --git a/test/install/5_release_script_args_test.sh b/test/install/5_release_script_args_test.sh new file mode 100755 index 000000000..1093b7f66 --- /dev/null +++ b/test/install/5_release_script_args_test.sh @@ -0,0 +1,41 @@ +. test_harness.sh + +# make certain main defers to the (stubbed) tagged release script regardless of the caller's env +DOWNLOAD_TAG_INSTALL_SCRIPT=true + +get_release_tag() { + printf '%s\n' "$3" +} + +prep_signature_verification() { + return 0 +} + +# the "tagged release script" records each argument it receives, one bracketed arg per line +http_copy() { + cat <<'SCRIPT' +printf '[%s]\n' "$@" > "$INSTALL_ARG_CAPTURE" +SCRIPT +} + +test_release_script_receives_original_args() { + test_dir=$(mktemp -d) + INSTALL_ARG_CAPTURE="${test_dir}/actual" + export INSTALL_ARG_CAPTURE + + # note: paths are read here instead of passed to run_test_case, which word-splits its arguments + while IFS= read -r install_path; do + main -b "${install_path}" -d v0.80.0 + + printf '[%s]\n' -b "${install_path}" -d v0.80.0 > "${test_dir}/expected" + assertFilesEqual "${test_dir}/expected" "${INSTALL_ARG_CAPTURE}" "args forwarded to the release script should match the original args (path='${install_path}')" + done <<'PATHS' +/tmp/bin with spaces +/tmp/bin* +/tmp/it's "quoted" +PATHS + + rm -rf -- "${test_dir}" +} + +run_test_case test_release_script_receives_original_args From c33fa32cf100ae1094f13dd977ce7520717b486b Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Thu, 1 Oct 2026 17:21:09 -0400 Subject: [PATCH 10/14] fix(dotnet): resolve packages.lock dependencies deterministically (#5210) * fix(dotnet): resolve packages.lock dependencies within their target framework Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> * fix(dotnet): make packages.lock package metadata deterministic across target frameworks Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> * fix: parse packages lock tests Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> * fix: update lockfile parse to read into stable structure Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> * fix: remove stale comments Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> * test: new test for project entry case Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> * fix(dotnet): key packages.lock packages case-insensitively NuGet IDs are case-insensitive, and edge lookup already treated them that way, but package identity used the exact-case name. Two spellings of one ID and version across target frameworks became two packages, and type precedence never ran between them. - packages merge on the lowercased name + version, display name comes from the winning entry - a package listing itself (under any casing) no longer produces a self edge Signed-off-by: Alex Goodman --------- Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Signed-off-by: Alex Goodman Co-authored-by: Alex Goodman --- .../cataloger/dotnet/parse_packages_lock.go | 255 ++++++++++----- .../dotnet/parse_packages_lock_test.go | 304 ++++++++++++++++++ .../packages.lock-multi-framework.json | 67 ++++ .../packages.lock-project-reference.json | 18 ++ 4 files changed, 572 insertions(+), 72 deletions(-) create mode 100644 syft/pkg/cataloger/dotnet/testdata/packages.lock-multi-framework.json create mode 100644 syft/pkg/cataloger/dotnet/testdata/packages.lock-project-reference.json diff --git a/syft/pkg/cataloger/dotnet/parse_packages_lock.go b/syft/pkg/cataloger/dotnet/parse_packages_lock.go index 3348d5a0f..685f60eed 100644 --- a/syft/pkg/cataloger/dotnet/parse_packages_lock.go +++ b/syft/pkg/cataloger/dotnet/parse_packages_lock.go @@ -4,8 +4,9 @@ import ( "context" "encoding/json" "fmt" + "maps" "slices" - "sort" + "strings" "github.com/anchore/packageurl-go" "github.com/anchore/syft/internal/log" @@ -18,6 +19,8 @@ import ( var _ generic.Parser = parseDotnetPackagesLock +var packagesLockTypePrecedence = []string{"Project", "Direct", "CentralTransitive", "Transitive"} + type dotnetPackagesLock struct { Version int `json:"version"` Dependencies map[string]map[string]dotnetPackagesLockDep `json:"dependencies"` @@ -31,11 +34,38 @@ type dotnetPackagesLockDep struct { Dependencies map[string]string `json:"dependencies,omitempty"` } -func parseDotnetPackagesLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { //nolint:funlen - var pkgs []pkg.Package - var pkgMap = make(map[string]pkg.Package) - var relationships []artifact.Relationship +type packagesLockEntry struct { + name string + dep dotnetPackagesLockDep +} +func (e packagesLockEntry) nameVersion() string { + return createNameAndVersion(e.name, e.dep.Resolved) +} + +// key identifies the package an entry describes. NuGet package IDs are case-insensitive, so two spellings of the +// same ID and version are one package. +func (e packagesLockEntry) key() string { + return createNameAndVersion(strings.ToLower(e.name), e.dep.Resolved) +} + +type packagesLockFramework struct { + name string + entries []packagesLockEntry + // byName is keyed by the lowercased package name, since NuGet package IDs are case-insensitive. + byName map[string]packagesLockEntry +} + +func (f packagesLockFramework) find(name string) (packagesLockEntry, bool) { + e, ok := f.byName[strings.ToLower(name)] + return e, ok +} + +type packagesLockEdge struct { + child, parent artifact.ID +} + +func parseDotnetPackagesLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { dec := json.NewDecoder(reader) // unmarshal file @@ -44,70 +74,21 @@ func parseDotnetPackagesLock(_ context.Context, _ file.Resolver, _ *generic.Envi return nil, nil, fmt.Errorf("failed to parse packages.lock.json file: %w", err) } - // collect all deps here - allDependencies := make(map[string]dotnetPackagesLockDep) - - var names []string - for _, dependencies := range lockFile.Dependencies { - for name, dep := range dependencies { - depNameVersion := createNameAndVersion(name, dep.Resolved) - - if slices.Contains(names, depNameVersion) { - continue - } - - names = append(names, depNameVersion) - allDependencies[depNameVersion] = dep - } - } - - // sort the names so that the order of the packages is deterministic - sort.Strings(names) + frameworks := newPackagesLockFrameworks(lockFile) // create artifact for each pkg - for _, nameVersion := range names { - name, _ := extractNameAndVersion(nameVersion) + var pkgs []pkg.Package + pkgMap := make(map[string]pkg.Package) - dep := allDependencies[nameVersion] - dotnetPkg := newDotnetPackagesLockPackage(name, dep, reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)) + for _, entry := range mergePackagesLockEntries(frameworks) { + dotnetPkg := newDotnetPackagesLockPackage(entry.name, entry.dep, reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)) if dotnetPkg != nil { pkgs = append(pkgs, *dotnetPkg) - pkgMap[nameVersion] = *dotnetPkg + pkgMap[entry.key()] = *dotnetPkg } } - // fill up relationships - for depNameVersion, dep := range allDependencies { - parentPkg, ok := pkgMap[depNameVersion] - if !ok { - log.Debugf("package \"%s\" not found in map of all packages", depNameVersion) - continue - } - - for childDepName, childDepVersion := range dep.Dependencies { - childDepNameVersion := createNameAndVersion(childDepName, childDepVersion) - - // try and find pkg for dependency with exact name and version - childPkg, ok := pkgMap[childDepNameVersion] - if !ok { - // no exact match found, lets match on name only, lockfile will contain other version of pkg - cpkg, ok := findPkgByName(childDepName, pkgMap) - if !ok { - log.Debugf("dependency \"%s\" of package \"%s\" not found in map of all packages", childDepNameVersion, depNameVersion) - continue - } - - childPkg = *cpkg - } - - rel := artifact.Relationship{ - From: childPkg, - To: parentPkg, - Type: artifact.DependencyOfRelationship, - } - relationships = append(relationships, rel) - } - } + relationships := packagesLockRelationships(frameworks, pkgMap) // sort the relationships for deterministic output relationship.Sort(relationships) @@ -115,6 +96,147 @@ func parseDotnetPackagesLock(_ context.Context, _ file.Resolver, _ *generic.Envi return pkgs, relationships, nil } +func newPackagesLockFrameworks(lockFile dotnetPackagesLock) []packagesLockFramework { + var frameworks []packagesLockFramework + + for _, frameworkName := range slices.Sorted(maps.Keys(lockFile.Dependencies)) { + deps := lockFile.Dependencies[frameworkName] + framework := packagesLockFramework{ + name: frameworkName, + byName: make(map[string]packagesLockEntry, len(deps)), + } + + for _, name := range slices.Sorted(maps.Keys(deps)) { + entry := packagesLockEntry{name: name, dep: deps[name]} + framework.entries = append(framework.entries, entry) + framework.byName[strings.ToLower(name)] = entry + } + + frameworks = append(frameworks, framework) + } + + return frameworks +} + +func mergePackagesLockEntries(frameworks []packagesLockFramework) []packagesLockEntry { + merged := make(map[string]packagesLockEntry) + + for _, framework := range frameworks { + for _, entry := range framework.entries { + key := entry.key() + if existing, ok := merged[key]; ok && !isMoreDirect(entry.dep.Type, existing.dep.Type) { + continue + } + merged[key] = entry + } + } + + var entries []packagesLockEntry + for _, key := range slices.Sorted(maps.Keys(merged)) { + entries = append(entries, merged[key]) + } + + return entries +} + +func isMoreDirect(a, b string) bool { + rankA, rankB := packagesLockTypeRank(a), packagesLockTypeRank(b) + if rankA != rankB { + return rankA < rankB + } + return a < b +} + +func packagesLockTypeRank(t string) int { + if i := slices.Index(packagesLockTypePrecedence, t); i >= 0 { + return i + } + return len(packagesLockTypePrecedence) +} + +func packagesLockRelationships(frameworks []packagesLockFramework, pkgMap map[string]pkg.Package) []artifact.Relationship { + var relationships []artifact.Relationship + + frameworksByName := make(map[string]packagesLockFramework, len(frameworks)) + for _, framework := range frameworks { + frameworksByName[framework.name] = framework + } + + seen := make(map[packagesLockEdge]struct{}) + for _, framework := range frameworks { + base, hasBase := frameworksByName[baseFrameworkName(framework.name)] + if !hasBase || base.name == framework.name { + base = packagesLockFramework{} + } + + for _, entry := range framework.entries { + parentPkg, ok := pkgMap[entry.key()] + if !ok { + log.Debugf("package %q not found in map of all packages", entry.nameVersion()) + continue + } + + relationships = append(relationships, packagesLockEntryRelationships(entry, parentPkg, framework, base, pkgMap, seen)...) + } + } + + return relationships +} + +func packagesLockEntryRelationships(entry packagesLockEntry, parentPkg pkg.Package, framework, base packagesLockFramework, pkgMap map[string]pkg.Package, seen map[packagesLockEdge]struct{}) []artifact.Relationship { + var relationships []artifact.Relationship + + for _, childName := range slices.Sorted(maps.Keys(entry.dep.Dependencies)) { + child, ok := findPackagesLockDependency(childName, framework, base) + if !ok { + log.Debugf("dependency %q of package %q not found under target framework %q", childName, entry.nameVersion(), framework.name) + continue + } + + childPkg, ok := pkgMap[child.key()] + if !ok { + log.Debugf("package %q not found in map of all packages", child.nameVersion()) + continue + } + + // a package listing itself (possibly under another casing) is not a dependency + if childPkg.ID() == parentPkg.ID() { + continue + } + + key := packagesLockEdge{child: childPkg.ID(), parent: parentPkg.ID()} + if _, exists := seen[key]; exists { + continue + } + seen[key] = struct{}{} + + relationships = append(relationships, artifact.Relationship{ + From: childPkg, + To: parentPkg, + Type: artifact.DependencyOfRelationship, + }) + } + + return relationships +} + +// findPackagesLockDependency finds the entry a dependency edge points at. The version on an edge is only the lower +// bound of a version range, so the edge resolves to whatever version its target framework resolved. Runtime-specific +// sections such as "net8.0/win-x64" may reference packages listed only in their base framework ("net8.0"). An edge +// that resolves nowhere is dropped rather than guessed from another target framework. +func findPackagesLockDependency(name string, framework, base packagesLockFramework) (packagesLockEntry, bool) { + if entry, ok := framework.find(name); ok { + return entry, true + } + return base.find(name) +} + +// baseFrameworkName returns the target framework of a runtime-specific section, such as "net8.0" for "net8.0/win-x64". +func baseFrameworkName(frameworkName string) string { + name, _, _ := strings.Cut(frameworkName, "/") + return name +} + func newDotnetPackagesLockPackage(name string, dep dotnetPackagesLockDep, locations ...file.Location) *pkg.Package { metadata := pkg.DotnetPackagesLockEntry{ Name: name, @@ -150,14 +272,3 @@ func packagesLockPackageURL(name, version string) string { "", ).ToString() } - -func findPkgByName(pkgName string, pkgMap map[string]pkg.Package) (*pkg.Package, bool) { - for pkgNameVersion, pkg := range pkgMap { - name, _ := extractNameAndVersion(pkgNameVersion) - if name == pkgName { - return &pkg, true - } - } - - return nil, false -} diff --git a/syft/pkg/cataloger/dotnet/parse_packages_lock_test.go b/syft/pkg/cataloger/dotnet/parse_packages_lock_test.go index 24ce33454..ffdf09892 100644 --- a/syft/pkg/cataloger/dotnet/parse_packages_lock_test.go +++ b/syft/pkg/cataloger/dotnet/parse_packages_lock_test.go @@ -3,6 +3,9 @@ package dotnet import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" @@ -197,3 +200,304 @@ func TestParseDotnetPackagesLock(t *testing.T) { pkgtest.TestFileParser(t, fixture, parseDotnetPackagesLock, expectedPkgs, expectedRelationships) } + +func TestParseDotnetPackagesLock_multipleTargetFrameworks(t *testing.T) { + fixture := "testdata/packages.lock-multi-framework.json" + fixtureLocationSet := file.NewLocationSet(file.NewLocation(fixture)) + + myLibPkg := pkg.Package{ + Name: "MyLib", + Version: "1.0.0", + PURL: "pkg:nuget/MyLib@1.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + Metadata: pkg.DotnetPackagesLockEntry{ + Name: "MyLib", + Version: "1.0.0", + ContentHash: "mylibhash==", + Type: "Direct", + }, + } + + log4net2Pkg := pkg.Package{ + Name: "log4net", + Version: "2.0.5", + PURL: "pkg:nuget/log4net@2.0.5", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + Metadata: pkg.DotnetPackagesLockEntry{ + Name: "log4net", + Version: "2.0.5", + ContentHash: "log4net205hash==", + Type: "Transitive", + }, + } + + log4net1Pkg := pkg.Package{ + Name: "log4net", + Version: "1.2.15", + PURL: "pkg:nuget/log4net@1.2.15", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + Metadata: pkg.DotnetPackagesLockEntry{ + Name: "log4net", + Version: "1.2.15", + ContentHash: "log4net1215hash==", + Type: "Transitive", + }, + } + + // resolved to the same version under both frameworks (spelled "newtonsoft.json" under one), but declared "Direct" by + // only one of them + newtonsoftPkg := pkg.Package{ + Name: "Newtonsoft.Json", + Version: "13.0.3", + PURL: "pkg:nuget/Newtonsoft.Json@13.0.3", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + Metadata: pkg.DotnetPackagesLockEntry{ + Name: "Newtonsoft.Json", + Version: "13.0.3", + ContentHash: "newtonsoft1303hash==", + Type: "Direct", + }, + } + + // only listed under the runtime-specific "net8.0/win-x64" section, and depends on a package from "net8.0" + myLibNativePkg := pkg.Package{ + Name: "runtime.win-x64.MyLib.Native", + Version: "1.0.0", + PURL: "pkg:nuget/runtime.win-x64.MyLib.Native@1.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + Metadata: pkg.DotnetPackagesLockEntry{ + Name: "runtime.win-x64.MyLib.Native", + Version: "1.0.0", + ContentHash: "mylibnativehash==", + Type: "Transitive", + }, + } + + // only listed under "net8.0", so the "netstandard2.0" edge to it must be dropped rather than guessed + serilogPkg := pkg.Package{ + Name: "Serilog", + Version: "3.1.1", + PURL: "pkg:nuget/Serilog@3.1.1", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + Metadata: pkg.DotnetPackagesLockEntry{ + Name: "Serilog", + Version: "3.1.1", + ContentHash: "serilog311hash==", + Type: "Transitive", + }, + } + + expectedPkgs := []pkg.Package{ + myLibPkg, + newtonsoftPkg, + log4net1Pkg, + log4net2Pkg, + myLibNativePkg, + serilogPkg, + } + + // the same package is resolved to a different version per target framework, so both edges must be captured, while + // the Newtonsoft.Json edge declared by both frameworks must appear only once + expectedRelationships := []artifact.Relationship{ + { + From: log4net1Pkg, + To: myLibPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: log4net2Pkg, + To: myLibPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: log4net2Pkg, + To: myLibNativePkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: newtonsoftPkg, + To: myLibPkg, + Type: artifact.DependencyOfRelationship, + }, + } + + pkgtest.TestFileParser(t, fixture, parseDotnetPackagesLock, expectedPkgs, expectedRelationships) +} + +func TestParseDotnetPackagesLock_projectReference(t *testing.T) { + fixture := "testdata/packages.lock-project-reference.json" + fixtureLocationSet := file.NewLocationSet(file.NewLocation(fixture)) + + newtonsoftPkg := pkg.Package{ + Name: "Newtonsoft.Json", + Version: "13.0.3", + PURL: "pkg:nuget/Newtonsoft.Json@13.0.3", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + Metadata: pkg.DotnetPackagesLockEntry{ + Name: "Newtonsoft.Json", + Version: "13.0.3", + ContentHash: "newtonsoft1303hash==", + Type: "Transitive", + }, + } + + // a project reference has no resolved version or content hash + myLibPkg := pkg.Package{ + Name: "mylib", + PURL: "pkg:nuget/mylib", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + Metadata: pkg.DotnetPackagesLockEntry{ + Name: "mylib", + Type: "Project", + }, + } + + expectedPkgs := []pkg.Package{ + newtonsoftPkg, + myLibPkg, + } + + // project references declare their dependencies as version ranges, which resolve to the version the target + // framework resolved + expectedRelationships := []artifact.Relationship{ + { + From: newtonsoftPkg, + To: myLibPkg, + Type: artifact.DependencyOfRelationship, + }, + } + + pkgtest.TestFileParser(t, fixture, parseDotnetPackagesLock, expectedPkgs, expectedRelationships) +} + +func Test_findPackagesLockDependency(t *testing.T) { + frameworks := newPackagesLockFrameworks(dotnetPackagesLock{ + Dependencies: map[string]map[string]dotnetPackagesLockDep{ + "net8.0": { + "log4net": {Resolved: "2.0.5"}, + }, + "net8.0/win-x64": { + "runtime.win-x64.Native": {Resolved: "1.0.0"}, + }, + "netstandard2.0": { + "log4net": {Resolved: "1.2.15"}, + "Newtonsoft.Json": {Resolved: "13.0.3"}, + }, + }, + }) + net8, rid, netstandard := frameworks[0], frameworks[1], frameworks[2] + + tests := []struct { + name string + depName string + framework packagesLockFramework + base packagesLockFramework + wantVersion string + wantFound bool + }{ + { + name: "resolves to the version pinned by its own target framework", + depName: "log4net", + framework: netstandard, + wantVersion: "1.2.15", + wantFound: true, + }, + { + name: "package names are matched case-insensitively", + depName: "LOG4NET", + framework: net8, + wantVersion: "2.0.5", + wantFound: true, + }, + { + name: "runtime-specific sections fall back to their base target framework", + depName: "log4net", + framework: rid, + base: net8, + wantVersion: "2.0.5", + wantFound: true, + }, + { + name: "a package absent from the target framework is not guessed from another one", + depName: "Newtonsoft.Json", + framework: net8, + wantFound: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, found := findPackagesLockDependency(tt.depName, tt.framework, tt.base) + require.Equal(t, tt.wantFound, found) + if !tt.wantFound { + return + } + assert.Equal(t, tt.wantVersion, got.dep.Resolved) + }) + } +} + +func Test_mergePackagesLockEntries_typePrecedence(t *testing.T) { + tests := []struct { + name string + types map[string]string // target framework -> type + wantType string + }{ + { + name: "direct wins over transitive", + types: map[string]string{"net8.0": "Transitive", "netstandard2.0": "Direct"}, + wantType: "Direct", + }, + { + name: "central transitive wins over transitive regardless of framework order", + types: map[string]string{"net8.0": "Transitive", "netstandard2.0": "CentralTransitive"}, + wantType: "CentralTransitive", + }, + { + name: "central transitive wins over transitive in the reverse framework order", + types: map[string]string{"net8.0": "CentralTransitive", "netstandard2.0": "Transitive"}, + wantType: "CentralTransitive", + }, + { + name: "direct wins over central transitive", + types: map[string]string{"net472": "CentralTransitive", "net8.0": "Direct", "netstandard2.0": "Transitive"}, + wantType: "Direct", + }, + { + name: "known types win over unknown ones", + types: map[string]string{"net8.0": "Unknown", "netstandard2.0": "Transitive"}, + wantType: "Transitive", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + lockFile := dotnetPackagesLock{Dependencies: map[string]map[string]dotnetPackagesLockDep{}} + for framework, depType := range tt.types { + lockFile.Dependencies[framework] = map[string]dotnetPackagesLockDep{ + "Newtonsoft.Json": {Type: depType, Resolved: "13.0.3"}, + } + } + + entries := mergePackagesLockEntries(newPackagesLockFrameworks(lockFile)) + require.Len(t, entries, 1) + assert.Equal(t, tt.wantType, entries[0].dep.Type) + }) + } +} diff --git a/syft/pkg/cataloger/dotnet/testdata/packages.lock-multi-framework.json b/syft/pkg/cataloger/dotnet/testdata/packages.lock-multi-framework.json new file mode 100644 index 000000000..e9d4dd200 --- /dev/null +++ b/syft/pkg/cataloger/dotnet/testdata/packages.lock-multi-framework.json @@ -0,0 +1,67 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "MyLib": { + "type": "Direct", + "requested": "[1.0.0, )", + "resolved": "1.0.0", + "contentHash": "mylibhash==", + "dependencies": { + "log4net": "2.0.5", + "Newtonsoft.Json": "13.0.3" + } + }, + "log4net": { + "type": "Transitive", + "resolved": "2.0.5", + "contentHash": "log4net205hash==" + }, + "Newtonsoft.Json": { + "type": "Direct", + "requested": "[13.0.3, )", + "resolved": "13.0.3", + "contentHash": "newtonsoft1303hash==" + }, + "Serilog": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "serilog311hash==" + } + }, + "net8.0/win-x64": { + "runtime.win-x64.MyLib.Native": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "mylibnativehash==", + "dependencies": { + "log4net": "2.0.0", + "RUNTIME.WIN-X64.MYLIB.NATIVE": "1.0.0" + } + } + }, + "netstandard2.0": { + "MyLib": { + "type": "Direct", + "requested": "[1.0.0, )", + "resolved": "1.0.0", + "contentHash": "mylibhash==", + "dependencies": { + "log4net": "1.2.15", + "newtonsoft.json": "13.0.3", + "Serilog": "3.1.1" + } + }, + "log4net": { + "type": "Transitive", + "resolved": "1.2.15", + "contentHash": "log4net1215hash==" + }, + "newtonsoft.json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "newtonsoft1303hash==" + } + } + } +} diff --git a/syft/pkg/cataloger/dotnet/testdata/packages.lock-project-reference.json b/syft/pkg/cataloger/dotnet/testdata/packages.lock-project-reference.json new file mode 100644 index 000000000..e09cfe2b0 --- /dev/null +++ b/syft/pkg/cataloger/dotnet/testdata/packages.lock-project-reference.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "newtonsoft1303hash==" + }, + "mylib": { + "type": "Project", + "dependencies": { + "Newtonsoft.Json": "[13.0.3, )" + } + } + } + } +} From bc68299565d1d2bc7f54159db2f1eb5567144aff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 13:30:02 +0000 Subject: [PATCH 11/14] chore(deps): bump github.com/klauspost/compress from 1.20.0 to 1.20.1 (#5369) Bumps [github.com/klauspost/compress](https://github.com/klauspost/compress) from 1.20.0 to 1.20.1. - [Release notes](https://github.com/klauspost/compress/releases) - [Commits](https://github.com/klauspost/compress/compare/v1.20.0...v1.20.1) --- updated-dependencies: - dependency-name: github.com/klauspost/compress dependency-version: 1.20.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 253dc7d7d..0099ae96f 100644 --- a/go.mod +++ b/go.mod @@ -62,7 +62,7 @@ require ( github.com/jedib0t/go-pretty/v6 v6.8.3 github.com/jinzhu/copier v0.4.0 github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 - github.com/klauspost/compress v1.20.0 + github.com/klauspost/compress v1.20.1 github.com/magiconair/properties v1.18.12 github.com/mholt/archives v0.1.5 github.com/moby/sys/mountinfo v0.7.2 diff --git a/go.sum b/go.sum index ebc5d20cf..02cee8380 100644 --- a/go.sum +++ b/go.sum @@ -666,8 +666,8 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF 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.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA= -github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= +github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ= +github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= From 6d2493bae42f8a04d29b1395552d864643d0e041 Mon Sep 17 00:00:00 2001 From: anchore-oss-update-bot Date: Sun, 4 Oct 2026 21:52:49 -0400 Subject: [PATCH 12/14] chore(deps): update CPE dictionary index (#5375) Signed-off-by: anchore-oss-update-bot Co-authored-by: anchore-oss-update-bot --- .../dictionary/data/cpe-index.json | 208 +++++++++++++++++- 1 file changed, 203 insertions(+), 5 deletions(-) diff --git a/syft/pkg/cataloger/internal/cpegenerate/dictionary/data/cpe-index.json b/syft/pkg/cataloger/internal/cpegenerate/dictionary/data/cpe-index.json index baa74273d..5361f9932 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/dictionary/data/cpe-index.json +++ b/syft/pkg/cataloger/internal/cpegenerate/dictionary/data/cpe-index.json @@ -3834,11 +3834,18 @@ "cpe:2.3:a:gaoxuyan_project:gaoxuyan:*:*:*:*:*:*:*:*" ], "gatsby-plugin-mdx": [ + "cpe:2.3:a:gatsbyjs:gatsby-plugin-mdx:*:*:*:*:*:node.js:*:*", "cpe:2.3:a:gatsbyjs:gatsby:*:*:*:*:*:node.js:*:*" ], + "gatsby-plugin-sharp": [ + "cpe:2.3:a:gatsbyjs:gatsby-plugin-sharp:*:*:*:*:*:node.js:*:*" + ], "gatsby-source-wordpress": [ "cpe:2.3:a:gatsbyjs:gatsby-source-wordpress:*:*:*:*:*:node.js:*:*" ], + "gatsby-transformer-remark": [ + "cpe:2.3:a:gatsbyjs:gatsby-transformer-remark:*:*:*:*:*:node.js:*:*" + ], "gedi": [ "cpe:2.3:a:gedi_project:gedi:*:*:*:*:*:node.js:*:*" ], @@ -6265,6 +6272,9 @@ "vega-functions": [ "cpe:2.3:a:vega-functions_project:vega-functions:*:*:*:*:*:node.js:*:*" ], + "vega-selections": [ + "cpe:2.3:a:vega_project:vega:*:*:*:*:*:node.js:*:*" + ], "velocityjs": [ "cpe:2.3:a:shepherdwind:velocity.js:*:*:*:*:*:node.js:*:*" ], @@ -6975,6 +6985,9 @@ "openlit": [ "cpe:2.3:a:openlit:openlit_software_development_kit:*:*:*:*:*:python:*:*" ], + "openml": [ + "cpe:2.3:a:openml:openml:*:*:*:*:*:python:*:*" + ], "openpyxl": [ "cpe:2.3:a:python:openpyxl:*:*:*:*:*:*:*:*" ], @@ -8796,6 +8809,9 @@ "adaptive-images": [ "cpe:2.3:a:nevma:adaptive_images:*:*:*:*:*:wordpress:*:*" ], + "adbuddy-adblocker-detection": [ + "cpe:2.3:a:netfunkdesign:adbuddy\\+_\\(adblocker_detection\\):*:*:*:*:*:wordpress:*:*" + ], "add-actions-and-filters": [ "cpe:2.3:a:add_shortcodes_actions_and_filters_project:add_shortcodes_actions_and_filters:*:*:*:*:*:wordpress:*:*" ], @@ -8948,7 +8964,8 @@ "cpe:2.3:a:niceit:advance_post_prefix:*:*:*:*:*:wordpress:*:*" ], "advance-search": [ - "cpe:2.3:a:advance_search_project:advance_search:*:*:*:*:*:wordpress:*:*" + "cpe:2.3:a:advance_search_project:advance_search:*:*:*:*:*:wordpress:*:*", + "cpe:2.3:a:mndpsingh287:advanced_search:*:*:*:*:*:wordpress:*:*" ], "advanced-access-manager": [ "cpe:2.3:a:vasyltech:advanced_access_manager:*:*:*:*:*:wordpress:*:*" @@ -9098,6 +9115,9 @@ "agp-font-awesome-collection": [ "cpe:2.3:a:profosbox:agp_font_awesome_collection:*:*:*:*:*:wordpress:*:*" ], + "ahathat": [ + "cpe:2.3:a:mitchelllevy:ahathat:*:*:*:*:*:wordpress:*:*" + ], "ai-assistant-by-10web": [ "cpe:2.3:a:10web:ai_assistant:*:*:*:*:*:wordpress:*:*" ], @@ -9524,6 +9544,9 @@ "asf-allow-svg-files": [ "cpe:2.3:a:allow_svg_files_project:allow_svg_files:*:*:*:*:*:wordpress:*:*" ], + "asgard": [ + "cpe:2.3:a:outself:asgard_security_scanner:*:*:*:*:*:wordpress:*:*" + ], "asgaros-forum": [ "cpe:2.3:a:asgaros:asgaros_forum:*:*:*:*:*:wordpress:*:*" ], @@ -9803,6 +9826,9 @@ "banner-effect-header": [ "cpe:2.3:a:banner_effect_header_project:banner_effect_header:*:*:*:*:*:wordpress:*:*" ], + "banner-garden": [ + "cpe:2.3:a:karacsi_maci:banner_garden:*:*:*:*:*:wordpress:*:*" + ], "banner-management-for-woocommerce": [ "cpe:2.3:a:dotstore:woocommerce_category_banner_management:*:*:*:*:*:wordpress:*:*", "cpe:2.3:a:multidots:banner_management_for_woocommerce:*:*:*:*:*:wordpress:*:*" @@ -10077,6 +10103,9 @@ "blue-admin": [ "cpe:2.3:a:blue-admin_project:blue-admin:*:*:*:*:*:wordpress:*:*" ], + "blue-triad-ezanalytics": [ + "cpe:2.3:a:jbahlquist:blue_triad_ezanalytics:*:*:*:*:*:wordpress:*:*" + ], "bluet-keywords-tooltip-generator": [ "cpe:2.3:a:tooltipy:tooltipy:*:*:*:*:*:wordpress:*:*" ], @@ -10334,6 +10363,9 @@ "bulk-image-resizer": [ "cpe:2.3:a:giuliopanda:bulk_images_optimizer:*:*:*:*:*:wordpress:*:*" ], + "bulk-me-now": [ + "cpe:2.3:a:ombu:bulk_me_now\\!:*:*:*:*:*:wordpress:*:*" + ], "bulk-noindex-nofollow-toolkit-by-mad-fish": [ "cpe:2.3:a:madfishdigital:bulk_noindex_\\\u0026_nofollow_toolkit:*:*:*:*:*:wordpress:*:*" ], @@ -10355,6 +10387,9 @@ "business": [ "cpe:2.3:a:snilesh:business:*:*:*:*:*:wordpress:*:*" ], + "business-card-by-esterox-100": [ + "cpe:2.3:a:esterox:business_card:*:*:*:*:*:wordpress:*:*" + ], "business-directory-plugin": [ "cpe:2.3:a:businessdirectoryplugin:business_directory:*:*:*:*:*:wordpress:*:*", "cpe:2.3:a:strategy11:business_directory_plugin_-_easy_listing_directories:*:*:*:*:*:wordpress:*:*" @@ -10542,7 +10577,8 @@ "cpe:2.3:a:vinojcardoza:ajax_post_search:*:*:*:*:*:wordpress:*:*" ], "cardoza-facebook-like-box": [ - "cpe:2.3:a:facebook_like_box_project:facebook_like_box:*:*:*:*:*:wordpress:*:*" + "cpe:2.3:a:facebook_like_box_project:facebook_like_box:*:*:*:*:*:wordpress:*:*", + "cpe:2.3:a:johnnash1975:easy_social_like_box_popup_sidebar_widget:*:*:*:*:*:wordpress:*:*" ], "cardoza-wordpress-poll": [ "cpe:2.3:a:cardozatechnologies:wordpress_poll:*:*:*:*:*:wordpress:*:*", @@ -11201,6 +11237,9 @@ "contact-list": [ "cpe:2.3:a:tammersoft:contact_list:*:*:*:*:*:wordpress:*:*" ], + "contact-us-by-lord-linus": [ + "cpe:2.3:a:lordlinus:contact_us:*:*:*:*:*:wordpress:*:*" + ], "contact-us-page-contact-people": [ "cpe:2.3:a:a3rev:contact_us_page_-_contact_people:*:*:*:*:*:wordpress:*:*" ], @@ -11371,6 +11410,9 @@ "countdown-builder": [ "cpe:2.3:a:edmonsoft:countdown_builder:*:*:*:*:*:wordpress:*:*" ], + "countdown-timer-block": [ + "cpe:2.3:a:flickdevs:countdown_timer_for_wordpress_block_editor:*:*:*:*:*:wordpress:*:*" + ], "countdown-timer-for-elementor": [ "cpe:2.3:a:flickdevs:countdown_timer_for_elementor:*:*:*:*:*:wordpress:*:*" ], @@ -11575,6 +11617,9 @@ "cpe:2.3:a:bestwebsoft:custom_admin:*:*:*:*:*:wordpress:*:*", "cpe:2.3:a:bestwebsoft:custom_admin_page:*:*:*:*:*:wordpress:*:*" ], + "custom-author-base": [ + "cpe:2.3:a:jfarthing:custom_author_base:*:*:*:*:*:wordpress:*:*" + ], "custom-banners": [ "cpe:2.3:a:goldplugins:custom_banners:*:*:*:*:*:wordpress:*:*" ], @@ -11651,6 +11696,9 @@ "custom-options-plus": [ "cpe:2.3:a:leocaseiro:custom_options_plus:*:*:*:*:*:wordpress:*:*" ], + "custom-order-statuses-for-woocommerce": [ + "cpe:2.3:a:nuggethon:custom_order_status_manager_for_woocommerce:*:*:*:*:*:wordpress:*:*" + ], "custom-permalinks": [ "cpe:2.3:a:samiahmedsiddiqui:custom_permalinks:*:*:*:*:*:wordpress:*:*" ], @@ -11882,6 +11930,9 @@ "demon-image-annotation": [ "cpe:2.3:a:superwhite:demon_image_annotation:*:*:*:*:*:wordpress:*:*" ], + "dental-optimizer-patient-generator-app": [ + "cpe:2.3:a:healthygrid:dental_optimizer_patient_generator_app:*:*:*:*:*:wordpress:*:*" + ], "deny-all-firewall": [ "cpe:2.3:a:deny_all_firewall_project:deny_all_firewall:*:*:*:*:*:wordpress:*:*" ], @@ -12028,6 +12079,9 @@ "donorbox-donation-form": [ "cpe:2.3:a:donorbox:donorbox:*:*:*:*:*:wordpress:*:*" ], + "dont-muck-my-markup": [ + "cpe:2.3:a:martynchamberlin:dont_muck_my_markup:*:*:*:*:*:wordpress:*:*" + ], "doofinder-for-woocommerce": [ "cpe:2.3:a:doofinder:doofinder:*:*:*:*:*:wordpress:*:*" ], @@ -12397,6 +12451,9 @@ "ecpay-ecommerce-for-woocommerce": [ "cpe:2.3:a:ecpay:ecpay_ecommerce_for_woocommerce:*:*:*:*:*:wordpress:*:*" ], + "ect-homepage-products": [ + "cpe:2.3:a:etemplates:ect_home_page_products:*:*:*:*:*:wordpress:*:*" + ], "ecwid-shopping-cart": [ "cpe:2.3:a:lightspeedhq:ecwid_ecommerce_shopping_cart:*:*:*:*:*:wordpress:*:*" ], @@ -13127,7 +13184,8 @@ "cpe:2.3:a:mehanoid:flatpm:*:*:*:*:*:wordpress:*:*" ], "flattr": [ - "cpe:2.3:a:fkrauthan:wp-mpdf:*:*:*:*:*:wordpress:*:*" + "cpe:2.3:a:fkrauthan:wp-mpdf:*:*:*:*:*:wordpress:*:*", + "cpe:2.3:a:flattr:flattr:*:*:*:*:*:wordpress:*:*" ], "flexi": [ "cpe:2.3:a:odude:flexi:*:*:*:*:*:wordpress:*:*" @@ -13160,6 +13218,9 @@ "flipping-cards": [ "cpe:2.3:a:info-d-74:flipping_cards:*:*:*:*:free:wordpress:*:*" ], + "float-block": [ + "cpe:2.3:a:computy:float_block:*:*:*:*:*:wordpress:*:*" + ], "float-menu": [ "cpe:2.3:a:wow-company:float_menu:*:*:*:*:*:wordpress:*:*" ], @@ -13422,6 +13483,9 @@ "full-customer": [ "cpe:2.3:a:full:full_-_customer:*:*:*:*:*:wordpress:*:*" ], + "full-screen-page-background-image-slideshow": [ + "cpe:2.3:a:mohsinrasool:full_screen_\\(page\\)_background_image_slideshow:*:*:*:*:*:wordpress:*:*" + ], "full-site-editing": [ "cpe:2.3:a:automattic:wordpress.com_editing_toolkit:*:*:*:*:*:wordpress:*:*" ], @@ -13558,6 +13622,9 @@ "gdpr-framework": [ "cpe:2.3:a:data443:gdpr_framework:*:*:*:*:*:wordpress:*:*" ], + "gearside-developer-dashboard": [ + "cpe:2.3:a:gearside:gearside_developer_dashboard:*:*:*:*:*:wordpress:*:*" + ], "gecka-terms-thumbnails": [ "cpe:2.3:a:gecka:terms_thumbnails:*:*:*:*:*:wordpress:*:*" ], @@ -13567,6 +13634,9 @@ "generate-pdf-using-contact-form-7": [ "cpe:2.3:a:zealousweb:generate_pdf_using_contact_form_7:*:*:*:*:*:wordpress:*:*" ], + "generateblocks": [ + "cpe:2.3:a:generatepress:generateblocks:*:*:*:*:free:wordpress:*:*" + ], "generatepress-premium": [ "cpe:2.3:a:generatepress:generatepress:*:*:*:*:premium:wordpress:*:*" ], @@ -13952,6 +14022,9 @@ "gtmetrix-for-wordpress": [ "cpe:2.3:a:gtmetrix:gtmetrix:*:*:*:*:*:wordpress:*:*" ], + "gtpayment-donation": [ + "cpe:2.3:a:harryhe:gtpayment_donations:*:*:*:*:*:wordpress:*:*" + ], "gtranslate": [ "cpe:2.3:a:gtranslate:gtranslate:*:*:*:*:enterprise:wordpress:*:*", "cpe:2.3:a:gtranslate:gtranslate:*:*:*:*:pro:wordpress:*:*", @@ -14693,6 +14766,9 @@ "iws-geo-form-fields": [ "cpe:2.3:a:iws-geo-form-fields_project:iws-geo-form-fields:*:*:*:*:*:wordpress:*:*" ], + "javo-core": [ + "cpe:2.3:a:javothemes:javo_core:*:*:*:*:*:wordpress:*:*" + ], "jayj-quicktag": [ "cpe:2.3:a:jayj_quicktag_project:jayj_quicktag:*:*:*:*:*:wordpress:*:*" ], @@ -14969,6 +15045,9 @@ "kwayy-html-sitemap": [ "cpe:2.3:a:kwayyinfotech:kwayy_html_sitemap:*:*:*:*:*:wordpress:*:*" ], + "l-squared-hub-wp-virtual-device": [ + "cpe:2.3:a:lsquared:l_squared_hub:*:*:*:*:*:wordpress:*:*" + ], "labtools": [ "cpe:2.3:a:creativityjuice:labtools:*:*:*:*:*:wordpress:*:*" ], @@ -15196,6 +15275,9 @@ "link-optimizer-lite": [ "cpe:2.3:a:link_optimizer_lite_project:link_optimizer_lite:*:*:*:*:*:wordpress:*:*" ], + "link-party": [ + "cpe:2.3:a:evanliewer:illi_link_party\\!:*:*:*:*:*:wordpress:*:*" + ], "link-to-bible": [ "cpe:2.3:a:bibleserver:link_to_bible:*:*:*:*:*:wordpress:*:*" ], @@ -15531,6 +15613,9 @@ "malinky-ajax-pagination": [ "cpe:2.3:a:malinky:malinky-ajax-pagination:*:*:*:*:*:wordpress:*:*" ], + "mambo-joomla-importer": [ + "cpe:2.3:a:misterpah:mambo_joomla_importer:*:*:*:*:*:wordpress:*:*" + ], "manage-notification-emails": [ "cpe:2.3:a:freeamigos:manage_notification_e-mails:*:*:*:*:*:wordpress:*:*" ], @@ -15592,6 +15677,9 @@ "marketing-and-seo-booster": [ "cpe:2.3:a:secretlab:marketing_and_seo_booster:*:*:*:*:*:wordpress:*:*" ], + "marketing-optimizer": [ + "cpe:2.3:a:marketingoptimizer:marketing_optimizer:*:*:*:*:*:wordpress:*:*" + ], "marketo-forms-and-tracking": [ "cpe:2.3:a:hutchhouse:marketo_forms_and_tracking:*:*:*:*:*:wordpress:*:*" ], @@ -16039,6 +16127,9 @@ "mq-woocommerce-products-price-bulk-edit": [ "cpe:2.3:a:mq-woocommerce-products-price-bulk-edit_project:mq-woocommerce-products-price-bulk-edit:*:*:*:*:*:wordpress:*:*" ], + "mrlegend-typedjs": [ + "cpe:2.3:a:mrlegend1235:typed_js:*:*:*:*:*:wordpress:*:*" + ], "ms-registration": [ "cpe:2.3:a:alphaefficiencyteam:custom_login_and_registration:*:*:*:*:*:wordpress:*:*" ], @@ -16290,6 +16381,9 @@ "news-kit-elementor-addons": [ "cpe:2.3:a:blazethemes:news_kit_elementor_addons:*:*:*:*:*:wordpress:*:*" ], + "news-list": [ + "cpe:2.3:a:mahinsha:newsticker:*:*:*:*:*:wordpress:*:*" + ], "newsletter": [ "cpe:2.3:a:thenewsletterplugin:newsletter:*:*:*:*:*:wordpress:*:*" ], @@ -16305,6 +16399,9 @@ "newsletter-popup": [ "cpe:2.3:a:mndpsingh287:newsletter_popup:*:*:*:*:*:wordpress:*:*" ], + "newsletter2go": [ + "cpe:2.3:a:newsletter2go:newsletter2go:*:*:*:*:*:wordpress:*:*" + ], "newsletters-lite": [ "cpe:2.3:a:tribulant:newsletters:*:*:*:*:*:wordpress:*:*", "cpe:2.3:a:tribulant:newsletters:*:*:*:*:free:wordpress:*:*" @@ -16324,7 +16421,8 @@ ], "nextend-facebook-connect": [ "cpe:2.3:a:nextendweb:facebook_connect:*:*:*:*:*:wordpress:*:*", - "cpe:2.3:a:nextendweb:nextend_facebook_connect:*:*:*:*:*:wordpress:*:*" + "cpe:2.3:a:nextendweb:nextend_facebook_connect:*:*:*:*:*:wordpress:*:*", + "cpe:2.3:a:nextendweb:nextend_social_login:*:*:*:*:free:wordpress:*:*" ], "nextend-twitter-connect": [ "cpe:2.3:a:nextendweb:nextend_twitter_connect:*:*:*:*:*:wordpress:*:*" @@ -16364,6 +16462,9 @@ "ninja-job-board": [ "cpe:2.3:a:wpmanageninja:ninja_job_board:*:*:*:*:*:wordpress:*:*" ], + "ninja-page-categories-and-tags": [ + "cpe:2.3:a:ninja_pages_project:ninja_pages:*:*:*:*:*:wordpress:*:*" + ], "ninja-tables": [ "cpe:2.3:a:wpmanageninja:ninja_tables:*:*:*:*:*:wordpress:*:*" ], @@ -16407,6 +16508,9 @@ "nofollow-links": [ "cpe:2.3:a:nofollow_links_project:nofollow_links:*:*:*:*:*:wordpress:*:*" ], + "nokaut-offers-box": [ + "cpe:2.3:a:nokautpl:nokaut_offers_box:*:*:*:*:*:wordpress:*:*" + ], "nokia-mapsplaces": [ "cpe:2.3:a:nokia_maps_\\\u0026_places_project:nokia_maps_\\\u0026_places:*:*:*:*:*:wordpress:*:*" ], @@ -16456,6 +16560,9 @@ "ns-woocommerce-watermark": [ "cpe:2.3:a:nsthemes:ns_watermark_for_woocommerce:*:*:*:*:*:wordpress:*:*" ], + "ntzantispam": [ + "cpe:2.3:a:ionutstaicu:ntz_atispam:*:*:*:*:*:wordpress:*:*" + ], "oauth-client": [ "cpe:2.3:a:miniorange:oauth_2.0_client_for_sso:*:*:*:*:*:wordpress:*:*" ], @@ -17618,6 +17725,9 @@ "pushbiz": [ "cpe:2.3:a:csimplifyit:pushbiz:*:*:*:*:*:wordpress:*:*" ], + "pvn-auth-popup": [ + "cpe:2.3:a:freebiesdownload:pvn_auth_popup:*:*:*:*:*:wordpress:*:*" + ], "pwa-for-wp": [ "cpe:2.3:a:magazine3:pwa_for_wp_\\\u0026_amp:*:*:*:*:*:wordpress:*:*" ], @@ -18054,6 +18164,9 @@ "responsive-vector-maps": [ "cpe:2.3:a:thinkupthemes:responsive_vector_maps:*:*:*:*:*:wordpress:*:*" ], + "responsive-video": [ + "cpe:2.3:a:kirstyburgoine:responsive_video:*:*:*:*:*:wordpress:*:*" + ], "responsive-video-embed": [ "cpe:2.3:a:lepileppanen:responsive_video_embed:*:*:*:*:*:wordpress:*:*" ], @@ -18069,6 +18182,9 @@ "restaurant-reservations": [ "cpe:2.3:a:fivestarplugins:five_star_restaurant_reservations:*:*:*:*:*:wordpress:*:*" ], + "restaurant-solutions-checklist": [ + "cpe:2.3:a:josephlopreste:restaurant_solutions_-_checklist:*:*:*:*:*:wordpress:*:*" + ], "restrict-content": [ "cpe:2.3:a:liquidweb:restrict_content:*:*:*:*:*:wordpress:*:*" ], @@ -18590,6 +18706,9 @@ "shortcode-addons": [ "cpe:2.3:a:oxilab:shortcode_addons:*:*:*:*:*:wordpress:*:*" ], + "shortcode-cleaner-lite": [ + "cpe:2.3:a:jozoor:shortcode_cleaner_lite:*:*:*:*:*:wordpress:*:*" + ], "shortcode-factory": [ "cpe:2.3:a:wpmadeasy:shortcode_factory:*:*:*:*:*:wordpress:*:*" ], @@ -18972,12 +19091,18 @@ "simple-video-embedder": [ "cpe:2.3:a:simple_video_embedder_project:simple_video_embedder:*:*:*:*:*:wordpress:*:*" ], + "simple-video-management-system": [ + "cpe:2.3:a:maximize:simple_video_management_system:*:*:*:*:*:wordpress:*:*" + ], "simple-yearly-archive": [ "cpe:2.3:a:simple_yearly_archive_project:simple_yearly_archive:*:*:*:*:*:wordpress:*:*" ], "simple-youtube-responsive": [ "cpe:2.3:a:simple_youtube_responsive_project:simple_youtube_responsive:*:*:*:*:*:wordpress:*:*" ], + "simplebooklet": [ + "cpe:2.3:a:simplebooklet:simplebooklet_pdf_viewer_and_embedder:*:*:*:*:*:wordpress:*:*" + ], "simplemap": [ "cpe:2.3:a:simplemap-plugin:simplemap_store_locator:*:*:*:*:*:wordpress:*:*" ], @@ -19448,6 +19573,9 @@ "spiffy-calendar": [ "cpe:2.3:a:spiffyplugins:spiffy_calendar:*:*:*:*:*:wordpress:*:*" ], + "spiritual-gifts-survey": [ + "cpe:2.3:a:mynamedia:spiritual_gifts_survey_\\(and_optional_s.h.a.p.e_survey\\):*:*:*:*:*:wordpress:*:*" + ], "splash-header": [ "cpe:2.3:a:zeesweb:splash_header:*:*:*:*:*:wordpress:*:*" ], @@ -20258,6 +20386,9 @@ "ttv-easy-embed-player": [ "cpe:2.3:a:streamweasels:twitch_player:*:*:*:*:*:wordpress:*:*" ], + "tube-video-ads-lite": [ + "cpe:2.3:a:gualdoni:tube_video_ads_lite:*:*:*:*:*:wordpress:*:*" + ], "tubepress": [ "cpe:2.3:a:tubepress:tubepress:*:*:*:*:*:wordpress:*:*" ], @@ -20416,6 +20547,9 @@ "cpe:2.3:a:ultimatemember:ultimate_member:*:*:*:*:*:wordpress:*:*", "cpe:2.3:a:ultimatemember:user_profile_\\\u0026_membership:*:*:*:*:*:wordpress:*:*" ], + "ultimate-noindex-nofollow-tool": [ + "cpe:2.3:a:jonkemp:ultimate_noindex_nofollow_tool:*:*:*:*:*:wordpress:*:*" + ], "ultimate-noindex-nofollow-tool-ii": [ "cpe:2.3:a:ultimate_noindex_nofollow_tool_ii_project:ultimate_noindex_nofollow_tool_ii:*:*:*:*:*:wordpress:*:*" ], @@ -20551,6 +20685,9 @@ "url-media-uploader": [ "cpe:2.3:a:apprhyme:url_media_uploader:*:*:*:*:*:wordpress:*:*" ], + "url-params": [ + "cpe:2.3:a:jeremyshapiro:url_params:*:*:*:*:*:wordpress:*:*" + ], "url-shortify": [ "cpe:2.3:a:kaizencoders:url_shortify:*:*:*:*:*:wordpress:*:*" ], @@ -20678,6 +20815,9 @@ "vayu-blocks": [ "cpe:2.3:a:themehunk:vayu_blocks:*:*:*:*:*:wordpress:*:*" ], + "vc-addons-by-bit14": [ + "cpe:2.3:a:pagebuilderaddons:web_and_woocommerce_addons_for_wpbakery_builder:*:*:*:*:*:wordpress:*:*" + ], "vc-tabs": [ "cpe:2.3:a:oxilab:responsive_tabs:*:*:*:*:*:wordpress:*:*" ], @@ -20780,6 +20920,9 @@ "visual-portfolio": [ "cpe:2.3:a:visualportfolio:visual_portfolio\\,_photo_gallery_\\\u0026_post_grid:*:*:*:*:*:wordpress:*:*" ], + "visual-sound": [ + "cpe:2.3:a:freakingwildchild:visual_sound:*:*:*:*:*:wordpress:*:*" + ], "visualcomposer": [ "cpe:2.3:a:visualcomposer:visual_composer_website_builder:*:*:*:*:*:wordpress:*:*" ], @@ -20950,6 +21093,9 @@ "web-application-firewall": [ "cpe:2.3:a:miniorange:web_application_firewall:*:*:*:*:*:wordpress:*:*" ], + "web-cam": [ + "cpe:2.3:a:murtuzamakda52:web-cam:*:*:*:*:*:wordpress:*:*" + ], "web-directory-free": [ "cpe:2.3:a:salephpscripts:web_directory_free:*:*:*:*:*:wordpress:*:*" ], @@ -21015,6 +21161,9 @@ "website-article-monetization-by-magenet": [ "cpe:2.3:a:magenet:website_article_monetization:*:*:*:*:*:wordpress:*:*" ], + "website-contact-form-with-file-upload": [ + "cpe:2.3:a:najeebmedia:website_contact_form_with_file_upload:*:*:*:*:*:wordpress:*:*" + ], "website-file-changes-monitor": [ "cpe:2.3:a:melapress:melapress_file_monitor:*:*:*:*:*:wordpress:*:*" ], @@ -21120,6 +21269,9 @@ "widgets-on-pages": [ "cpe:2.3:a:widgets-on-pages_project:widgets-on-pages:*:*:*:*:*:wordpress:*:*" ], + "widgets-reset": [ + "cpe:2.3:a:justintadlock:widgets_reset:*:*:*:*:*:wordpress:*:*" + ], "winterlock": [ "cpe:2.3:a:wp_system_log_project:wp_system_log:*:*:*:*:*:wordpress:*:*" ], @@ -21127,7 +21279,11 @@ "cpe:2.3:a:themeinprogress:wip_custom_login:*:*:*:*:*:wordpress:*:*" ], "wise-chat": [ - "cpe:2.3:a:kaine:wise_chat:*:*:*:*:*:wordpress:*:*" + "cpe:2.3:a:kaine:wise_chat:*:*:*:*:*:wordpress:*:*", + "cpe:2.3:a:kainex:wise_chat:*:*:*:*:*:wordpress:*:*" + ], + "wise-forms": [ + "cpe:2.3:a:kainex:wise_forms:*:*:*:*:*:wordpress:*:*" ], "wiseagentleadform": [ "cpe:2.3:a:wiseagent:wise_agent_capture_forms:*:*:*:*:*:wordpress:*:*" @@ -21227,6 +21383,9 @@ "woo-ecommerce-tracking-for-google-and-facebook": [ "cpe:2.3:a:multidots:enhanced_ecommerce_google_analytics_for_woocommerce:*:*:*:*:*:wordpress:*:*" ], + "woo-edit-templates": [ + "cpe:2.3:a:ioannup:edit_woocommerce_templates:*:*:*:*:*:wordpress:*:*" + ], "woo-esto": [ "cpe:2.3:a:rebing:woocommerce_esto:*:*:*:*:*:wordpress:*:*" ], @@ -21539,6 +21698,9 @@ "wordpress-access-control": [ "cpe:2.3:a:brandonwamboldt:wordpress_access_control:*:*:*:*:*:wordpress:*:*" ], + "wordpress-activity-o-meter": [ + "cpe:2.3:a:erwinwolff:wordpress_activity-o-meter:*:*:*:*:*:wordpress:*:*" + ], "wordpress-backup-to-dropbox": [ "cpe:2.3:a:wordpress_backup_to_dropbox_project:wordpress_backup_to_dropbox:*:*:*:*:*:wordpress:*:*" ], @@ -21598,6 +21760,9 @@ "wordpress-tooltips": [ "cpe:2.3:a:tooltips:wordpress_tooltips:*:*:*:*:free:wordpress:*:*" ], + "wordpress-twitterbot": [ + "cpe:2.3:a:corbyboy:marketing_twitter_bot:*:*:*:*:*:wordpress:*:*" + ], "wordpress-users": [ "cpe:2.3:a:jonathonkemp:wordpress_users:*:*:*:*:*:wordpress:*:*" ], @@ -21610,6 +21775,9 @@ "wow-carousel-for-divi-lite": [ "cpe:2.3:a:elegantthemes:divi_carousel:*:*:*:*:free:wordpress:*:*" ], + "wow-entrance-effects-wee": [ + "cpe:2.3:a:darkosxrc:wow_entrance_effects_\\(wee\\!\\):*:*:*:*:*:wordpress:*:*" + ], "wow-moodboard-lite": [ "cpe:2.3:a:wownmedia:wow_moodboard:*:*:*:*:lite:wordpress:*:*" ], @@ -21632,6 +21800,9 @@ "wp-accessibility-helper": [ "cpe:2.3:a:volkov:wp_accessibility_helper:*:*:*:*:*:wordpress:*:*" ], + "wp-action-network": [ + "cpe:2.3:a:actionnetwork:action_network:*:*:*:*:*:wordpress:*:*" + ], "wp-admin-logo-changer": [ "cpe:2.3:a:wp_admin_logo_changer_project:wp_admin_logo_changer:*:*:*:*:*:wordpress:*:*" ], @@ -21641,6 +21812,9 @@ "wp-airbnb-review-slider": [ "cpe:2.3:a:ljapps:wp_airbnb_review_slider:*:*:*:*:*:wordpress:*:*" ], + "wp-ajax-contact-form": [ + "cpe:2.3:a:sportsdanka:wp_ajax_contact_form:*:*:*:*:*:wordpress:*:*" + ], "wp-all-export": [ "cpe:2.3:a:soflyy:export_any_wordpress_data_to_xml\\/csv:*:*:*:*:*:wordpress:*:*", "cpe:2.3:a:soflyy:wp_all_export:*:*:*:*:pro:wordpress:*:*" @@ -22034,6 +22208,9 @@ "wp-email-capture": [ "cpe:2.3:a:winwar:wp_email_capture:*:*:*:*:*:wordpress:*:*" ], + "wp-email-newsletter": [ + "cpe:2.3:a:megamindstechnologies:wordpress_email_newsletter:*:*:*:*:*:wordpress:*:*" + ], "wp-email-users": [ "cpe:2.3:a:techspawn:wp-email-users:*:*:*:*:*:wordpress:*:*" ], @@ -22168,6 +22345,9 @@ "wp-gdpr-core": [ "cpe:2.3:a:appsaloon:wp-gdpr:*:*:*:*:*:wordpress:*:*" ], + "wp-geshi-highlight": [ + "cpe:2.3:a:jgehrcke:wp-geshi-highlight:*:*:*:*:*:wordpress:*:*" + ], "wp-githuber-md": [ "cpe:2.3:a:terryl:wp_githuber_md:*:*:*:*:*:wordpress:*:*" ], @@ -22261,6 +22441,9 @@ "wp-image-slideshow": [ "cpe:2.3:a:gopiplus:wp_image_slideshow:*:*:*:*:*:wordpress:*:*" ], + "wp-image-uploader": [ + "cpe:2.3:a:ivanm:wp_image_uploader:*:*:*:*:*:wordpress:*:*" + ], "wp-image-zoooom": [ "cpe:2.3:a:silkypress:wp_image_zoom:*:*:*:*:*:wordpress:*:*" ], @@ -22460,6 +22643,9 @@ "wp-multi-store-locator": [ "cpe:2.3:a:wpexperts:wp_multi_store_locator:*:*:*:*:*:wordpress:*:*" ], + "wp-multitasking": [ + "cpe:2.3:a:ngothang:wp_multitasking:*:*:*:*:*:wordpress:*:*" + ], "wp-nested-pages": [ "cpe:2.3:a:kylephillips:nested_pages:*:*:*:*:*:wordpress:*:*" ], @@ -22472,6 +22658,9 @@ "wp-olivecart": [ "cpe:2.3:a:wp-olivecart_project:wp-olivecart:*:*:*:*:*:wordpress:*:*" ], + "wp-online-users-stats": [ + "cpe:2.3:a:hk1993:wp_online_users_stats:*:*:*:*:*:wordpress:*:*" + ], "wp-open-graph": [ "cpe:2.3:a:custom4web:wp_open_graph:*:*:*:*:*:wordpress:*:*" ], @@ -22903,6 +23092,9 @@ "wp-symposium-pro": [ "cpe:2.3:a:wpsymposiumpro:wp_symposium_pro:*:*:*:*:*:wordpress:*:*" ], + "wp-syntax": [ + "cpe:2.3:a:connections-pro:wp-syntax:*:*:*:*:*:wordpress:*:*" + ], "wp-t-wap": [ "cpe:2.3:a:wp-t-wap_project:wp-t-wap:*:*:*:*:*:wordpress:*:*" ], @@ -22948,6 +23140,9 @@ "wp-timed-popup": [ "cpe:2.3:a:timed_popup_project:timed_popup:*:*:*:*:*:wordpress:*:*" ], + "wp-tithely": [ + "cpe:2.3:a:jordangillman:tithe.ly_giving_button:*:*:*:*:*:wordpress:*:*" + ], "wp-todo": [ "cpe:2.3:a:delower:wp_to_do:*:*:*:*:*:wordpress:*:*" ], @@ -23570,6 +23765,9 @@ "z-url-preview": [ "cpe:2.3:a:z-url_preview_project:z-url_preview:*:*:*:*:*:wordpress:*:*" ], + "zalomeni": [ + "cpe:2.3:a:kybernaut:zalomeni:*:*:*:*:*:wordpress:*:*" + ], "zarinpal-paid-downloads": [ "cpe:2.3:a:amini7:zarinpal_paid_download:*:*:*:*:*:wordpress:*:*" ], From 6c9bb732dd06db54b35f434f4bbfcfada564e2fc Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 5 Oct 2026 10:41:03 -0400 Subject: [PATCH 13/14] ci: run the full unit suite on windows (#5370) * ci: run the full unit suite on windows via make unit Drops the curated `unit:windows` subset; the windows job now bootstraps like linux and runs `make unit`. Exceptions get added as failures show up. Signed-off-by: Alex Goodman * ci: skip fixture refresh for windows unit tests binny has no windows release, so the bootstrap action can't install the tools the fixture refresh needs. Signed-off-by: Alex Goodman * test: skip linux container fixtures when docker can't run them Windows CI runners only run windows containers, so image fixtures go through `testutils.SkipWithoutLinuxContainers`. No behavior change on linux/mac. Signed-off-by: Alex Goodman * fix: close leaked file handles and posix squashfs paths on windows - output writers are now closed on early returns in `scan`/`convert` (open handles block cleanup on windows) - `newSBOMMultiWriter` used `path.Dir` on host paths, so output subdirectories were never created on windows - squashfs walk paths were built with `filepath.Join` - skip a few posix-only test cases (dir perms, xdg fallback, go-rpmdb ndb handle leak upstream) Signed-off-by: Alex Goodman * fix(golang): resolve go source module dir natively on windows - `parseGoModFile` passed the resolver's posix (`/c/...`) path to the go toolchain, so go source resolution silently found nothing on windows - skip container-built fixtures (go archs, java builds) and golden image tarballs on windows; stereoscope's layer cache filenames contain `:`, which windows rejects Signed-off-by: Alex Goodman * fix: keep posix paths posix on windows - `ChrootContext.ToChrootPath` nested volumes (`D:\cwd\C:\root`) when the scan root was on another drive, so the prefix was never trimmed - bitnami, python wheel/egg, and alpm parsers used `filepath` on posix location paths - alpm backup-file lookups silently missed on windows Signed-off-by: Alex Goodman * ci: pull the test fixture cache for windows unit tests Signed-off-by: Alex Goodman * fix: don't map virtual file tree paths through the host on windows `FiletreeResolver` without a chroot (snaps) ran request paths through `filepath.Abs`, rooting them on the current volume (`/payload.txt` -> `/d/payload.txt`), so snap scans on windows found nothing. Signed-off-by: Alex Goodman * fix: match tar entry globs as posix on windows - `matchesAnyGlob` used `doublestar.PathMatch` (host separator) on tar entry names, so tar-wrapped java archives silently produced nothing on windows - test helpers now treat binary snippet keys and `FromFile` locations as posix Signed-off-by: Alex Goodman * incorporate windows path fix Signed-off-by: Alex Goodman * incorporate go-rpmdb close fix Signed-off-by: Alex Goodman * close squashfs fixture writers before finalizing - windows dir listings can report a zero size for files still open, so the manifest was packed empty (also adds a temporary windows diagnostic for the binary cataloger) Signed-off-by: Alex Goodman * temp: surface windows binary cataloger diagnostics Signed-off-by: Alex Goodman * fix: keep directory symlink targets posix on windows - the directory indexer joined symlink targets with native separators, so links in the posix file tree pointed nowhere and globs on link names found nothing - binary fixture logical keys used native separators, so nested snippets were picked as the fixture dir on windows Signed-off-by: Alex Goodman * keep addSymlinkToIndex under the funlen limit Signed-off-by: Alex Goodman * drop windows skips covered by the stereoscope and go-rpmdb bumps Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- .github/workflows/validations.yaml | 14 ++++++-- .make/main.go | 29 ++++++----------- cmd/syft/internal/commands/convert.go | 4 +++ cmd/syft/internal/commands/scan.go | 5 +++ cmd/syft/internal/options/cache_test.go | 13 ++++++-- cmd/syft/internal/options/writer.go | 32 ++++++++++++++++--- cmd/syft/internal/options/writer_test.go | 4 +-- go.mod | 4 +-- go.sum | 12 +++---- internal/cache/filesystem_test.go | 4 +++ .../generate/discover_catalogers.go | 2 +- .../generate/discover_catalogers_test.go | 3 +- internal/file/squashfs.go | 7 ++-- internal/file/squashfs_test.go | 2 ++ internal/file/tar_file_traversal.go | 3 +- internal/os/feature_detection_test.go | 2 ++ internal/testutils/docker.go | 30 +++++++++++++++++ internal/tmpdir/tmpdir_test.go | 3 +- .../cataloger/filedigest/cataloger_test.go | 3 ++ .../cataloger/filemetadata/cataloger_test.go | 3 ++ .../internal/all_regular_files_test.go | 16 +++++++--- syft/format/github/encoder_test.go | 5 +++ syft/format/internal/testutil/image_input.go | 2 ++ syft/format/syftjson/encoder_test.go | 5 +++ syft/format/text/encoder_test.go | 5 +++ syft/internal/fileresolver/chroot_context.go | 9 ++++-- .../chroot_context_windows_test.go | 17 ++++++++++ .../container_image_all_layers_test.go | 9 ++++++ .../container_image_deep_squash_test.go | 9 ++++++ .../container_image_squash_test.go | 10 ++++++ .../fileresolver/directory_indexer.go | 28 ++++++++-------- .../fileresolver/filetree_resolver.go | 16 ++++++++++ .../cataloger/ai/parse_safetensors_test.go | 10 +++--- syft/pkg/cataloger/arch/parse_alpm_db.go | 6 ++-- .../binary/classifier_cataloger_test.go | 2 ++ .../internal/config/binary_from_image_test.go | 9 +++--- .../internal/manager/internal/list_entries.go | 3 +- .../manager/internal/list_entries_test.go | 10 +++--- .../internal/manager/internal/utils_test.go | 3 +- .../manager/testutil/snippet_or_binary.go | 20 +++++++----- syft/pkg/cataloger/bitnami/cataloger.go | 4 +-- syft/pkg/cataloger/bitnami/package.go | 3 +- .../pkg/cataloger/bitnami/parse_components.go | 4 +-- .../cataloger/golang/license_finder_test.go | 30 +++++++++++++++-- .../cataloger/golang/parse_go_binary_test.go | 3 ++ syft/pkg/cataloger/golang/parse_go_mod.go | 8 ++++- syft/pkg/cataloger/golang/upx_test.go | 2 ++ .../cataloger/internal/dotnet/pe/pe_test.go | 2 ++ .../internal/pkgtest/test_generic_parser.go | 6 +++- .../pkg/cataloger/java/archive_parser_test.go | 8 ++++- .../java/internal/maven/config_test.go | 2 ++ .../java/internal/maven/test/mock_repo.go | 2 +- syft/pkg/cataloger/perl/cataloger_test.go | 2 ++ syft/pkg/cataloger/python/parse_wheel_egg.go | 15 ++++----- .../python/parse_wheel_egg_metadata.go | 22 ++++++------- .../python/parse_wheel_egg_record.go | 5 ++- .../swift/parse_package_resolved_test.go | 3 +- syft/source/snapsource/snap_source_test.go | 18 +++++++---- syft/source/snapsource/snap_test.go | 12 +++++-- .../stereoscopesource/image_source_test.go | 2 ++ 60 files changed, 391 insertions(+), 135 deletions(-) create mode 100644 internal/testutils/docker.go create mode 100644 syft/internal/fileresolver/chroot_context_windows_test.go diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 0186c5ace..942ab1eb2 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -96,16 +96,24 @@ jobs: with: persist-credentials: false - # go-make's setup only (not ./.github/actions/bootstrap): no apt, binny tools, or fixture cache are needed + # go-make's setup only (not ./.github/actions/bootstrap): binny ships no windows release, so binny tools + # are unavailable here (`make unit` skips the fixture refresh on windows) - name: Setup go + go-make tooling uses: anchore/go-make/.github/actions/setup@fa5421b3bf24c9d18f11a736c5eb4172f2b7d683 # v0.8.1 with: # keep in sync with the go-version default in ./.github/actions/bootstrap go-version: "1.26.2" - # the full unit suite leans on linux docker fixtures, so this runs the windows-relevant subset + # the binny-free version of `make download-test-fixture-cache` (keep the oras version in sync with .binny.yaml) + - name: Download test fixture cache + shell: bash + run: | + curl -sSfL https://github.com/oras-project/oras/releases/download/v1.3.4/oras_1.3.4_windows_amd64.zip -o "$RUNNER_TEMP/oras.zip" + unzip -q "$RUNNER_TEMP/oras.zip" oras.exe -d "$RUNNER_TEMP" + "$RUNNER_TEMP/oras.exe" pull ghcr.io/anchore/oss-cache/syft-test-fixture-cache:v2 + - name: Run unit tests - run: make unit:windows + run: make unit Integration-Test: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline diff --git a/.make/main.go b/.make/main.go index 58db41a96..dc18a31d7 100644 --- a/.make/main.go +++ b/.make/main.go @@ -1,7 +1,6 @@ package main import ( - "os" "path/filepath" "runtime" "strconv" @@ -97,23 +96,6 @@ func main() { }, }, - // windows unit tests: deliberately not hooked into "test" (no RunsOn). The full unit suite leans on - // linux docker fixture images and posix-only assumptions, so this covers the packages that own - // host path handling (where windows bugs live) and skips the tests that need docker image fixtures. - // ponytail: curated package list, widen it as more of the suite is made windows-safe. - Task{ - Name: "unit:windows", - Description: "run the windows-relevant subset of unit tests", - Run: func() { - Run("go test -count=1"+ - ` -skip "Image|Squash|AllLayers|MixFileTypes|GivenCoordinates"`+ - " ./syft/internal/windows/... ./syft/internal/fileresolver/... ./syft/source/directorysource/... ./syft/source/filesource/..."+ - " ./syft/file ./syft/file/cataloger/filedigest/... ./syft/file/cataloger/filecontent/...", - run.Stdout(os.Stderr), - ) - }, - }, - // default validation pipeline (replaces Taskfile `default`/`pr-validations`/`validations`). Task{ Name: "default", @@ -157,7 +139,7 @@ func main() { Task{ Name: "unit:syft", RunsOn: lang.List("unit"), - Dependencies: Deps("refresh-fixtures"), + Dependencies: unitFixtureDeps(), }, Task{ Name: "clean:syft", @@ -185,6 +167,15 @@ func raceEnabled() bool { return config.CI && !config.Windows } +// unitFixtureDeps skips the fixture cache refresh on windows: it needs binny-managed tools (binny ships +// no windows release) and linux docker, so fixture-backed tests there have to skip themselves instead. +func unitFixtureDeps() []string { + if config.Windows { + return nil + } + return Deps("refresh-fixtures") +} + // race applies raceEnabled() to a gotest suite. gotest exposes no functional option // for the race detector, but gotest.Config.Race is exported. func race() gotest.Option { diff --git a/cmd/syft/internal/commands/convert.go b/cmd/syft/internal/commands/convert.go index a6189b847..83cba62c5 100644 --- a/cmd/syft/internal/commands/convert.go +++ b/cmd/syft/internal/commands/convert.go @@ -114,6 +114,10 @@ func RunConvert(ctx context.Context, opts *ConvertOptions, userInput string) err if err != nil { return err } + // output files are opened up front; release them if we bail out before writing + if c, ok := writer.(io.Closer); ok { + defer func() { _ = c.Close() }() + } } for _, output := range unchanged { diff --git a/cmd/syft/internal/commands/scan.go b/cmd/syft/internal/commands/scan.go index 7db17fb7d..7f92491ca 100644 --- a/cmd/syft/internal/commands/scan.go +++ b/cmd/syft/internal/commands/scan.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "io" "os" "reflect" "strings" @@ -181,6 +182,10 @@ func runScan(ctx context.Context, id clio.Identification, opts *scanOptions, use if err != nil { return err } + // output files are opened up front; release them if we bail out before writing + if c, ok := writer.(io.Closer); ok { + defer func() { _ = c.Close() }() + } sources := opts.From if len(sources) == 0 { diff --git a/cmd/syft/internal/options/cache_test.go b/cmd/syft/internal/options/cache_test.go index f92f658d0..a42f5f665 100644 --- a/cmd/syft/internal/options/cache_test.go +++ b/cmd/syft/internal/options/cache_test.go @@ -4,6 +4,7 @@ import ( "io" "os" "path/filepath" + "runtime" "strings" "testing" "time" @@ -22,9 +23,10 @@ func Test_defaultDir(t *testing.T) { homeDir := filepath.Join(tmpDir, "fake-home") tests := []struct { - name string - env map[string]string - expected string + name string + env map[string]string + expected string + skipOnWindows bool }{ { name: "no-xdg", @@ -32,6 +34,8 @@ func Test_defaultDir(t *testing.T) { "HOME": homeDir, }, expected: homeDir, + // xdg falls back to LOCALAPPDATA on windows, not HOME + skipOnWindows: true, }, { name: "xdg-cache", @@ -59,6 +63,9 @@ func Test_defaultDir(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + if test.skipOnWindows && runtime.GOOS == "windows" { + t.Skip("posix-only cache dir fallback") + } restoreCache(t) for k, v := range test.env { diff --git a/cmd/syft/internal/options/writer.go b/cmd/syft/internal/options/writer.go index d53d3bfcc..f82530732 100644 --- a/cmd/syft/internal/options/writer.go +++ b/cmd/syft/internal/options/writer.go @@ -2,10 +2,11 @@ package options import ( "bytes" + "errors" "fmt" "io" "os" - "path" + "path/filepath" "sort" "strings" @@ -20,7 +21,10 @@ import ( "github.com/anchore/syft/syft/sbom" ) -var _ sbom.Writer = (*sbomMultiWriter)(nil) +var _ interface { + io.Closer + sbom.Writer +} = (*sbomMultiWriter)(nil) var _ interface { io.Closer @@ -164,6 +168,12 @@ func newSBOMMultiWriter(options ...sbomWriterDescription) (_ *sbomMultiWriter, e } out := &sbomMultiWriter{} + defer func() { + // don't leak the files already opened when a later output fails + if err != nil { + _ = out.Close() + } + }() for _, option := range options { switch len(option.Path) { @@ -172,8 +182,8 @@ func newSBOMMultiWriter(options ...sbomWriterDescription) (_ *sbomMultiWriter, e format: option.Format, }) default: - // create any missing subdirectories - dir := path.Dir(option.Path) + // create any missing subdirectories (option.Path is a host path, so this must be filepath) + dir := filepath.Dir(option.Path) if dir != "" { s, err := os.Stat(dir) if err != nil { @@ -210,6 +220,20 @@ func (m *sbomMultiWriter) Write(s sbom.SBOM) (errs error) { return errs } +// Close releases any files held by the writers. Write already closes each file it writes to, so this matters +// when the SBOM is never written (e.g. an error after the writer was created). +func (m *sbomMultiWriter) Close() error { + var errs error + for _, w := range m.writers { + if c, ok := w.(io.Closer); ok { + if err := c.Close(); err != nil && !errors.Is(err, os.ErrClosed) { + errs = multierror.Append(errs, err) + } + } + } + return errs +} + // sbomStreamWriter implements sbom.Writer for a given format and io.Writer, also providing a close function for cleanup type sbomStreamWriter struct { format sbom.FormatEncoder diff --git a/cmd/syft/internal/options/writer_test.go b/cmd/syft/internal/options/writer_test.go index 44d2dc213..b0ce9ad1b 100644 --- a/cmd/syft/internal/options/writer_test.go +++ b/cmd/syft/internal/options/writer_test.go @@ -202,9 +202,9 @@ func Test_newSBOMMultiWriter(t *testing.T) { if test.err { assert.Error(t, err) return - } else { - assert.NoError(t, err) } + require.NoError(t, err) + t.Cleanup(func() { _ = mw.Close() }) assert.Len(t, mw.writers, len(test.expected)) diff --git a/go.mod b/go.mod index 0099ae96f..996aa4ad6 100644 --- a/go.mod +++ b/go.mod @@ -18,11 +18,11 @@ require ( github.com/anchore/go-homedir v0.1.1 github.com/anchore/go-logger v0.2.0 github.com/anchore/go-macholibre v0.1.1 - github.com/anchore/go-rpmdb v0.2.0 + github.com/anchore/go-rpmdb v0.2.2-0.20261002172926-a488674d6c87 github.com/anchore/go-sync v0.1.2 github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b github.com/anchore/packageurl-go v0.2.0 - github.com/anchore/stereoscope v0.3.3 + github.com/anchore/stereoscope v0.3.4-0.20261002170525-59d3a69590a6 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be github.com/aquasecurity/go-pep440-version v0.0.1 github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef diff --git a/go.sum b/go.sum index 02cee8380..b82ae0667 100644 --- a/go.sum +++ b/go.sum @@ -140,8 +140,8 @@ github.com/anchore/go-lzo v0.1.1 h1:IwL/fvkdtlIrYIXck6WxZ3nb8WjjHziYYmGxlooyOnM= github.com/anchore/go-lzo v0.1.1/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk= github.com/anchore/go-macholibre v0.1.1 h1:sTBHi3S1WVIOCamc6NyT8Sn9mBmD30DCTfjfVm4k6/M= github.com/anchore/go-macholibre v0.1.1/go.mod h1:YNq2610RlvGCK0Za+Klz/OCMtUaEnwwAGA/VfUAQaro= -github.com/anchore/go-rpmdb v0.2.0 h1:ixcyMg/guhBL9XjBVwhTv9CG48xO07Qkg2uDX2+zx/c= -github.com/anchore/go-rpmdb v0.2.0/go.mod h1:ATsRlpCXstnoYfzqBfhwGw0U2dolx1BBQ9rAU8jWBAw= +github.com/anchore/go-rpmdb v0.2.2-0.20261002172926-a488674d6c87 h1:aY7DcEkjgzMRbCCJsGHu8eQeL5I88V8G9ISEWtfrlCU= +github.com/anchore/go-rpmdb v0.2.2-0.20261002172926-a488674d6c87/go.mod h1:Y64EbetdqhryMjtk8IzXkdCQw/aNDx+feZCT9aj6rjs= github.com/anchore/go-struct-converter v0.2.0-rc2 h1:q+859fW2/jbHJHB2etbNfRlFwYpknyvbqqk1hUdamQ4= github.com/anchore/go-struct-converter v0.2.0-rc2/go.mod h1:cDBA5vhcR62nXWo8QH9/Kk2807o65ISaHPNPX66L+Uw= github.com/anchore/go-sync v0.1.2 h1:RDD5RCanrWqhbtBulqPSZpERadUlgcYjpybYzYd3Uk8= @@ -150,8 +150,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.2.0 h1:CkrM4RMUwrEGAiE1OVlxaZNzWj0TuHRey7o4T/EAErk= github.com/anchore/packageurl-go v0.2.0/go.mod h1:2JCgOQMIsqZ7TmliXG4PnUthPJAKE3mWQbsW2XHjAOE= -github.com/anchore/stereoscope v0.3.3 h1:sur8G2THAOnOrSXDjIc1SXzQwco8hjFBtGFq1jFvNVA= -github.com/anchore/stereoscope v0.3.3/go.mod h1:LxL4iOFtB8XMbA1MykUUTkmtQmDuYdbr4+TkBkCfljU= +github.com/anchore/stereoscope v0.3.4-0.20261002170525-59d3a69590a6 h1:6l3YwREGEXX/w4tBhRcPb7efYWo4pf8+fmAy0mND4EY= +github.com/anchore/stereoscope v0.3.4-0.20261002170525-59d3a69590a6/go.mod h1:LxL4iOFtB8XMbA1MykUUTkmtQmDuYdbr4+TkBkCfljU= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= @@ -428,8 +428,8 @@ github.com/gkampitakis/ciinfo v0.3.4 h1:5eBSibVuSMbb/H6Elc0IIEFbkzCJi3lm94n0+U7Z github.com/gkampitakis/ciinfo v0.3.4/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-snaps v0.5.23 h1:okh5QR48zpUjpWtu65AtqxdCY8huJq+dEDuUzd1PuKg= github.com/gkampitakis/go-snaps v0.5.23/go.mod h1:uy3lVzCCRRsAwYqSocyw5fY8xRLCYEfqoOJNxr8HonM= -github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4= -github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0= +github.com/glebarez/go-sqlite v1.23.0 h1:FyhIq4jqmgphQAUlY79zPldYGwISEZikaDfhiGWkkaI= +github.com/glebarez/go-sqlite v1.23.0/go.mod h1:IIYrOH3L0rHY3jb4IXOHoWdklNajSGUN2eJcvK8WrnI= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= diff --git a/internal/cache/filesystem_test.go b/internal/cache/filesystem_test.go index 47c693042..046341434 100644 --- a/internal/cache/filesystem_test.go +++ b/internal/cache/filesystem_test.go @@ -6,6 +6,7 @@ import ( "net/url" "os" "path/filepath" + "runtime" "strings" "testing" "time" @@ -95,6 +96,9 @@ func Test_makeDiskKey(t *testing.T) { } func Test_errors(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("windows ignores unix permission bits on directories") + } tmp := t.TempDir() cache := filepath.Join(tmp, "cache") // make a non-writable directory diff --git a/internal/capabilities/generate/discover_catalogers.go b/internal/capabilities/generate/discover_catalogers.go index 3dc5419c7..789d7dd4e 100644 --- a/internal/capabilities/generate/discover_catalogers.go +++ b/internal/capabilities/generate/discover_catalogers.go @@ -212,7 +212,7 @@ func parseGenericCatalogerFunction(funcDecl *ast.FuncDecl, filePath, repoRoot st // extractPackageNameFromPath extracts the package name from a cataloger source file path // e.g., "syft/pkg/cataloger/swift/cataloger.go" -> "swift" func extractPackageNameFromPath(filePath string) string { - parts := strings.Split(filePath, string(filepath.Separator)) + parts := strings.Split(filepath.ToSlash(filePath), "/") // find the LAST occurrence of "cataloger" in the path // (to handle test fixtures with multiple "cataloger" segments) diff --git a/internal/capabilities/generate/discover_catalogers_test.go b/internal/capabilities/generate/discover_catalogers_test.go index 0672e9f6f..e953be131 100644 --- a/internal/capabilities/generate/discover_catalogers_test.go +++ b/internal/capabilities/generate/discover_catalogers_test.go @@ -4,6 +4,7 @@ import ( "go/ast" "go/parser" "go/token" + "path/filepath" "testing" "github.com/stretchr/testify/require" @@ -337,7 +338,7 @@ func TestResolveImportPath(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := resolveImportPath(tt.importPath, testRepoRoot) - require.Equal(t, tt.want, got) + require.Equal(t, filepath.FromSlash(tt.want), got) }) } } diff --git a/internal/file/squashfs.go b/internal/file/squashfs.go index 303dde993..7aef361dc 100644 --- a/internal/file/squashfs.go +++ b/internal/file/squashfs.go @@ -4,7 +4,7 @@ import ( "errors" "io/fs" "os" - "path/filepath" + "path" "strings" "github.com/diskfs/go-diskfs/filesystem" @@ -25,7 +25,8 @@ func WalkDiskDir(fsys filesystem.FileSystem, root string, fn WalkDiskDirFunc) er } for _, info := range infos { - p := filepath.Join(root, info.Name()) + // squashfs paths are posix regardless of host os + p := path.Join(root, info.Name()) err = walkDiskDir(fsys, p, info, fn) if err != nil { if errors.Is(err, fs.SkipDir) { @@ -74,7 +75,7 @@ func walkDiskDir(fsys filesystem.FileSystem, name string, d os.FileInfo, walkDir } for _, d1 := range dirs { - name1 := filepath.Join(name, d1.Name()) + name1 := path.Join(name, d1.Name()) if err := walkDiskDir(fsys, name1, d1, walkDirFn); err != nil { if errors.Is(err, fs.SkipDir) { break diff --git a/internal/file/squashfs_test.go b/internal/file/squashfs_test.go index a2be519dc..265919187 100644 --- a/internal/file/squashfs_test.go +++ b/internal/file/squashfs_test.go @@ -19,6 +19,8 @@ func createTestFS(t *testing.T) filesystem.FileSystem { filename := "test.squashfs" f, err := os.Create(filepath.Join(dir, filename)) require.NoError(t, err) + // windows can't remove the temp dir while the backing file is open + t.Cleanup(func() { _ = f.Close() }) b := file.New(f, false) fsys, err := squashfs.Create(b, 0, 0, 4096) diff --git a/internal/file/tar_file_traversal.go b/internal/file/tar_file_traversal.go index d3c9656a7..54f10448d 100644 --- a/internal/file/tar_file_traversal.go +++ b/internal/file/tar_file_traversal.go @@ -84,7 +84,8 @@ func ExtractGlobsFromTarToUniqueTempFile(ctx context.Context, archivePath, dir s func matchesAnyGlob(name string, globs ...string) bool { for _, glob := range globs { - if matches, err := doublestar.PathMatch(glob, name); err == nil && matches { + // tar entry names are always posix, so don't match with the host separator (PathMatch) + if matches, err := doublestar.Match(glob, name); err == nil && matches { return true } } diff --git a/internal/os/feature_detection_test.go b/internal/os/feature_detection_test.go index b4f2ed38d..15a8f6b44 100644 --- a/internal/os/feature_detection_test.go +++ b/internal/os/feature_detection_test.go @@ -16,6 +16,7 @@ import ( "github.com/anchore/syft/internal/os" "github.com/anchore/syft/internal/sbomsync" "github.com/anchore/syft/internal/task" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/linux" @@ -84,6 +85,7 @@ func Test_EnvironmentTask(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) tarPath := imagetest.GetFixtureImageTarPath(t, test.name) // get the source diff --git a/internal/testutils/docker.go b/internal/testutils/docker.go new file mode 100644 index 000000000..8279a7723 --- /dev/null +++ b/internal/testutils/docker.go @@ -0,0 +1,30 @@ +package testutils + +import ( + "os/exec" + "runtime" + "strings" + "sync" + "testing" +) + +var dockerServerOS = sync.OnceValue(func() string { + out, err := exec.Command("docker", "version", "--format", "{{.Server.Os}}").Output() + if err != nil { + return "" + } + return strings.TrimSpace(string(out)) +}) + +// SkipWithoutLinuxContainers skips tests whose fixtures are linux container images (built or run via docker). +// This only kicks in on windows, where the docker daemon is often running windows containers (or is absent), +// so the linux fixture images can't be built. Everywhere else the fixture tooling is left to fail loudly. +func SkipWithoutLinuxContainers(t testing.TB) { + t.Helper() + if runtime.GOOS != "windows" { + return + } + if os := dockerServerOS(); os != "linux" { + t.Skipf("linux container fixtures unavailable (docker server os=%q)", os) + } +} diff --git a/internal/tmpdir/tmpdir_test.go b/internal/tmpdir/tmpdir_test.go index aeb208403..81142dfe5 100644 --- a/internal/tmpdir/tmpdir_test.go +++ b/internal/tmpdir/tmpdir_test.go @@ -265,8 +265,9 @@ func TestLazyCreation(t *testing.T) { // root dir is not created until needed assert.Equal(t, "", td.root) - _, _, err := td.NewFile("trigger-*") + f, _, err := td.NewFile("trigger-*") require.NoError(t, err) + require.NoError(t, f.Close()) assert.NotEmpty(t, td.root) diff --git a/syft/file/cataloger/filedigest/cataloger_test.go b/syft/file/cataloger/filedigest/cataloger_test.go index 6188bd066..2c63d3bdb 100644 --- a/syft/file/cataloger/filedigest/cataloger_test.go +++ b/syft/file/cataloger/filedigest/cataloger_test.go @@ -15,6 +15,7 @@ import ( stereoscopeFile "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/imagetest" intFile "github.com/anchore/syft/internal/file" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/internal/unknown" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/source" @@ -92,6 +93,7 @@ func TestDigestsCataloger(t *testing.T) { func TestDigestsCataloger_MixFileTypes(t *testing.T) { testImage := "image-file-type-mix" + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", testImage) src := stereoscopesource.New(img, stereoscopesource.ImageConfig{ @@ -162,6 +164,7 @@ func TestDigestsCataloger_MixFileTypes(t *testing.T) { func TestFileDigestCataloger_GivenCoordinates(t *testing.T) { testImage := "image-file-type-mix" + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", testImage) c := NewCataloger([]crypto.Hash{crypto.SHA256}) diff --git a/syft/file/cataloger/filemetadata/cataloger_test.go b/syft/file/cataloger/filemetadata/cataloger_test.go index fee17c0b1..3547a043a 100644 --- a/syft/file/cataloger/filemetadata/cataloger_test.go +++ b/syft/file/cataloger/filemetadata/cataloger_test.go @@ -10,6 +10,7 @@ import ( stereoscopeFile "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source/stereoscopesource" @@ -18,6 +19,7 @@ import ( func TestFileMetadataCataloger(t *testing.T) { testImage := "image-file-type-mix" + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", testImage) c := NewCataloger() @@ -157,6 +159,7 @@ func TestFileMetadataCataloger(t *testing.T) { func TestFileMetadataCataloger_GivenCoordinates(t *testing.T) { testImage := "image-file-type-mix" + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", testImage) c := NewCataloger() diff --git a/syft/file/cataloger/internal/all_regular_files_test.go b/syft/file/cataloger/internal/all_regular_files_test.go index cec05bf61..f421ba9c1 100644 --- a/syft/file/cataloger/internal/all_regular_files_test.go +++ b/syft/file/cataloger/internal/all_regular_files_test.go @@ -2,6 +2,7 @@ package internal import ( "context" + "runtime" "testing" "github.com/google/go-cmp/cmp" @@ -10,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source/directorysource" @@ -19,15 +21,16 @@ import ( func Test_allRegularFiles(t *testing.T) { tests := []struct { name string - setup func() file.Resolver + setup func(t *testing.T) file.Resolver wantRealPaths *strset.Set wantAccessPaths *strset.Set }{ { name: "image", - setup: func() file.Resolver { + setup: func(t *testing.T) file.Resolver { testImage := "image-file-type-mix" + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", testImage) s := stereoscopesource.New(img, stereoscopesource.ImageConfig{ @@ -44,7 +47,12 @@ func Test_allRegularFiles(t *testing.T) { }, { name: "directory", - setup: func() file.Resolver { + setup: func(t *testing.T) file.Resolver { + if runtime.GOOS == "windows" { + // file symlinks from a git checkout don't index as access paths on windows yet (same gap the + // fileresolver symlink fixtures skip for) + t.Skip("fixture relies on symlinks") + } s, err := directorysource.NewFromPath("testdata/symlinked-root/nested/link-root") require.NoError(t, err) r, err := s.FileResolver(source.SquashedScope) @@ -57,7 +65,7 @@ func Test_allRegularFiles(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - resolver := tt.setup() + resolver := tt.setup(t) locations := AllRegularFiles(context.Background(), resolver) realLocations := strset.New() virtualLocations := strset.New() diff --git a/syft/format/github/encoder_test.go b/syft/format/github/encoder_test.go index 865cce073..ce63d881c 100644 --- a/syft/format/github/encoder_test.go +++ b/syft/format/github/encoder_test.go @@ -2,6 +2,7 @@ package github import ( "flag" + "runtime" "testing" "github.com/anchore/syft/syft/format/internal/testutil" @@ -11,6 +12,10 @@ var updateSnapshot = flag.Bool("update-github", false, "update the *.golden file var updateImage = flag.Bool("update-image", false, "update the golden image used for image encoder testing") func TestGithubDirectoryEncoder(t *testing.T) { + if runtime.GOOS == "windows" { + // the golden snapshot embeds the posix source path (and the source ID derived from it) + t.Skip("directory snapshot is posix path specific") + } dir := t.TempDir() testutil.AssertEncoderAgainstGoldenSnapshot(t, testutil.EncoderSnapshotTestConfig{ diff --git a/syft/format/internal/testutil/image_input.go b/syft/format/internal/testutil/image_input.go index 881c0952a..08306a024 100644 --- a/syft/format/internal/testutil/image_input.go +++ b/syft/format/internal/testutil/image_input.go @@ -11,6 +11,7 @@ import ( "github.com/anchore/stereoscope/pkg/filetree" "github.com/anchore/stereoscope/pkg/image" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/linux" @@ -35,6 +36,7 @@ func ImageInput(t testing.TB, testImage string, options ...ImageOption) sbom.SBO case true: img = imagetest.GetGoldenFixtureImage(t, testImage) default: + testutils.SkipWithoutLinuxContainers(t) img = imagetest.GetFixtureImage(t, "docker-archive", testImage) } diff --git a/syft/format/syftjson/encoder_test.go b/syft/format/syftjson/encoder_test.go index c07581884..2415e5640 100644 --- a/syft/format/syftjson/encoder_test.go +++ b/syft/format/syftjson/encoder_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "flag" + "runtime" "strings" "testing" @@ -90,6 +91,10 @@ func TestEscapeHTML(t *testing.T) { } func TestDirectoryEncoder(t *testing.T) { + if runtime.GOOS == "windows" { + // the golden snapshot embeds the posix source path (and the source ID derived from it) + t.Skip("directory snapshot is posix path specific") + } cfg := DefaultEncoderConfig() cfg.Pretty = true enc, err := NewFormatEncoderWithConfig(cfg) diff --git a/syft/format/text/encoder_test.go b/syft/format/text/encoder_test.go index 2a11f1eb4..6ad39a31d 100644 --- a/syft/format/text/encoder_test.go +++ b/syft/format/text/encoder_test.go @@ -2,6 +2,7 @@ package text import ( "flag" + "runtime" "testing" "github.com/anchore/syft/syft/format/internal/testutil" @@ -11,6 +12,10 @@ var updateSnapshot = flag.Bool("update-text", false, "update the *.golden files var updateImage = flag.Bool("update-image", false, "update the golden image used for image encoder testing") func TestTextDirectoryEncoder(t *testing.T) { + if runtime.GOOS == "windows" { + // the golden snapshot embeds the posix source path (and the source ID derived from it) + t.Skip("directory snapshot is posix path specific") + } dir := t.TempDir() testutil.AssertEncoderAgainstGoldenSnapshot(t, testutil.EncoderSnapshotTestConfig{ diff --git a/syft/internal/fileresolver/chroot_context.go b/syft/internal/fileresolver/chroot_context.go index deb8cd000..8f7eef399 100644 --- a/syft/internal/fileresolver/chroot_context.go +++ b/syft/internal/fileresolver/chroot_context.go @@ -210,8 +210,13 @@ func (r ChrootContext) ToChrootPath(nativePath string) string { prefix = r.base } else { // we need to account for the cwd relative to the running process and the given root for the directory resolver - prefix = filepath.Clean(filepath.Join(r.cwd, r.cwdRelativeToRoot)) - prefix += string(filepath.Separator) + prefix = r.cwdRelativeToRoot + // on windows an absolute root stays absolute here (see ChangeDirectory), and joining it onto the cwd + // would nest one volume inside another (D:\cwd\C:\root) + if !filepath.IsAbs(prefix) { + prefix = filepath.Join(r.cwd, prefix) + } + prefix = filepath.Clean(prefix) + string(filepath.Separator) } responsePath = strings.TrimPrefix(responsePath, prefix) } diff --git a/syft/internal/fileresolver/chroot_context_windows_test.go b/syft/internal/fileresolver/chroot_context_windows_test.go new file mode 100644 index 000000000..1884c4219 --- /dev/null +++ b/syft/internal/fileresolver/chroot_context_windows_test.go @@ -0,0 +1,17 @@ +package fileresolver + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// windows CI runs from D: while t.TempDir() lives on C:, so the root and cwd don't share a volume +func TestChrootContext_ToChrootPath_rootOnAnotherVolume(t *testing.T) { + ctx := ChrootContext{ + root: `C:\scan\root`, + cwd: `D:\work`, + cwdRelativeToRoot: `C:\scan\root`, + } + assert.Equal(t, "dir/file.txt", ctx.ToChrootPath("/c/scan/root/dir/file.txt")) +} diff --git a/syft/internal/fileresolver/container_image_all_layers_test.go b/syft/internal/fileresolver/container_image_all_layers_test.go index 7d36baa8b..e8cc2750c 100644 --- a/syft/internal/fileresolver/container_image_all_layers_test.go +++ b/syft/internal/fileresolver/container_image_all_layers_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/file" ) @@ -91,6 +92,7 @@ func TestAllLayersResolver_FilesByPath(t *testing.T) { } for _, c := range cases { t.Run(c.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageAllLayers(img) @@ -201,6 +203,7 @@ func TestAllLayersResolver_FilesByGlob(t *testing.T) { } for _, c := range cases { t.Run(c.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageAllLayers(img) @@ -249,6 +252,7 @@ func Test_imageAllLayersResolver_FilesByMIMEType(t *testing.T) { } for _, test := range tests { t.Run(test.fixtureName, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureName) resolver, err := NewFromContainerImageAllLayers(img) @@ -266,6 +270,7 @@ func Test_imageAllLayersResolver_FilesByMIMEType(t *testing.T) { } func Test_imageAllLayersResolver_hasFilesystemIDInLocation(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-duplicate-path") resolver, err := NewFromContainerImageAllLayers(img) @@ -326,6 +331,7 @@ func TestAllLayersImageResolver_FilesContents(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageAllLayers(img) @@ -353,6 +359,7 @@ func TestAllLayersImageResolver_FilesContents(t *testing.T) { func TestAllLayersImageResolver_FilesContents_errorOnDirRequest(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageAllLayers(img) @@ -500,6 +507,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageAllLayers(img) @@ -514,6 +522,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { } func TestAllLayersResolver_AllLocations(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted") resolver, err := NewFromContainerImageAllLayers(img) diff --git a/syft/internal/fileresolver/container_image_deep_squash_test.go b/syft/internal/fileresolver/container_image_deep_squash_test.go index c1a6945a3..f88afdf13 100644 --- a/syft/internal/fileresolver/container_image_deep_squash_test.go +++ b/syft/internal/fileresolver/container_image_deep_squash_test.go @@ -16,6 +16,7 @@ import ( "go.uber.org/goleak" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/file" ) @@ -115,6 +116,7 @@ func Test_ContainerImageDeepSquash_FilesByPath(t *testing.T) { } for _, c := range cases { t.Run(c.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageDeepSquash(img) @@ -231,6 +233,7 @@ func Test_ContainerImageDeepSquash_FilesByGlob(t *testing.T) { } for _, c := range cases { t.Run(c.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageDeepSquash(img) @@ -288,6 +291,7 @@ func Test_ContainerImageDeepSquash_FilesByMIMEType(t *testing.T) { for _, test := range tests { t.Run(test.fixtureName, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureName) resolver, err := NewFromContainerImageDeepSquash(img) @@ -305,6 +309,7 @@ func Test_ContainerImageDeepSquash_FilesByMIMEType(t *testing.T) { } func Test_ContainerImageDeepSquash_hasFilesystemIDInLocation(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-duplicate-path") resolver, err := NewFromContainerImageDeepSquash(img) @@ -365,6 +370,7 @@ func Test_ContainerImageDeepSquash_FilesContents(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageDeepSquash(img) @@ -389,6 +395,7 @@ func Test_ContainerImageDeepSquash_FilesContents(t *testing.T) { } func Test_ContainerImageDeepSquash_FilesContents_errorOnDirRequest(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageDeepSquash(img) @@ -557,6 +564,7 @@ func Test_ContainerImageDeepSquash_resolvesLinks(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageDeepSquash(img) @@ -571,6 +579,7 @@ func Test_ContainerImageDeepSquash_resolvesLinks(t *testing.T) { } func Test_ContainerImageDeepSquash_AllLocations(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted") resolver, err := NewFromContainerImageDeepSquash(img) diff --git a/syft/internal/fileresolver/container_image_squash_test.go b/syft/internal/fileresolver/container_image_squash_test.go index b1ceaca0d..f9c8c45e8 100644 --- a/syft/internal/fileresolver/container_image_squash_test.go +++ b/syft/internal/fileresolver/container_image_squash_test.go @@ -16,6 +16,7 @@ import ( stereoscopeFile "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/file" ) @@ -75,6 +76,7 @@ func TestImageSquashResolver_FilesByPath(t *testing.T) { } for _, c := range cases { t.Run(c.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageSquash(img) @@ -184,6 +186,7 @@ func TestImageSquashResolver_FilesByGlob(t *testing.T) { } for _, c := range cases { t.Run(c.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageSquash(img) @@ -241,6 +244,7 @@ func Test_imageSquashResolver_FilesByMIMEType(t *testing.T) { for _, test := range tests { t.Run(test.fixtureName, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureName) resolver, err := NewFromContainerImageSquash(img) @@ -258,6 +262,7 @@ func Test_imageSquashResolver_FilesByMIMEType(t *testing.T) { } func Test_imageSquashResolver_hasFilesystemIDInLocation(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-duplicate-path") resolver, err := NewFromContainerImageSquash(img) @@ -316,6 +321,7 @@ func TestSquashImageResolver_FilesContents(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageSquash(img) @@ -341,6 +347,7 @@ func TestSquashImageResolver_FilesContents(t *testing.T) { func TestSquashImageResolver_FilesContents_errorOnDirRequest(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageSquash(img) @@ -499,6 +506,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks") resolver, err := NewFromContainerImageSquash(img) @@ -545,6 +553,7 @@ func locationSorter(a, b file.Location) int { } func TestSquashResolver_AllLocations(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted") resolver, err := NewFromContainerImageSquash(img) @@ -580,6 +589,7 @@ func TestSquashResolver_AllLocations(t *testing.T) { // content and metadata), so that image results are in parity with directory results (which cannot distinguish a // hardlink from a regular file). this must hold for both the squashed and all-layers resolvers. func TestImageResolvers_Hardlinks(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-hardlinks") resolvers := map[string]file.Resolver{} diff --git a/syft/internal/fileresolver/directory_indexer.go b/syft/internal/fileresolver/directory_indexer.go index e748a5015..3f6216d73 100644 --- a/syft/internal/fileresolver/directory_indexer.go +++ b/syft/internal/fileresolver/directory_indexer.go @@ -351,17 +351,12 @@ func (r directoryIndexer) addFileToIndex(p string, info os.FileInfo) error { func (r directoryIndexer) addSymlinkToIndex(p string, info os.FileInfo) (string, error) { linkTarget, err := os.Readlink(p) if err != nil { - isOnWindows := windows.HostRunningOnWindows() - if isOnWindows { - p = windows.FromPosix(p) - } - - linkTarget, err = filepath.EvalSymlinks(p) - - if isOnWindows { - p = windows.ToPosix(p) + nativePath := p + if windows.HostRunningOnWindows() { + nativePath = windows.FromPosix(p) } + linkTarget, err = filepath.EvalSymlinks(nativePath) if err != nil { return "", fmt.Errorf("unable to readlink for path=%q: %w", p, err) } @@ -414,16 +409,21 @@ func (r directoryIndexer) addSymlinkToIndex(p string, info os.FileInfo) (string, } } - ref, err := r.tree.AddSymLink(file.Path(p), file.Path(linkTarget)) - if err != nil { - return "", err - } - targetAbsPath := linkTarget if !filepath.IsAbs(targetAbsPath) { targetAbsPath = filepath.Clean(filepath.Join(path.Dir(p), linkTarget)) } + // the tree is posix (like p), but the joins above produce native separators on windows + if windows.HostRunningOnWindows() { + linkTarget = windows.ToPosix(linkTarget) + } + + ref, err := r.tree.AddSymLink(file.Path(p), file.Path(linkTarget)) + if err != nil { + return "", err + } + metadata := NewMetadataFromPath(p, info) metadata.LinkDestination = linkTarget r.index.Add(*ref, metadata) diff --git a/syft/internal/fileresolver/filetree_resolver.go b/syft/internal/fileresolver/filetree_resolver.go index 20c60b57a..d8a5f71d3 100644 --- a/syft/internal/fileresolver/filetree_resolver.go +++ b/syft/internal/fileresolver/filetree_resolver.go @@ -35,9 +35,19 @@ func nativeOSFileOpener(ref stereoscopeFile.Reference) (io.ReadCloser, error) { return stereoscopeFile.NewLazyReadCloser(filePath), nil } +// virtual reports whether the tree is not backed by the host filesystem (e.g. a squashfs), in which case there is +// no chroot and request/response paths are already the posix tree paths. Mapping them through the host (as the +// chroot does) would root them on the current volume on windows (/payload.txt -> /d/payload.txt). +func (r FiletreeResolver) virtual() bool { + return r.Chroot == ChrootContext{} +} + // requestPath converts a user (chroot) path into the path the file tree is keyed on. The tree is always posix // (windows paths are volume-encoded, e.g. /c/some/path), while ToNativePath yields a native host path. func (r *FiletreeResolver) requestPath(userPath string) (string, error) { + if r.virtual() { + return userPath, nil + } nativePath, err := r.Chroot.ToNativePath(userPath) if err != nil { return "", err @@ -50,6 +60,9 @@ func (r *FiletreeResolver) requestPath(userPath string) (string, error) { // responsePath takes a path from the underlying fs domain and converts it to a path that is relative to the root of the file resolver. func (r FiletreeResolver) responsePath(path string) string { + if r.virtual() { + return path + } return r.Chroot.ToChrootPath(path) } @@ -111,6 +124,9 @@ func (r FiletreeResolver) FilesByPath(userPaths ...string) ([]file.Location, err // requestGlob is the glob equivalent of requestPath. func (r FiletreeResolver) requestGlob(pattern string) (string, error) { + if r.virtual() { + return pattern, nil + } glob, err := r.Chroot.ToNativeGlob(pattern) if err != nil { return "", err diff --git a/syft/pkg/cataloger/ai/parse_safetensors_test.go b/syft/pkg/cataloger/ai/parse_safetensors_test.go index c5d4457cb..0fbdd5508 100644 --- a/syft/pkg/cataloger/ai/parse_safetensors_test.go +++ b/syft/pkg/cataloger/ai/parse_safetensors_test.go @@ -8,6 +8,7 @@ import ( "io" "math" "os" + "path" "path/filepath" "slices" "strings" @@ -522,14 +523,15 @@ func TestSafeTensorsMergeProcessor(t *testing.T) { t.Run("dir scan: config.json _name_or_path beats the parent directory fallback", func(t *testing.T) { // When a sibling config.json carries _name_or_path - dir := t.TempDir() - require.NoError(t, os.WriteFile(filepath.Join(dir, "config.json"), + // (location paths are posix, so keep the temp dir slash-separated for windows) + dir := filepath.ToSlash(t.TempDir()) + require.NoError(t, os.WriteFile(path.Join(dir, "config.json"), []byte(`{"_name_or_path":"org/preferred-name"}`), 0o644)) - stPath := filepath.Join(dir, "weights.safetensors") + stPath := path.Join(dir, "weights.safetensors") p := dirPkg(stPath, pkg.SafeTensorsModelInfo{ Format: "safetensors", TensorCount: 1, MetadataHash: "abc", }) - resolver := file.NewMockResolverForPaths(filepath.Join(dir, "config.json")) + resolver := file.NewMockResolverForPaths(path.Join(dir, "config.json")) out, _, err := safeTensorsMergeProcessor(context.Background(), resolver, []pkg.Package{p}, nil, nil) require.NoError(t, err) require.Len(t, out, 1) diff --git a/syft/pkg/cataloger/arch/parse_alpm_db.go b/syft/pkg/cataloger/arch/parse_alpm_db.go index db7505be6..ac63a4141 100644 --- a/syft/pkg/cataloger/arch/parse_alpm_db.go +++ b/syft/pkg/cataloger/arch/parse_alpm_db.go @@ -114,7 +114,7 @@ func fetchPkgFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, f func fetchBackupFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, file.Location, error) { // We only really do this to get any backup database entries from the files database - target := filepath.Join(base, "files") + target := path.Join(base, "files") loc, err := getLocation(target, resolver) if err != nil { @@ -395,7 +395,9 @@ func parseMtreeWithLimits(r io.Reader, maxSize int64, maxLines int) ([]pkg.AlpmF if ok := ignoredFiles[f.Name]; ok { continue } - path := fmt.Sprintf("/%s", f.Name) + // go-mtree runs filepath.Clean on entry names, which turns them into host paths on windows. + // ponytail: a vis-encoded backslash (\134) in a name gets mangled on windows only, unvis first if that matters + path := fmt.Sprintf("/%s", filepath.ToSlash(f.Name)) fileFields["path"] = path for _, kv := range f.Keywords { kw := string(kv.Keyword()) diff --git a/syft/pkg/cataloger/binary/classifier_cataloger_test.go b/syft/pkg/cataloger/binary/classifier_cataloger_test.go index 52768213c..a9d0a676e 100644 --- a/syft/pkg/cataloger/binary/classifier_cataloger_test.go +++ b/syft/pkg/cataloger/binary/classifier_cataloger_test.go @@ -17,6 +17,7 @@ import ( "github.com/anchore/packageurl-go" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" @@ -3169,6 +3170,7 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) { t.Run(test.name, func(t *testing.T) { c := NewClassifierCataloger(DefaultClassifierCatalogerConfig()) + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureImage) src := stereoscopesource.New(img, stereoscopesource.ImageConfig{ Reference: test.fixtureImage, diff --git a/syft/pkg/cataloger/binary/internal/manager/internal/config/binary_from_image_test.go b/syft/pkg/cataloger/binary/internal/manager/internal/config/binary_from_image_test.go index 55bb3ee40..8d7153e16 100644 --- a/syft/pkg/cataloger/binary/internal/manager/internal/config/binary_from_image_test.go +++ b/syft/pkg/cataloger/binary/internal/manager/internal/config/binary_from_image_test.go @@ -1,6 +1,7 @@ package config import ( + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -123,10 +124,10 @@ func TestAllStorePaths(t *testing.T) { }, dest: "dest", expected: []string{ - "dest/test/1.0/platform1/test1", - "dest/test/1.0/platform1/test2", - "dest/test/1.0/platform2/test1", - "dest/test/1.0/platform2/test2", + filepath.FromSlash("dest/test/1.0/platform1/test1"), + filepath.FromSlash("dest/test/1.0/platform1/test2"), + filepath.FromSlash("dest/test/1.0/platform2/test1"), + filepath.FromSlash("dest/test/1.0/platform2/test2"), }, }, } diff --git a/syft/pkg/cataloger/binary/internal/manager/internal/list_entries.go b/syft/pkg/cataloger/binary/internal/manager/internal/list_entries.go index de35b3731..ce54e936e 100644 --- a/syft/pkg/cataloger/binary/internal/manager/internal/list_entries.go +++ b/syft/pkg/cataloger/binary/internal/manager/internal/list_entries.go @@ -4,6 +4,7 @@ import ( "fmt" "iter" "os" + "path" "path/filepath" "slices" "sort" @@ -167,7 +168,7 @@ func getLogicalKey(managedBinaryPath string) (*LogicalEntryKey, error) { OrgName: items[0], Version: items[1], Platform: items[2], - Filename: filepath.Join(items[3:]...), + Filename: path.Join(items[3:]...), // posix, since this is part of the logical key }, nil } diff --git a/syft/pkg/cataloger/binary/internal/manager/internal/list_entries_test.go b/syft/pkg/cataloger/binary/internal/manager/internal/list_entries_test.go index 82d430346..3294c19c2 100644 --- a/syft/pkg/cataloger/binary/internal/manager/internal/list_entries_test.go +++ b/syft/pkg/cataloger/binary/internal/manager/internal/list_entries_test.go @@ -64,8 +64,8 @@ func TestListAllBinaries(t *testing.T) { assert.Equal(t, Entries{ - LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: "testdata/bin/busybox/1.3.6/linux-amd64/busybox", SnippetPath: ""}, - LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: "testdata/bin/busybox/1.3.6/linux-arm64/busybox", SnippetPath: ""}, + LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-amd64/busybox"), SnippetPath: ""}, + LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-arm64/busybox"), SnippetPath: ""}, }, entries, ) @@ -105,10 +105,10 @@ func TestListAllEntries(t *testing.T) { assert.Equal(t, Entries{ - LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: "testdata/bin/busybox/1.3.6/linux-amd64/busybox", SnippetPath: "testdata/snippets/busybox/1.3.6/linux-amd64/busybox"}, - LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: "testdata/bin/busybox/1.3.6/linux-arm64/busybox", SnippetPath: "testdata/snippets/busybox/1.3.6/linux-arm64/busybox"}, + LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-amd64/busybox"), SnippetPath: filepath.FromSlash("testdata/snippets/busybox/1.3.6/linux-amd64/busybox")}, + LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-arm64/busybox"), SnippetPath: filepath.FromSlash("testdata/snippets/busybox/1.3.6/linux-arm64/busybox")}, // note the standalone snippet! - LogicalEntryKey{OrgName: "postgres", Version: "9.6.10", Platform: "linux-amd64", Filename: "postgres"}: EntryInfo{IsConfigured: false, BinaryPath: "", SnippetPath: "testdata/snippets/postgres/9.6.10/linux-amd64/postgres"}, + LogicalEntryKey{OrgName: "postgres", Version: "9.6.10", Platform: "linux-amd64", Filename: "postgres"}: EntryInfo{IsConfigured: false, BinaryPath: "", SnippetPath: filepath.FromSlash("testdata/snippets/postgres/9.6.10/linux-amd64/postgres")}, }, entries, ) diff --git a/syft/pkg/cataloger/binary/internal/manager/internal/utils_test.go b/syft/pkg/cataloger/binary/internal/manager/internal/utils_test.go index ab8d7a111..51f5f8e54 100644 --- a/syft/pkg/cataloger/binary/internal/manager/internal/utils_test.go +++ b/syft/pkg/cataloger/binary/internal/manager/internal/utils_test.go @@ -1,6 +1,7 @@ package internal import ( + "path/filepath" "strings" "testing" @@ -13,7 +14,7 @@ func TestSplitFilepath(t *testing.T) { path string expected []string }{ - {path: "a/b/c", expected: []string{"a", "b", "c"}}, + {path: filepath.Join("a", "b", "c"), expected: []string{"a", "b", "c"}}, } for _, test := range tests { diff --git a/syft/pkg/cataloger/binary/internal/manager/testutil/snippet_or_binary.go b/syft/pkg/cataloger/binary/internal/manager/testutil/snippet_or_binary.go index 9a4ef20fc..9d81f2847 100644 --- a/syft/pkg/cataloger/binary/internal/manager/testutil/snippet_or_binary.go +++ b/syft/pkg/cataloger/binary/internal/manager/testutil/snippet_or_binary.go @@ -3,7 +3,9 @@ package testutil import ( "fmt" "os" + "path" "path/filepath" + "strings" "testing" "github.com/stretchr/testify/require" @@ -18,10 +20,11 @@ import ( // not specify the "bin" or "snippets" parent path... this is determined logically [snippets > binary unless told // otherwise]). Path should also be to the directory containing the binary or snippets of interest (not the binaries // or snippets itself). -func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string { +func SnippetOrBinary(t *testing.T, logicalPath string, requireBinary bool) string { t.Helper() - require.Len(t, internal.SplitFilepath(path), 3, "path must be a in the form //") + // logicalPath is a posix key, not a host path + require.Len(t, strings.Split(logicalPath, "/"), 3, "path must be a in the form //") // cd to testdata directory and load the config @@ -43,11 +46,11 @@ func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string { var fixturePath string for k, v := range entries.Sorted() { - if filepath.Dir(k.Path()) == path { + if path.Dir(k.Path()) == logicalPath { // prefer the snippet over the binary if !requireBinary { if v.SnippetPath != "" { - t.Logf("using snippet for %q", path) + t.Logf("using snippet for %q", logicalPath) require.NoError(t, validateSnippet(v.BinaryPath, v.SnippetPath)) fixturePath = v.SnippetPath break @@ -56,10 +59,10 @@ func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string { fixturePath = v.BinaryPath break } - t.Fatalf("no binary or snippet found for %q", path) + t.Fatalf("no binary or snippet found for %q", logicalPath) } if v.BinaryPath != "" { - t.Logf("forcing the use of the original binary for %q", path) + t.Logf("forcing the use of the original binary for %q", logicalPath) fixturePath = v.BinaryPath break } @@ -68,12 +71,12 @@ func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string { t.Skip("no binary found, but is covered by a snippet. Please add this case to the 'binary/testdata/config.yaml' and recreate the snippet") } - t.Fatalf("no binary found for %q", path) + t.Fatalf("no binary found for %q", logicalPath) } } if fixturePath == "" { - t.Fatalf("no fixture found for %q", path) + t.Fatalf("no fixture found for %q", logicalPath) } // this should be relative to the tests-fixtures directory and should be the directory containing the binary or @@ -101,6 +104,7 @@ func validateSnippet(binaryPath, snippetPath string) error { if err != nil { return err } + defer f.Close() expected, err := internal.Sha256SumFile(f) if err != nil { diff --git a/syft/pkg/cataloger/bitnami/cataloger.go b/syft/pkg/cataloger/bitnami/cataloger.go index 71f6518e2..2d845d6d1 100644 --- a/syft/pkg/cataloger/bitnami/cataloger.go +++ b/syft/pkg/cataloger/bitnami/cataloger.go @@ -5,7 +5,7 @@ package bitnami import ( "context" - "path/filepath" + "path" "strings" "github.com/anchore/syft/internal/log" @@ -73,7 +73,7 @@ func parseSBOM(_ context.Context, resolver file.Resolver, _ *generic.Environment // Bitnami packages reported in a SPDX file are shipped under the same directory // as the SPDX file itself. - metadata.Path = filepath.Dir(reader.RealPath) + metadata.Path = path.Dir(reader.RealPath) if p.ID() != mainPkgID { metadata.Files = packageFiles(s.Relationships, p, metadata.Path) secondaryPkgsFiles = append(secondaryPkgsFiles, metadata.Files...) diff --git a/syft/pkg/cataloger/bitnami/package.go b/syft/pkg/cataloger/bitnami/package.go index db29aec48..cb46d5453 100644 --- a/syft/pkg/cataloger/bitnami/package.go +++ b/syft/pkg/cataloger/bitnami/package.go @@ -3,7 +3,6 @@ package bitnami import ( "fmt" "path" - "path/filepath" "slices" "strings" @@ -73,7 +72,7 @@ func packageFiles(relationships []artifact.Relationship, p pkg.Package, baseDire // mainPkgFiles returns the files owned by the main package in the SPDX file. func mainPkgFiles(resolver file.Resolver, spdxFilePath string, secondaryPkgsFiles []string) ([]string, error) { - ownedPathGlob := fmt.Sprintf("%s/**", filepath.Dir(spdxFilePath)) + ownedPathGlob := fmt.Sprintf("%s/**", path.Dir(spdxFilePath)) ownedLocations, err := resolver.FilesByGlob(ownedPathGlob) if err != nil { return nil, err diff --git a/syft/pkg/cataloger/bitnami/parse_components.go b/syft/pkg/cataloger/bitnami/parse_components.go index 155a89831..d2eb6d592 100644 --- a/syft/pkg/cataloger/bitnami/parse_components.go +++ b/syft/pkg/cataloger/bitnami/parse_components.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" "fmt" - "path/filepath" + "path" "sort" "strings" @@ -67,7 +67,7 @@ func parseComponentsJSON(_ context.Context, _ file.Resolver, _ *generic.Environm Revision: revision, Architecture: entry.Arch, Distro: entry.Distro, - Path: filepath.Join(filepath.Dir(reader.RealPath), name), + Path: path.Join(path.Dir(reader.RealPath), name), } p := pkg.Package{ diff --git a/syft/pkg/cataloger/golang/license_finder_test.go b/syft/pkg/cataloger/golang/license_finder_test.go index 8be2f8d1d..81976bbe9 100644 --- a/syft/pkg/cataloger/golang/license_finder_test.go +++ b/syft/pkg/cataloger/golang/license_finder_test.go @@ -1,6 +1,9 @@ package golang import ( + "path/filepath" + "runtime" + "strings" "testing" "github.com/spf13/afero" @@ -202,10 +205,23 @@ func TestFindAllLicenseCandidatesUpwards(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Create in-memory filesystem fs := afero.NewMemMapFs() - tt.setupFS(fs) + setupFS, startDir, stopAt, expectedFiles := afero.Fs(fs), tt.startDir, tt.stopAt, tt.expectedFiles + if runtime.GOOS == "windows" { + // the finder wants native absolute paths, so root the posix-style cases on a drive + setupFS = afero.NewBasePathFs(fs, `C:\`) + startDir, stopAt = winAbs(startDir), winAbs(stopAt) + expectedFiles = nil + if tt.expectedFiles != nil { + expectedFiles = []string{} + } + for _, f := range tt.expectedFiles { + expectedFiles = append(expectedFiles, winAbs(f)) + } + } + tt.setupFS(setupFS) // Run the function - result, err := findAllLicenseCandidatesUpwards(tt.startDir, tt.stopAt, fs) + result, err := findAllLicenseCandidatesUpwards(startDir, stopAt, fs) // Check error expectation if tt.expectedError { @@ -214,7 +230,15 @@ func TestFindAllLicenseCandidatesUpwards(t *testing.T) { } require.NoError(t, err, tt.description) - assert.Equal(t, tt.expectedFiles, result, tt.description) + assert.Equal(t, expectedFiles, result, tt.description) }) } } + +// winAbs roots posix absolute paths on C:\, leaving relative ones relative (some cases assert they're rejected) +func winAbs(p string) string { + if !strings.HasPrefix(p, "/") { + return p + } + return filepath.Join(`C:\`, filepath.FromSlash(p)) +} diff --git a/syft/pkg/cataloger/golang/parse_go_binary_test.go b/syft/pkg/cataloger/golang/parse_go_binary_test.go index 3f704c5d3..7e33b0155 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary_test.go +++ b/syft/pkg/cataloger/golang/parse_go_binary_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/cataloging" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/internal/fileresolver" @@ -30,6 +31,8 @@ import ( // make will run the default make target for the given test fixture path func runMakeTarget(t *testing.T, fixtureName string) { + // the fixture binaries are built inside linux containers + testutils.SkipWithoutLinuxContainers(t) cwd, err := os.Getwd() require.NoError(t, err) fixtureDir := filepath.Join(cwd, "testdata/", fixtureName) diff --git a/syft/pkg/cataloger/golang/parse_go_mod.go b/syft/pkg/cataloger/golang/parse_go_mod.go index e9d021729..c4a727987 100644 --- a/syft/pkg/cataloger/golang/parse_go_mod.go +++ b/syft/pkg/cataloger/golang/parse_go_mod.go @@ -20,6 +20,7 @@ import ( "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/internal/fileresolver" + "github.com/anchore/syft/syft/internal/windows" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/generic" ) @@ -38,7 +39,12 @@ func newGoModCataloger(opts CatalogerConfig) *goModCataloger { // parseGoModFile takes a go.mod and tries to resolve and lists all packages discovered. func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) (pkgs []pkg.Package, relationships []artifact.Relationship, err error) { - modDir := filepath.Dir(string(reader.Location.Reference().RealPath)) + realPath := string(reader.Location.Reference().RealPath) + if windows.HostRunningOnWindows() { + // directory resolvers hand back volume-encoded posix paths (/c/...), the go toolchain needs the native path + realPath = windows.FromPosix(realPath) + } + modDir := filepath.Dir(realPath) digests, err := parseGoSumFile(resolver, reader) if err != nil { log.Debugf("unable to get go.sum: %v", err) diff --git a/syft/pkg/cataloger/golang/upx_test.go b/syft/pkg/cataloger/golang/upx_test.go index 05df518e2..928211874 100644 --- a/syft/pkg/cataloger/golang/upx_test.go +++ b/syft/pkg/cataloger/golang/upx_test.go @@ -10,6 +10,7 @@ import ( stereoscopeFile "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/internal/testutils" ) // every case here is one the magic scan must reject, asserted on parseUPXInfo's errNotUPX result. @@ -114,6 +115,7 @@ func TestParseUPXInfo_ValidHeader(t *testing.T) { // ignores that padding if there is some. A build that needs none passes either way, which is how the bug // went unnoticed. The case without padding is covered by TestDecompressUPX_TailExtentsArePlacedPastTheLoader. func TestImageSmallUPXNeedsLoaderPadding(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", "image-small-upx") rc, err := img.OpenPathFromSquash(stereoscopeFile.Path("/run-me")) require.NoError(t, err) diff --git a/syft/pkg/cataloger/internal/dotnet/pe/pe_test.go b/syft/pkg/cataloger/internal/dotnet/pe/pe_test.go index b612e7bc0..ca518ac03 100644 --- a/syft/pkg/cataloger/internal/dotnet/pe/pe_test.go +++ b/syft/pkg/cataloger/internal/dotnet/pe/pe_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source/stereoscopesource" @@ -199,6 +200,7 @@ func fixtureResolvers(t *testing.T, fixtures ...string) map[string]file.Resolver continue } + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", fixture) s := stereoscopesource.New(img, stereoscopesource.ImageConfig{ diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go index e74a3a06e..22042fcf1 100644 --- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go +++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go @@ -24,6 +24,7 @@ import ( "github.com/anchore/syft/internal/cmptest" "github.com/anchore/syft/internal/licenses" "github.com/anchore/syft/internal/relationship" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/internal/tmpdir" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" @@ -150,9 +151,11 @@ func (p *CatalogTester) FromFile(t *testing.T, path string) *CatalogTester { fixture, err := os.Open(path) require.NoError(t, err) + // parsers don't own the reader (the generic cataloger closes it), so close it here + t.Cleanup(func() { _ = fixture.Close() }) p.reader = file.LocationReadCloser{ - Location: file.NewVirtualLocationFromDirectory(fixture.Name(), fixture.Name(), *stereofile.NewFileReference(stereofile.Path(absPath))), + Location: file.NewVirtualLocationFromDirectory(filepath.ToSlash(fixture.Name()), filepath.ToSlash(fixture.Name()), *stereofile.NewFileReference(stereofile.Path(absPath))), ReadCloser: fixture, } return p @@ -201,6 +204,7 @@ func (p *CatalogTester) WithImageResolver(t *testing.T, fixtureName string) *Cat return p } + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", fixtureName) s := stereoscopesource.New(img, stereoscopesource.ImageConfig{ diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index 84167c5ef..c586a421e 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -6,6 +6,7 @@ import ( "io" "os" "os/exec" + "path" "path/filepath" "strings" "syscall" @@ -18,6 +19,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/license" @@ -1605,6 +1607,9 @@ func generateJavaBuildFixture(t *testing.T, fixturePath string) { return } + // the fixtures are built with maven/gradle inside linux containers + testutils.SkipWithoutLinuxContainers(t) + makeTask := strings.TrimPrefix(fixturePath, "testdata/java-builds/") t.Log(color.Bold.Sprintf("Generating Fixture from 'make %s'", makeTask)) @@ -1630,7 +1635,8 @@ func generateJavaMetadataJarFixture(t *testing.T, fixtureName string, fileExtens return fixturePath } - makeTask := filepath.Join("cache", fixtureName+"."+fileExtension) + // make targets are posix paths regardless of host + makeTask := path.Join("cache", fixtureName+"."+fileExtension) t.Log(color.Bold.Sprintf("Generating Fixture from 'make %s'", makeTask)) cwd, err := os.Getwd() diff --git a/syft/pkg/cataloger/java/internal/maven/config_test.go b/syft/pkg/cataloger/java/internal/maven/config_test.go index 1a6ab4c50..80de474bd 100644 --- a/syft/pkg/cataloger/java/internal/maven/config_test.go +++ b/syft/pkg/cataloger/java/internal/maven/config_test.go @@ -47,6 +47,8 @@ func Test_defaultMavenLocalRepoDir(t *testing.T) { home, err := filepath.Abs(test.home) require.NoError(t, err) t.Setenv("HOME", home) + // os.UserHomeDir reads USERPROFILE on windows + t.Setenv("USERPROFILE", home) } got := defaultMavenLocalRepoDir() require.Equal(t, test.expected, got) diff --git a/syft/pkg/cataloger/java/internal/maven/test/mock_repo.go b/syft/pkg/cataloger/java/internal/maven/test/mock_repo.go index 6ab462f6b..a2ed64895 100644 --- a/syft/pkg/cataloger/java/internal/maven/test/mock_repo.go +++ b/syft/pkg/cataloger/java/internal/maven/test/mock_repo.go @@ -31,7 +31,7 @@ func MockRepo(t *testing.T, dir string) (url string) { t.Cleanup(server.Close) - matches, err := doublestar.Glob(os.DirFS(dir), filepath.Join("**", "*.pom")) + matches, err := doublestar.Glob(os.DirFS(dir), "**/*.pom") require.NoError(t, err) for _, match := range matches { diff --git a/syft/pkg/cataloger/perl/cataloger_test.go b/syft/pkg/cataloger/perl/cataloger_test.go index 78f9551b7..6fad8926a 100644 --- a/syft/pkg/cataloger/perl/cataloger_test.go +++ b/syft/pkg/cataloger/perl/cataloger_test.go @@ -8,6 +8,7 @@ import ( "github.com/anchore/stereoscope/pkg/imagetest" "github.com/anchore/syft/internal/packagemetadata" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" @@ -523,6 +524,7 @@ func TestCpanCatalogers_metadataTypes(t *testing.T) { func imageResolver(t *testing.T, fixture string) file.Resolver { t.Helper() + testutils.SkipWithoutLinuxContainers(t) img := imagetest.GetFixtureImage(t, "docker-archive", fixture) resolver, err := stereoscopesource.New(img, stereoscopesource.ImageConfig{Reference: fixture}). FileResolver(source.SquashedScope) diff --git a/syft/pkg/cataloger/python/parse_wheel_egg.go b/syft/pkg/cataloger/python/parse_wheel_egg.go index 0c4cf1563..33d2d0161 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg.go @@ -5,7 +5,6 @@ import ( "context" "encoding/json" "path" - "path/filepath" "strings" "github.com/scylladb/go-set/strset" @@ -53,7 +52,7 @@ func fetchInstalledFiles(resolver file.Resolver, metadataLocation file.Location, // to reconcile the installed-files.txt path to the same layer (or the next adjacent lower layer). // find the installed-files.txt file relative to the directory where the METADATA file resides (in path AND layer structure) - installedFilesPath := filepath.Join(filepath.Dir(metadataLocation.RealPath), "installed-files.txt") + installedFilesPath := path.Join(path.Dir(metadataLocation.RealPath), "installed-files.txt") installedFilesRef := resolver.RelativeFileByPath(metadataLocation, installedFilesPath) if installedFilesRef != nil { @@ -83,7 +82,7 @@ func fetchRecordFiles(resolver file.Resolver, metadataLocation file.Location) (f // to reconcile the RECORD path to the same layer (or the next adjacent lower layer). // find the RECORD file relative to the directory where the METADATA file resides (in path AND layer structure) - recordPath := filepath.Join(filepath.Dir(metadataLocation.RealPath), "RECORD") + recordPath := path.Join(path.Dir(metadataLocation.RealPath), "RECORD") recordRef := resolver.RelativeFileByPath(metadataLocation, recordPath) if recordRef != nil { @@ -107,8 +106,8 @@ func fetchRecordFiles(resolver file.Resolver, metadataLocation file.Location) (f // fetchTopLevelPackages finds a corresponding top_level.txt file for the given python package metadata file and returns the set of package names contained. func fetchTopLevelPackages(resolver file.Resolver, metadataLocation file.Location) (pkgs []string, sources []file.Location, err error) { // a top_level.txt file specifies the python top-level packages (provided by this python package) installed into site-packages - parentDir := filepath.Dir(metadataLocation.RealPath) - topLevelPath := filepath.Join(parentDir, "top_level.txt") + parentDir := path.Dir(metadataLocation.RealPath) + topLevelPath := path.Join(parentDir, "top_level.txt") topLevelLocation := resolver.RelativeFileByPath(metadataLocation, topLevelPath) if topLevelLocation == nil { @@ -157,8 +156,8 @@ type vcsInfo struct { } func fetchDirectURLData(resolver file.Resolver, metadataLocation file.Location) (d *pkg.PythonDirectURLOriginInfo, sources []file.Location, err error) { - parentDir := filepath.Dir(metadataLocation.RealPath) - directURLPath := filepath.Join(parentDir, "direct_url.json") + parentDir := path.Dir(metadataLocation.RealPath) + directURLPath := path.Join(parentDir, "direct_url.json") directURLLocation := resolver.RelativeFileByPath(metadataLocation, directURLPath) if directURLLocation == nil { @@ -269,7 +268,7 @@ func findLicenses(ctx context.Context, resolver file.Resolver, m parsedData) pkg continue } - if licenses.IsLicenseFile(filepath.Base(f.Path)) { + if licenses.IsLicenseFile(path.Base(f.Path)) { candidatePaths.Add(path.Join(m.SitePackagesRootPath, f.Path)) } } diff --git a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go index 5b0572e3a..05c49f5f4 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg_metadata.go @@ -2,7 +2,7 @@ package python import ( "fmt" - "path/filepath" + "path" "strings" "github.com/bmatcuk/doublestar/v4" @@ -48,13 +48,13 @@ func parseWheelOrEggMetadata(locationReader file.LocationReadCloser) (parsedData } // add additional metadata not stored in the egg/wheel metadata file - path := locationReader.Path() + metadataPath := locationReader.Path() - pd.SitePackagesRootPath = determineSitePackagesRootPath(path) + pd.SitePackagesRootPath = determineSitePackagesRootPath(metadataPath) if pd.Licenses != "" || pd.LicenseExpression != "" { - pd.LicenseFilePath = path + pd.LicenseFilePath = metadataPath } else if pd.LicenseFile != "" { - pd.LicenseFilePath = filepath.Join(filepath.Dir(path), pd.LicenseFile) + pd.LicenseFilePath = path.Join(path.Dir(metadataPath), pd.LicenseFile) } pd.DistInfoLocation = locationReader.Location @@ -144,18 +144,18 @@ func getFieldType(key, in string) any { // isEggRegularFile determines if the specified path is the regular file variant // of egg metadata (as opposed to a directory that contains more metadata // files). -func isEggRegularFile(path string) bool { - return doublestar.MatchUnvalidated(eggInfoGlob, path) +func isEggRegularFile(p string) bool { + return doublestar.MatchUnvalidated(eggInfoGlob, p) } // determineSitePackagesRootPath returns the path of the site packages root, // given the egg metadata file or directory specified in the path. -func determineSitePackagesRootPath(path string) string { - if isEggRegularFile(path) { - return filepath.Clean(filepath.Dir(path)) +func determineSitePackagesRootPath(p string) string { + if isEggRegularFile(p) { + return path.Clean(path.Dir(p)) } - return filepath.Clean(filepath.Dir(filepath.Dir(path))) + return path.Clean(path.Dir(path.Dir(p))) } // handleFieldBodyContinuation returns the updated value for the specified field after processing the specified line. diff --git a/syft/pkg/cataloger/python/parse_wheel_egg_record.go b/syft/pkg/cataloger/python/parse_wheel_egg_record.go index 94982f10b..12c983673 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg_record.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg_record.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "path" "path/filepath" "strings" @@ -87,11 +88,13 @@ func parseInstalledFiles(reader io.Reader, location, sitePackagesRootPath string } if location != "" && sitePackagesRootPath != "" { - joinedPath := filepath.Join(filepath.Dir(location), line) + joinedPath := path.Join(path.Dir(location), line) + // the path package has no Rel, and filepath.Rel hands back host separators on windows line, err = filepath.Rel(sitePackagesRootPath, joinedPath) if err != nil { return nil, err } + line = filepath.ToSlash(line) } installedFile := pkg.PythonFileRecord{ diff --git a/syft/pkg/cataloger/swift/parse_package_resolved_test.go b/syft/pkg/cataloger/swift/parse_package_resolved_test.go index 494a164c1..98803e43d 100644 --- a/syft/pkg/cataloger/swift/parse_package_resolved_test.go +++ b/syft/pkg/cataloger/swift/parse_package_resolved_test.go @@ -122,8 +122,7 @@ func TestParsePackageResolved_empty(t *testing.T) { dir := t.TempDir() fixture = filepath.Join(dir, "Package.resolved") - _, err := os.Create(fixture) - require.NoError(t, err) + require.NoError(t, os.WriteFile(fixture, nil, 0600)) pkgtest.TestFileParser(t, fixture, parsePackageResolved, nil, nil) } diff --git a/syft/source/snapsource/snap_source_test.go b/syft/source/snapsource/snap_source_test.go index 6a4b23e4e..bf03a59a4 100644 --- a/syft/source/snapsource/snap_source_test.go +++ b/syft/source/snapsource/snap_source_test.go @@ -30,6 +30,10 @@ import ( ) func TestNewFromLocal(t *testing.T) { + // the request is resolved with filepath.Abs, which adds a volume on windows + localSnap, err := filepath.Abs("/test/local.snap") + require.NoError(t, err) + tests := []struct { name string cfg Config @@ -40,13 +44,13 @@ func TestNewFromLocal(t *testing.T) { { name: "local file exists", cfg: Config{ - Request: "/test/local.snap", + Request: localSnap, DigestAlgorithms: []crypto.Hash{crypto.SHA256}, }, setup: func(fs afero.Fs) { - require.NoError(t, createMockSquashfsFile(fs, "/test/local.snap")) + require.NoError(t, createMockSquashfsFile(fs, localSnap)) }, - wantRequest: "/test/local.snap", + wantRequest: localSnap, }, { name: "resolve home dir exists", @@ -65,18 +69,18 @@ func TestNewFromLocal(t *testing.T) { { name: "local file with architecture specified", cfg: Config{ - Request: "/test/local.snap", + Request: localSnap, Platform: &image.Platform{ Architecture: "arm64", }, }, setup: func(fs afero.Fs) { - require.NoError(t, createMockSquashfsFile(fs, "/test/local.snap")) + require.NoError(t, createMockSquashfsFile(fs, localSnap)) }, wantErr: func(t assert.TestingT, err error, msgAndArgs ...any) bool { return assert.Error(t, err, msgAndArgs...) && assert.Contains(t, err.Error(), "architecture cannot be specified for local snap files", msgAndArgs...) }, - wantRequest: "/test/local.snap", + wantRequest: localSnap, }, } for _, tt := range tests { @@ -288,6 +292,8 @@ func writeSquashfs(t *testing.T, manifest string) string { require.NoError(t, err) _, err = w.Write([]byte(contents)) require.NoError(t, err) + // close before finalizing: windows directory listings can report a stale (zero) size for open files + require.NoError(t, w.Close()) } // note: go-diskfs wants paths relative to the image root, without a leading slash diff --git a/syft/source/snapsource/snap_test.go b/syft/source/snapsource/snap_test.go index e6b3ac450..a55d4e664 100644 --- a/syft/source/snapsource/snap_test.go +++ b/syft/source/snapsource/snap_test.go @@ -145,7 +145,9 @@ func TestNewSnapFromFile(t *testing.T) { DigestAlgorithms: []crypto.Hash{crypto.SHA256}, }, setup: func() string { - path := "/test/valid.snap" + // newSnapFromFile resolves the request with filepath.Abs, which adds a volume on windows + path, err := filepath.Abs("/test/valid.snap") + require.NoError(t, err) require.NoError(t, createMockSquashfsFile(fs, path)) return path }, @@ -179,7 +181,9 @@ func TestNewSnapFromFile(t *testing.T) { name: "path is directory", cfg: Config{}, setup: func() string { - path := "/test/directory" + // newSnapFromFile resolves the request with filepath.Abs, which adds a volume on windows + path, err := filepath.Abs("/test/directory") + require.NoError(t, err) require.NoError(t, fs.MkdirAll(path, 0755)) return path }, @@ -471,7 +475,9 @@ func TestGetSnapFileInfo(t *testing.T) { { name: "path is directory", setup: func() string { - path := "/test/directory" + // newSnapFromFile resolves the request with filepath.Abs, which adds a volume on windows + path, err := filepath.Abs("/test/directory") + require.NoError(t, err) require.NoError(t, fs.MkdirAll(path, 0755)) return path }, diff --git a/syft/source/stereoscopesource/image_source_test.go b/syft/source/stereoscopesource/image_source_test.go index 4b8f709af..fc42d12a4 100644 --- a/syft/source/stereoscopesource/image_source_test.go +++ b/syft/source/stereoscopesource/image_source_test.go @@ -10,6 +10,7 @@ import ( "github.com/anchore/stereoscope" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/internal/testutils" "github.com/anchore/syft/syft/internal/testutil" "github.com/anchore/syft/syft/source" ) @@ -78,6 +79,7 @@ func Test_StereoscopeImage_Exclusions(t *testing.T) { for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { + testutils.SkipWithoutLinuxContainers(t) imageName := strings.SplitN(imagetest.PrepareFixtureImage(t, "docker-archive", test.input), ":", 2)[1] img, err := stereoscope.GetImage(context.TODO(), imageName) From 29b92a58c188a5bf0247d321e6d863b9ab505d7b Mon Sep 17 00:00:00 2001 From: Bruce <37983148+Bru456@users.noreply.github.com> Date: Mon, 5 Oct 2026 16:02:37 +0100 Subject: [PATCH 14/14] Add `container-storage` support for Podman and Buildah (#5368) * feat: add containers-storage support for Podman and Buildah, update tests and documentation Signed-off-by: Bruce Clark * test: update containers-storage tests to validate package.json and handle missing images Signed-off-by: Bruce Clark * test: fix rootless containers-storage test setup Install uidmap for Podman and Buildah CI jobs. Align temporary rootful and rootless storage paths. Clean up rootless stores within the builder's user namespace. Signed-off-by: Bruce Clark * fix: only use containers-storage when explicitly requested - containers-storage is no longer part of automatic image resolution; it's used only for `--from containers-storage`, the `containers-storage:` scheme, or `default-pull-source: containers-storage` (now an accepted value). Opening a store runs full graph driver init (mounts, locks, store writes), which is too invasive as a probe on every plain image reference. - rootless stores must be read from inside the builder's user namespace (`podman unshare syft ...`), documented in the README. - the `containers_image_openpgp` tag is now applied to unit/integration tests and lint, so dev tooling compiles the same provider that ships. - containers-storage CI runs through `make containers-storage-test` and covers both vfs and overlay stores. Signed-off-by: Alex Goodman * fix: exclude cgo btrfs driver and unmount overlay store in cleanup - cgo-enabled linux builds (`-race` tests, lint) compile go.podman.io's btrfs graph driver, which needs libbtrfs headers. Add `exclude_graphdriver_btrfs` alongside `containers_image_openpgp` everywhere the tag is set; release builds are `CGO_ENABLED=0` and never included it. - the overlay driver bind-mounts its graphroot inside the rootless user namespace, so the containers-storage test unmounts it before removing the store, and cleanup no longer fails the run. Signed-off-by: Alex Goodman --------- Signed-off-by: Bruce Clark Signed-off-by: Alex Goodman Co-authored-by: Bruce Clark Co-authored-by: Alex Goodman --- .github/workflows/release.yaml | 2 +- .github/workflows/validations.yaml | 66 ++++++++++++ .golangci.yaml | 4 + .goreleaser.yaml | 1 + .make/main.go | 29 +++++- README.md | 12 +++ cmd/syft/internal/options/source.go | 4 +- syft/get_source_config.go | 16 +++ syft/get_source_config_test.go | 63 ++++++++++- test/containers-storage/source-test.sh | 138 +++++++++++++++++++++++++ 10 files changed, 328 insertions(+), 7 deletions(-) create mode 100755 test/containers-storage/source-test.sh diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 72ec17297..d1cad71b8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -46,7 +46,7 @@ jobs: # these are checks that should be run on pull-request and merges to main. # we do NOT want to kick off a release if these have not been verified on main. # Please see the validations.yaml workflow for the names that should be used here. - checks: '["Acceptance tests (Linux)", "Acceptance tests (Mac)", "Build snapshot artifacts", "CLI tests (Linux)", "Integration tests", "Static analysis", "Unit tests"]' + checks: '["Acceptance tests (Linux)", "Acceptance tests (Mac)", "Build snapshot artifacts", "CLI tests (Linux)", "Containers-storage tests (Podman)", "Containers-storage tests (Buildah)", "Integration tests", "Static analysis", "Unit tests"]' release: needs: [check-gate, version-available] diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 942ab1eb2..998ba78ef 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -177,6 +177,72 @@ jobs: path: snapshot/ retention-days: 30 + Containers-Storage-Podman: + name: "Containers-storage tests (Podman)" + needs: [Build-Snapshot-Artifacts] + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1 + with: + persist-credentials: false + + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1 + with: + name: snapshot + path: snapshot + + # go-make's setup only (not ./.github/actions/bootstrap): the test only needs make + the snapshot binary + - name: Setup go + go-make tooling + uses: anchore/go-make/.github/actions/setup@fa5421b3bf24c9d18f11a736c5eb4172f2b7d683 # v0.8.1 + with: + # keep in sync with the go-version default in ./.github/actions/bootstrap + go-version: "1.26.2" + + - name: Install Podman + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends -y jq podman uidmap + + - name: Scan image from Podman's local store + run: make containers-storage-test + env: + BUILDER: podman + + Containers-Storage-Buildah: + name: "Containers-storage tests (Buildah)" + needs: [Build-Snapshot-Artifacts] + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1 + with: + persist-credentials: false + + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1 + with: + name: snapshot + path: snapshot + + # go-make's setup only (not ./.github/actions/bootstrap): the test only needs make + the snapshot binary + - name: Setup go + go-make tooling + uses: anchore/go-make/.github/actions/setup@fa5421b3bf24c9d18f11a736c5eb4172f2b7d683 # v0.8.1 + with: + # keep in sync with the go-version default in ./.github/actions/bootstrap + go-version: "1.26.2" + + - name: Install Buildah + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends -y buildah jq uidmap + + - name: Scan image from Buildah's local store + run: make containers-storage-test + env: + BUILDER: buildah + Acceptance-Linux: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline name: "Acceptance tests (Linux)" diff --git a/.golangci.yaml b/.golangci.yaml index 5aa2a4905..bdb4e0a22 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,6 +1,10 @@ version: "2" run: tests: false + # keep in sync with buildTags in .make/main.go + build-tags: + - containers_image_openpgp + - exclude_graphdriver_btrfs linters: default: none enable: diff --git a/.goreleaser.yaml b/.goreleaser.yaml index a06e04762..4f4470432 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -15,6 +15,7 @@ builds: binary: syft goos: [linux] goarch: [amd64, arm64, ppc64le, riscv64, s390x] + tags: [containers_image_openpgp, exclude_graphdriver_btrfs] mod_timestamp: &build-timestamp '{{ .CommitTimestamp }}' ldflags: &build-ldflags | -w diff --git a/.make/main.go b/.make/main.go index dc18a31d7..73f398454 100644 --- a/.make/main.go +++ b/.make/main.go @@ -16,6 +16,12 @@ import ( "github.com/anchore/go-make/tasks/gotest" ) +// buildTags must match the linux build in .goreleaser.yaml so tests and lint compile the same code that ships +// (containers_image_openpgp compiles in stereoscope's real containers-storage provider instead of its stub). +// exclude_graphdriver_btrfs drops the cgo-only btrfs driver, which needs libbtrfs headers whenever cgo is on (e.g. +// under -race); release builds are CGO_ENABLED=0 and never include it anyway. +const buildTags = "containers_image_openpgp,exclude_graphdriver_btrfs" + func main() { Makefile( // shared anchore tasks @@ -29,6 +35,7 @@ func main() { gotest.Name("unit"), gotest.ExcludeGlob("**/test/**"), gotest.CoverageThreshold(62), + gotest.Tags(buildTags), race(), ), @@ -52,7 +59,7 @@ func main() { raceFlag = " -race" } Run( - "go test -count=1 -timeout=30m"+raceFlag+" ./cmd/syft/internal/test/integration/...", + "go test -count=1 -timeout=30m -tags="+buildTags+raceFlag+" ./cmd/syft/internal/test/integration/...", run.Env("GODEBUG", "dontfreezetheworld=1"), ) }, @@ -66,7 +73,7 @@ func main() { Log("race detector disabled (RACE=false); skipping race smoke") return } - Run("go run -race cmd/syft/main.go anchore/test_images:grype-quality-dotnet-69f15d2") + Run("go run -race -tags="+buildTags+" cmd/syft/main.go anchore/test_images:grype-quality-dotnet-69f15d2") }, }, @@ -96,6 +103,24 @@ func main() { }, }, + // containers-storage tests: build a fixture image with BUILDER (podman or buildah) into an isolated local store + // and scan it with the snapshot binary. Linux only, and not hooked into "test" since it needs the builder installed. + Task{ + Name: "containers-storage-test", + Description: "Run containers-storage tests (BUILDER=podman|buildah)", + Run: func() { + bin := snapshotBinPath() + if !file.Exists(bin) { + Log("snapshot binary not found at %s; building single-target snapshot", bin) + Run("make snapshot:single-target") + } + Run( + "bash test/containers-storage/source-test.sh "+config.Env("BUILDER", "podman"), + run.Env("SYFT_BINARY_LOCATION", bin), + ) + }, + }, + // default validation pipeline (replaces Taskfile `default`/`pr-validations`/`validations`). Task{ Name: "default", diff --git a/README.md b/README.md index d864e1428..9ffd2affa 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,18 @@ syft -o cyclonedx-json syft -o spdx-json=./spdx.json -o cyclonedx-json=./cdx.json ``` +### Local containers-storage images + +On Linux, syft can scan images from a local containers-storage store (e.g. built with Buildah or Podman) when asked explicitly; plain image references never look there: + +```bash +syft --from containers-storage localhost/myimage:latest + +# rootless stores must be read from inside the builder's user namespace +podman unshare syft --from containers-storage localhost/myimage:latest +``` + +This is included in the Linux release binaries. When building from source, add `-tags containers_image_openpgp,exclude_graphdriver_btrfs`. > [!TIP] > **Check out the [Getting Started guide](https://oss.anchore.com/docs/guides/sbom/getting-started/)** to explore all of the capabilities and features. diff --git a/cmd/syft/internal/options/source.go b/cmd/syft/internal/options/source.go index 76cae4580..2c517946b 100644 --- a/cmd/syft/internal/options/source.go +++ b/cmd/syft/internal/options/source.go @@ -36,7 +36,7 @@ var _ clio.PostLoader = (*imageSource)(nil) func (o *sourceConfig) DescribeFields(descriptions clio.FieldDescriptionSet) { descriptions.Add(&o.File.Digests, `the file digest algorithms to use on the scanned file (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512")`) descriptions.Add(&o.Image.DefaultPullSource, `allows users to specify which image source should be used to generate the sbom -valid values are: registry, docker, podman`) +valid values are: registry, docker, podman, containers-storage`) } type imageSource struct { @@ -74,7 +74,7 @@ func (c *imageSource) PostLoad() error { return checkDefaultSourceValues(c.DefaultPullSource) } -var validDefaultSourceValues = []string{"registry", "docker", "podman", ""} +var validDefaultSourceValues = []string{"registry", "docker", "podman", "containers-storage", ""} func checkDefaultSourceValues(source string) error { validValues := strset.New(validDefaultSourceValues...) diff --git a/syft/get_source_config.go b/syft/get_source_config.go index 7a163f1d1..735dba516 100644 --- a/syft/get_source_config.go +++ b/syft/get_source_config.go @@ -3,6 +3,7 @@ package syft import ( "crypto" "fmt" + "slices" "github.com/anchore/go-collections" "github.com/anchore/stereoscope/pkg/image" @@ -10,6 +11,11 @@ import ( "github.com/anchore/syft/syft/source/sourceproviders" ) +// GetSourceConfig controls which source providers are tried, and in what order, when resolving user input to a source. +// +// The "containers-storage" provider (local Buildah / Podman store) is never part of automatic resolution: it is only +// used when named in Sources or DefaultImagePullSource. It is also only functional when built with the +// containers_image_openpgp build tag (as syft's Linux release binaries are); otherwise it always returns an error. type GetSourceConfig struct { // SourceProviderConfig may optionally be provided to be used when constructing the default set of source providers, unused if All specified SourceProviderConfig *sourceproviders.Config @@ -64,6 +70,12 @@ func (c *GetSourceConfig) WithDefaultImagePullSource(defaultImagePullSource stri func (c *GetSourceConfig) getProviders(userInput string) ([]source.Provider, error) { providers := collections.TaggedValueSet[source.Provider]{}.Join(sourceproviders.All(userInput, c.SourceProviderConfig)...) + // opening a containers-storage store runs full graph driver init (mounts, locks, possible store writes), which is + // too invasive for an "is this image local?" probe on every plain image reference. Only use it when asked by name. + if !c.requestsSource(image.ContainersStorageSource) { + providers = providers.Remove(image.ContainersStorageSource) + } + // if the "default image pull source" is set, we move this as the first pull source if c.DefaultImagePullSource != "" { base := providers.Remove(sourceproviders.PullTag) @@ -84,6 +96,10 @@ func (c *GetSourceConfig) getProviders(userInput string) ([]source.Provider, err return providers.Values(), nil } +func (c *GetSourceConfig) requestsSource(name string) bool { + return c.DefaultImagePullSource == name || slices.Contains(c.Sources, name) +} + func DefaultGetSourceConfig() *GetSourceConfig { return &GetSourceConfig{ SourceProviderConfig: sourceproviders.DefaultConfig(), diff --git a/syft/get_source_config_test.go b/syft/get_source_config_test.go index 912131494..0d12c3cf7 100644 --- a/syft/get_source_config_test.go +++ b/syft/get_source_config_test.go @@ -1,9 +1,15 @@ package syft import ( + "slices" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/anchore/stereoscope" + "github.com/anchore/stereoscope/pkg/image" + "github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source/sourceproviders" ) @@ -17,8 +23,9 @@ func TestGetProviders_DefaultImagePullSource(t *testing.T) { t.Errorf("Expected no error for DefaultImagePullSource parameter, got: %v", err) } - if len(providers) != len(allSourceProviders) { - t.Errorf("Expected %d providers, got %d", len(allSourceProviders), len(providers)) + // everything except containers-storage, which is only used when requested by name + if len(providers) != len(allSourceProviders)-1 { + t.Errorf("Expected %d providers, got %d", len(allSourceProviders)-1, len(providers)) } } @@ -36,3 +43,55 @@ func TestGetProviders_Sources(t *testing.T) { t.Errorf("Expected 2 providers, got %d", len(providers)) } } + +func TestGetProviders_ContainersStorageOnlyWhenRequested(t *testing.T) { + const storage = image.ContainersStorageSource + + tests := []struct { + name string + cfg *GetSourceConfig + wantFound bool + // if set, containers-storage must be ordered ahead of this provider + wantBefore string + }{ + { + name: "excluded from automatic resolution", + cfg: DefaultGetSourceConfig(), + }, + { + name: "excluded when selecting all pull sources", + cfg: DefaultGetSourceConfig().WithSources(sourceproviders.PullTag), + }, + { + name: "excluded when a different default pull source is set", + cfg: DefaultGetSourceConfig().WithDefaultImagePullSource(stereoscope.RegistryTag), + }, + { + name: "included when explicitly selected", + cfg: DefaultGetSourceConfig().WithSources(storage), + wantFound: true, + }, + { + name: "included ahead of other pull sources when it is the default pull source", + cfg: DefaultGetSourceConfig().WithDefaultImagePullSource(storage), + wantFound: true, + wantBefore: image.DockerDaemonSource, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + providers, err := tt.cfg.getProviders("localhost/myimage:latest") + require.NoError(t, err) + require.NotEmpty(t, providers) + + assert.Equal(t, tt.wantFound, providerIndex(providers, storage) >= 0) + if tt.wantBefore != "" { + assert.Less(t, providerIndex(providers, storage), providerIndex(providers, tt.wantBefore)) + } + }) + } +} + +func providerIndex(providers []source.Provider, name string) int { + return slices.IndexFunc(providers, func(p source.Provider) bool { return p.Name() == name }) +} diff --git a/test/containers-storage/source-test.sh b/test/containers-storage/source-test.sh new file mode 100755 index 000000000..cf4d98607 --- /dev/null +++ b/test/containers-storage/source-test.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +# build an image with podman or buildah into an isolated containers-storage store, then verify syft can scan it out +# of that store (for both the vfs and overlay drivers) and that plain image references never touch the store. +# +# usage: SYFT_BINARY_LOCATION=/path/to/syft source-test.sh podman|buildah +set -euo pipefail + +builder="${1:?expected podman or buildah}" +case "$builder" in + podman|buildah) ;; + *) + printf 'unsupported image builder: %s\n' "$builder" >&2 + exit 2 + ;; +esac + +syft_binary="${SYFT_BINARY_LOCATION:?expected SYFT_BINARY_LOCATION to point at a linux syft binary built with containers-storage support}" +if [[ ! -f "$syft_binary" ]]; then + printf 'syft binary not found at %s\n' "$syft_binary" >&2 + exit 1 +fi +chmod +x "$syft_binary" + +test_dir="$(mktemp -d)" +# the overlay driver bind-mounts its graphroot onto itself (in the builder's user namespace when rootless), which must +# be unmounted before the store can be removed. Cleanup is best-effort so it never masks the test result. +cleanup_store() { + umount -l "$1"/*/graphroot/overlay 2>/dev/null || true + rm -rf "$1" +} +cleanup() { + if [[ "$(id -u)" -eq 0 ]]; then + cleanup_store "$test_dir" + else + "$builder" unshare bash -c "$(declare -f cleanup_store); cleanup_store \"\$1\"" _ "$test_dir" + fi || printf 'warning: unable to fully clean up %s\n' "$test_dir" >&2 +} +trap cleanup EXIT + +# a rootless store can only be opened from inside the builder's user namespace (as podman/buildah/skopeo do for +# themselves), so rootless users must run syft under " unshare". Root opens the store directly. +run_syft() { + if [[ "$(id -u)" -eq 0 ]]; then + "$syft_binary" "$@" + else + "$builder" unshare "$syft_binary" "$@" + fi +} + +build_context="$test_dir/context" +mkdir -p "$build_context" +cat > "$build_context/package.json" <<'EOF' +{"name":"syft-cs-fixture","version":"1.2.3"} +EOF +cat > "$build_context/Containerfile" <<'EOF' +FROM scratch +COPY package.json /app/node_modules/syft-cs-fixture/package.json +EOF + +# note: the image name deliberately avoids the string "containers-storage" so stderr greps below are unambiguous +image_ref="localhost/syft-cs-fixture:latest" +missing_ref="localhost/syft-cs-missing:latest" + +for driver in vfs overlay; do + store_dir="$test_dir/$driver" + mkdir -p "$store_dir" + export CONTAINERS_STORAGE_CONF="$store_dir/storage.conf" + cat > "$CONTAINERS_STORAGE_CONF" < "$out.json" 2> "$out.stderr" + ;; + scheme) + run_syft -vv "containers-storage:$image_ref" --output json > "$out.json" 2> "$out.stderr" + ;; + default-pull-source) + SYFT_SOURCE_IMAGE_DEFAULT_PULL_SOURCE=containers-storage \ + run_syft -vv "$image_ref" --output json > "$out.json" 2> "$out.stderr" + ;; + esac + + # prove the image came out of the store, not from some other provider that happened to answer + if ! grep -q 'copied image from containers-storage' "$out.stderr"; then + printf 'Expected %s scan to resolve via containers-storage\n' "$scan_mode" >&2 + cat "$out.stderr" >&2 + exit 1 + fi + jq -e --arg image_ref "$image_ref" ' + .source.type == "image" and + .source.metadata.userInput == $image_ref and + any(.artifacts[]; .name == "syft-cs-fixture" and .version == "1.2.3" and .type == "npm") + ' "$out.json" >/dev/null + done + + printf 'Testing plain reference skips containers-storage with %s (%s driver)\n' "$builder" "$driver" + # the image only exists in the local store, so automatic resolution (daemons, then registry) must fail without + # ever trying containers-storage + if run_syft "$image_ref" --output json > "$store_dir/plain.json" 2> "$store_dir/plain.stderr"; then + printf 'Expected plain reference scan to fail since the image only exists in containers-storage\n' >&2 + exit 1 + fi + if grep -q 'containers-storage' "$store_dir/plain.stderr"; then + printf 'Plain reference unexpectedly attempted containers-storage resolution\n' >&2 + cat "$store_dir/plain.stderr" >&2 + exit 1 + fi + + printf 'Testing missing image with %s (%s driver)\n' "$builder" "$driver" + if run_syft --from containers-storage "$missing_ref" --output json > "$store_dir/missing.json" 2> "$store_dir/missing.stderr"; then + printf 'Expected the missing containers-storage image scan to fail\n' >&2 + exit 1 + fi + if ! grep -q 'does not resolve to an image ID' "$store_dir/missing.stderr"; then + printf 'Expected a containers-storage "image not found" error\n' >&2 + cat "$store_dir/missing.stderr" >&2 + exit 1 + fi +done