Compare commits

..
Author SHA1 Message Date
Alan Pope 7c8aad9e1b testing 2025-04-09 14:54:35 +01:00
70 changed files with 876 additions and 1544 deletions
+4 -4
View File
@@ -26,7 +26,7 @@ tools:
# used for linting
- name: golangci-lint
version:
want: v2.1.5
want: v2.0.2
method: github-release
with:
repo: golangci/golangci-lint
@@ -42,7 +42,7 @@ tools:
# used for signing the checksums file at release
- name: cosign
version:
want: v2.5.0
want: v2.4.3
method: github-release
with:
repo: sigstore/cosign
@@ -103,7 +103,7 @@ tools:
# used for running all local and CI tasks
- name: task
version:
want: v3.43.2
want: v3.42.1
method: github-release
with:
repo: go-task/task
@@ -111,7 +111,7 @@ tools:
# used for triggering a release
- name: gh
version:
want: v2.71.2
want: v2.69.0
method: github-release
with:
repo: cli/cli
+1 -1
View File
@@ -45,7 +45,7 @@ jobs:
path: test/results/**/*
- name: Update PR benchmark results comment
uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db #v2.9.2
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 #v2.9.1
continue-on-error: true
with:
header: benchmark
+3 -3
View File
@@ -45,7 +45,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 #v3.28.16
uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 #v3.28.13
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@28deaeda66b76a05916b6923827895f2b14ab387 #v3.28.16
uses: github/codeql-action/autobuild@1b549b9259bda1cb5ddde3b41741a82a2d15a841 #v3.28.13
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -70,4 +70,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 #v3.28.16
uses: github/codeql-action/analyze@1b549b9259bda1cb5ddde3b41741a82a2d15a841 #v3.28.13
+2 -2
View File
@@ -40,7 +40,7 @@ jobs:
- name: Delete existing comment
if: ${{ hashFiles( env.CI_COMMENT_FILE ) == '' }}
uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db #v2.9.2
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 #v2.9.1
with:
header: ${{ env.COMMENT_HEADER }}
hide: true
@@ -48,7 +48,7 @@ jobs:
- name: Add comment
if: ${{ hashFiles( env.CI_COMMENT_FILE ) != '' }}
uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db #v2.9.2
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 #v2.9.1
with:
header: ${{ env.COMMENT_HEADER }}
path: ${{ env.CI_COMMENT_FILE }}
+1 -1
View File
@@ -194,7 +194,7 @@ jobs:
runs-on: macos-latest
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@v3.8.2
uses: sigstore/cosign-installer@v3.8.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
-10
View File
@@ -227,16 +227,6 @@ rough outline how that works:
## Testing
### Testing commands
* `make help` shows a list of available commands
* `make unit`, `make integration`, `make cli`, and `make acceptance` run those test suites (see below)
* `make test` runs all those tests (and is therefore pretty slow)
* `make fixtures` clears and re-fetches all test fixtures.
* `go test ./syft/pkg/` for example can test particular packages, assuming fixtures are already made
* `make clean-cache` cleans all test cache. Note that subsequent test runs will be slower after this
### Levels of testing
- `unit`: The default level of test which is distributed throughout the repo are unit tests. Any `_test.go` file that
+1 -1
View File
@@ -224,7 +224,7 @@ tasks:
- "go run -race cmd/syft/main.go alpine:latest"
validate-cyclonedx-schema:
desc: Validate that Syft produces valid CycloneDX documents
desc: Run integration tests
cmds:
- "cd schema/cyclonedx && make"
+3 -6
View File
@@ -13,7 +13,6 @@ import (
"github.com/spf13/cobra"
"github.com/anchore/clio"
"github.com/anchore/syft/cmd/syft/internal/options"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/task"
"github.com/anchore/syft/syft/cataloging"
@@ -87,13 +86,11 @@ func runCatalogerList(opts *catalogerListOptions) error {
}
func catalogerListReport(opts *catalogerListOptions, allTaskGroups [][]task.Task) (string, error) {
defaultCatalogers := options.Flatten(opts.DefaultCatalogers)
selectCatalogers := options.Flatten(opts.SelectCatalogers)
selectedTaskGroups, selectionEvidence, err := task.SelectInGroups(
allTaskGroups,
cataloging.NewSelectionRequest().
WithDefaults(defaultCatalogers...).
WithExpression(selectCatalogers...),
WithDefaults(opts.DefaultCatalogers...).
WithExpression(opts.SelectCatalogers...),
)
if err != nil {
return "", fmt.Errorf("unable to select catalogers: %w", err)
@@ -102,7 +99,7 @@ func catalogerListReport(opts *catalogerListOptions, allTaskGroups [][]task.Task
switch opts.Output {
case "json":
report, err = renderCatalogerListJSON(flattenTaskGroups(selectedTaskGroups), selectionEvidence, defaultCatalogers, selectCatalogers)
report, err = renderCatalogerListJSON(flattenTaskGroups(selectedTaskGroups), selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
case "table", "":
if opts.ShowHidden {
report = renderCatalogerListTables(allTaskGroups, selectionEvidence)
@@ -184,11 +184,11 @@ Default selections: 1
• 'image'
Selection expressions: 6
• 'b' (intersect)
• '-c' (remove)
• '-directory' (remove)
• '-c' (remove)
• '-file' (remove)
• '+file-task1' (add)
• '+task3' (add)
• '+file-task1' (add)
┌────────────────┬──────────────────┐
│ FILE CATALOGER │ TAGS │
├────────────────┼──────────────────┤
@@ -200,43 +200,6 @@ Selection expressions: 6
│ task1 │ 1, a, b, image │
│ task3 │ 3, c, d, directory │
└───────────────────┴────────────────────┘
`,
},
{
name: "with comma separated expressions, table",
options: func() *catalogerListOptions {
c := defaultCatalogerListOptions()
c.Output = "table"
c.DefaultCatalogers = []string{
"image,task3,file-task1",
}
c.SelectCatalogers = []string{
"-task2,c,b",
}
return c
}(),
want: `
Default selections: 4
• 'file-task1'
• 'image'
• 'task3'
• 'file'
Selection expressions: 3
• 'b' (intersect)
• 'c' (intersect)
• '-task2' (remove)
┌────────────────┬──────────────────┐
│ FILE CATALOGER │ TAGS │
├────────────────┼──────────────────┤
│ file-task1 │ file, ft, ft-1-b │
│ file-task2 │ file, ft, ft-2-b │
└────────────────┴──────────────────┘
┌───────────────────┬────────────────────┐
│ PACKAGE CATALOGER │ TAGS │
├───────────────────┼────────────────────┤
│ task1 │ 1, a, b, image │
│ task3 │ 3, c, d, directory │
└───────────────────┴────────────────────┘
`,
},
{
@@ -256,7 +219,7 @@ Selection expressions: 3
return c
}(),
want: `
{"default":["image"],"selection":["+task3","-c","-directory","b"],"catalogers":[{"name":"file-task1","tags":["file"]},{"name":"file-task2","tags":["file"]},{"name":"task1","tags":["b","image"]},{"name":"task3","tags":["task3"]}]}
{"default":["image"],"selection":["-directory","+task3","-c","b"],"catalogers":[{"name":"file-task1","tags":["file"]},{"name":"file-task2","tags":["file"]},{"name":"task1","tags":["b","image"]},{"name":"task3","tags":["task3"]}]}
`,
},
}
+6 -7
View File
@@ -170,7 +170,6 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
Dotnet: dotnet.DefaultCatalogerConfig().
WithDepPackagesMustHaveDLL(cfg.Dotnet.DepPackagesMustHaveDLL).
WithDepPackagesMustClaimDLL(cfg.Dotnet.DepPackagesMustClaimDLL).
WithPropagateDLLClaimsToParents(cfg.Dotnet.PropagateDLLClaimsToParents).
WithRelaxDLLClaimsWhenBundlingDetected(cfg.Dotnet.RelaxDLLClaimsWhenBundlingDetected),
Golang: golang.DefaultCatalogerConfig().
WithSearchLocalModCacheLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.SearchLocalModCacheLicenses)).
@@ -273,12 +272,12 @@ func (cfg *Catalog) PostLoad() error {
return fmt.Errorf("cannot use both 'catalogers' and 'select-catalogers'/'default-catalogers' flags")
}
cfg.From = Flatten(cfg.From)
cfg.From = flatten(cfg.From)
cfg.Catalogers = Flatten(cfg.Catalogers)
cfg.DefaultCatalogers = Flatten(cfg.DefaultCatalogers)
cfg.SelectCatalogers = Flatten(cfg.SelectCatalogers)
cfg.Enrich = Flatten(cfg.Enrich)
cfg.Catalogers = flatten(cfg.Catalogers)
cfg.DefaultCatalogers = flatten(cfg.DefaultCatalogers)
cfg.SelectCatalogers = flatten(cfg.SelectCatalogers)
cfg.Enrich = flatten(cfg.Enrich)
// for backwards compatibility
cfg.DefaultCatalogers = append(cfg.DefaultCatalogers, cfg.Catalogers...)
@@ -296,7 +295,7 @@ func (cfg *Catalog) PostLoad() error {
return nil
}
func Flatten(commaSeparatedEntries []string) []string {
func flatten(commaSeparatedEntries []string) []string {
var out []string
for _, v := range commaSeparatedEntries {
for _, s := range strings.Split(v, ",") {
+1 -1
View File
@@ -139,7 +139,7 @@ func Test_enrichmentEnabled(t *testing.T) {
for _, test := range tests {
t.Run(test.directives, func(t *testing.T) {
got := enrichmentEnabled(Flatten([]string{test.directives}), test.test)
got := enrichmentEnabled(flatten([]string{test.directives}), test.test)
assert.Equal(t, test.expected, got)
})
}
+2 -6
View File
@@ -10,8 +10,6 @@ type dotnetConfig struct {
DepPackagesMustClaimDLL bool `mapstructure:"dep-packages-must-claim-dll" json:"dep-packages-must-claim-dll" yaml:"dep-packages-must-claim-dll"`
PropagateDLLClaimsToParents bool `mapstructure:"propagate-dll-claims-to-parents" json:"propagate-dll-claims-to-parents" yaml:"propagate-dll-claims-to-parents"`
RelaxDLLClaimsWhenBundlingDetected bool `mapstructure:"relax-dll-claims-when-bundling-detected" json:"relax-dll-claims-when-bundling-detected" yaml:"relax-dll-claims-when-bundling-detected"`
}
@@ -20,9 +18,8 @@ var _ interface {
} = (*dotnetConfig)(nil)
func (o *dotnetConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&o.DepPackagesMustHaveDLL, `only keep dep.json packages which an executable on disk is found. The package is also included if a DLL is found for any child package, even if the package itself does not have a DLL.`)
descriptions.Add(&o.DepPackagesMustClaimDLL, `only keep dep.json packages which have a runtime/resource DLL claimed in the deps.json targets section (but not necessarily found on disk). The package is also included if any child package claims a DLL, even if the package itself does not claim a DLL.`)
descriptions.Add(&o.PropagateDLLClaimsToParents, `treat DLL claims or on-disk evidence for child packages as DLL claims or on-disk evidence for any parent package`)
descriptions.Add(&o.DepPackagesMustHaveDLL, `only keep dep.json packages which an executable on disk can be found for`)
descriptions.Add(&o.DepPackagesMustClaimDLL, `only keep dep.json packages which have a runtime/resource DLL claimed in the deps.json targets section (but not necessarily found on disk)`)
descriptions.Add(&o.RelaxDLLClaimsWhenBundlingDetected, `show all packages from the deps.json if bundling tooling is present as a dependency (e.g. ILRepack)`)
}
@@ -31,7 +28,6 @@ func defaultDotnetConfig() dotnetConfig {
return dotnetConfig{
DepPackagesMustHaveDLL: def.DepPackagesMustHaveDLL,
DepPackagesMustClaimDLL: def.DepPackagesMustClaimDLL,
PropagateDLLClaimsToParents: def.PropagateDLLClaimsToParents,
RelaxDLLClaimsWhenBundlingDetected: def.RelaxDLLClaimsWhenBundlingDetected,
}
}
+17 -19
View File
@@ -22,13 +22,13 @@ require (
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115
github.com/anchore/stereoscope v0.1.4
github.com/anchore/stereoscope v0.1.2
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
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/bmatcuk/doublestar/v4 v4.8.1
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbles v0.20.0
github.com/charmbracelet/bubbletea v1.3.4
github.com/charmbracelet/lipgloss v1.1.0
github.com/dave/jennifer v1.7.1
@@ -40,7 +40,7 @@ require (
github.com/github/go-spdx/v2 v2.3.2
github.com/gkampitakis/go-snaps v0.5.11
github.com/go-git/go-billy/v5 v5.6.2
github.com/go-git/go-git/v5 v5.16.0
github.com/go-git/go-git/v5 v5.14.0
github.com/go-test/deep v1.1.1
github.com/go-viper/mapstructure/v2 v2.2.1
github.com/gohugoio/hashstructure v0.5.0
@@ -57,8 +57,8 @@ require (
github.com/jinzhu/copier v0.4.0
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
github.com/knqyf263/go-rpmdb v0.1.1
github.com/magiconair/properties v1.8.10
github.com/mholt/archives v0.1.1
github.com/magiconair/properties v1.8.9
github.com/mholt/archives v0.1.0
github.com/moby/sys/mountinfo v0.7.2
github.com/olekukonko/tablewriter v0.0.5
github.com/opencontainers/go-digest v1.0.0
@@ -85,7 +85,7 @@ require (
go.uber.org/goleak v1.3.0
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
golang.org/x/mod v0.24.0
golang.org/x/net v0.39.0
golang.org/x/net v0.38.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.37.0
)
@@ -118,7 +118,7 @@ require (
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cloudflare/circl v1.6.0 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/containerd v1.7.27 // indirect
github.com/containerd/containerd/api v1.8.0 // indirect
@@ -132,9 +132,9 @@ require (
github.com/containerd/typeurl/v2 v2.1.1 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v28.1.1+incompatible // indirect
github.com/docker/cli v28.0.4+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v28.1.1+incompatible // indirect
github.com/docker/docker v28.0.4+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
@@ -145,7 +145,7 @@ require (
github.com/felixge/fgprof v0.9.5 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gkampitakis/ciinfo v0.3.1 // indirect
github.com/gkampitakis/go-diff v1.3.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
@@ -182,7 +182,7 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/signal v0.7.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
@@ -191,7 +191,7 @@ require (
github.com/muesli/termenv v0.16.0 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/nwaples/rardecode/v2 v2.1.0 // indirect
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
@@ -240,11 +240,11 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/term v0.31.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/tools v0.31.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
@@ -259,8 +259,6 @@ require (
require github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6
require github.com/minio/minlz v1.0.0 // indirect
retract (
v0.53.2
v0.53.1 // Published accidentally with incorrect license in depdencies
+34 -42
View File
@@ -122,8 +122,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek=
github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw=
github.com/anchore/stereoscope v0.1.2 h1:0+Jcf7hoImYKfrH2XzN6vvbmbpm68A/woabC43gZ4eU=
github.com/anchore/stereoscope v0.1.2/go.mod h1:kU4LkiocOimxuSnlmJgilHCfDMCR5t/UroKIpzI8jNw=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
@@ -149,8 +149,6 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA=
github.com/becheran/wildmatch-go v1.0.0/go.mod h1:gbMvj0NtVdJ15Mg/mH9uxk2R1QCistMyU7d9KFzroX4=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -181,8 +179,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI=
github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
@@ -195,8 +193,6 @@ github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2ll
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
@@ -211,8 +207,8 @@ github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -264,12 +260,12 @@ github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4i
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k=
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A=
github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I=
github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
@@ -320,8 +316,8 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/github/go-spdx/v2 v2.3.2 h1:IfdyNHTqzs4zAJjXdVQfRnxt1XMfycXoHBE2Vsm1bjs=
github.com/github/go-spdx/v2 v2.3.2/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
@@ -341,8 +337,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ=
github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k=
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=
@@ -573,8 +569,8 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
@@ -602,13 +598,11 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
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=
github.com/mholt/archives v0.1.1 h1:c7J3qXN1FB54y0qiUXiq9Bxk4eCUc8pdXWwOhZdRzeY=
github.com/mholt/archives v0.1.1/go.mod h1:FQVz01Q2uXKB/35CXeW/QFO23xT+hSCGZHVtha78U4I=
github.com/mholt/archives v0.1.0 h1:FacgJyrjiuyomTuNA92X5GyRBRZjE43Y/lrzKIlF35Q=
github.com/mholt/archives v0.1.0/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I=
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/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ=
github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
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=
@@ -626,12 +620,10 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
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/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
@@ -658,8 +650,8 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U=
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 h1:MYzLheyVx1tJVDqfu3YnN4jtnyALNzLvwl+f58TcvQY=
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
@@ -923,8 +915,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1010,8 +1002,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1041,8 +1033,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1118,13 +1110,13 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
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.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1135,8 +1127,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+2 -9
View File
@@ -6,19 +6,12 @@ import (
type licenseScannerKey struct{}
var ctxKey = licenseScannerKey{}
func SetContextLicenseScanner(ctx context.Context, s Scanner) context.Context {
return context.WithValue(ctx, ctxKey, s)
}
func IsContextLicenseScannerSet(ctx context.Context) bool {
_, ok := ctx.Value(ctxKey).(Scanner)
return ok
return context.WithValue(ctx, licenseScannerKey{}, s)
}
func ContextLicenseScanner(ctx context.Context) (Scanner, error) {
if s, ok := ctx.Value(ctxKey).(Scanner); ok {
if s, ok := ctx.Value(licenseScannerKey{}).(Scanner); ok {
return s, nil
}
return NewDefaultScanner()
-48
View File
@@ -1,48 +0,0 @@
package licenses
import (
"context"
"testing"
"github.com/stretchr/testify/require"
)
func TestSetContextLicenseScanner(t *testing.T) {
scanner := testScanner(true)
ctx := context.Background()
ctx = SetContextLicenseScanner(ctx, scanner)
val := ctx.Value(ctxKey)
require.NotNil(t, val)
s, ok := val.(Scanner)
require.True(t, ok)
require.Equal(t, scanner, s)
}
func TestIsContextLicenseScannerSet(t *testing.T) {
scanner := testScanner(true)
ctx := context.Background()
require.False(t, IsContextLicenseScannerSet(ctx))
ctx = SetContextLicenseScanner(ctx, scanner)
require.True(t, IsContextLicenseScannerSet(ctx))
}
func TestContextLicenseScanner(t *testing.T) {
t.Run("with scanner", func(t *testing.T) {
scanner := testScanner(true)
ctx := SetContextLicenseScanner(context.Background(), scanner)
s, err := ContextLicenseScanner(ctx)
if err != nil || s != scanner {
t.Fatal("expected scanner from context")
}
})
t.Run("without scanner", func(t *testing.T) {
ctx := context.Background()
s, err := ContextLicenseScanner(ctx)
if err != nil || s == nil {
t.Fatal("expected default scanner")
}
})
}
+22 -46
View File
@@ -9,7 +9,6 @@ import (
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
)
@@ -33,11 +32,11 @@ type ownershipByFilesMetadata struct {
Files []string `json:"files"`
}
func ByFileOwnershipOverlapWorker(resolver file.Resolver, accessor sbomsync.Accessor) {
func ByFileOwnershipOverlapWorker(accessor sbomsync.Accessor) {
var relationships []artifact.Relationship
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
relationships = byFileOwnershipOverlap(resolver, s.Artifacts.Packages)
relationships = byFileOwnershipOverlap(s.Artifacts.Packages)
})
accessor.WriteToSBOM(func(s *sbom.SBOM) {
@@ -47,8 +46,8 @@ func ByFileOwnershipOverlapWorker(resolver file.Resolver, accessor sbomsync.Acce
// byFileOwnershipOverlap creates a package-to-package relationship based on discovering which packages have
// evidence locations that overlap with ownership claim from another package's package manager metadata.
func byFileOwnershipOverlap(resolver file.Resolver, catalog *pkg.Collection) []artifact.Relationship {
var relationships = findOwnershipByFilesRelationships(resolver, catalog)
func byFileOwnershipOverlap(catalog *pkg.Collection) []artifact.Relationship {
var relationships = findOwnershipByFilesRelationships(catalog)
var edges []artifact.Relationship
for parentID, children := range relationships {
@@ -85,7 +84,7 @@ func byFileOwnershipOverlap(resolver file.Resolver, catalog *pkg.Collection) []a
// findOwnershipByFilesRelationships find overlaps in file ownership with a file that defines another package. Specifically, a .Location.Path of
// a package is found to be owned by another (from the owner's .Metadata.Files[]).
func findOwnershipByFilesRelationships(resolver file.Resolver, catalog *pkg.Collection) map[artifact.ID]map[artifact.ID]*strset.Set { //nolint:gocognit
func findOwnershipByFilesRelationships(catalog *pkg.Collection) map[artifact.ID]map[artifact.ID]*strset.Set {
var relationships = make(map[artifact.ID]map[artifact.ID]*strset.Set)
if catalog == nil {
@@ -103,31 +102,27 @@ func findOwnershipByFilesRelationships(resolver file.Resolver, catalog *pkg.Coll
if !ok {
continue
}
for _, ownedFilePath := range pkgFileOwner.OwnedFiles() {
// find the first path that results in a hit
for _, ownedPath := range allPaths(ownedFilePath, resolver) {
if matchesAny(ownedPath, globsForbiddenFromBeingOwned) {
// we skip over known exceptions to file ownership, such as the RPM package owning
// the RPM DB path, otherwise the RPM package would "own" all RPMs, which is not intended
if matchesAny(ownedFilePath, globsForbiddenFromBeingOwned) {
// we skip over known exceptions to file ownership, such as the RPM package owning
// the RPM DB path, otherwise the RPM package would "own" all RPMs, which is not intended
continue
}
// look for package(s) in the catalog that may be owned by this package and mark the relationship
for _, subPackage := range catalog.PackagesByPath(ownedFilePath) {
subID := subPackage.ID()
if subID == id {
continue
}
// look for package(s) in the catalog that may be owned by this package and mark the relationship
for _, subPackage := range catalog.PackagesByPath(ownedPath) {
subID := subPackage.ID()
if subID == id {
continue
}
if _, exists := relationships[id]; !exists {
relationships[id] = make(map[artifact.ID]*strset.Set)
}
if _, exists := relationships[id][subID]; !exists {
relationships[id][subID] = strset.New()
}
relationships[id][subID].Add(ownedPath)
if _, exists := relationships[id]; !exists {
relationships[id] = make(map[artifact.ID]*strset.Set)
}
if _, exists := relationships[id][subID]; !exists {
relationships[id][subID] = strset.New()
}
relationships[id][subID].Add(ownedFilePath)
}
}
}
@@ -135,25 +130,6 @@ func findOwnershipByFilesRelationships(resolver file.Resolver, catalog *pkg.Coll
return relationships
}
func allPaths(ownedFilePath string, resolver file.Resolver) []string {
// though we have a string path, we need to resolve symlinks and other filesystem oddities since we cannot assume this is a real path
var locs []file.Location
var err error
if resolver != nil {
locs, err = resolver.FilesByPath(ownedFilePath)
if err != nil {
log.WithFields("error", err, "path", ownedFilePath).Trace("unable to find path for owned file")
locs = nil
}
}
ownedFilePaths := strset.New(ownedFilePath)
for _, loc := range locs {
ownedFilePaths.Add(loc.RealPath)
}
return ownedFilePaths.List()
}
func matchesAny(s string, globs []string) bool {
for _, g := range globs {
matches, err := doublestar.Match(g, s)
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/internal/cmptest"
"github.com/anchore/syft/syft/artifact"
@@ -12,29 +12,11 @@ import (
"github.com/anchore/syft/syft/pkg"
)
type mockFR struct {
file.Resolver
translate map[string]string
}
func (m mockFR) FilesByPath(paths ...string) ([]file.Location, error) {
var results []file.Location
for _, p := range paths {
tPath, ok := m.translate[p]
if !ok {
tPath = p
}
results = append(results, file.NewLocation(tPath))
}
return results, nil
}
func TestOwnershipByFilesRelationship(t *testing.T) {
tests := []struct {
name string
resolver file.Resolver
setup func(t testing.TB) ([]pkg.Package, []artifact.Relationship)
name string
setup func(t testing.TB) ([]pkg.Package, []artifact.Relationship)
}{
{
name: "owns-by-real-path",
@@ -78,75 +60,6 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
return []pkg.Package{parent, child}, []artifact.Relationship{relationship}
},
},
{
name: "misses-by-dead-symlink",
resolver: mockFR{
translate: map[string]string{
"/bin/gzip": "", // treat this as a dead symlink
},
},
setup: func(t testing.TB) ([]pkg.Package, []artifact.Relationship) {
parent := pkg.Package{
Type: pkg.DebPkg,
Metadata: pkg.DpkgDBEntry{
Files: []pkg.DpkgFileRecord{
{Path: "/bin/gzip"}, // this symlinks to gzip via /bin -> /usr/bin
},
},
}
parent.SetID()
child := pkg.Package{
Locations: file.NewLocationSet(
file.NewVirtualLocation("/usr/bin/gzip", "/usr/bin/gzip"),
),
Type: pkg.BinaryPkg,
}
child.SetID()
return []pkg.Package{parent, child}, nil // importantly, no relationship is expected
},
},
{
name: "owns-by-symlink",
resolver: mockFR{
translate: map[string]string{
"/bin/gzip": "/usr/bin/gzip", // if there is a string path of /bin/gzip then return the real path of /usr/bin/gzip
},
},
setup: func(t testing.TB) ([]pkg.Package, []artifact.Relationship) {
parent := pkg.Package{
Type: pkg.DebPkg,
Metadata: pkg.DpkgDBEntry{
Files: []pkg.DpkgFileRecord{
{Path: "/bin/gzip"}, // this symlinks to gzip via /bin -> /usr/bin
},
},
}
parent.SetID()
child := pkg.Package{
Locations: file.NewLocationSet(
file.NewVirtualLocation("/usr/bin/gzip", "/usr/bin/gzip"),
),
Type: pkg.BinaryPkg,
}
child.SetID()
relationship := artifact.Relationship{
From: parent,
To: child,
Type: artifact.OwnershipByFileOverlapRelationship,
Data: ownershipByFilesMetadata{
Files: []string{
"/usr/bin/gzip",
},
},
}
return []pkg.Package{parent, child}, []artifact.Relationship{relationship}
},
},
{
name: "owns-by-virtual-path",
setup: func(t testing.TB) ([]pkg.Package, []artifact.Relationship) {
@@ -227,9 +140,9 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
pkgs, expectedRelations := test.setup(t)
c := pkg.NewCollection(pkgs...)
relationships := byFileOwnershipOverlap(test.resolver, c)
relationships := byFileOwnershipOverlap(c)
require.Len(t, relationships, len(expectedRelations))
assert.Len(t, relationships, len(expectedRelations))
for idx, expectedRelationship := range expectedRelations {
actualRelationship := relationships[idx]
if d := cmp.Diff(expectedRelationship, actualRelationship, cmptest.DefaultOptions()...); d != "" {
+1 -1
View File
@@ -46,7 +46,7 @@ func finalizeRelationships(resolver file.Resolver, builder sbomsync.Builder, cfg
// add relationships showing packages that are evident by a file which is owned by another package (package-to-package)
if cfg.PackageFileOwnershipOverlap {
relationship.ByFileOwnershipOverlapWorker(resolver, accessor)
relationship.ByFileOwnershipOverlapWorker(accessor)
}
// conditionally remove binary packages based on file ownership overlap relationships found
+4 -11
View File
@@ -96,21 +96,14 @@ func setupContext(ctx context.Context, cfg *CreateSBOMConfig) (context.Context,
ctx = setContextExecutors(ctx, cfg)
// configure license scanner
// skip injecting a license scanner if one already set on context
if licenses.IsContextLicenseScannerSet(ctx) {
return ctx, nil
}
return SetContextLicenseScanner(ctx, cfg.Licenses)
return setContextLicenseScanner(ctx, cfg)
}
// SetContextLicenseScanner creates and sets a license scanner
// on the provided context using the provided license config.
func SetContextLicenseScanner(ctx context.Context, cfg cataloging.LicenseConfig) (context.Context, error) {
func setContextLicenseScanner(ctx context.Context, cfg *CreateSBOMConfig) (context.Context, error) {
// inject a single license scanner and content config for all package cataloging tasks into context
licenseScanner, err := licenses.NewDefaultScanner(
licenses.WithIncludeLicenseContent(cfg.IncludeUnkownLicenseContent),
licenses.WithCoverage(cfg.Coverage),
licenses.WithIncludeLicenseContent(cfg.Licenses.IncludeUnkownLicenseContent),
licenses.WithCoverage(cfg.Licenses.Coverage),
)
if err != nil {
return nil, fmt.Errorf("could not build licenseScanner for cataloging: %w", err)
+1 -2
View File
@@ -58,8 +58,7 @@ func (s *Source) UnmarshalJSON(b []byte) error {
func unpackSrcMetadata(s *Source, unpacker sourceUnpacker) error {
rt := sourcemetadata.ReflectTypeFromJSONName(s.Type)
if rt == nil {
// in cases where we are converting from an SBOM without any source information, we don't want this to be fatal
return nil
return fmt.Errorf("unable to find source metadata type=%q", s.Type)
}
val := reflect.New(rt).Interface()
@@ -22,15 +22,6 @@ func TestSource_UnmarshalJSON(t *testing.T) {
expected *Source
wantErr require.ErrorAssertionFunc
}{
{
name: "empty",
input: []byte(`{
"id": "",
"type": "",
"metadata": null
}`),
expected: &Source{},
},
{
name: "directory",
input: []byte(`{
+1 -5
View File
@@ -169,11 +169,7 @@ func (r ChrootContext) ToNativeGlob(chrootPath string) (string, error) {
return "", err
}
hasSlash := strings.HasSuffix(parts[0], "/")
parts[0] = responsePath
if hasSlash {
parts[0] = strings.TrimRight(parts[0], "/") + "/"
}
parts[0] = strings.TrimRight(responsePath, "/") + "/"
return strings.Join(parts, "*"), nil
}
@@ -558,16 +558,6 @@ func TestToNativeGlob(t *testing.T) {
expectedResult: "/cwd/relative/path/**/file*.txt",
expectedError: nil,
},
{
name: "ending with glob",
chrootContext: ChrootContext{
root: "/root",
cwdRelativeToRoot: "/cwd",
},
chrootPath: "/var/lib/dpkg/info/name.*",
expectedResult: "/root/var/lib/dpkg/info/name.*",
expectedError: nil,
},
}
for _, tt := range tests {
@@ -699,17 +699,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("go-binary-hint"),
},
},
{
// note: this is for compatability with dev version of golang tip image, which resolves the issue #3681
logicalFixture: "go-version-hint/1.25/any",
expected: pkg.Package{
Name: "go",
Version: "1.25-d524e1e",
PURL: "pkg:generic/go@1.25-d524e1e",
Locations: locations("VERSION.cache"),
Metadata: metadata("go-binary-hint"),
},
},
{
// note: this is testing BUSYBOX which is typically through a link to "[" (in this case a symlink but in
// practice this is often a hard link).
@@ -1342,28 +1331,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("jq-binary"),
},
},
{
logicalFixture: "chrome/126.0.6478.182/linux-amd64",
expected: pkg.Package{
Name: "chrome",
Version: "126.0.6478.182",
Type: "binary",
PURL: "pkg:generic/chrome@126.0.6478.182",
Locations: locations("chrome"),
Metadata: metadata("chrome-binary"),
},
},
{
logicalFixture: "chrome/127.0.6533.119/linux-amd64",
expected: pkg.Package{
Name: "chrome",
Version: "127.0.6533.119",
Type: "binary",
PURL: "pkg:generic/chrome@127.0.6533.119",
Locations: locations("chrome"),
Metadata: metadata("chrome-binary"),
},
},
}
for _, test := range tests {
+2 -13
View File
@@ -171,9 +171,9 @@ func DefaultClassifiers() []Classifier {
},
{
Class: "go-binary-hint",
FileGlob: "**/VERSION*",
FileGlob: "**/VERSION",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)go(?P<version>[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?(-[0-9a-f]{7})?)`),
`(?m)go(?P<version>[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?)`),
Package: "go",
PURL: mustPURL("pkg:generic/go@version"),
CPEs: singleCPE("cpe:2.3:a:golang:go:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
@@ -670,17 +670,6 @@ func DefaultClassifiers() []Classifier {
PURL: mustPURL("pkg:generic/jq@version"),
CPEs: singleCPE("cpe:2.3:a:jqlang:jq:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "chrome-binary",
FileGlob: "**/chrome",
EvidenceMatcher: FileContentsVersionMatcher(
// [NUL]127.0.6533.119[NUL]Default
`\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\x00Default`,
),
Package: "chrome",
PURL: mustPURL("pkg:generic/chrome@version"),
CPEs: singleCPE("cpe:2.3:a:google:chrome:*:*:*:*:*:*:*:*"),
},
}
}
@@ -1,2 +0,0 @@
devel go1.25-d524e1e Sun Feb 16 07:23:13 2025 -0800
3devel go1.25-d524e1e Sun Feb 16 07:23:13 2025 -0800
@@ -130,12 +130,6 @@ from-images:
paths:
- /usr/local/go/bin/go
- version: 1.25
images:
- ref: golang:tip@sha256:319b219d4df413ef30123a7f69bdf00032d9841af58d7b0ef6971542b29875d5
platform: linux/amd64
paths:
- /usr/local/go/bin/go
# TODO: this is no longer available from dockerhub! (the snippet is vital)
- version: 1.5.14
images:
+2 -2
View File
@@ -47,14 +47,14 @@ func parseConanLock(_ context.Context, _ file.Resolver, _ *generic.Environment,
// in a second iteration
var indexToPkgMap = map[string]pkg.Package{}
v2Pkgs := handleConanLockV2(cl, reader, indexToPkgMap)
v1Pkgs := handleConanLockV2(cl, reader, indexToPkgMap)
// we do not want to store the index list requires in the conan metadata, because it is not useful to have it in
// the SBOM. Instead, we will store it in a map and then use it to build the relationships
// maps pkg.ID to a list of indices
var parsedPkgRequires = map[artifact.ID][]string{}
v1Pkgs := handleConanLockV1(cl, reader, parsedPkgRequires, indexToPkgMap)
v2Pkgs := handleConanLockV1(cl, reader, parsedPkgRequires, indexToPkgMap)
var relationships []artifact.Relationship
var pkgs []pkg.Package
+1 -3
View File
@@ -288,6 +288,7 @@ func TestDpkgArchiveCataloger(t *testing.T) {
})
}
}
func TestCataloger_Globs(t *testing.T) {
tests := []struct {
name string
@@ -304,9 +305,6 @@ func TestCataloger_Globs(t *testing.T) {
"var/lib/dpkg/status.d/pkg-1.0",
"usr/lib/opkg/info/pkg-1.0.control",
"usr/lib/opkg/status",
"usr/lib/dpkg/info/libpam-runtime.conffiles",
"usr/lib/dpkg/info/libpam-runtime.md5sums",
"usr/share/doc/libpam-runtime/copyright",
},
},
}
@@ -1,2 +0,0 @@
/etc/pam.conf
/etc/pam.d/other
@@ -1,4 +0,0 @@
55f905631797551d4d936a34c7e73474 lib/x86_64-linux-gnu/libz.so.1.2.11
cede84bda30d2380217f97753c8ccf3a usr/share/doc/zlib1g/changelog.Debian.gz
f3c9dafa6da7992c47328b4464f6d122 usr/share/doc/zlib1g/changelog.gz
a4fae96070439a5209a62ae5b8017ab2 usr/share/doc/zlib1g/copyright
@@ -1 +0,0 @@
# some shell script...
@@ -1,23 +1 @@
Package:
Package: libpam-runtime
Status: install ok installed
Priority: required
Section: admin
Installed-Size: 1016
Maintainer: Steve Langasek <vorlon@debian.org>
Architecture: all
Multi-Arch: foreign
Source: pam
Version: 1.1.8-3.6
Replaces: libpam0g-dev, libpam0g-util
Depends: debconf (>= 0.5) | debconf-2.0, debconf (>= 1.5.19) | cdebconf, libpam-modules (>= 1.0.1-6)
Conflicts: libpam0g-util
Conffiles:
/etc/pam.conf 87fc76f18e98ee7d3848f6b81b3391e5
/etc/pam.d/other 31aa7f2181889ffb00b87df4126d1701
Description: Runtime support for the PAM library
Contains configuration files and directories required for
authentication to work on Debian systems. This package is required
on almost all installations.
Homepage: http://www.linux-pam.org/
bogus status
@@ -1,43 +0,0 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: auditd
Source: https://people.redhat.com/sgrubb/audit/
Files: *
Copyright: 2012-2016 Steve Grubb <sgrubb@redhat.com>
2006-2012 Rik Faith
License: GPL-2
Files: src/libev/*
Copyright: 2007-2009 Marc Alexamder Lehmann
License: GPL-2
Files: lib/*
Copyright: 2005-2008 Steve Grubb <sgrubb@redhat.com>
License: LGPL-2.1
The audit daemon's library libaudit.* is released under LGPL
so that it may be linked with 3rd party software.
.
On Debian systems, refer to /usr/share/common-licenses/LGPL-2.1
for the complete text of the GNU Lesser General Public License.
Files: debian/*
Copyright: 2007-2011 Philipp Matthias Hahn <pmhahn@debian.org>
2012-2016 Laurent Bigonville <bigon@debian.org>
License: GPL-2
License: GPL-2
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2,
as published by the Free Software Foundation.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this package; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
.
On Debian systems, the complete text of the GNU General
Public License can be found in `/usr/share/common-licenses/GPL-1'.
+52 -132
View File
@@ -1,9 +1,9 @@
package dotnet
import (
"strings"
"testing"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/artifact"
@@ -119,10 +119,6 @@ func TestCataloger(t *testing.T) {
}
net8AppExpectedDepPkgs = append(net8AppExpectedDepPkgs, net8AppExpectedDepPkgsWithoutUnpairedDlls...)
var net8AppExpectedDepPkgsWithRuntime []string
net8AppExpectedDepPkgsWithRuntime = append(net8AppExpectedDepPkgsWithRuntime, net8AppExpectedDepPkgs...)
net8AppExpectedDepPkgsWithRuntime = append(net8AppExpectedDepPkgsWithRuntime, "Microsoft.NETCore.App.Runtime.linux-x64 @ 8.0.14 (/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.14/Microsoft.NETCore.App.deps.json)")
// app binaries (always dlls)
net8AppBinaryOnlyPkgs := []string{
"Humanizer @ 2.14.1.48190 (/app/Humanizer.dll)",
@@ -284,17 +280,11 @@ func TestCataloger(t *testing.T) {
net8AppDepOnlyRelationships = append(net8AppDepOnlyRelationships, net8AppDepOnlyRelationshipsWithoutHumanizer...)
net8AppDepOnlyRelationships = append(net8AppDepOnlyRelationships, humanizerToAppDepsRelationship)
var net8AppDepOnlyRelationshipsWithRuntime []string
net8AppDepOnlyRelationshipsWithRuntime = append(net8AppDepOnlyRelationshipsWithRuntime, net8AppDepOnlyRelationships...)
net8AppDepOnlyRelationshipsWithRuntime = append(net8AppDepOnlyRelationshipsWithRuntime,
"Microsoft.NETCore.App.Runtime.linux-x64 @ 8.0.14 (/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.14/Microsoft.NETCore.App.deps.json) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.deps.json)",
)
var net8AppExpectedDepRelationships []string
net8AppExpectedDepRelationships = append(net8AppExpectedDepRelationships, net8AppDepOnlyRelationships...)
var net8AppExpectedDepSelfContainedPkgs []string
net8AppExpectedDepSelfContainedPkgs = append(net8AppExpectedDepSelfContainedPkgs, net8AppExpectedDepPkgs...)
net8AppExpectedDepSelfContainedPkgs = append(net8AppExpectedDepSelfContainedPkgs, net8AppExpectedDepPkgsWithoutUnpairedDlls...)
net8AppExpectedDepSelfContainedPkgs = append(net8AppExpectedDepSelfContainedPkgs,
// add the CLR runtime packages...
"runtimepack.Microsoft.NETCore.App.Runtime.win-x64 @ 8.0.14 (/app/dotnetapp.deps.json)",
@@ -308,7 +298,7 @@ func TestCataloger(t *testing.T) {
)
var net8AppExpectedDepSelfContainedRelationships []string
net8AppExpectedDepSelfContainedRelationships = append(net8AppExpectedDepSelfContainedRelationships, net8AppDepOnlyRelationships...)
net8AppExpectedDepSelfContainedRelationships = append(net8AppExpectedDepSelfContainedRelationships, net8AppDepOnlyRelationshipsWithoutHumanizer...)
net8AppExpectedDepSelfContainedRelationships = append(net8AppExpectedDepSelfContainedRelationships,
// add the CLR runtime relationships...
"runtimepack.Microsoft.NETCore.App.Runtime.win-x64 @ 8.0.14 (/app/dotnetapp.deps.json) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.deps.json)",
@@ -716,20 +706,36 @@ func TestCataloger(t *testing.T) {
assertion: assertAllDepEntriesWithoutExecutables,
},
{
name: "combined cataloger",
fixture: "image-net8-app",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
expectedPkgs: net8AppExpectedDepPkgs,
expectedRels: net8AppDepOnlyRelationships,
assertion: assertAlmostAllDepEntriesWithExecutables, // important! this is what makes this case different from the previous one... dep entries have attached executables
name: "combined cataloger",
fixture: "image-net8-app",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
// if we don't care about DLL claims in the deps.json, then this is right
//expectedPkgs: net8AppExpectedDepPkgs,
//expectedRels: net8AppExpectedDepRelationships,
// we care about DLL claims in the deps.json, so the main application inherits all relationships to/from humanizer
expectedPkgs: net8AppExpectedDepPkgsWithoutUnpairedDlls,
expectedRels: replaceAll(net8AppDepOnlyRelationshipsWithoutHumanizer, "Humanizer @ 2.14.1", "dotnetapp @ 1.0.0"),
assertion: assertAlmostAllDepEntriesWithExecutables, // important! this is what makes this case different from the previous one... dep entries have attached executables
},
{
name: "combined cataloger (with runtime)",
fixture: "image-net8-app-with-runtime",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
expectedPkgs: net8AppExpectedDepPkgsWithRuntime,
expectedRels: net8AppDepOnlyRelationshipsWithRuntime,
assertion: assertAccurateNetRuntimePackage,
name: "combined cataloger (with runtime)",
fixture: "image-net8-app-with-runtime",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
expectedPkgs: func() []string {
pkgs := net8AppExpectedDepPkgsWithoutUnpairedDlls
pkgs = append(pkgs, "Microsoft.NETCore.App.Runtime.linux-x64 @ 8.0.14 (/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.14/Microsoft.NETCore.App.deps.json)")
return pkgs
}(),
expectedRels: func() []string {
x := replaceAll(net8AppDepOnlyRelationshipsWithoutHumanizer, "Humanizer @ 2.14.1", "dotnetapp @ 1.0.0")
// the main application should also have a relationship to the runtime package
x = append(x, "Microsoft.NETCore.App.Runtime.linux-x64 @ 8.0.14 (/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.14/Microsoft.NETCore.App.deps.json) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.deps.json)")
return x
}(),
assertion: assertAccurateNetRuntimePackage,
},
{
name: "combined cataloger (with runtime, no deps.json anywhere)",
@@ -917,7 +923,11 @@ func TestCataloger(t *testing.T) {
fixture: "image-net8-app-self-contained",
cataloger: NewDotnetDepsCataloger(),
expectedPkgs: net8AppExpectedDepsSelfContainedPkgs,
expectedRels: net8AppExpectedDepSelfContainedRelationships,
expectedRels: func() []string {
x := net8AppExpectedDepSelfContainedRelationships
x = append(x, humanizerToAppDepsRelationship)
return x
}(),
},
{
name: "combined cataloger (self-contained)",
@@ -925,8 +935,13 @@ func TestCataloger(t *testing.T) {
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
// we care about DLL claims in the deps.json, so the main application inherits all relationships to/from humarizer
expectedPkgs: net8AppExpectedDepSelfContainedPkgs,
expectedRels: net8AppExpectedDepSelfContainedRelationships,
assertion: assertAccurateNetRuntimePackage,
expectedRels: func() []string {
x := replaceAll(net8AppExpectedDepSelfContainedRelationships, "Humanizer @ 2.14.1", "dotnetapp @ 1.0.0")
// the main application also has a dependency on the runtime package
x = append(x, "runtimepack.Microsoft.NETCore.App.Runtime.win-x64 @ 8.0.14 (/app/dotnetapp.deps.json) [dependency-of] dotnetapp @ 1.0.0 (/app/dotnetapp.deps.json)")
return x
}(),
assertion: assertAccurateNetRuntimePackage,
},
{
name: "pe cataloger (self-contained)",
@@ -989,37 +1004,19 @@ func TestCataloger(t *testing.T) {
fixture: "image-net2-app",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
expectedPkgs: []string{
"Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
"Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
"Serilog @ 2.10.0 (/app/helloworld.deps.json)",
"Serilog.Sinks.Console @ 4.0.1 (/app/helloworld.deps.json)",
"helloworld @ 1.0.0 (/app/helloworld.deps.json)",
"runtime.linux-x64.Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
"runtime.linux-x64.Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)", // a compile target reference
},
expectedRels: []string{
"Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json) [dependency-of] Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
"Serilog @ 2.10.0 (/app/helloworld.deps.json) [dependency-of] Serilog.Sinks.Console @ 4.0.1 (/app/helloworld.deps.json)",
"Serilog @ 2.10.0 (/app/helloworld.deps.json) [dependency-of] helloworld @ 1.0.0 (/app/helloworld.deps.json)",
"Serilog.Sinks.Console @ 4.0.1 (/app/helloworld.deps.json) [dependency-of] helloworld @ 1.0.0 (/app/helloworld.deps.json)",
"runtime.linux-x64.Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json) [dependency-of] Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
"runtime.linux-x64.Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json) [dependency-of] helloworld @ 1.0.0 (/app/helloworld.deps.json)",
"runtime.linux-x64.Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json) [dependency-of] Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
},
assertion: assertAccurateNetRuntimePackage,
},
{
name: "libman support",
fixture: "image-net6-asp-libman",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
expectedPkgs: []string{
"LibManSample @ 1.0.0 (/app/LibManSample.deps.json)",
"jquery @ 3.3.1 (/app/libman.json)",
},
expectedRels: []string{
"jquery @ 3.3.1 (/app/libman.json) [dependency-of] LibManSample @ 1.0.0 (/app/LibManSample.deps.json)",
},
},
}
for _, tt := range cases {
@@ -1038,26 +1035,7 @@ func TestCataloger(t *testing.T) {
}
}
func TestDotnetDepsCataloger_regressions(t *testing.T) {
assertPackages := func(mustHave []string, mustNotHave []string) func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
expected := strset.New(mustHave...)
notExpected := strset.New(mustNotHave...)
return func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
for _, p := range pkgs {
expected.Remove(p.Name)
if notExpected.Has(p.Name) {
t.Errorf("unexpected package: %s", p.Name)
}
}
if expected.IsEmpty() {
return
}
t.Errorf("missing packages: %s", expected.List())
}
}
func TestDotnetDepsCataloger_problemCases(t *testing.T) {
cases := []struct {
name string
fixture string
@@ -1094,72 +1072,6 @@ func TestDotnetDepsCataloger_regressions(t *testing.T) {
pkgtest.AssertPackagesEqualIgnoreLayers(t, expected, actual)
},
},
{
name: "indirect packages references",
fixture: "image-net8-compile-target",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
assertion: assertPackages(
[]string{
"DotNetNuke.Core", // uses a compile target reference in the deps.json
"Umbraco.Cms", // this is the parent of other packages which do have DLLs included (even though it does not have any DLLs)
},
[]string{
"StyleCop.Analyzers", // this is a development tool
"Microsoft.NET.Test.Sdk", // this is a development tool
"jQuery", // has no DLLs but has javascript assets
},
),
},
{
name: "not propagating claims",
fixture: "image-net8-compile-target",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig().WithPropagateDLLClaimsToParents(false)),
assertion: assertPackages(
[]string{
"DotNetNuke.Core", // uses a compile target reference in the deps.json
},
[]string{
"Umbraco.Cms", // this is the parent of other packages which do have DLLs included (even though it does not have any DLLs)
"StyleCop.Analyzers", // this is a development tool
"Microsoft.NET.Test.Sdk", // this is a development tool under the debug configuration (we build the release configuration)
"jQuery", // has no DLLs but has javascript assets -- this is bad behavior (as we want to detect this)
},
),
},
{
name: "not requiring claims finds jquery",
fixture: "image-net8-compile-target",
cataloger: NewDotnetDepsBinaryCataloger(CatalogerConfig{
DepPackagesMustHaveDLL: false,
DepPackagesMustClaimDLL: false,
PropagateDLLClaimsToParents: false,
RelaxDLLClaimsWhenBundlingDetected: false,
}),
assertion: assertPackages(
[]string{
"jQuery", // has no DLLs but has javascript assets
"StyleCop.Analyzers", // this is a development tool -- this is bad behavior (since we should not detect this), but cannot be helped
},
[]string{
"Microsoft.NET.Test.Sdk", // this is a development tool under the debug configuration (we build the release configuration)
},
),
},
{
name: "libman support",
fixture: "image-net6-asp-libman",
cataloger: NewDotnetDepsBinaryCataloger(DefaultCatalogerConfig()),
assertion: assertPackages(
[]string{
"jquery", // a javascript package, not the nuget package
},
[]string{
"vendor", // this is the string reference for a filesystem provider
"lodash", // this is from a filesystem provider, which is not supported
},
),
},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
@@ -1548,3 +1460,11 @@ func extractMatchingPackage(t *testing.T, name string, pkgs []pkg.Package) pkg.P
t.Fatalf("expected to find package %s", name)
return pkg.Package{}
}
func replaceAll(ss []string, find, replace string) []string {
var results []string
for _, s := range ss {
results = append(results, strings.ReplaceAll(s, find, replace))
}
return results
}
-9
View File
@@ -8,9 +8,6 @@ type CatalogerConfig struct {
// This does not require such claimed DLLs to exist on disk. The behavior of this
DepPackagesMustClaimDLL bool `mapstructure:"dep-packages-must-claim-dll" json:"dep-packages-must-claim-dll" yaml:"dep-packages-must-claim-dll"`
// PropagateDLLClaimsToParents allows for deps.json packages to be included if any child (transitive) package claims a DLL. This applies to both the claims configuration and evidence-on-disk configurations.
PropagateDLLClaimsToParents bool `mapstructure:"propagate-dll-claims-to-parents" json:"propagate-dll-claims-to-parents" yaml:"propagate-dll-claims-to-parents"`
// RelaxDLLClaimsWhenBundlingDetected will look for indications of IL bundle tooling via deps.json package names
// and, if found (and this config option is enabled), will relax the DepPackagesMustClaimDLL value to `false` only in those cases.
RelaxDLLClaimsWhenBundlingDetected bool `mapstructure:"relax-dll-claims-when-bundling-detected" json:"relax-dll-claims-when-bundling-detected" yaml:"relax-dll-claims-when-bundling-detected"`
@@ -31,16 +28,10 @@ func (c CatalogerConfig) WithRelaxDLLClaimsWhenBundlingDetected(relax bool) Cata
return c
}
func (c CatalogerConfig) WithPropagateDLLClaimsToParents(propagate bool) CatalogerConfig {
c.PropagateDLLClaimsToParents = propagate
return c
}
func DefaultCatalogerConfig() CatalogerConfig {
return CatalogerConfig{
DepPackagesMustHaveDLL: false,
DepPackagesMustClaimDLL: true,
PropagateDLLClaimsToParents: true,
RelaxDLLClaimsWhenBundlingDetected: true,
}
}
@@ -78,9 +78,6 @@ func (c depsBinaryCataloger) Catalog(_ context.Context, resolver file.Resolver)
var runtimePkgs []*pkg.Package
for i := range pkgs {
p := &pkgs[i]
if p.Type != pkg.DotnetPkg {
continue
}
if isRuntime(p.Name) {
existingRuntimeVersions.Add(p.Version)
runtimePkgs = append(runtimePkgs, p)
@@ -114,7 +111,7 @@ func (c depsBinaryCataloger) Catalog(_ context.Context, resolver file.Resolver)
runtimePkgs = append(runtimePkgs, &rtp)
}
// create a relationship from every runtime package to every root package...
// create a relationship from every runtime package to every root package
for _, root := range roots {
for _, runtimePkg := range runtimePkgs {
relationships = append(relationships, artifact.Relationship{
@@ -125,8 +122,7 @@ func (c depsBinaryCataloger) Catalog(_ context.Context, resolver file.Resolver)
}
}
// in the process of creating root-to-runtime relationships, we may have created duplicate relationships. Use the relationship index to deduplicate.
return pkgs, relationship.NewIndex(relationships...).All(), unknowns
return pkgs, relationships, unknowns
}
var runtimeDLLPathPattern = regexp.MustCompile(`/Microsoft\.NETCore\.App/(?P<version>\d+\.\d+\.\d+)/[^/]+\.dll`)
@@ -208,14 +204,6 @@ func attachAssociatedExecutables(dep *logicalDepsJSON, pe logicalPE) bool {
continue
}
if targetPath, ok := p.CompilePathsByRelativeDLLPath[relativeDllPath]; ok {
pe.TargetPath = targetPath
p.Executables = append(p.Executables, pe)
dep.PackagesByNameVersion[key] = p // update the map with the modified package
found = true
continue
}
if p.NativePaths.Has(relativeDllPath) {
pe.TargetPath = relativeDllPath
p.Executables = append(p.Executables, pe)
@@ -271,14 +259,15 @@ func packagesFromLogicalDepsJSON(doc logicalDepsJSON, config CatalogerConfig) (*
continue
}
lp := doc.PackagesByNameVersion[nameVersion]
if config.DepPackagesMustHaveDLL && !lp.FoundDLLs(config.PropagateDLLClaimsToParents) {
if config.DepPackagesMustHaveDLL && len(lp.Executables) == 0 {
// could not find a paired DLL and the user required this...
skippedDepPkgs[nameVersion] = lp
continue
}
// check to see if we should skip this package because it does not claim a DLL (or has not dependency that claims a DLL)
if config.DepPackagesMustClaimDLL && !lp.ClaimsDLLs(config.PropagateDLLClaimsToParents) {
claimsDLLs := len(lp.RuntimePathsByRelativeDLLPath) > 0 || len(lp.ResourcePathsByRelativeDLLPath) > 0
if config.DepPackagesMustClaimDLL && !claimsDLLs {
if config.RelaxDLLClaimsWhenBundlingDetected && !doc.BundlingDetected || !config.RelaxDLLClaimsWhenBundlingDetected {
// could not find a runtime or resource path and the user required this...
// and there is no evidence of a bundler in the dependencies (e.g. ILRepack)
@@ -293,22 +282,8 @@ func packagesFromLogicalDepsJSON(doc logicalDepsJSON, config CatalogerConfig) (*
pkgMap[nameVersion] = *dotnetPkg
}
}
rels := relationshipsFromLogicalDepsJSON(doc, pkgMap, skippedDepPkgs)
// ensure that any libman packages are associated with the all root packages
for _, libmanPkg := range doc.LibmanPackages {
pkgs = append(pkgs, libmanPkg)
if rootPkg == nil {
continue
}
rels = append(rels, artifact.Relationship{
From: libmanPkg,
To: *rootPkg,
Type: artifact.DependencyOfRelationship,
})
}
return rootPkg, pkgs, rels
return rootPkg, pkgs, relationshipsFromLogicalDepsJSON(doc, pkgMap, skippedDepPkgs)
}
// relationshipsFromLogicalDepsJSON creates relationships from a logicalDepsJSON document for only the given syft packages.
@@ -406,13 +381,7 @@ func findDepsJSON(resolver file.Resolver) ([]logicalDepsJSON, error, error) {
continue
}
libman, err := findLibmanJSON(resolver, loc)
if err != nil {
unknownErr = unknown.Append(unknownErr, loc, err)
libman = nil
}
depsJSONs = append(depsJSONs, getLogicalDepsJSON(*dj, libman))
depsJSONs = append(depsJSONs, getLogicalDepsJSON(*dj))
}
return depsJSONs, unknownErr, nil
+21 -130
View File
@@ -9,7 +9,6 @@ import (
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
type depsJSON struct {
@@ -27,50 +26,9 @@ type depsTarget struct {
Dependencies map[string]string `json:"dependencies"`
Runtime map[string]map[string]string `json:"runtime"`
Resources map[string]map[string]string `json:"resources"`
Compile map[string]map[string]string `json:"compile"`
Native map[string]map[string]string `json:"native"`
}
func (t depsTarget) nativePaths() *strset.Set {
results := strset.New()
for path := range t.Native {
results.Add(path)
}
return results
}
func (t depsTarget) compilePaths() map[string]string {
result := make(map[string]string)
for path := range t.Compile {
trimmedPath := trimLibPrefix(path)
if _, exists := result[trimmedPath]; exists {
continue
}
result[trimmedPath] = path
}
return result
}
func (t depsTarget) resourcePaths() map[string]string {
result := make(map[string]string)
for path := range t.Resources {
trimmedPath := trimLibPrefix(path)
if _, exists := result[trimmedPath]; exists {
continue
}
result[trimmedPath] = path
}
return result
}
func (t depsTarget) runtimePaths() map[string]string {
result := make(map[string]string)
for path := range t.Runtime {
result[trimLibPrefix(path)] = path
}
return result
}
type depsLibrary struct {
Type string `json:"type"`
Path string `json:"path"`
@@ -85,12 +43,6 @@ type logicalDepsJSONPackage struct {
Targets *depsTarget
Library *depsLibrary
// anyChildClaimsDLLs is a flag that indicates if any of the children of this package claim a DLL associated with them in the deps.json.
anyChildClaimsDLLs bool
// anyChildHasDLLs is a flag that indicates if any of the children of this package have a DLL associated with them (found on disk).
anyChildHasDLLs bool
// RuntimePathsByRelativeDLLPath is a map of the relative path to the DLL relative to the deps.json file
// to the target path as described in the deps.json target entry under "runtime".
RuntimePathsByRelativeDLLPath map[string]string
@@ -99,10 +51,6 @@ type logicalDepsJSONPackage struct {
// to the target path as described in the deps.json target entry under "resource".
ResourcePathsByRelativeDLLPath map[string]string
// CompilePathsByRelativeDLLPath is a map of the relative path to the DLL relative to the deps.json file
// to the target path as described in the deps.json target entry under "compile".
CompilePathsByRelativeDLLPath map[string]string
// NativePathsByRelativeDLLPath is a map of the relative path to the DLL relative to the deps.json file
// to the target path as described in the deps.json target entry under "native". These should not have
// any runtime references to trim from the front of the path.
@@ -114,41 +62,12 @@ type logicalDepsJSONPackage struct {
Executables []logicalPE
}
func (l *logicalDepsJSONPackage) dependencyNameVersions() []string {
if l.Targets == nil {
return nil
}
var results []string
for name, version := range l.Targets.Dependencies {
results = append(results, createNameAndVersion(name, version))
}
return results
}
// ClaimsDLLs indicates if this package has any DLLs associated with it (directly or indirectly with a dependency).
func (l *logicalDepsJSONPackage) ClaimsDLLs(includeChildren bool) bool {
selfClaim := len(l.RuntimePathsByRelativeDLLPath) > 0 || len(l.ResourcePathsByRelativeDLLPath) > 0 || len(l.CompilePathsByRelativeDLLPath) > 0 || len(l.NativePaths.List()) > 0
if !includeChildren {
return selfClaim
}
return selfClaim || l.anyChildClaimsDLLs
}
func (l *logicalDepsJSONPackage) FoundDLLs(includeChildren bool) bool {
selfClaim := len(l.Executables) > 0
if !includeChildren {
return selfClaim
}
return selfClaim || l.anyChildHasDLLs
}
type logicalDepsJSON struct {
Location file.Location
RuntimeTarget runtimeTarget
PackagesByNameVersion map[string]logicalDepsJSONPackage
PackageNameVersions *strset.Set
BundlingDetected bool
LibmanPackages []pkg.Package
}
func (l logicalDepsJSON) RootPackage() (logicalDepsJSONPackage, bool) {
@@ -198,7 +117,7 @@ var knownBundlers = strset.New(
"Fody", // IL weaving framework
)
func getLogicalDepsJSON(deps depsJSON, lm *libmanJSON) logicalDepsJSON {
func getLogicalDepsJSON(deps depsJSON) logicalDepsJSON {
packageMap := make(map[string]*logicalDepsJSONPackage)
nameVersions := strset.New()
@@ -214,15 +133,31 @@ func getLogicalDepsJSON(deps depsJSON, lm *libmanJSON) logicalDepsJSON {
if ok {
lib = &l
}
runtimePaths := make(map[string]string)
for path := range target.Runtime {
runtimePaths[trimLibPrefix(path)] = path
}
resourcePaths := make(map[string]string)
for path := range target.Resources {
trimmedPath := trimLibPrefix(path)
if _, exists := resourcePaths[trimmedPath]; exists {
continue
}
resourcePaths[trimmedPath] = path
}
nativePaths := strset.New()
for path := range target.Native {
nativePaths.Add(path)
}
p := &logicalDepsJSONPackage{
NameVersion: libName,
Library: lib,
Targets: &target,
RuntimePathsByRelativeDLLPath: target.runtimePaths(),
ResourcePathsByRelativeDLLPath: target.resourcePaths(),
CompilePathsByRelativeDLLPath: target.compilePaths(),
NativePaths: target.nativePaths(),
RuntimePathsByRelativeDLLPath: runtimePaths,
ResourcePathsByRelativeDLLPath: resourcePaths,
NativePaths: nativePaths,
}
packageMap[libName] = p
nameVersions.Add(libName)
@@ -235,8 +170,6 @@ func getLogicalDepsJSON(deps depsJSON, lm *libmanJSON) logicalDepsJSON {
if !bundlingDetected && knownBundlers.Has(name) {
bundlingDetected = true
}
p.anyChildClaimsDLLs = searchForDLLClaims(packageMap, p.dependencyNameVersions()...)
p.anyChildHasDLLs = searchForDLLEvidence(packageMap, p.dependencyNameVersions()...)
packages[p.NameVersion] = *p
}
@@ -246,51 +179,9 @@ func getLogicalDepsJSON(deps depsJSON, lm *libmanJSON) logicalDepsJSON {
PackagesByNameVersion: packages,
PackageNameVersions: nameVersions,
BundlingDetected: bundlingDetected,
LibmanPackages: lm.packages(),
}
}
type visitorFunc func(p *logicalDepsJSONPackage) bool
// searchForDLLEvidence recursively searches for executables found for any of the given nameVersions and children recursively.
func searchForDLLEvidence(packageMap map[string]*logicalDepsJSONPackage, nameVersions ...string) bool {
return traverseDependencies(packageMap, func(p *logicalDepsJSONPackage) bool {
return p.FoundDLLs(true)
}, nameVersions...)
}
// searchForDLLClaims recursively searches for DLL claims in the deps.json for any of the given nameVersions and children recursively.
func searchForDLLClaims(packageMap map[string]*logicalDepsJSONPackage, nameVersions ...string) bool {
return traverseDependencies(packageMap, func(p *logicalDepsJSONPackage) bool {
return p.ClaimsDLLs(true)
}, nameVersions...)
}
func traverseDependencies(packageMap map[string]*logicalDepsJSONPackage, visitor visitorFunc, nameVersions ...string) bool {
if len(nameVersions) == 0 {
return false
}
for _, nameVersion := range nameVersions {
if p, ok := packageMap[nameVersion]; ok {
if visitor(p) {
return true
}
var children []string
for name, version := range p.Targets.Dependencies {
children = append(children, createNameAndVersion(name, version))
}
if traverseDependencies(packageMap, visitor, children...) {
return true
}
}
}
return false
}
var libPathPattern = regexp.MustCompile(`^(?:runtimes/[^/]+/)?lib/net[^/]+/(?P<targetPath>.+)`)
// trimLibPrefix removes prefixes like "lib/net6.0/" or "runtimes/linux-arm/lib/netcoreapp2.2/" from a path.
-109
View File
@@ -1,109 +0,0 @@
package dotnet
import (
"encoding/json"
"path"
"strings"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
// libmanJSON represents the libman.json file format in ASP.NET projects for describing javascript assets to be downloaded and bundled
// see https://github.com/aspnet/LibraryManager/wiki/libman.json-reference
type libmanJSON struct {
Location file.Location `json:"-"`
Version string `json:"version"`
DefaultProvider string `json:"defaultProvider"`
Libraries []struct {
Library string `json:"library"`
Files []string `json:"files"`
Destination string `json:"destination"`
Provider string `json:"provider,omitempty"`
} `json:"libraries"`
}
func (l *libmanJSON) packages() []pkg.Package {
if l == nil {
return nil
}
var pkgs []pkg.Package
for _, lib := range l.Libraries {
if lib.Provider == "filesystem" {
// there is no name and version with filesystem providers
continue
}
fields := strings.Split(lib.Library, "@")
if len(fields) != 2 {
continue
}
name := fields[0]
version := fields[1]
p := pkg.Package{
Name: name,
Version: version,
Locations: file.NewLocationSet(l.Location),
Type: pkg.NpmPkg,
PURL: packageurl.NewPackageURL(
packageurl.TypeNPM,
"",
name,
version,
nil,
"",
).ToString(),
Language: pkg.JavaScript,
}
p.SetID()
pkgs = append(pkgs, p)
}
return pkgs
}
func newLibmanJSON(reader file.LocationReadCloser) (*libmanJSON, error) {
var doc libmanJSON
dec := json.NewDecoder(reader)
if err := dec.Decode(&doc); err != nil {
return nil, err
}
for i := range doc.Libraries {
l := &doc.Libraries[i]
if l.Provider == "" {
l.Provider = doc.DefaultProvider
}
}
doc.Location = reader.Location
return &doc, nil
}
func findLibmanJSON(resolver file.Resolver, depsJSON file.Location) (*libmanJSON, error) {
parent := path.Dir(depsJSON.RealPath)
loc := resolver.RelativeFileByPath(depsJSON, path.Join(parent, "libman.json"))
if loc == nil {
return nil, nil
}
reader, err := resolver.FileContentsByLocation(*loc)
defer internal.CloseAndLogError(reader, loc.RealPath)
if err != nil {
return nil, err
}
internal.CloseAndLogError(reader, loc.RealPath)
lj, err := newLibmanJSON(file.NewLocationReadCloser(*loc, reader))
if err != nil {
return nil, err
}
return lj, nil
}
+3 -2
View File
@@ -196,8 +196,9 @@ func extractNameAndVersion(nameVersion string) (name, version string) {
return
}
func createNameAndVersion(name, version string) string {
return fmt.Sprintf("%s/%s", name, version)
func createNameAndVersion(name, version string) (nameVersion string) {
nameVersion = fmt.Sprintf("%s/%s", name, version)
return
}
func packageURL(m pkg.DotnetDepsEntry) string {
@@ -1,2 +0,0 @@
.gitignore
Dockerfile
@@ -1,2 +0,0 @@
/app
/extract.sh
@@ -1,13 +0,0 @@
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
ARG RUNTIME=win-x64
COPY . .
WORKDIR /src
RUN dotnet restore -r $RUNTIME
RUN dotnet publish -c Release --no-restore -o /app
FROM busybox:latest
WORKDIR /app
COPY --from=build /app .
@@ -1 +0,0 @@
wwwroot/lib/
@@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- A PackageReference to Microsoft.AspNetCore.App is not necessary when targeting .NET Core 3.0 or higher. If Microsoft.NET.Sdk.Web is used, the shared framework will be referenced automatically (or override with FrameworkReference) -->
<!-- <PackageReference Include="Microsoft.AspNetCore.App" /> -->
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
</ItemGroup>
</Project>
@@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace LibManSample
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}
@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace LibManSample
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
}
}
@@ -1,24 +0,0 @@
{
"version": "1.0",
"defaultProvider": "cdnjs",
"libraries": [
{
"library": "jquery@3.3.1",
"files": [
"jquery.min.js",
"jquery.js",
"jquery.min.map"
],
"destination": "wwwroot/lib/jquery/"
},
{
"provider": "filesystem",
"library": "vendor",
"files": [
"lodash.js",
"lodash.min.js"
],
"destination": "wwwroot/lib/lodash/"
}
]
}
@@ -1 +0,0 @@
// jk! nothing to see here!
@@ -1 +0,0 @@
// still nothing here...
@@ -1,2 +0,0 @@
/app
/extract.sh
@@ -1,17 +0,0 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine@sha256:3f93439f47fea888d94e6e228d0d0de841f4122ef46f8bfd04f8bd78cbce7ddb AS build
ARG RUNTIME=win-x64
WORKDIR /src
COPY src/helloworld.csproj .
RUN dotnet restore -r $RUNTIME
COPY src/*.cs .
RUN dotnet publish -c Release -r $RUNTIME --self-contained false -o /app
# note: we're not using a runtime here to make testing easier... so you cannot run this container and expect it to work
# we do this to keep the test assertions small since the don't want to include the several other runtime packages
FROM busybox:latest
WORKDIR /app
COPY --from=build /app .
@@ -1,29 +0,0 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
namespace HelloWorld
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// configure the HTTP request pipeline
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
// enable serving static files (including jQuery)
app.UseStaticFiles();
app.MapGet("/", () => "Hello World!");
Console.WriteLine("Application starting...");
app.Run();
}
}
}
@@ -1,58 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNetNuke.Core" Version="9.9.1"/>
<!-- jQuery wraps the javascript library, so no DLLs -->
<PackageReference Include="jQuery" Version="3.4.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor" Version="2.2.0" />
<!-- ChakraCore is an open source Javascript engine with a C API -->
<PackageReference Include="Microsoft.ChakraCore" Version="1.11.24">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<!-- Nuget.CommandLine is a command line tool for managing NuGet packages -->
<PackageReference Include="Nuget.CommandLine" Version="6.3.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<!-- Umbraco.Cms has dependencies with software, but itself has no DLLs -->
<PackageReference Include="Umbraco.Cms" Version="11.3.0" />
<!-- For code analysis/style checking, only needed during development -->
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" />
<!-- For unit testing, only needed during development -->
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" Condition="'$(Configuration)' == 'Debug'" PrivateAssets="All" />
</ItemGroup>
<!-- create directories if they don't exist -->
<Target Name="CreateWwwroot" BeforeTargets="PrepareForPublish">
<MakeDir Directories="$(PublishDir)wwwroot\lib\jquery" />
</Target>
<!-- explicitly copy jQuery files during publish phase -->
<Target Name="CopyJQueryToPublishOutput" AfterTargets="CreateWwwroot" BeforeTargets="ComputeFilesToPublish">
<ItemGroup>
<!-- find jQuery files in the packages folder -->
<JQueryFiles Include="$(NuGetPackageRoot)\jquery\3.4.1\Content\Scripts\*.js" />
<!-- add these to the publish output -->
<ResolvedFileToPublish Include="@(JQueryFiles)">
<RelativePath>wwwroot\lib\jquery\%(Filename)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>
+6 -13
View File
@@ -77,25 +77,18 @@ func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resol
// the old path and new path may be the same, in which case this is a noop,
// but if they're different we need to remove the old package.
// note that we may change the path but we should always reference the new version (since the old version
// cannot be trusted as a correct value).
var finalPath string
if !strings.HasPrefix(m.New.Path, ".") && !strings.HasPrefix(m.New.Path, "/") {
finalPath = m.New.Path
delete(packages, m.Old.Path)
} else {
finalPath = m.Old.Path
}
packages[finalPath] = pkg.Package{
Name: finalPath,
delete(packages, m.Old.Path)
packages[m.New.Path] = pkg.Package{
Name: m.New.Path,
Version: m.New.Version,
Licenses: pkg.NewLicenseSet(lics...),
Locations: file.NewLocationSet(reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
PURL: packageURL(finalPath, m.New.Version),
PURL: packageURL(m.New.Path, m.New.Version),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangModuleEntry{
H1Digest: digests[fmt.Sprintf("%s %s", finalPath, m.New.Version)],
H1Digest: digests[fmt.Sprintf("%s %s", m.New.Path, m.New.Version)],
},
}
}
@@ -28,20 +28,6 @@ func TestParseGoMod(t *testing.T) {
},
},
},
{
fixture: "test-fixtures/relative-replace",
expected: []pkg.Package{
{
Name: "github.com/aws/aws-sdk-go-v2",
Version: "",
PURL: "pkg:golang/github.com/aws/aws-sdk-go-v2",
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/relative-replace")),
Language: pkg.Go,
Type: pkg.GoModulePkg,
Metadata: pkg.GolangModuleEntry{},
},
},
},
{
fixture: "test-fixtures/many-packages",
@@ -1,7 +0,0 @@
module github.com/aws/aws-sdk-go-v2/feature/ec2/imds
go 1.22
require github.com/aws/aws-sdk-go-v2 v1.36.3
replace github.com/aws/aws-sdk-go-v2 => ../../../
@@ -121,9 +121,6 @@
"github.com/whilp/git-urls": [
"cpe:2.3:a:git-urls_project:git-urls:*:*:*:*:*:go:*:*"
],
"golang.org/x/crypto": [
"cpe:2.3:a:go:ssh:*:*:*:*:*:go:*:*"
],
"golang.org/x/crypto/ssh": [
"cpe:2.3:a:golang:package_ssh:*:*:*:*:*:*:*:*",
"cpe:2.3:a:golang:ssh:*:*:*:*:*:*:*:*"
@@ -147,9 +144,6 @@
"golang.org/x/net/http2/hpack": [
"cpe:2.3:a:golang:hpack:*:*:*:*:*:go:*:*"
],
"golang.org/x/oauth2/jws": [
"cpe:2.3:a:go:jws:*:*:*:*:*:go:*:*"
],
"golang.org/x/text": [
"cpe:2.3:a:golang:text:*:*:*:*:*:*:*:*"
],
@@ -1671,9 +1665,6 @@
"sidebar-link": [
"cpe:2.3:a:jenkins:sidebar_link:*:*:*:*:*:jenkins:*:*"
],
"simple-queue": [
"cpe:2.3:a:jenkins:simple_queue:*:*:*:*:*:jenkins:*:*"
],
"simple-travis-runner": [
"cpe:2.3:a:jenkins:simple_travis_pipeline_runner:*:*:*:*:*:jenkins:*:*"
],
@@ -1756,9 +1747,6 @@
"ssh2easy": [
"cpe:2.3:a:jenkins:ssh2_easy:*:*:*:*:*:jenkins:*:*"
],
"stackhammer": [
"cpe:2.3:a:jenkins:stack_hammer:*:*:*:*:*:jenkins:*:*"
],
"starteam": [
"cpe:2.3:a:jenkins:starteam:*:*:*:*:*:jenkins:*:*"
],
@@ -2469,12 +2457,6 @@
"bestzip": [
"cpe:2.3:a:bestzip_project:bestzip:*:*:*:*:*:node.js:*:*"
],
"better-aut": [
"cpe:2.3:a:better-auth:better_auth:*:*:*:*:*:node.js:*:*"
],
"better-auth": [
"cpe:2.3:a:better-auth:better_auth:*:*:*:*:*:node.js:*:*"
],
"bionode-sra": [
"cpe:2.3:a:bionode:bionode-sra:*:*:*:*:*:node.js:*:*"
],
@@ -6358,9 +6340,6 @@
"private_address_check": [
"cpe:2.3:a:private_address_check_project:private_address_check:*:*:*:*:*:ruby:*:*"
],
"publify_core": [
"cpe:2.3:a:publify:publify_core:*:*:*:*:*:ruby:*:*"
],
"puma": [
"cpe:2.3:a:puma:puma:*:*:*:*:*:ruby:*:*"
],
@@ -7101,9 +7080,6 @@
"3d-flipbook-dflip-lite": [
"cpe:2.3:a:dearhive:dearflip:*:*:*:*:*:wordpress:*:*"
],
"3d-photo-gallery": [
"cpe:2.3:a:webdevocean:3d_photo_gallery:*:*:*:*:*:wordpress:*:*"
],
"3d-presentation": [
"cpe:2.3:a:lucapaggetti:3d_presentation:*:*:*:*:*:wordpress:*:*"
],
@@ -7295,9 +7271,6 @@
"aco-product-labels-for-woocommerce": [
"cpe:2.3:a:acowebs:product_labels_for_woocommerce_\\(sale_badges\\):*:*:*:*:*:wordpress:*:*"
],
"actionwear-products-sync": [
"cpe:2.3:a:marcoingraiti:actionwear_products_sync:*:*:*:*:*:wordpress:*:*"
],
"activecampaign-for-woocommerce": [
"cpe:2.3:a:activecampaign:activecampaign_for_woocommerce:*:*:*:*:*:wordpress:*:*"
],
@@ -7620,9 +7593,6 @@
"ai-engine": [
"cpe:2.3:a:meowapps:ai_engine:*:*:*:*:*:wordpress:*:*"
],
"ai-image-alt-text-generator-for-wp": [
"cpe:2.3:a:wpmessiah:ai_image_alt_text_generator_for_wp:*:*:*:*:*:wordpress:*:*"
],
"ai-post-generator": [
"cpe:2.3:a:autowriter:ai_post_generator_\\|_autowriter:*:*:*:*:*:wordpress:*:*"
],
@@ -7783,9 +7753,6 @@
"amministrazione-trasparente": [
"cpe:2.3:a:amministrazione_trasparente_project:amministrazione_trasparente:*:*:*:*:*:wordpress:*:*"
],
"amo-team-showcase": [
"cpe:2.3:a:amothemo:amo_team_showcase:*:*:*:*:*:wordpress:*:*"
],
"amp-img-shortcode": [
"cpe:2.3:a:wpza:amp_img_shortcode:*:*:*:*:*:wordpress:*:*"
],
@@ -7828,9 +7795,6 @@
"analytics-tracker": [
"cpe:2.3:a:analytics_tracker_project:analytics_tracker:*:*:*:*:*:wordpress:*:*"
],
"analyticswp": [
"cpe:2.3:a:analyticswp:analyticswp:*:*:*:*:*:wordpress:*:*"
],
"anchor-episodes-index": [
"cpe:2.3:a:jesweb:anchor_episodes_index:*:*:*:*:*:wordpress:*:*"
],
@@ -8373,9 +8337,6 @@
"cpe:2.3:a:ithemes:security:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:solidwp:solid_security:*:*:*:*:*:wordpress:*:*"
],
"betterdocs": [
"cpe:2.3:a:wpdeveloper:betterdocs:*:*:*:*:*:wordpress:*:*"
],
"betterlinks": [
"cpe:2.3:a:wpdeveloper:betterlinks:*:*:*:*:free:wordpress:*:*"
],
@@ -8403,9 +8364,6 @@
"bigmart-elements": [
"cpe:2.3:a:wpcirqle:bigmart_elements:*:*:*:*:*:wordpress:*:*"
],
"bilingual-linker": [
"cpe:2.3:a:ylefebvre:bilingual_linker:*:*:*:*:*:wordpress:*:*"
],
"billingo": [
"cpe:2.3:a:official_integration_for_billingo_project:official_integration_for_billingo:*:*:*:*:*:wordpress:*:*"
],
@@ -8816,12 +8774,6 @@
"byconsole-woo-order-delivery-time": [
"cpe:2.3:a:byconsole:wooodt_lite:*:*:*:*:*:wordpress:*:*"
],
"c9-admin-dashboard": [
"cpe:2.3:a:covertnine:c9_admin_dashboard:*:*:*:*:*:wordpress:*:*"
],
"c9-blocks": [
"cpe:2.3:a:covertnine:c9_blocks:*:*:*:*:*:wordpress:*:*"
],
"cab-fare-calculator": [
"cpe:2.3:a:kanev:cab_fare_calculator:*:*:*:*:*:wordpress:*:*"
],
@@ -8930,9 +8882,6 @@
"caret-country-access-limit": [
"cpe:2.3:a:caret:country_access_limit:*:*:*:*:*:wordpress:*:*"
],
"carousel-anything": [
"cpe:2.3:a:webdevocean:carousel_anything:*:*:*:*:*:wordpress:*:*"
],
"carousel-ck": [
"cpe:2.3:a:ceikay:carousel_ck:*:*:*:*:*:wordpress:*:*"
],
@@ -9119,9 +9068,6 @@
"church-admin": [
"cpe:2.3:a:church_admin_project:church_admin:*:*:*:*:*:wordpress:*:*"
],
"circle-image-slider-with-lightbox": [
"cpe:2.3:a:i13websolution:team_circle_image_slider_with_lightbox:*:*:*:*:*:wordpress:*:*"
],
"circles-gallery": [
"cpe:2.3:a:greentreelabs:circles_gallery:*:*:*:*:*:wordpress:*:*"
],
@@ -10335,9 +10281,6 @@
"drozd-addons-for-elementor": [
"cpe:2.3:a:urchenko:drozd:*:*:*:*:*:wordpress:*:*"
],
"ds-cf7-math-captcha": [
"cpe:2.3:a:dotsquares:contact_form_7_math_captcha:*:*:*:*:*:wordpress:*:*"
],
"dsgvo-all-in-one-for-wp": [
"cpe:2.3:a:dsgvo-for-wp:dsgvo_all_in_one_for_wp:*:*:*:*:*:wordpress:*:*"
],
@@ -10489,9 +10432,6 @@
"easy-menu-manager-wpzest": [
"cpe:2.3:a:wpzest:easy_menu_manager:*:*:*:*:*:wordpress:*:*"
],
"easy-mls-listings-import": [
"cpe:2.3:a:homeasap:easy_mls_listings_import:*:*:*:*:*:wordpress:*:*"
],
"easy-modal": [
"cpe:2.3:a:easy_modal_project:easy_modal:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:easymodal_project:easy_modal:*:*:*:*:*:wordpress:*:*"
@@ -10529,9 +10469,6 @@
"easy-social-icons": [
"cpe:2.3:a:cybernetikz:easy_social_icons:*:*:*:*:*:wordpress:*:*"
],
"easy-social-share-buttons": [
"cpe:2.3:a:idiom:easy_social_share_buttons:*:*:*:*:*:wordpress:*:*"
],
"easy-student-results": [
"cpe:2.3:a:easy_student_results_project:easy_student_results:*:*:*:*:*:wordpress:*:*"
],
@@ -10596,9 +10533,6 @@
"ebook-download": [
"cpe:2.3:a:zedna_ebook_download_project:zedna_ebook_download:*:*:*:*:*:wordpress:*:*"
],
"ebook-downloader": [
"cpe:2.3:a:infoway:ebook_downloader:*:*:*:*:*:wordpress:*:*"
],
"ebook-store": [
"cpe:2.3:a:shopfiles:ebook_store:*:*:*:*:*:wordpress:*:*"
],
@@ -10664,9 +10598,6 @@
"elegant-themes-icons": [
"cpe:2.3:a:wpai:elegant_themes_icons:*:*:*:*:*:wordpress:*:*"
],
"elemenda": [
"cpe:2.3:a:dankedev:elemenda:*:*:*:*:*:wordpress:*:*"
],
"element-ready-lite": [
"cpe:2.3:a:quomodosoft:elementsready:*:*:*:*:free:wordpress:*:*"
],
@@ -10739,9 +10670,6 @@
"cpe:2.3:a:icegram:email_subscribers_\\\u0026_newsletters:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:icegram:icegram_express:*:*:*:*:*:wordpress:*:*"
],
"email-template-customizer-for-woo": [
"cpe:2.3:a:villatheme:woocommerce_email_template_customizer:*:*:*:*:*:wordpress:*:*"
],
"email-users": [
"cpe:2.3:a:email_users_project:email_users:*:*:*:*:*:wordpress:*:*"
],
@@ -10821,9 +10749,6 @@
"envato-elements": [
"cpe:2.3:a:envato:envato_elements:*:*:*:*:*:wordpress:*:*"
],
"envialosimple-email-marketing-y-newsletters-gratis": [
"cpe:2.3:a:donweb:envialosimple:*:*:*:*:*:wordpress:*:*"
],
"envira-gallery-lite": [
"cpe:2.3:a:enviragallery:envira_gallery:*:*:*:*:lite:wordpress:*:*"
],
@@ -10837,9 +10762,6 @@
"eonet-manual-user-approve": [
"cpe:2.3:a:xtendify:eonet_manual_user_approve:*:*:*:*:*:wordpress:*:*"
],
"epoll-wp-voting": [
"cpe:2.3:a:infotheme:wp_poll_maker:*:*:*:*:*:wordpress:*:*"
],
"eps-301-redirects": [
"cpe:2.3:a:webfactoryltd:301_redirects:*:*:*:*:*:wordpress:*:*"
],
@@ -11098,9 +11020,6 @@
"facebook-wall-and-social-integration": [
"cpe:2.3:a:facebook-wall-and-social-integration_project:facebook-wall-and-social-integration:*:*:*:*:*:wordpress:*:*"
],
"falang": [
"cpe:2.3:a:faboba:falang:*:*:*:*:*:wordpress:*:*"
],
"fancier-author-box": [
"cpe:2.3:a:thematosoup:fancier_author_box:*:*:*:*:*:wordpress:*:*"
],
@@ -11848,9 +11767,6 @@
"google-language-translator": [
"cpe:2.3:a:gtranslate:google_language_translator:*:*:*:*:*:wordpress:*:*"
],
"google-map-locations": [
"cpe:2.3:a:dotsquares:google_map_locations:*:*:*:*:*:wordpress:*:*"
],
"google-map-shortcode": [
"cpe:2.3:a:web-argument:google-map-shortcode:*:*:*:*:*:wordpress:*:*"
],
@@ -12011,9 +11927,6 @@
"guest-author-name": [
"cpe:2.3:a:shooflysolutions:\\(simply\\)_guest_author_name:*:*:*:*:*:wordpress:*:*"
],
"gum-elementor-addon": [
"cpe:2.3:a:celomitan:gum_elementor_addon:*:*:*:*:*:wordpress:*:*"
],
"gumlet-video": [
"cpe:2.3:a:gumlet:video:*:*:*:*:*:wordpress:*:*"
],
@@ -12088,9 +12001,6 @@
"header-enhancement": [
"cpe:2.3:a:catchplugins:header_enhancement:*:*:*:*:*:wordpress:*:*"
],
"header-footer-code": [
"cpe:2.3:a:ninjateam:header_footer_custom_code:*:*:*:*:*:wordpress:*:*"
],
"header-footer-code-manager": [
"cpe:2.3:a:draftpress:header_footer_code_manager:*:*:*:*:*:wordpress:*:*"
],
@@ -12504,9 +12414,6 @@
"inlinks": [
"cpe:2.3:a:inlinks_project:inlinks:*:*:*:*:*:wordpress:*:*"
],
"inlocation": [
"cpe:2.3:a:gabrieldarezzo:inlocation:*:*:*:*:*:wordpress:*:*"
],
"innovs-hr-manager": [
"cpe:2.3:a:theinnovs:innovs_hr:*:*:*:*:*:wordpress:*:*"
],
@@ -13050,9 +12957,6 @@
"light-messages": [
"cpe:2.3:a:light_messages_project:light_messages:*:*:*:*:*:wordpress:*:*"
],
"light-poll": [
"cpe:2.3:a:dmytropopov:light_poll:*:*:*:*:*:wordpress:*:*"
],
"lightbox-gallery": [
"cpe:2.3:a:wpgogo:lightbox-gallery:*:*:*:*:*:wordpress:*:*"
],
@@ -13118,9 +13022,6 @@
"list-category-posts": [
"cpe:2.3:a:fernandobriano:list_category_posts:*:*:*:*:*:wordpress:*:*"
],
"listamester": [
"cpe:2.3:a:listamester:listamester:*:*:*:*:*:wordpress:*:*"
],
"litespeed-cache": [
"cpe:2.3:a:litespeedtech:litespeed_cache:*:*:*:*:*:wordpress:*:*"
],
@@ -13343,9 +13244,6 @@
"makestories-helper": [
"cpe:2.3:a:makestories:makestories_\\(for_google_web_stories\\):*:*:*:*:*:wordpress:*:*"
],
"makewebbetter-hubspot-for-woocommerce": [
"cpe:2.3:a:makewebbetter:hubspot_for_woocommerce:*:*:*:*:*:wordpress:*:*"
],
"malinky-ajax-pagination": [
"cpe:2.3:a:malinky:malinky-ajax-pagination:*:*:*:*:*:wordpress:*:*"
],
@@ -13376,9 +13274,6 @@
"cpe:2.3:a:mappresspro:mappress:*:*:*:*:pro:wordpress:*:*",
"cpe:2.3:a:mappresspro:mappress_maps_for_wordpress:*:*:*:*:*:wordpress:*:*"
],
"maps-for-wp": [
"cpe:2.3:a:icopydoc:maps_for_wp:*:*:*:*:*:wordpress:*:*"
],
"mapster-wp-maps": [
"cpe:2.3:a:mapster:mapster_wp_maps:*:*:*:*:*:wordpress:*:*"
],
@@ -14100,9 +13995,6 @@
"ninja-tables": [
"cpe:2.3:a:wpmanageninja:ninja_tables:*:*:*:*:*:wordpress:*:*"
],
"ninjafirewall": [
"cpe:2.3:a:nintechnet:ninjafirewall:*:*:*:*:*:wordpress:*:*"
],
"nirweb-support": [
"cpe:2.3:a:nirweb:nirweb_support:*:*:*:*:*:wordpress:*:*"
],
@@ -14462,9 +14354,6 @@
"password-protected": [
"cpe:2.3:a:wpexperts:password_protected:*:*:*:*:*:wordpress:*:*"
],
"passwords-manager": [
"cpe:2.3:a:hirewebxperts:passwords_manager:*:*:*:*:*:wordpress:*:*"
],
"patreon-connect": [
"cpe:2.3:a:patreon:patreon_wordpress:*:*:*:*:*:wordpress:*:*"
],
@@ -14879,9 +14768,6 @@
"post-list-designer": [
"cpe:2.3:a:infornweb:posts_list_designer_by_category:*:*:*:*:*:wordpress:*:*"
],
"post-lockdown": [
"cpe:2.3:a:andypalmer:post_lockdown:*:*:*:*:*:wordpress:*:*"
],
"post-meta-data-manager": [
"cpe:2.3:a:wpexpertplugins:post_meta_data_manager:*:*:*:*:*:wordpress:*:*"
],
@@ -14954,13 +14840,10 @@
"cpe:2.3:a:posttabs_project:posttabs:*:*:*:*:*:wordpress:*:*"
],
"powerpack-addon-for-beaver-builder": [
"cpe:2.3:a:ideabox:powerpack_for_beaver_builder:*:*:*:*:lite:wordpress:*:*"
],
"powerpack-elements": [
"cpe:2.3:a:ideabox:powerpack_addons_for_elementor:*:*:*:*:pro:wordpress:*:*"
"cpe:2.3:a:wpbeaveraddons:powerpack_lite_for_beaver_builder:*:*:*:*:*:wordpress:*:*"
],
"powerpack-lite-for-elementor": [
"cpe:2.3:a:ideabox:powerpack_addons_for_elementor:*:*:*:*:free:wordpress:*:*"
"cpe:2.3:a:ideabox:powerpack_addons_for_elementor:*:*:*:*:*:wordpress:*:*"
],
"powerpress": [
"cpe:2.3:a:blubrry:powerpress:*:*:*:*:*:wordpress:*:*"
@@ -15739,9 +15622,6 @@
"saan-world-clock": [
"cpe:2.3:a:saan:world_clock:*:*:*:*:*:wordpress:*:*"
],
"safe-ai-malware-protection-for-wp": [
"cpe:2.3:a:wpmessiah:safe_ai_malware_protection_for_wp:*:*:*:*:*:wordpress:*:*"
],
"safe-editor": [
"cpe:2.3:a:kodebyraaet:safe_editor:*:*:*:*:*:wordpress:*:*"
],
@@ -15803,9 +15683,6 @@
"scoutnet-kalender": [
"cpe:2.3:a:scoutnet:kalender:*:*:*:*:*:wordpress:*:*"
],
"scratch-win-giveaways-for-website-facebook": [
"cpe:2.3:a:akashmalik:scratch_\\\u0026_win:*:*:*:*:*:wordpress:*:*"
],
"scribble-maps": [
"cpe:2.3:a:scribblemaps:scribble_maps:*:*:*:*:*:wordpress:*:*"
],
@@ -16290,18 +16167,12 @@
"simple-posts-ticker": [
"cpe:2.3:a:sayandatta:simple_posts_ticker:*:*:*:*:*:wordpress:*:*"
],
"simple-pricing-tables-vc-extension": [
"cpe:2.3:a:webdevocean:pricing_tables:*:*:*:*:*:wordpress:*:*"
],
"simple-quotation": [
"cpe:2.3:a:sedlex:simple_quotation:*:*:*:*:*:wordpress:*:*"
],
"simple-real-estate-pack-4": [
"cpe:2.3:a:simple_real_estate_pack_project:simple_real_estate_pack:*:*:*:*:*:wordpress:*:*"
],
"simple-responsive-slider": [
"cpe:2.3:a:marcelotorres:simple_responsive_slider:*:*:*:*:*:wordpress:*:*"
],
"simple-revisions-delete": [
"cpe:2.3:a:b-website:simple_revisions_delete:*:*:*:*:*:wordpress:*:*"
],
@@ -17036,9 +16907,6 @@
"svgmagic": [
"cpe:2.3:a:andibauer:svgmagic:*:*:*:*:*:wordpress:*:*"
],
"svs-pricing-tables": [
"cpe:2.3:a:svs-websoft:svs_pricing_tables:*:*:*:*:*:wordpress:*:*"
],
"swatchly": [
"cpe:2.3:a:hasthemes:swatchly:*:*:*:*:*:wordpress:*:*"
],
@@ -17228,6 +17096,7 @@
"cpe:2.3:a:posimyth:the_plus_addons_for_elementor_page_builder_lite:*:*:*:*:*:wordpress:*:*"
],
"the-post-grid": [
"cpe:2.3:a:radiustheme:post_grid:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:radiustheme:the_post_grid:*:*:*:*:*:wordpress:*:*"
],
"the-sorter": [
@@ -17288,7 +17157,8 @@
"cpe:2.3:a:brandevolutionco:themeshark_templates_\\\u0026_widgets_for_elementor:*:*:*:*:*:wordpress:*:*"
],
"themify-builder": [
"cpe:2.3:a:themify:builder:*:*:*:*:-:wordpress:*:*"
"cpe:2.3:a:themify:builder:*:*:*:*:-:wordpress:*:*",
"cpe:2.3:a:themify:themify_builder:*:*:*:*:*:wordpress:*:*"
],
"themify-portfolio-post": [
"cpe:2.3:a:themify:portfolio_post:*:*:*:*:*:wordpress:*:*"
@@ -17602,9 +17472,6 @@
"ultimate-category-excluder": [
"cpe:2.3:a:infolific:ultimate_category_excluder:*:*:*:*:*:wordpress:*:*"
],
"ultimate-classified-listings": [
"cpe:2.3:a:webcodingplace:ultimate_classified_listings:*:*:*:*:*:wordpress:*:*"
],
"ultimate-dashboard": [
"cpe:2.3:a:davidvongries:ultimate_dashboard:*:*:*:*:*:wordpress:*:*"
],
@@ -17999,9 +17866,6 @@
"w-dalil": [
"cpe:2.3:a:w-dalil_project:w-dalil:*:*:*:*:*:wordpress:*:*"
],
"w2s-migrate-woo-to-shopify": [
"cpe:2.3:a:villatheme:w2s:*:*:*:*:*:wordpress:*:*"
],
"w3-total-cache": [
"cpe:2.3:a:boldgrid:w3_total_cache:*:*:*:*:*:wordpress:*:*"
],
@@ -18035,9 +17899,6 @@
"watu": [
"cpe:2.3:a:kibokolabs:watu_quiz:*:*:*:*:*:wordpress:*:*"
],
"wc-affiliate": [
"cpe:2.3:a:codexpert:wc_affiliate:*:*:*:*:*:wordpress:*:*"
],
"wc-dynamic-pricing-and-discounts": [
"cpe:2.3:a:rightpress:woocommerce_dynamic_pricing_\\\u0026_discounts:*:*:*:*:*:wordpress:*:*"
],
@@ -18123,9 +17984,6 @@
"web-stories": [
"cpe:2.3:a:google:web_stories:*:*:*:*:*:wordpress:*:*"
],
"web-stories-enhancer": [
"cpe:2.3:a:magazine3:web_stories_enhancer:*:*:*:*:*:wordpress:*:*"
],
"web3-authentication": [
"cpe:2.3:a:miniorange:web3_-_crypto_wallet_login_\\\u0026_nft_token_gating:*:*:*:*:*:wordpress:*:*"
],
@@ -18588,9 +18446,6 @@
"woocommerce-store-toolkit": [
"cpe:2.3:a:visser:store_toolkit_for_woocommerce:*:*:*:*:*:wordpress:*:*"
],
"woocommerce-superfaktura": [
"cpe:2.3:a:superfaktura:superfaktura_woocommerce:*:*:*:*:*:wordpress:*:*"
],
"woocommerce-wholesale-prices": [
"cpe:2.3:a:rymera:wholesale_suite:*:*:*:*:*:wordpress:*:*"
],
@@ -18710,15 +18565,9 @@
"wordpress-users": [
"cpe:2.3:a:jonathonkemp:wordpress_users:*:*:*:*:*:wordpress:*:*"
],
"workreap": [
"cpe:2.3:a:amentotech:workreap:*:*:*:*:*:wordpress:*:*"
],
"wow-moodboard-lite": [
"cpe:2.3:a:wownmedia:wow_moodboard:*:*:*:*:lite:wordpress:*:*"
],
"wowpth": [
"cpe:2.3:a:andreafarracani:wowpth:*:*:*:*:*:wordpress:*:*"
],
"wp-2fa": [
"cpe:2.3:a:melapress:wp_2fa:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wpwhitesecurity:wp_2fa:*:*:*:*:*:wordpress:*:*"
@@ -19229,9 +19078,6 @@
"wp-gmappity-easy-google-maps": [
"cpe:2.3:a:matthewschwartz:google_maps_made_simple:*:*:*:*:*:wordpress:*:*"
],
"wp-google-analytics-events": [
"cpe:2.3:a:wpgoaltracker:wp_google_analytics_events:*:*:*:*:*:wordpress:*:*"
],
"wp-google-fonts": [
"cpe:2.3:a:wp_google_fonts_project:wp_google_fonts:*:*:*:*:*:*:*:*"
],
@@ -19671,9 +19517,6 @@
"wp-reset": [
"cpe:2.3:a:webfactoryltd:wp_reset:*:*:*:*:*:wordpress:*:*"
],
"wp-responsive-photo-gallery": [
"cpe:2.3:a:i13websolution:photo_gallery_slideshow_\\\u0026_masonry_tiled_gallery:*:*:*:*:*:wordpress:*:*"
],
"wp-responsive-slider-with-lightbox": [
"cpe:2.3:a:i13websolution:thumbnail_slider_with_lightbox:*:*:*:*:*:wordpress:*:*"
],
@@ -20341,9 +20184,6 @@
"xllentech-english-islamic-calendar": [
"cpe:2.3:a:xllentech:english_islamic_calendar:*:*:*:*:*:wordpress:*:*"
],
"xml-for-google-merchant-center": [
"cpe:2.3:a:icopydoc:xml_for_google_merchant_center:*:*:*:*:*:wordpress:*:*"
],
"xml-sitemap-generator-for-google": [
"cpe:2.3:a:wpgrim:dynamic_xml_sitemaps_generator_for_google:*:*:*:*:*:wordpress:*:*"
],
@@ -20502,9 +20342,6 @@
"yuzo-related-post": [
"cpe:2.3:a:yuzopro:yuzo:*:*:*:*:*:wordpress:*:*"
],
"z-companion": [
"cpe:2.3:a:wpzita:z_companion:*:*:*:*:*:wordpress:*:*"
],
"z-url-preview": [
"cpe:2.3:a:z-url_preview_project:z-url_preview:*:*:*:*:*:wordpress:*:*"
],
@@ -20558,9 +20395,6 @@
"zm-gallery": [
"cpe:2.3:a:zm-gallery_project:zm-gallery:*:*:*:*:*:wordpress:*:*"
],
"zoho-campaigns": [
"cpe:2.3:a:zoho:zoho_campaigns:*:*:*:*:*:*:*:*"
],
"zoho-crm-forms": [
"cpe:2.3:a:zoho:lead_magnet:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:zohocorp:zoho_crm_lead_magnet:*:*:*:*:*:wordpress:*:*"
@@ -20578,9 +20412,6 @@
"cpe:2.3:a:katieseaborn:zotpress:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:zotpress_project:zotpress:*:*:*:*:*:wordpress:*:*"
],
"zstore-manager-basic": [
"cpe:2.3:a:ikjweb:zstore_manager_basic:*:*:*:*:*:wordpress:*:*"
],
"zx-csv-upload": [
"cpe:2.3:a:zx-csv-upload_project:zx-csv-upload:*:*:*:*:*:wordpress:*:*"
],
@@ -20677,9 +20508,6 @@
"carspot": [
"cpe:2.3:a:scriptsbundle:carspot:*:*:*:*:*:wordpress:*:*"
],
"click-mag": [
"cpe:2.3:a:mvpthemes:click_mag:*:*:*:*:*:wordpress:*:*"
],
"colibri-wp": [
"cpe:2.3:a:colibriwp:colibri:*:*:*:*:*:wordpress:*:*"
],
@@ -20769,9 +20597,6 @@
"hueman": [
"cpe:2.3:a:presscustomizr:hueman:*:*:*:*:*:wordpress:*:*"
],
"idyllic": [
"cpe:2.3:a:themefreesia:idyllic:*:*:*:*:*:wordpress:*:*"
],
"illdy": [
"cpe:2.3:a:colorlib:illdy:*:*:*:*:*:wordpress:*:*"
],
@@ -20855,9 +20680,6 @@
"pinzolo": [
"cpe:2.3:a:thriveweb:pinzolo:*:*:*:*:*:wordpress:*:*"
],
"pixgraphy": [
"cpe:2.3:a:themefreesia:pixgraphy:*:*:*:*:*:wordpress:*:*"
],
"pixova-lite": [
"cpe:2.3:a:colorlib:pixova_lite:*:*:*:*:*:wordpress:*:*"
],
@@ -20945,12 +20767,6 @@
],
"zigcy-lite": [
"cpe:2.3:a:accesspressthemes:zigcy_lite:*:*:*:*:*:wordpress:*:*"
],
"zox-news": [
"cpe:2.3:a:mvpthemes:zox_news:*:*:*:*:*:wordpress:*:*"
],
"zoxpress": [
"cpe:2.3:a:mvpthemes:zoxpress:*:*:*:*:*:wordpress:*:*"
]
}
}
+113 -6
View File
@@ -6,6 +6,9 @@ package nix
import (
"context"
"fmt"
"path/filepath"
"regexp"
"strings"
"github.com/bmatcuk/doublestar/v4"
@@ -28,6 +31,30 @@ func (c *storeCataloger) Name() string {
return catalogerName
}
// Find the parent Nix store path for a given path
func findParentNixStorePath(path string) string {
// Handle standard /nix/store/ paths
parts := strings.Split(path, "/")
for i := 0; i < len(parts)-2; i++ {
if parts[i] == "nix" && parts[i+1] == "store" && i+2 < len(parts) {
// Check if it matches the hash-name pattern
storeItem := parts[i+2]
if matched, _ := regexp.MatchString(`^[a-z0-9]{32}-.*`, storeItem); matched {
return filepath.Join("/", filepath.Join(parts[:i+3]...))
}
}
}
// Handle short-form Nix paths (without the /nix/store/ prefix)
// These would be paths like /[hash]-[package-name]/...
shortPathPattern := regexp.MustCompile(`^/([a-z0-9]{32}-[^/]+)`)
if matches := shortPathPattern.FindStringSubmatch(path); len(matches) == 2 {
return "/" + matches[1]
}
return ""
}
func (c *storeCataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
// we want to search for only directories, which isn't possible via the stereoscope API, so we need to apply the glob manually on all returned paths
var pkgs []pkg.Package
@@ -37,9 +64,23 @@ func (c *storeCataloger) Catalog(ctx context.Context, resolver file.Resolver) ([
for location := range resolver.AllLocations(ctx) {
matchesStorePath, err := doublestar.Match("**/nix/store/*", location.RealPath)
if err != nil {
log.Debugf("Error matching path %s: %v", location.RealPath, err)
return nil, nil, fmt.Errorf("failed to match nix store path: %w", err)
}
if !matchesStorePath {
// Check if this is a "short-form" Nix store path (without the /nix/store/ prefix)
// This pattern matches paths like /[hash]-[package-name]/...
shortPathPattern := regexp.MustCompile(`^/([a-z0-9]{32})-([^/]+)`)
if shortPathPattern.MatchString(location.RealPath) {
matchesStorePath = true
log.Debugf("Matched short-form Nix path: %s", location.RealPath)
}
}
if !matchesStorePath {
log.Debugf("Not a Nix store path: %s", location.RealPath)
continue
}
parentStorePath := findParentNixStorePath(location.RealPath)
if parentStorePath != "" {
if _, ok := filesByPath[parentStorePath]; !ok {
@@ -50,17 +91,24 @@ func (c *storeCataloger) Catalog(ctx context.Context, resolver file.Resolver) ([
}
if !matchesStorePath {
log.Debugf("Not a Nix store path: %s", location.RealPath)
continue
}
storePath := parseNixStorePath(location.RealPath)
if storePath == nil || !storePath.isValidPackage() {
if storePath == nil {
log.Debugf("Failed to parse Nix path: %s", location.RealPath)
continue
}
p := newNixStorePackage(*storePath, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
pkgs = append(pkgs, p)
// Only create packages for non-derivation/source paths
if storePath.shouldIncludeAsPackage() {
p := newNixStorePackage(*storePath, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
pkgs = append(pkgs, p)
} else {
log.Debugf("Skipping non-package path: %s (type: %s)", location.RealPath, storePath.pathType)
}
}
// add file sets to packages
@@ -80,7 +128,64 @@ func (c *storeCataloger) Catalog(ctx context.Context, resolver file.Resolver) ([
appendFiles(p, files.ToSlice()...)
}
return pkgs, nil, nil
// After all packages are created, enrich them with derivation data
for i := range pkgs {
p := &pkgs[i]
metadata, ok := p.Metadata.(pkg.NixStoreEntry)
if !ok {
continue
}
// Find and parse the derivation file
derivPath := findDeriverPath(metadata.Path)
if derivPath != "" {
if deriv, err := ParseDerivation(derivPath); err == nil {
// Enrich package metadata with derivation data
if deriv.PName != "" {
p.Name = deriv.PName
}
if deriv.Version != "" {
p.Version = deriv.Version
}
// Add license, homepage, etc. to metadata
metadata.License = deriv.License
metadata.Homepage = deriv.Homepage
metadata.Description = deriv.Description
metadata.DeriverPath = derivPath
// Update the package metadata
p.Metadata = metadata
}
}
}
// Find and add relationships between packages
relationships, err := findDependencies(pkgs)
if err != nil {
log.Warnf("failed to resolve nix dependencies: %v", err)
}
// Deduplicate packages based on name and version
dedupedPkgs := deduplicateNixPackages(pkgs)
pkgs = dedupedPkgs
return pkgs, relationships, nil
}
// ensureNixStorePrefix makes sure the path has the /nix/store/ prefix
func ensureNixStorePrefix(path string) string {
// Check if the path already has the /nix/store/ prefix
if strings.Contains(path, "/nix/store/") {
return path
}
// Check if this is a short-form path starting with /[hash]-[name]
shortPathPattern := regexp.MustCompile(`^/([a-z0-9]{32}-[^/]+)(.*)$`)
if matches := shortPathPattern.FindStringSubmatch(path); len(matches) >= 3 {
// Convert short form path to full path with /nix/store/ prefix
return "/nix/store/" + matches[1] + matches[2]
}
return path
}
func appendFiles(p *pkg.Package, location ...file.Location) {
@@ -91,7 +196,9 @@ func appendFiles(p *pkg.Package, location ...file.Location) {
}
for _, l := range location {
metadata.Files = append(metadata.Files, l.RealPath)
// Normalize the path to ensure it has the /nix/store/ prefix
normalizedPath := ensureNixStorePrefix(l.RealPath)
metadata.Files = append(metadata.Files, normalizedPath)
}
if metadata.Files == nil {
+207
View File
@@ -0,0 +1,207 @@
package nix
import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
)
// findDependencies attempts to find relationships between Nix packages
// without using external commands
func findDependencies(pkgs []pkg.Package) ([]artifact.Relationship, error) {
var relationships []artifact.Relationship
packageByPath := make(map[string]*pkg.Package)
packageByStorePath := make(map[string]*pkg.Package)
// Index packages by store path and path for quick lookups
for i := range pkgs {
p := &pkgs[i]
metadata, ok := p.Metadata.(pkg.NixStoreEntry)
if !ok {
continue
}
packageByStorePath[metadata.Path] = p
// Also index by the package files
for _, filePath := range metadata.Files {
packageByPath[filePath] = p
}
}
// For each package, try to find references to other packages
for i := range pkgs {
p := &pkgs[i]
metadata, ok := p.Metadata.(pkg.NixStoreEntry)
if !ok {
continue
}
// Scan for references using the derivation file if available
if metadata.DeriverPath != "" {
refs, err := findReferencesInDerivation(metadata.DeriverPath, packageByStorePath)
if err == nil {
relationships = append(relationships, refs...)
}
}
// Scan for references within the package files
if len(metadata.Files) > 0 {
refs, err := findReferencesInFiles(metadata.Files, p, packageByStorePath)
if err == nil {
relationships = append(relationships, refs...)
}
}
}
return relationships, nil
}
// findReferencesInDerivation tries to extract dependencies from a derivation file
func findReferencesInDerivation(drvPath string, packageByPath map[string]*pkg.Package) ([]artifact.Relationship, error) {
var relationships []artifact.Relationship
// Read the derivation file contents
content, err := os.ReadFile(drvPath)
if err != nil {
return nil, err
}
// Look for store paths in the derivation file
storePathPattern := regexp.MustCompile(`/nix/store/[a-z0-9]{32}-[^"'\s]+`)
matches := storePathPattern.FindAllString(string(content), -1)
// Unique matches
uniquePaths := make(map[string]struct{})
for _, match := range matches {
uniquePaths[match] = struct{}{}
}
// Create relationships for found dependencies
for path := range uniquePaths {
depPkg, exists := packageByPath[path]
if !exists {
continue
}
// Skip self-references
fromMeta, ok := depPkg.Metadata.(pkg.NixStoreEntry)
if !ok || fromMeta.Path == drvPath {
continue
}
// Add the dependency relationship
pkg, exists := packageByPath[drvPath]
if !exists {
continue
}
relationships = append(relationships, artifact.Relationship{
From: *pkg,
To: *depPkg,
Type: artifact.DependencyOfRelationship,
Data: map[string]string{"dependencyType": "buildtime"},
})
}
return relationships, nil
}
// findReferencesInFiles looks for references to other packages within file contents
func findReferencesInFiles(files []string, fromPkg *pkg.Package, packageByPath map[string]*pkg.Package) ([]artifact.Relationship, error) {
var relationships []artifact.Relationship
uniqueRefs := make(map[string]struct{})
// Limit search to a reasonable number of files to avoid performance issues
maxFiles := 10
fileCount := 0
for _, file := range files {
if fileCount >= maxFiles {
break
}
// Skip files that are too large
info, err := os.Stat(file)
if err != nil || info.Size() > 1024*1024 {
continue
}
// Skip directories
if info.IsDir() {
continue
}
// Look for binary files that might have references
if isBinary(file) {
fileCount++
refs, err := findReferencesInBinary(file)
if err != nil {
continue
}
for _, ref := range refs {
uniqueRefs[ref] = struct{}{}
}
}
}
// Create relationships for found dependencies
for path := range uniqueRefs {
depPkg, exists := packageByPath[path]
if !exists {
continue
}
// Skip self-references
fromMeta, ok := fromPkg.Metadata.(pkg.NixStoreEntry)
if !ok || fromMeta.Path == path {
continue
}
relationships = append(relationships, artifact.Relationship{
From: *fromPkg,
To: *depPkg,
Type: artifact.DependencyOfRelationship,
Data: map[string]string{"dependencyType": "runtime"},
})
}
return relationships, nil
}
// Helper functions
// isBinary returns true if the file appears to be a binary
func isBinary(path string) bool {
ext := strings.ToLower(filepath.Ext(path))
return ext == ".so" || ext == ".dylib" || ext == ".a" ||
strings.HasSuffix(path, ".so.1") ||
strings.Contains(path, ".so.")
}
// findReferencesInBinary extracts Nix store references from binary files
func findReferencesInBinary(path string) ([]string, error) {
var references []string
// Use string search instead of executing nix-store
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
// Search for store paths in the binary
storePathPattern := regexp.MustCompile(`/nix/store/[a-z0-9]{32}-[^\\:\*\?"<>\|\x00-\x1F]+`)
matches := storePathPattern.FindAll(data, -1)
for _, match := range matches {
references = append(references, string(match))
}
return references, nil
}
+136
View File
@@ -0,0 +1,136 @@
package nix
import (
"bufio"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/anchore/syft/internal/log"
)
// NixDerivation represents a parsed Nix derivation file
type NixDerivation struct {
Path string
Name string
PName string
Version string
System string
Builder string
Args []string
EnvVars map[string]string
Outputs map[string]string
InputDrvs map[string][]string
InputSrcs []string
License string
Homepage string
Description string
OutputHash string
}
// ParseDerivation attempts to parse a Nix derivation file without external commands
func ParseDerivation(path string) (*NixDerivation, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
derivation := &NixDerivation{
Path: path,
EnvVars: make(map[string]string),
Outputs: make(map[string]string),
InputDrvs: make(map[string][]string),
}
// Simple parser for .drv files
scanner := bufio.NewScanner(file)
inEnvVars := false
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
// Process environment variables section
if strings.Contains(line, "envVars = {") {
inEnvVars = true
continue
} else if inEnvVars && strings.Contains(line, "};") {
inEnvVars = false
continue
} else if inEnvVars && strings.Contains(line, " = ") {
// Parse environment variables
parts := strings.SplitN(line, " = ", 2)
if len(parts) == 2 {
key := strings.Trim(parts[0], " \"")
value := strings.Trim(parts[1], " \";")
derivation.EnvVars[key] = value
}
}
}
// Extract package information from environment variables
derivation.Name = derivation.EnvVars["name"]
derivation.PName = derivation.EnvVars["pname"]
derivation.Version = derivation.EnvVars["version"]
derivation.System = derivation.EnvVars["system"]
derivation.License = derivation.EnvVars["license"]
derivation.Homepage = derivation.EnvVars["homepage"]
derivation.Description = derivation.EnvVars["description"]
// If pname is not set but name follows pname-version pattern, extract it
if derivation.PName == "" && derivation.Name != "" {
re := regexp.MustCompile(`^(.*?)-([0-9].*)$`)
if matches := re.FindStringSubmatch(derivation.Name); len(matches) == 3 {
derivation.PName = matches[1]
if derivation.Version == "" {
derivation.Version = matches[2]
}
} else {
derivation.PName = derivation.Name
}
}
// Check if this is a patched package with security fixes
for key := range derivation.EnvVars {
if strings.HasPrefix(key, "patches") {
value := derivation.EnvVars[key]
if strings.Contains(value, "CVE-") {
log.Debugf("Found security patch in %s: %s", derivation.Name, value)
}
}
}
return derivation, nil
}
// findDeriverPath attempts to find the derivation file for a store path
func findDeriverPath(storePath string) string {
// If the path is already a derivation, return it
if strings.HasSuffix(storePath, ".drv") {
return storePath
}
// Look for a .drv file with a similar name
drvPathGuess := storePath + ".drv"
if _, err := os.Stat(drvPathGuess); err == nil {
return drvPathGuess
}
// Fall back strategy using string manipulation
// This is an approximation without running nix-store commands
parts := strings.Split(storePath, "-")
if len(parts) >= 2 {
// Try to find the derivation file using the hash part
hash := strings.Split(filepath.Base(storePath), "-")[0]
if matched, _ := regexp.MatchString(`^[a-z0-9]{32}$`, hash); matched {
drvDir := filepath.Join("/nix/store", hash+"-*.drv")
matches, err := filepath.Glob(drvDir)
if err == nil && len(matches) > 0 {
return matches[0]
}
}
}
return ""
}
+64
View File
@@ -56,3 +56,67 @@ func packageURL(storePath nixStorePath) string {
"")
return pURL.ToString()
}
// deduplicateNixPackages combines information from packages with the same name and version
func deduplicateNixPackages(pkgs []pkg.Package) []pkg.Package {
if len(pkgs) == 0 {
return pkgs
}
// Use map to group by name+version
packageMap := make(map[string]*pkg.Package)
for i := range pkgs {
p := &pkgs[i]
key := p.Name + ":" + p.Version
existing, exists := packageMap[key]
if !exists {
// First time seeing this name+version
packageMap[key] = p
continue
}
// Merge information from this package into the existing one
mergePackageInfo(existing, p)
}
// Convert map back to slice
result := make([]pkg.Package, 0, len(packageMap))
for _, p := range packageMap {
result = append(result, *p)
}
return result
}
// mergePackageInfo combines information from src into dest
func mergePackageInfo(dest, src *pkg.Package) {
// Merge locations
for _, loc := range src.Locations.ToSlice() {
dest.Locations.Add(loc)
}
// Merge metadata if both have NixStoreEntry type
destMeta, destOk := dest.Metadata.(pkg.NixStoreEntry)
srcMeta, srcOk := src.Metadata.(pkg.NixStoreEntry)
if destOk && srcOk {
// Combine files lists
destMeta.Files = append(destMeta.Files, srcMeta.Files...)
// Prefer non-empty fields from src
if destMeta.License == "" && srcMeta.License != "" {
destMeta.License = srcMeta.License
}
if destMeta.Homepage == "" && srcMeta.Homepage != "" {
destMeta.Homepage = srcMeta.Homepage
}
if destMeta.Description == "" && srcMeta.Description != "" {
destMeta.Description = srcMeta.Description
}
// Update the metadata
dest.Metadata = destMeta
}
}
+122 -101
View File
@@ -1,134 +1,155 @@
package nix
import (
"fmt"
"path"
"regexp"
"strings"
)
var (
numericPattern = regexp.MustCompile(`\d`)
// attempts to find the right-most example of something that appears to be a version (semver or otherwise)
// example input: h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin
// example output:
// version: "2.34-210"
// major: "2"
// minor: "34"
// patch: "210"
// (there are other capture groups, but they can be ignored)
rightMostVersionIshPattern = regexp.MustCompile(`-(?P<version>(?P<major>[0-9][a-zA-Z0-9]*)(\.(?P<minor>[0-9][a-zA-Z0-9]*))?(\.(?P<patch>0|[1-9][a-zA-Z0-9]*)){0,3}(?:-(?P<prerelease>\d*[.a-zA-Z-][.0-9a-zA-Z-]*)*)?(?:\+(?P<metadata>[.0-9a-zA-Z-]+(?:\.[.0-9a-zA-Z-]+)*))?)`)
unstableVersion = regexp.MustCompile(`-(?P<version>unstable-\d{4}-\d{2}-\d{2})$`)
)
// checkout the package naming conventions here: https://nixos.org/manual/nixpkgs/stable/#sec-package-naming
type nixStorePath struct {
path string
hash string
outputHash string
name string
version string
output string
// New field to indicate the type of Nix path
pathType string // "package", "derivation", "source", or "other"
}
func (p nixStorePath) isValidPackage() bool {
return p.name != "" && p.version != ""
}
func findParentNixStorePath(source string) string {
source = strings.TrimRight(source, "/")
indicator := "nix/store/"
start := strings.Index(source, indicator)
if start == -1 {
return ""
}
startOfHash := start + len(indicator)
nextField := strings.Index(source[startOfHash:], "/")
if nextField == -1 {
return ""
}
startOfSubPath := startOfHash + nextField
return source[0:startOfSubPath]
}
func parseNixStorePath(source string) *nixStorePath {
if strings.HasSuffix(source, ".drv") {
// ignore derivations
// parseNixStorePath extracts package information from a Nix store path.
// This is a more permissive implementation that accepts more formats.
func parseNixStorePath(path string) *nixStorePath {
// Extract the store path component - handle both standard and short forms
// Standard form: /nix/store/[hash]-[name]
// Short form: /[hash]-[name]
storePathPattern := regexp.MustCompile(`(^|.*?)(?:/nix/store/|/)([a-z0-9]{32})-(.+?)(/.*)?$`)
matches := storePathPattern.FindStringSubmatch(path)
if len(matches) < 4 {
return nil
}
source = path.Base(source)
// Extract hash and full package name
hash := matches[2]
fullName := matches[3]
versionStartIdx, versionIsh, prerelease := findVersionIsh(source)
if versionStartIdx == -1 {
return nil
// Extract path to first / after the hash-name pattern
basePath := "/nix/store/" + hash + "-" + fullName
if len(matches) > 4 && matches[4] != "" {
// We have a file within a store path
basePath = "/nix/store/" + hash + "-" + fullName
}
hashName := strings.TrimSuffix(source[0:versionStartIdx], "-")
hashNameFields := strings.Split(hashName, "-")
if len(hashNameFields) < 2 {
return nil
}
hash, name := hashNameFields[0], strings.Join(hashNameFields[1:], "-")
prereleaseFields := strings.Split(prerelease, "-")
lastPrereleaseField := prereleaseFields[len(prereleaseFields)-1]
var version = versionIsh
var output string
if !hasNumeric(lastPrereleaseField) {
// this last prerelease field is probably a nix output
version = strings.TrimSuffix(versionIsh, fmt.Sprintf("-%s", lastPrereleaseField))
output = lastPrereleaseField
}
return &nixStorePath{
// Create the basic result
result := &nixStorePath{
path: basePath,
hash: hash,
outputHash: hash,
name: name,
version: version,
output: output,
}
// Determine the path type based on extension or patterns
if strings.HasSuffix(fullName, ".drv") {
result.pathType = "derivation"
// Remove .drv suffix for name parsing
fullName = strings.TrimSuffix(fullName, ".drv")
} else if strings.Contains(fullName, ".tar.") ||
strings.HasSuffix(fullName, ".tgz") ||
strings.HasSuffix(fullName, ".tar.gz") ||
strings.HasSuffix(fullName, ".tar.bz2") ||
strings.HasSuffix(fullName, ".tar.xz") ||
strings.HasSuffix(fullName, ".zip") ||
strings.HasSuffix(fullName, ".patch") {
result.pathType = "source"
} else {
result.pathType = "package"
}
// Try to parse the output from the path (if present)
if outputParts := strings.Split(fullName, "-"); len(outputParts) > 1 {
lastPart := outputParts[len(outputParts)-1]
// Common Nix output names
outputs := map[string]bool{
"bin": true, "dev": true, "lib": true, "man": true,
"doc": true, "info": true, "out": true,
}
if outputs[lastPart] {
result.output = lastPart
fullName = strings.Join(outputParts[:len(outputParts)-1], "-")
}
}
// For non-drv files, try different version extraction patterns
versionPatterns := []*regexp.Regexp{
// Standard version: name-1.2.3
regexp.MustCompile(`^(.+)-([0-9][0-9.]+(?:-[0-9]+)?)$`),
// Unstable version: name-unstable-2022-05-15
regexp.MustCompile(`^(.+)-unstable-([0-9]{4}-[0-9]{2}-[0-9]{2})$`),
// Version with prefix: name-v1.2.3
regexp.MustCompile(`^(.+)-(v[0-9][0-9.]+)$`),
// Year-based versions: name-2022.05
regexp.MustCompile(`^(.+)-([0-9]{4}(?:\.[0-9]+)*)$`),
// Version with suffix: name-1.2.3-suffix
regexp.MustCompile(`^(.+)-([0-9][0-9.]+(?:-[a-z0-9]+)*)$`),
}
// Try each pattern to extract name and version
for _, pattern := range versionPatterns {
if nameVerMatches := pattern.FindStringSubmatch(fullName); len(nameVerMatches) == 3 {
result.name = nameVerMatches[1]
result.version = nameVerMatches[2]
return result
}
}
// If no version pattern matched, consider the entire string the package name
result.name = fullName
result.version = ""
return result
}
func hasNumeric(s string) bool {
return numericPattern.MatchString(s)
// isLikelyOutput checks if a string is likely to be a Nix output name
func isLikelyOutput(s string) bool {
commonOutputs := map[string]bool{
"bin": true, "lib": true, "dev": true, "out": true,
"doc": true, "man": true, "info": true, "devdoc": true,
}
return commonOutputs[s]
}
func findVersionIsh(input string) (int, string, string) {
// we want to return the index of the start of the "version" group (the first capture group).
// note that the match indices are in the form of [start, end, start, end, ...]. Also note that the
// capture group for version in both regexes are the same index, but if the regexes are changed
// this code will start to fail.
versionGroup := 1
// isLikelyVersion checks if a string looks like a version number
func isLikelyVersion(s string) bool {
// Simple check for common version patterns
versionPattern := regexp.MustCompile(`^v?\d+(\.\d+)*(-\w+)*$`)
return versionPattern.MatchString(s)
}
match := unstableVersion.FindAllStringSubmatchIndex(input, -1)
if len(match) > 0 && len(match[0]) > 0 {
return match[0][versionGroup*2], input[match[0][versionGroup*2]:match[0][(versionGroup*2)+1]], ""
// shouldIncludeAsPackage determines if this store path should be included as a package
func (n *nixStorePath) shouldIncludeAsPackage() bool {
// Only include actual packages, not derivations or source files
if n.pathType == "derivation" || n.pathType == "source" {
return false
}
match = rightMostVersionIshPattern.FindAllStringSubmatchIndex(input, -1)
if len(match) == 0 || len(match[0]) == 0 {
return -1, "", ""
// Skip paths that look like source archives even if not explicitly marked
if strings.HasSuffix(n.path, ".tar.gz") ||
strings.HasSuffix(n.path, ".tgz") ||
strings.HasSuffix(n.path, ".tar.bz2") ||
strings.HasSuffix(n.path, ".tar.xz") ||
strings.HasSuffix(n.path, ".zip") ||
strings.HasSuffix(n.path, ".drv") {
return false
}
var version string
versionStart, versionStop := match[0][versionGroup*2], match[0][(versionGroup*2)+1]
if versionStart != -1 || versionStop != -1 {
version = input[versionStart:versionStop]
}
prereleaseGroup := 7
var prerelease string
prereleaseStart, prereleaseStop := match[0][prereleaseGroup*2], match[0][(prereleaseGroup*2)+1]
if prereleaseStart != -1 && prereleaseStop != -1 {
prerelease = input[prereleaseStart:prereleaseStop]
}
// Make sure it has a name (at minimum)
return n.name != ""
}
return versionStart,
version,
prerelease
// isValidPackage should be much more permissive
func (n *nixStorePath) isValidPackage() bool {
// Any path with a hash and name is considered valid
return n.hash != "" && n.name != ""
}
+2 -10
View File
@@ -201,11 +201,7 @@ func (c *Collection) deleteNameFromIndex(p Package) {
nameIndex := c.idsByName[p.Name]
nameIndex.delete(p.id)
if len(nameIndex.slice) == 0 {
delete(c.idsByName, p.Name)
} else {
c.idsByName[p.Name] = nameIndex
}
c.idsByName[p.Name] = nameIndex
}
func (c *Collection) deleteTypeFromIndex(p Package) {
@@ -213,11 +209,7 @@ func (c *Collection) deleteTypeFromIndex(p Package) {
typeIndex := c.idsByType[p.Type]
typeIndex.delete(p.id)
if len(typeIndex.slice) == 0 {
delete(c.idsByType, p.Type)
} else {
c.idsByType[p.Type] = typeIndex
}
c.idsByType[p.Type] = typeIndex
}
func (c *Collection) deletePathsFromIndex(p Package) {
-18
View File
@@ -184,24 +184,6 @@ func TestCatalogDeleteRemovesPackages(t *testing.T) {
},
},
},
{
name: "delete idsBy key entries when all deleted",
pkgs: []Package{
{
id: artifact.ID("pkg:deb/debian/1"),
Name: "debian",
Version: "1",
Type: DebPkg,
Locations: file.NewLocationSet(
file.NewVirtualLocation("/c/path", "/another/path1"),
),
},
},
deleteIDs: []artifact.ID{
artifact.ID("pkg:deb/debian/1"),
},
expectedIndexes: expectedIndexes{},
},
}
for _, test := range tests {
+7 -14
View File
@@ -26,11 +26,10 @@ import (
var _ source.Source = (*fileSource)(nil)
type Config struct {
Path string
Exclude source.ExcludeConfig
DigestAlgorithms []crypto.Hash
Alias source.Alias
SkipExtractArchive bool
Path string
Exclude source.ExcludeConfig
DigestAlgorithms []crypto.Hash
Alias source.Alias
}
type fileSource struct {
@@ -59,7 +58,7 @@ func New(cfg Config) (source.Source, error) {
return nil, fmt.Errorf("given path is a directory: %q", cfg.Path)
}
analysisPath, cleanupFn := fileAnalysisPath(cfg.Path, cfg.SkipExtractArchive)
analysisPath, cleanupFn := fileAnalysisPath(cfg.Path)
var digests []file.Digest
if len(cfg.DigestAlgorithms) > 0 {
@@ -207,15 +206,9 @@ func (s *fileSource) Close() error {
// fileAnalysisPath returns the path given, or in the case the path is an archive, the location where the archive
// contents have been made available. A cleanup function is provided for any temp files created (if any).
// Users can disable unpacking archives, allowing individual cataloguers to extract them instead (where
// supported)
func fileAnalysisPath(path string, skipExtractArchive bool) (string, func() error) {
var cleanupFn = func() error { return nil }
func fileAnalysisPath(path string) (string, func() error) {
var analysisPath = path
if skipExtractArchive {
return analysisPath, cleanupFn
}
var cleanupFn = func() error { return nil }
// if the given file is an archive (as indicated by the file extension and not MIME type) then unarchive it and
// use the contents as the source. Note: this does NOT recursively unarchive contents, only the given path is
+10 -22
View File
@@ -99,14 +99,13 @@ func TestNewFromFile_WithArchive(t *testing.T) {
testutil.Chdir(t, "..") // run with source/test-fixtures
testCases := []struct {
desc string
input string
expString string
inputPaths []string
expRefs int
layer2 bool
contents string
skipExtractArchive bool
desc string
input string
expString string
inputPaths []string
expRefs int
layer2 bool
contents string
}{
{
desc: "path detected",
@@ -122,25 +121,14 @@ func TestNewFromFile_WithArchive(t *testing.T) {
layer2: true,
contents: "Another .vimrc file",
},
{
desc: "skip extract archive",
input: "test-fixtures/path-detected",
inputPaths: []string{"/.vimrc"},
expRefs: 0,
layer2: false,
skipExtractArchive: true,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
archivePath := setupArchiveTest(t, test.input, test.layer2)
cfg := Config{
Path: archivePath,
SkipExtractArchive: test.skipExtractArchive,
}
src, err := New(cfg)
src, err := New(Config{
Path: archivePath,
})
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, src.Close())