From be0283374a17612b6d0671a3b53d1886f791deff Mon Sep 17 00:00:00 2001 From: Simeon Stoykov Date: Wed, 11 Jun 2025 10:50:06 +0200 Subject: [PATCH 01/11] failing test Signed-off-by: Simeon Stoykov --- syft/pkg/cataloger/conda/cataloger.go | 24 +++++ syft/pkg/cataloger/conda/cataloger_test.go | 96 +++++++++++++++++++ .../jupyterlab-4.4.3-pyhd8ed1ab_0.json | 63 ++++++++++++ syft/pkg/conda.go | 42 ++++++++ syft/pkg/type.go | 6 ++ 5 files changed, 231 insertions(+) create mode 100644 syft/pkg/cataloger/conda/cataloger.go create mode 100644 syft/pkg/cataloger/conda/cataloger_test.go create mode 100644 syft/pkg/cataloger/conda/test-fixtures/conda-metas/jupyterlab/jupyterlab-4.4.3-pyhd8ed1ab_0.json create mode 100644 syft/pkg/conda.go diff --git a/syft/pkg/cataloger/conda/cataloger.go b/syft/pkg/cataloger/conda/cataloger.go new file mode 100644 index 00000000000..28d30258cc5 --- /dev/null +++ b/syft/pkg/cataloger/conda/cataloger.go @@ -0,0 +1,24 @@ +/* +Package conda provides a concrete Cataloger implementation for packages within the Conda ecosystem. +*/ +package conda + +import ( + "context" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" +) + +// NewCondaCataloger returns a new cataloger object for Conda environments. +func NewCondaCataloger() pkg.Cataloger { + return generic.NewCataloger("conda-cataloger"). + WithParserByGlobs(parseCondaMeta, "**/conda-meta/*.json") +} + +// parseCondaMeta is a stub parser for conda-meta JSON files. +func parseCondaMeta(_ context.Context, _ file.Resolver, _ *generic.Environment, _ file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + return nil, nil, nil +} diff --git a/syft/pkg/cataloger/conda/cataloger_test.go b/syft/pkg/cataloger/conda/cataloger_test.go new file mode 100644 index 00000000000..b73270e2b51 --- /dev/null +++ b/syft/pkg/cataloger/conda/cataloger_test.go @@ -0,0 +1,96 @@ +package conda + +import ( + "context" + "testing" + + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func Test_CondaCataloger(t *testing.T) { + ctx := context.TODO() + + tests := []struct { + name string + fixture string + expectedPackages []pkg.Package + }{ + { + name: "regular python package", + fixture: "test-fixtures/conda-metas/jupyterlab", + expectedPackages: []pkg.Package{ + { + Name: "jupyterlab", + Version: "4.4.3", + FoundBy: "conda-conda-meta-cataloger", + PURL: "pkg:generic/jupyterlab@4.4.3", // TODO CONDAPKG: We do not have conda-specific grype support yet, so we use generic. + Locations: file.NewLocationSet( + file.NewLocation("jupyterlab-4.4.3-pyhd8ed1ab_0.json"), + ), + Language: pkg.UnknownLanguage, + Type: pkg.CondaPkg, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocationsWithContext(ctx, "BSD-3-Clause", file.NewLocation("jupyterlab-4.4.3-pyhd8ed1ab_0.json")), + ), + Metadata: // Example instantiation of pkg.CondaPackage for jupyterlab-4.4.3-pyhd8ed1ab_0 + + pkg.CondaPackage{ + Name: "jupyterlab", + Version: "4.4.3", + Build: "pyhd8ed1ab_0", + BuildNumber: 0, + Channel: "https://conda.anaconda.org/conda-forge/", + Subdir: "noarch", + Noarch: "python", + License: "BSD-3-Clause", + LicenseFamily: "BSD", + MD5: "4861a0c2a5a5d0481a450a9dfaf9febe", + SHA256: "fc0235a71d852734fe92183a78cb91827367573450eba82465ae522c64230736", + Size: 8236973, + Timestamp: 1748273017680, + Filename: "jupyterlab-4.4.3-pyhd8ed1ab_0.conda", + URL: "https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.3-pyhd8ed1ab_0.conda", + ExtractedPackageDir: "/Users/simeon/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", + Depends: []string{ + "async-lru >=1.0.0", + "httpx >=0.25.0", + "importlib-metadata >=4.8.3", + "ipykernel >=6.5.0", + "jinja2 >=3.0.3", + "jupyter-lsp >=2.0.0", + "jupyter_core", + "jupyter_server >=2.4.0,<3", + "jupyterlab_server >=2.27.1,<3", + "notebook-shim >=0.2", + "packaging", + "python >=3.9", + "setuptools >=41.1.0", + "tomli >=1.2.2", + "tornado >=6.2.0", + "traitlets", + }, + Files: []string{ + "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json", + "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json", + "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/package.json.orig", + "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/palette.json", + "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/print.json", + // ... (truncated for brevity, add more as needed) + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + (pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + Expects(test.expectedPackages, nil). + TestCataloger(t, NewCondaCataloger())) + }) + } +} diff --git a/syft/pkg/cataloger/conda/test-fixtures/conda-metas/jupyterlab/jupyterlab-4.4.3-pyhd8ed1ab_0.json b/syft/pkg/cataloger/conda/test-fixtures/conda-metas/jupyterlab/jupyterlab-4.4.3-pyhd8ed1ab_0.json new file mode 100644 index 00000000000..8f049e55dda --- /dev/null +++ b/syft/pkg/cataloger/conda/test-fixtures/conda-metas/jupyterlab/jupyterlab-4.4.3-pyhd8ed1ab_0.json @@ -0,0 +1,63 @@ +{ + "build": "pyhd8ed1ab_0", + "build_number": 0, + "depends": [ + "async-lru >=1.0.0", + "httpx >=0.25.0", + "importlib-metadata >=4.8.3", + "ipykernel >=6.5.0", + "jinja2 >=3.0.3", + "jupyter-lsp >=2.0.0", + "jupyter_core", + "jupyter_server >=2.4.0,<3", + "jupyterlab_server >=2.27.1,<3", + "notebook-shim >=0.2", + "packaging", + "python >=3.9", + "setuptools >=41.1.0", + "tomli >=1.2.2", + "tornado >=6.2.0", + "traitlets" + ], + "license": "BSD-3-Clause", + "license_family": "BSD", + "md5": "4861a0c2a5a5d0481a450a9dfaf9febe", + "name": "jupyterlab", + "noarch": "python", + "sha256": "fc0235a71d852734fe92183a78cb91827367573450eba82465ae522c64230736", + "size": 8236973, + "subdir": "noarch", + "timestamp": 1748273017680, + "version": "4.4.3", + "fn": "jupyterlab-4.4.3-pyhd8ed1ab_0.conda", + "url": "https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.3-pyhd8ed1ab_0.conda", + "channel": "https://conda.anaconda.org/conda-forge/", + "extracted_package_dir": "/Users/simeon/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", + "files": [ + "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json", + "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json" + ], + "paths_data": { + "paths_version": 1, + "paths": [ + { + "_path": "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json", + "path_type": "hardlink", + "sha256": "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27", + "sha256_in_prefix": "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27", + "size_in_bytes": 935 + }, + { + "_path": "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json", + "path_type": "hardlink", + "sha256": "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8", + "sha256_in_prefix": "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8", + "size_in_bytes": 1565 + } + ] + }, + "link": { + "source": "/Users/simeon/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", + "type": 1 + } +} \ No newline at end of file diff --git a/syft/pkg/conda.go b/syft/pkg/conda.go new file mode 100644 index 00000000000..7c6c5c9afe4 --- /dev/null +++ b/syft/pkg/conda.go @@ -0,0 +1,42 @@ +package pkg + +type CondaPathData struct { + Path string `json:"_path"` + PathType string `json:"path_type"` + SHA256 string `json:"sha256"` + SHA256InPrefix string `json:"sha256_in_prefix"` + SizeInBytes int64 `json:"size_in_bytes"` +} + +type CondaPathsData struct { + PathsVersion int `json:"paths_version"` + Paths []CondaPathData `json:"paths"` +} + +type CondaLink struct { + Source string `json:"source"` + Type int `json:"type"` +} + +type CondaPackage struct { + Name string `json:"name"` + Version string `json:"version"` + Build string `json:"build"` + BuildNumber int `json:"build_number"` + Channel string `json:"channel,omitempty"` + Subdir string `json:"subdir,omitempty"` + Noarch string `json:"noarch,omitempty"` + License string `json:"license,omitempty"` + LicenseFamily string `json:"license_family,omitempty"` + MD5 string `json:"md5,omitempty"` + SHA256 string `json:"sha256,omitempty"` + Size int64 `json:"size,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` + Filename string `json:"fn,omitempty"` + URL string `json:"url,omitempty"` + ExtractedPackageDir string `json:"extracted_package_dir,omitempty"` + Depends []string `json:"depends,omitempty"` + Files []string `json:"files,omitempty"` + PathsData *CondaPathsData `json:"paths_data,omitempty"` + Link *CondaLink `json:"link,omitempty"` +} diff --git a/syft/pkg/type.go b/syft/pkg/type.go index 04c08cd406e..edf586f3620 100644 --- a/syft/pkg/type.go +++ b/syft/pkg/type.go @@ -16,6 +16,7 @@ const ( BitnamiPkg Type = "bitnami" CocoapodsPkg Type = "pod" ConanPkg Type = "conan" + CondaPkg Type = "conda" DartPubPkg Type = "dart-pub" DebPkg Type = "deb" DotnetPkg Type = "dotnet" @@ -59,6 +60,7 @@ var AllPkgs = []Type{ BitnamiPkg, CocoapodsPkg, ConanPkg, + CondaPkg, DartPubPkg, DebPkg, DotnetPkg, @@ -109,6 +111,8 @@ func (t Type) PackageURLType() string { return packageurl.TypeCocoapods case ConanPkg: return packageurl.TypeConan + case CondaPkg: + return packageurl.TypeGeneric // TODO CONDAPKG: conda does not have a specific purl type, so we use generic case DartPubPkg: return packageurl.TypePub case DebPkg: @@ -206,6 +210,8 @@ func TypeByName(name string) Type { return GemPkg case "cargo", "crate": return RustPkg + case "conda": + return CondaPkg case packageurl.TypePub: return DartPubPkg case "dotnet": // here to support legacy use cases From 597e664d7f395089de32870a6397887aae98555b Mon Sep 17 00:00:00 2001 From: Simeon Stoykov Date: Wed, 11 Jun 2025 18:54:51 +0200 Subject: [PATCH 02/11] conda sboms are generated Signed-off-by: Simeon Stoykov --- internal/task/package_tasks.go | 2 + syft/pkg/cataloger/conda/cataloger.go | 37 +++++++++++++--- syft/pkg/cataloger/conda/cataloger_test.go | 43 +++++++++++++------ .../jupyterlab-4.4.3-pyhd8ed1ab_0.json | 0 syft/pkg/conda.go | 2 +- 5 files changed, 66 insertions(+), 18 deletions(-) rename syft/pkg/cataloger/conda/test-fixtures/{conda-metas/jupyterlab => conda-meta-jupyterlab/conda-meta}/jupyterlab-4.4.3-pyhd8ed1ab_0.json (100%) diff --git a/internal/task/package_tasks.go b/internal/task/package_tasks.go index e350e912cbc..7f13ab2f75a 100644 --- a/internal/task/package_tasks.go +++ b/internal/task/package_tasks.go @@ -7,6 +7,7 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/arch" "github.com/anchore/syft/syft/pkg/cataloger/binary" bitnamiSbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/bitnami" + "github.com/anchore/syft/syft/pkg/cataloger/conda" "github.com/anchore/syft/syft/pkg/cataloger/cpp" "github.com/anchore/syft/syft/pkg/cataloger/dart" "github.com/anchore/syft/syft/pkg/cataloger/debian" @@ -171,6 +172,7 @@ func DefaultPackageTaskFactories() Factories { newSimplePackageTaskFactory(wordpress.NewWordpressPluginCataloger, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "wordpress"), newSimplePackageTaskFactory(terraform.NewLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "terraform"), newSimplePackageTaskFactory(homebrew.NewCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "homebrew"), + newSimplePackageTaskFactory(conda.NewCondaMetaCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "conda"), // deprecated catalogers //////////////////////////////////////// // these are catalogers that should not be selectable other than specific inclusion via name or "deprecated" tag (to remain backwards compatible) diff --git a/syft/pkg/cataloger/conda/cataloger.go b/syft/pkg/cataloger/conda/cataloger.go index 28d30258cc5..984e43c160e 100644 --- a/syft/pkg/cataloger/conda/cataloger.go +++ b/syft/pkg/cataloger/conda/cataloger.go @@ -5,6 +5,8 @@ package conda import ( "context" + "encoding/json" + "fmt" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" @@ -12,13 +14,38 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/generic" ) -// NewCondaCataloger returns a new cataloger object for Conda environments. -func NewCondaCataloger() pkg.Cataloger { - return generic.NewCataloger("conda-cataloger"). +// NewCondaMetaCataloger returns a new cataloger object for Conda environments. +func NewCondaMetaCataloger() pkg.Cataloger { + return generic.NewCataloger("conda-meta-cataloger"). WithParserByGlobs(parseCondaMeta, "**/conda-meta/*.json") } // parseCondaMeta is a stub parser for conda-meta JSON files. -func parseCondaMeta(_ context.Context, _ file.Resolver, _ *generic.Environment, _ file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { - return nil, nil, nil +func parseCondaMeta(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + dec := json.NewDecoder(reader) + var meta pkg.CondaMetaPackage + if err := dec.Decode(&meta); err != nil { + return nil, nil, fmt.Errorf("failed to parse conda-meta package file: %w", err) + } + + p := pkg.Package{ + Name: meta.Name, + Version: meta.Version, + PURL: fmt.Sprintf("pkg:generic/%s@%s", meta.Name, meta.Version), + Locations: file.NewLocationSet(reader.Location), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocationsWithContext(ctx, meta.License, reader.Location), + ), + Language: pkg.UnknownLanguage, + Type: pkg.CondaPkg, + Metadata: meta, + } + + p.SetID() + + pkgs := []pkg.Package{ + p, + } + + return pkgs, nil, nil } diff --git a/syft/pkg/cataloger/conda/cataloger_test.go b/syft/pkg/cataloger/conda/cataloger_test.go index b73270e2b51..8b96870da90 100644 --- a/syft/pkg/cataloger/conda/cataloger_test.go +++ b/syft/pkg/cataloger/conda/cataloger_test.go @@ -2,6 +2,7 @@ package conda import ( "context" + "fmt" "testing" "github.com/anchore/syft/syft/file" @@ -19,24 +20,22 @@ func Test_CondaCataloger(t *testing.T) { }{ { name: "regular python package", - fixture: "test-fixtures/conda-metas/jupyterlab", + fixture: "test-fixtures/conda-meta-jupyterlab", expectedPackages: []pkg.Package{ { Name: "jupyterlab", Version: "4.4.3", - FoundBy: "conda-conda-meta-cataloger", + FoundBy: "conda-meta-cataloger", PURL: "pkg:generic/jupyterlab@4.4.3", // TODO CONDAPKG: We do not have conda-specific grype support yet, so we use generic. Locations: file.NewLocationSet( - file.NewLocation("jupyterlab-4.4.3-pyhd8ed1ab_0.json"), + file.NewLocation("conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json"), ), Language: pkg.UnknownLanguage, Type: pkg.CondaPkg, Licenses: pkg.NewLicenseSet( - pkg.NewLicenseFromLocationsWithContext(ctx, "BSD-3-Clause", file.NewLocation("jupyterlab-4.4.3-pyhd8ed1ab_0.json")), + pkg.NewLicenseFromLocationsWithContext(ctx, "BSD-3-Clause", file.NewLocation("conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json")), ), - Metadata: // Example instantiation of pkg.CondaPackage for jupyterlab-4.4.3-pyhd8ed1ab_0 - - pkg.CondaPackage{ + Metadata: pkg.CondaMetaPackage{ Name: "jupyterlab", Version: "4.4.3", Build: "pyhd8ed1ab_0", @@ -74,10 +73,29 @@ func Test_CondaCataloger(t *testing.T) { Files: []string{ "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json", "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json", - "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/package.json.orig", - "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/palette.json", - "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/print.json", - // ... (truncated for brevity, add more as needed) + }, + PathsData: &pkg.CondaPathsData{ + PathsVersion: 1, + Paths: []pkg.CondaPathData{ + { + Path: "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json", + PathType: "hardlink", + SHA256: "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27", + SHA256InPrefix: "081a7e126deffbcd596863f3349a19416fbbe1fd570ab392270315f7cf5a8c27", + SizeInBytes: 935, + }, + { + Path: "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json", + PathType: "hardlink", + SHA256: "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8", + SHA256InPrefix: "f9f42636592f62cdd03e3d5552b020811e3f8be6fc47c03d5a92396941b8d5d8", + SizeInBytes: 1565, + }, + }, + }, + Link: &pkg.CondaLink{ + Source: "/Users/simeon/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", + Type: 1, }, }, }, @@ -85,12 +103,13 @@ func Test_CondaCataloger(t *testing.T) { }, } + fmt.Println("Hello from the test!") for _, test := range tests { t.Run(test.name, func(t *testing.T) { (pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). Expects(test.expectedPackages, nil). - TestCataloger(t, NewCondaCataloger())) + TestCataloger(t, NewCondaMetaCataloger())) }) } } diff --git a/syft/pkg/cataloger/conda/test-fixtures/conda-metas/jupyterlab/jupyterlab-4.4.3-pyhd8ed1ab_0.json b/syft/pkg/cataloger/conda/test-fixtures/conda-meta-jupyterlab/conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json similarity index 100% rename from syft/pkg/cataloger/conda/test-fixtures/conda-metas/jupyterlab/jupyterlab-4.4.3-pyhd8ed1ab_0.json rename to syft/pkg/cataloger/conda/test-fixtures/conda-meta-jupyterlab/conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json diff --git a/syft/pkg/conda.go b/syft/pkg/conda.go index 7c6c5c9afe4..39d2ca1e451 100644 --- a/syft/pkg/conda.go +++ b/syft/pkg/conda.go @@ -18,7 +18,7 @@ type CondaLink struct { Type int `json:"type"` } -type CondaPackage struct { +type CondaMetaPackage struct { Name string `json:"name"` Version string `json:"version"` Build string `json:"build"` From b7328237528ae7a5d6026cfbc8272fce53805264 Mon Sep 17 00:00:00 2001 From: Simeon Stoykov Date: Thu, 12 Jun 2025 15:49:01 +0200 Subject: [PATCH 03/11] no purl; more tests Signed-off-by: Simeon Stoykov --- syft/pkg/cataloger/conda/cataloger.go | 10 +- syft/pkg/cataloger/conda/cataloger_test.go | 102 +++++++++++++++++- .../package-1.2.3-pyhd8ed1ab_0.json | 1 + .../jupyterlab-4.4.3-pyhd8ed1ab_0.json | 0 .../conda-meta/zlib-1.2.11-h90dfc92_1014.json | 75 +++++++++++++ 5 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 syft/pkg/cataloger/conda/test-fixtures/conda-meta-bad-json/conda-meta/package-1.2.3-pyhd8ed1ab_0.json rename syft/pkg/cataloger/conda/test-fixtures/{conda-meta-jupyterlab => conda-meta-python-c-etc}/conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json (100%) create mode 100644 syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/zlib-1.2.11-h90dfc92_1014.json diff --git a/syft/pkg/cataloger/conda/cataloger.go b/syft/pkg/cataloger/conda/cataloger.go index 984e43c160e..81b3fba920f 100644 --- a/syft/pkg/cataloger/conda/cataloger.go +++ b/syft/pkg/cataloger/conda/cataloger.go @@ -25,13 +25,12 @@ func parseCondaMeta(ctx context.Context, resolver file.Resolver, _ *generic.Envi dec := json.NewDecoder(reader) var meta pkg.CondaMetaPackage if err := dec.Decode(&meta); err != nil { - return nil, nil, fmt.Errorf("failed to parse conda-meta package file: %w", err) + return nil, nil, fmt.Errorf("failed to parse conda-meta package file at %s: %w", reader.Location, err) } p := pkg.Package{ Name: meta.Name, Version: meta.Version, - PURL: fmt.Sprintf("pkg:generic/%s@%s", meta.Name, meta.Version), Locations: file.NewLocationSet(reader.Location), Licenses: pkg.NewLicenseSet( pkg.NewLicenseFromLocationsWithContext(ctx, meta.License, reader.Location), @@ -40,12 +39,9 @@ func parseCondaMeta(ctx context.Context, resolver file.Resolver, _ *generic.Envi Type: pkg.CondaPkg, Metadata: meta, } - p.SetID() - pkgs := []pkg.Package{ + return []pkg.Package{ p, - } - - return pkgs, nil, nil + }, nil, nil } diff --git a/syft/pkg/cataloger/conda/cataloger_test.go b/syft/pkg/cataloger/conda/cataloger_test.go index 8b96870da90..6d1d3c6dd09 100644 --- a/syft/pkg/cataloger/conda/cataloger_test.go +++ b/syft/pkg/cataloger/conda/cataloger_test.go @@ -2,7 +2,6 @@ package conda import ( "context" - "fmt" "testing" "github.com/anchore/syft/syft/file" @@ -19,14 +18,13 @@ func Test_CondaCataloger(t *testing.T) { expectedPackages []pkg.Package }{ { - name: "regular python package", - fixture: "test-fixtures/conda-meta-jupyterlab", + name: "multiple packages in conda meta (python, c binaries, ...)", + fixture: "test-fixtures/conda-meta-python-c-etc", expectedPackages: []pkg.Package{ { Name: "jupyterlab", Version: "4.4.3", FoundBy: "conda-meta-cataloger", - PURL: "pkg:generic/jupyterlab@4.4.3", // TODO CONDAPKG: We do not have conda-specific grype support yet, so we use generic. Locations: file.NewLocationSet( file.NewLocation("conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json"), ), @@ -99,11 +97,105 @@ func Test_CondaCataloger(t *testing.T) { }, }, }, + { + Name: "zlib", + Version: "1.2.11", + FoundBy: "conda-meta-cataloger", + Locations: file.NewLocationSet( + file.NewLocation("conda-meta/zlib-1.2.11-h90dfc92_1014.json"), + ), + Language: pkg.UnknownLanguage, + Type: pkg.CondaPkg, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromLocationsWithContext(ctx, "Zlib", file.NewLocation("conda-meta/zlib-1.2.11-h90dfc92_1014.json")), + ), + Metadata: pkg.CondaMetaPackage{ + Name: "zlib", + Version: "1.2.11", + Build: "h90dfc92_1014", + BuildNumber: 1014, + Channel: "https://conda.anaconda.org/conda-forge/", + Subdir: "osx-arm64", + Noarch: "", + License: "Zlib", + LicenseFamily: "Other", + MD5: "348a30b1350c9d91a4dbf05f5e46e0bb", + SHA256: "a70c028fd3b9af1d7ea3d7099d810f3d2588096237bb472db331a51a36f931c0", + Size: 86757, + Timestamp: 1648307332172, + Filename: "zlib-1.2.11-h90dfc92_1014.tar.bz2", + URL: "https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.2.11-h90dfc92_1014.tar.bz2", + ExtractedPackageDir: "/Users/simeon/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", + Depends: []string{ + "libzlib 1.2.11 h90dfc92_1014", + }, + Files: []string{ + "include/zconf.h", + "include/zlib.h", + "lib/pkgconfig/zlib.pc", + "lib/libz.a", + "lib/libz.dylib", + }, + PathsData: &pkg.CondaPathsData{ + PathsVersion: 1, + Paths: []pkg.CondaPathData{ + { + Path: "include/zconf.h", + PathType: "hardlink", + SHA256: "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541", + SHA256InPrefix: "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541", + SizeInBytes: 16262, + }, + { + Path: "include/zlib.h", + PathType: "hardlink", + SHA256: "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8", + SHA256InPrefix: "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8", + SizeInBytes: 96239, + }, + { + Path: "lib/pkgconfig/zlib.pc", + PathType: "hardlink", + SHA256: "357773df3c44a5ebd77fdadd0869b5b06394bbf556c2d6c9736dd53e9df3b2c2", + SHA256InPrefix: "5b4eb6062f97875eaadb3b6c7cf8cfeff3808798ecbf2bfc095f18dcecc509bf", + SizeInBytes: 285, + }, + { + Path: "lib/libz.a", + PathType: "hardlink", + SHA256: "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0", + SHA256InPrefix: "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0", + SizeInBytes: 107128, + }, + { + Path: "lib/libz.dylib", + PathType: "softlink", + SHA256: "67ed489e2f378880f72fb1c0d1cc916e184b9632eccf2b5c1b34ddf01ed1701c", + SHA256InPrefix: "09e47dbc60aa970e153913fe84551ad7f5aa51c21907591340a0cc999adab859", + SizeInBytes: 122478, + }, + }, + }, + Link: &pkg.CondaLink{ + Source: "/Users/simeon/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", + Type: 1, + }, + }, + }, }, }, + { + name: "badly formatted conda meta json file", + fixture: "test-fixtures/conda-meta-bad-json", + expectedPackages: nil, + }, + { + name: "nonexistent conda meta folder", + fixture: "test-fixtures/conda-meta-nonexistent", + expectedPackages: nil, + }, } - fmt.Println("Hello from the test!") for _, test := range tests { t.Run(test.name, func(t *testing.T) { (pkgtest.NewCatalogTester(). diff --git a/syft/pkg/cataloger/conda/test-fixtures/conda-meta-bad-json/conda-meta/package-1.2.3-pyhd8ed1ab_0.json b/syft/pkg/cataloger/conda/test-fixtures/conda-meta-bad-json/conda-meta/package-1.2.3-pyhd8ed1ab_0.json new file mode 100644 index 00000000000..d3ec19eb9a7 --- /dev/null +++ b/syft/pkg/cataloger/conda/test-fixtures/conda-meta-bad-json/conda-meta/package-1.2.3-pyhd8ed1ab_0.json @@ -0,0 +1 @@ +not json, will cause a parsing error diff --git a/syft/pkg/cataloger/conda/test-fixtures/conda-meta-jupyterlab/conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json b/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json similarity index 100% rename from syft/pkg/cataloger/conda/test-fixtures/conda-meta-jupyterlab/conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json rename to syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json diff --git a/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/zlib-1.2.11-h90dfc92_1014.json b/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/zlib-1.2.11-h90dfc92_1014.json new file mode 100644 index 00000000000..f02d82191bf --- /dev/null +++ b/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/zlib-1.2.11-h90dfc92_1014.json @@ -0,0 +1,75 @@ +{ + "arch": "arm64", + "build": "h90dfc92_1014", + "build_number": 1014, + "depends": [ + "libzlib 1.2.11 h90dfc92_1014" + ], + "license": "Zlib", + "license_family": "Other", + "md5": "348a30b1350c9d91a4dbf05f5e46e0bb", + "name": "zlib", + "platform": "osx", + "sha256": "a70c028fd3b9af1d7ea3d7099d810f3d2588096237bb472db331a51a36f931c0", + "size": 86757, + "subdir": "osx-arm64", + "timestamp": 1648307332172, + "version": "1.2.11", + "fn": "zlib-1.2.11-h90dfc92_1014.tar.bz2", + "url": "https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.2.11-h90dfc92_1014.tar.bz2", + "channel": "https://conda.anaconda.org/conda-forge/", + "extracted_package_dir": "/Users/simeon/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", + "files": [ + "include/zconf.h", + "include/zlib.h", + "lib/pkgconfig/zlib.pc", + "lib/libz.a", + "lib/libz.dylib" + ], + "paths_data": { + "paths_version": 1, + "paths": [ + { + "_path": "include/zconf.h", + "path_type": "hardlink", + "sha256": "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541", + "sha256_in_prefix": "77304005ceb5f0d03ad4c37eb8386a10866e4ceeb204f7c3b6599834c7319541", + "size_in_bytes": 16262 + }, + { + "_path": "include/zlib.h", + "path_type": "hardlink", + "sha256": "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8", + "sha256_in_prefix": "4ddc82b4af931ab55f44d977bde81bfbc4151b5dcdccc03142831a301b5ec3c8", + "size_in_bytes": 96239 + }, + { + "_path": "lib/pkgconfig/zlib.pc", + "path_type": "hardlink", + "sha256": "357773df3c44a5ebd77fdadd0869b5b06394bbf556c2d6c9736dd53e9df3b2c2", + "sha256_in_prefix": "5b4eb6062f97875eaadb3b6c7cf8cfeff3808798ecbf2bfc095f18dcecc509bf", + "size_in_bytes": 285, + "file_mode": "text", + "prefix_placeholder": "/Users/runner/miniforge3/conda-bld/zlib-split_1648307175973/_h_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehol" + }, + { + "_path": "lib/libz.a", + "path_type": "hardlink", + "sha256": "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0", + "sha256_in_prefix": "40c056a5d8155d9b3f42adfe35f7fc6e5fa15cc6588ffad0f09fe67517feada0", + "size_in_bytes": 107128 + }, + { + "_path": "lib/libz.dylib", + "path_type": "softlink", + "sha256": "67ed489e2f378880f72fb1c0d1cc916e184b9632eccf2b5c1b34ddf01ed1701c", + "sha256_in_prefix": "09e47dbc60aa970e153913fe84551ad7f5aa51c21907591340a0cc999adab859", + "size_in_bytes": 122478 + } + ] + }, + "link": { + "source": "/Users/simeon/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", + "type": 1 + } +} From 46dc0dd9b73d976943426b019d4d302c7b9d2140 Mon Sep 17 00:00:00 2001 From: Simeon Stoykov Date: Thu, 12 Jun 2025 18:32:41 +0200 Subject: [PATCH 04/11] update comments Signed-off-by: Simeon Stoykov --- syft/pkg/cataloger/conda/cataloger.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/syft/pkg/cataloger/conda/cataloger.go b/syft/pkg/cataloger/conda/cataloger.go index 81b3fba920f..af32b1d0d48 100644 --- a/syft/pkg/cataloger/conda/cataloger.go +++ b/syft/pkg/cataloger/conda/cataloger.go @@ -14,14 +14,13 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/generic" ) -// NewCondaMetaCataloger returns a new cataloger object for Conda environments. +// NewCondaMetaCataloger returns a new cataloger object for Conda environments by parsing the package metadata files in conda-meta. func NewCondaMetaCataloger() pkg.Cataloger { return generic.NewCataloger("conda-meta-cataloger"). - WithParserByGlobs(parseCondaMeta, "**/conda-meta/*.json") + WithParserByGlobs(parseCondaMetaJSON, "**/conda-meta/*.json") } -// parseCondaMeta is a stub parser for conda-meta JSON files. -func parseCondaMeta(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { +func parseCondaMetaJSON(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { dec := json.NewDecoder(reader) var meta pkg.CondaMetaPackage if err := dec.Decode(&meta); err != nil { From 7e5a23e8c525bd5d54e5e3fbc2259cb1274af2d0 Mon Sep 17 00:00:00 2001 From: Simeon Stoykov Date: Thu, 12 Jun 2025 18:37:33 +0200 Subject: [PATCH 05/11] example-user Signed-off-by: Simeon Stoykov --- syft/pkg/cataloger/conda/cataloger_test.go | 8 ++++---- .../conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json | 6 +++--- .../conda-meta/zlib-1.2.11-h90dfc92_1014.json | 4 ++-- syft/pkg/type.go | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/syft/pkg/cataloger/conda/cataloger_test.go b/syft/pkg/cataloger/conda/cataloger_test.go index 6d1d3c6dd09..abedd25ddf4 100644 --- a/syft/pkg/cataloger/conda/cataloger_test.go +++ b/syft/pkg/cataloger/conda/cataloger_test.go @@ -49,7 +49,7 @@ func Test_CondaCataloger(t *testing.T) { Timestamp: 1748273017680, Filename: "jupyterlab-4.4.3-pyhd8ed1ab_0.conda", URL: "https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.3-pyhd8ed1ab_0.conda", - ExtractedPackageDir: "/Users/simeon/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", + ExtractedPackageDir: "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", Depends: []string{ "async-lru >=1.0.0", "httpx >=0.25.0", @@ -92,7 +92,7 @@ func Test_CondaCataloger(t *testing.T) { }, }, Link: &pkg.CondaLink{ - Source: "/Users/simeon/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", + Source: "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", Type: 1, }, }, @@ -125,7 +125,7 @@ func Test_CondaCataloger(t *testing.T) { Timestamp: 1648307332172, Filename: "zlib-1.2.11-h90dfc92_1014.tar.bz2", URL: "https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.2.11-h90dfc92_1014.tar.bz2", - ExtractedPackageDir: "/Users/simeon/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", + ExtractedPackageDir: "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", Depends: []string{ "libzlib 1.2.11 h90dfc92_1014", }, @@ -177,7 +177,7 @@ func Test_CondaCataloger(t *testing.T) { }, }, Link: &pkg.CondaLink{ - Source: "/Users/simeon/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", + Source: "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", Type: 1, }, }, diff --git a/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json b/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json index 8f049e55dda..2c6447d716c 100644 --- a/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json +++ b/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/jupyterlab-4.4.3-pyhd8ed1ab_0.json @@ -32,7 +32,7 @@ "fn": "jupyterlab-4.4.3-pyhd8ed1ab_0.conda", "url": "https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.4.3-pyhd8ed1ab_0.conda", "channel": "https://conda.anaconda.org/conda-forge/", - "extracted_package_dir": "/Users/simeon/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", + "extracted_package_dir": "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", "files": [ "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/kernels-settings.json", "lib/python3.13/site-packages/jupyterlab/schemas/@jupyterlab/apputils-extension/notification.json" @@ -57,7 +57,7 @@ ] }, "link": { - "source": "/Users/simeon/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", + "source": "/Users/example-user/Library/Caches/rattler/cache/pkgs/jupyterlab-4.4.3-pyhd8ed1ab_0", "type": 1 } -} \ No newline at end of file +} diff --git a/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/zlib-1.2.11-h90dfc92_1014.json b/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/zlib-1.2.11-h90dfc92_1014.json index f02d82191bf..bd85a3d7468 100644 --- a/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/zlib-1.2.11-h90dfc92_1014.json +++ b/syft/pkg/cataloger/conda/test-fixtures/conda-meta-python-c-etc/conda-meta/zlib-1.2.11-h90dfc92_1014.json @@ -18,7 +18,7 @@ "fn": "zlib-1.2.11-h90dfc92_1014.tar.bz2", "url": "https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.2.11-h90dfc92_1014.tar.bz2", "channel": "https://conda.anaconda.org/conda-forge/", - "extracted_package_dir": "/Users/simeon/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", + "extracted_package_dir": "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", "files": [ "include/zconf.h", "include/zlib.h", @@ -69,7 +69,7 @@ ] }, "link": { - "source": "/Users/simeon/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", + "source": "/Users/example-user/Library/Caches/rattler/cache/pkgs/zlib-1.2.11-h90dfc92_1014", "type": 1 } } diff --git a/syft/pkg/type.go b/syft/pkg/type.go index edf586f3620..7dea22586e0 100644 --- a/syft/pkg/type.go +++ b/syft/pkg/type.go @@ -112,7 +112,7 @@ func (t Type) PackageURLType() string { case ConanPkg: return packageurl.TypeConan case CondaPkg: - return packageurl.TypeGeneric // TODO CONDAPKG: conda does not have a specific purl type, so we use generic + return packageurl.TypeGeneric case DartPubPkg: return packageurl.TypePub case DebPkg: From 8ae3d4b6f4aad232d0cd5d3aa2b868a7ff4ea2ce Mon Sep 17 00:00:00 2001 From: Simeon Stoykov Date: Thu, 12 Jun 2025 21:15:57 +0200 Subject: [PATCH 06/11] packagetag Signed-off-by: Simeon Stoykov --- internal/task/package_tasks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/task/package_tasks.go b/internal/task/package_tasks.go index 7f13ab2f75a..6ba93272450 100644 --- a/internal/task/package_tasks.go +++ b/internal/task/package_tasks.go @@ -172,7 +172,7 @@ func DefaultPackageTaskFactories() Factories { newSimplePackageTaskFactory(wordpress.NewWordpressPluginCataloger, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "wordpress"), newSimplePackageTaskFactory(terraform.NewLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "terraform"), newSimplePackageTaskFactory(homebrew.NewCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "homebrew"), - newSimplePackageTaskFactory(conda.NewCondaMetaCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "conda"), + newSimplePackageTaskFactory(conda.NewCondaMetaCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.PackageTag, "conda"), // deprecated catalogers //////////////////////////////////////// // these are catalogers that should not be selectable other than specific inclusion via name or "deprecated" tag (to remain backwards compatible) From 3f44451534d3fbb1bebfb7c725bcc4b38e35b209 Mon Sep 17 00:00:00 2001 From: Simeon Stoykov Date: Thu, 31 Jul 2025 19:35:12 +0200 Subject: [PATCH 07/11] add arch field Signed-off-by: Simeon Stoykov --- syft/pkg/cataloger/conda/cataloger_test.go | 1 + syft/pkg/conda.go | 1 + 2 files changed, 2 insertions(+) diff --git a/syft/pkg/cataloger/conda/cataloger_test.go b/syft/pkg/cataloger/conda/cataloger_test.go index abedd25ddf4..3f440e857d4 100644 --- a/syft/pkg/cataloger/conda/cataloger_test.go +++ b/syft/pkg/cataloger/conda/cataloger_test.go @@ -110,6 +110,7 @@ func Test_CondaCataloger(t *testing.T) { pkg.NewLicenseFromLocationsWithContext(ctx, "Zlib", file.NewLocation("conda-meta/zlib-1.2.11-h90dfc92_1014.json")), ), Metadata: pkg.CondaMetaPackage{ + Arch: "arm64", Name: "zlib", Version: "1.2.11", Build: "h90dfc92_1014", diff --git a/syft/pkg/conda.go b/syft/pkg/conda.go index 39d2ca1e451..85dd8ba1d4b 100644 --- a/syft/pkg/conda.go +++ b/syft/pkg/conda.go @@ -19,6 +19,7 @@ type CondaLink struct { } type CondaMetaPackage struct { + Arch string `json:"arch,omitempty"` Name string `json:"name"` Version string `json:"version"` Build string `json:"build"` From 36b52546e8b989ad0fabf484e02af0806e7d44dd Mon Sep 17 00:00:00 2001 From: Simeon Stoykov Date: Thu, 31 Jul 2025 19:46:44 +0200 Subject: [PATCH 08/11] add file ownership Signed-off-by: Simeon Stoykov --- syft/pkg/cataloger/conda/cataloger_test.go | 37 ++++++++++++++++++++++ syft/pkg/conda.go | 18 +++++++++++ 2 files changed, 55 insertions(+) diff --git a/syft/pkg/cataloger/conda/cataloger_test.go b/syft/pkg/cataloger/conda/cataloger_test.go index 3f440e857d4..35e2f1525cd 100644 --- a/syft/pkg/cataloger/conda/cataloger_test.go +++ b/syft/pkg/cataloger/conda/cataloger_test.go @@ -2,11 +2,13 @@ package conda import ( "context" + "strings" "testing" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" + "github.com/go-test/deep" ) func Test_CondaCataloger(t *testing.T) { @@ -206,3 +208,38 @@ func Test_CondaCataloger(t *testing.T) { }) } } + +func TestCondaMetaPackageMetadata_FileOwner(t *testing.T) { + tests := []struct { + metadata pkg.CondaMetaPackage + expected []string + }{ + { + metadata: pkg.CondaMetaPackage{ + Files: []string{ + "include/zconf.h", + "include/zlib.h", + "lib/pkgconfig/zlib.pc", + "lib/libz.a", + "lib/libz.dylib", + }, + }, + expected: []string{ + "include/zconf.h", + "include/zlib.h", + "lib/libz.a", + "lib/libz.dylib", + "lib/pkgconfig/zlib.pc", + }, + }, + } + + for _, test := range tests { + t.Run(strings.Join(test.expected, ","), func(t *testing.T) { + actual := test.metadata.OwnedFiles() + for _, d := range deep.Equal(test.expected, actual) { + t.Errorf("diff: %+v", d) + } + }) + } +} diff --git a/syft/pkg/conda.go b/syft/pkg/conda.go index 85dd8ba1d4b..9d9d3ad43eb 100644 --- a/syft/pkg/conda.go +++ b/syft/pkg/conda.go @@ -1,5 +1,11 @@ package pkg +import ( + "sort" + + "github.com/scylladb/go-set/strset" +) + type CondaPathData struct { Path string `json:"_path"` PathType string `json:"path_type"` @@ -41,3 +47,15 @@ type CondaMetaPackage struct { PathsData *CondaPathsData `json:"paths_data,omitempty"` Link *CondaLink `json:"link,omitempty"` } + +func (m CondaMetaPackage) OwnedFiles() (result []string) { + s := strset.New() + for _, f := range m.Files { + if f != "" { + s.Add(f) + } + } + result = s.List() + sort.Strings(result) + return result +} From 1c9fbe6985e0b47fc6f3eb05df47ab004a784a9a Mon Sep 17 00:00:00 2001 From: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Date: Tue, 19 Aug 2025 21:32:48 -0400 Subject: [PATCH 09/11] pr: update static analysis; generate new schema with new type Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> --- internal/constants.go | 2 +- schema/json/schema-16.0.38.json | 3316 ++++++++++++++++++++ schema/json/schema-latest.json | 145 +- syft/internal/packagemetadata/generated.go | 1 + syft/internal/packagemetadata/names.go | 1 + 5 files changed, 3463 insertions(+), 2 deletions(-) create mode 100644 schema/json/schema-16.0.38.json diff --git a/internal/constants.go b/internal/constants.go index a80f0e6b299..c0065b539d5 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -3,5 +3,5 @@ package internal const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "16.0.36" + JSONSchemaVersion = "16.0.38" ) diff --git a/schema/json/schema-16.0.38.json b/schema/json/schema-16.0.38.json new file mode 100644 index 00000000000..5e326135aaa --- /dev/null +++ b/schema/json/schema-16.0.38.json @@ -0,0 +1,3316 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.38/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmDbEntry": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "ApkDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "BinarySignature": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "BitnamiSbomEntry": { + "properties": { + "name": { + "type": "string" + }, + "arch": { + "type": "string" + }, + "distro": { + "type": "string" + }, + "revision": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "arch", + "distro", + "revision", + "version", + "path", + "files" + ] + }, + "CConanFileEntry": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanInfoEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockEntry": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "$ref": "#/$defs/KeyValues" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CConanLockV2Entry": { + "properties": { + "ref": { + "type": "string" + }, + "packageID": { + "type": "string" + }, + "username": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "recipeRevision": { + "type": "string" + }, + "packageRevision": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "CPE": { + "properties": { + "cpe": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "cpe" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoaPodfileLockEntry": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "CondaLink": { + "properties": { + "source": { + "type": "string" + }, + "type": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "source", + "type" + ] + }, + "CondaMetadataEntry": { + "properties": { + "arch": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "build": { + "type": "string" + }, + "build_number": { + "type": "integer" + }, + "channel": { + "type": "string" + }, + "subdir": { + "type": "string" + }, + "noarch": { + "type": "string" + }, + "license": { + "type": "string" + }, + "license_family": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "sha256": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "timestamp": { + "type": "integer" + }, + "fn": { + "type": "string" + }, + "url": { + "type": "string" + }, + "extracted_package_dir": { + "type": "string" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "paths_data": { + "$ref": "#/$defs/CondaPathsData" + }, + "link": { + "$ref": "#/$defs/CondaLink" + } + }, + "type": "object", + "required": [ + "name", + "version", + "build", + "build_number" + ] + }, + "CondaPathData": { + "properties": { + "_path": { + "type": "string" + }, + "path_type": { + "type": "string" + }, + "sha256": { + "type": "string" + }, + "sha256_in_prefix": { + "type": "string" + }, + "size_in_bytes": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "_path", + "path_type", + "sha256", + "sha256_in_prefix", + "size_in_bytes" + ] + }, + "CondaPathsData": { + "properties": { + "paths_version": { + "type": "integer" + }, + "paths": { + "items": { + "$ref": "#/$defs/CondaPathData" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "paths_version", + "paths" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubspec": { + "properties": { + "homepage": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "documentation": { + "type": "string" + }, + "publish_to": { + "type": "string" + }, + "environment": { + "$ref": "#/$defs/DartPubspecEnvironment" + }, + "platforms": { + "items": { + "type": "string" + }, + "type": "array" + }, + "ignored_advisories": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "DartPubspecEnvironment": { + "properties": { + "sdk": { + "type": "string" + }, + "flutter": { + "type": "string" + } + }, + "type": "object" + }, + "DartPubspecLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + }, + "executables": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPackagesLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "contentHash": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "contentHash", + "type" + ] + }, + "DotnetPortableExecutableEntry": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgArchiveEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgDbEntry": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "ELFSecurityFeatures": { + "properties": { + "symbolTableStripped": { + "type": "boolean" + }, + "stackCanary": { + "type": "boolean" + }, + "nx": { + "type": "boolean" + }, + "relRO": { + "type": "string" + }, + "pie": { + "type": "boolean" + }, + "dso": { + "type": "boolean" + }, + "safeStack": { + "type": "boolean" + }, + "cfi": { + "type": "boolean" + }, + "fortify": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "symbolTableStripped", + "nx", + "relRO", + "pie", + "dso" + ] + }, + "ElfBinaryPackageNoteJsonPayload": { + "properties": { + "type": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "osCPE": { + "type": "string" + }, + "os": { + "type": "string" + }, + "osVersion": { + "type": "string" + }, + "system": { + "type": "string" + }, + "vendor": { + "type": "string" + }, + "sourceRepo": { + "type": "string" + }, + "commit": { + "type": "string" + } + }, + "type": "object" + }, + "ElixirMixLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "ErlangRebarLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Executable": { + "properties": { + "format": { + "type": "string" + }, + "hasExports": { + "type": "boolean" + }, + "hasEntrypoint": { + "type": "boolean" + }, + "importedLibraries": { + "items": { + "type": "string" + }, + "type": "array" + }, + "elfSecurityFeatures": { + "$ref": "#/$defs/ELFSecurityFeatures" + } + }, + "type": "object", + "required": [ + "format", + "hasExports", + "hasEntrypoint", + "importedLibraries" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + }, + "executable": { + "$ref": "#/$defs/Executable" + }, + "unknowns": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GithubActionsUseStatement": { + "properties": { + "value": { + "type": "string" + }, + "comment": { + "type": "string" + } + }, + "type": "object", + "required": [ + "value" + ] + }, + "GoModuleBuildinfoEntry": { + "properties": { + "goBuildSettings": { + "$ref": "#/$defs/KeyValues" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + }, + "goExperiments": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GoModuleEntry": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackEntry": { + "properties": { + "pkgHash": { + "type": "string" + } + }, + "type": "object" + }, + "HaskellHackageStackLockEntry": { + "properties": { + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object" + }, + "HomebrewFormula": { + "properties": { + "tap": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaArchive": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/JavaPomProperties" + }, + "pomProject": { + "$ref": "#/$defs/JavaPomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "JavaJvmInstallation": { + "properties": { + "release": { + "$ref": "#/$defs/JavaVMRelease" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "release", + "files" + ] + }, + "JavaManifest": { + "properties": { + "main": { + "$ref": "#/$defs/KeyValues" + }, + "sections": { + "items": { + "$ref": "#/$defs/KeyValues" + }, + "type": "array" + } + }, + "type": "object" + }, + "JavaPomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "JavaPomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/JavaPomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "JavaPomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "JavaVMRelease": { + "properties": { + "implementor": { + "type": "string" + }, + "implementorVersion": { + "type": "string" + }, + "javaRuntimeVersion": { + "type": "string" + }, + "javaVersion": { + "type": "string" + }, + "javaVersionDate": { + "type": "string" + }, + "libc": { + "type": "string" + }, + "modules": { + "items": { + "type": "string" + }, + "type": "array" + }, + "osArch": { + "type": "string" + }, + "osName": { + "type": "string" + }, + "osVersion": { + "type": "string" + }, + "source": { + "type": "string" + }, + "buildSource": { + "type": "string" + }, + "buildSourceRepo": { + "type": "string" + }, + "sourceRepo": { + "type": "string" + }, + "fullVersion": { + "type": "string" + }, + "semanticVersion": { + "type": "string" + }, + "buildInfo": { + "type": "string" + }, + "jvmVariant": { + "type": "string" + }, + "jvmVersion": { + "type": "string" + }, + "imageType": { + "type": "string" + }, + "buildType": { + "type": "string" + } + }, + "type": "object" + }, + "JavascriptNpmPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "JavascriptNpmPackageLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "JavascriptYarnLockEntry": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "KeyValue": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "key", + "value" + ] + }, + "KeyValues": { + "items": { + "$ref": "#/$defs/KeyValue" + }, + "type": "array" + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "contents": { + "type": "string" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelArchive": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModule": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + }, + "extendedSupport": { + "type": "boolean" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "accessPath": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "accessPath" + ] + }, + "LuarocksPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "dependencies": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "name", + "version", + "license", + "homepage", + "description", + "url", + "dependencies" + ] + }, + "MicrosoftKbPatch": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "NixDerivation": { + "properties": { + "path": { + "type": "string" + }, + "system": { + "type": "string" + }, + "inputDerivations": { + "items": { + "$ref": "#/$defs/NixDerivationReference" + }, + "type": "array" + }, + "inputSources": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "NixDerivationReference": { + "properties": { + "path": { + "type": "string" + }, + "outputs": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "NixStoreEntry": { + "properties": { + "path": { + "type": "string" + }, + "output": { + "type": "string" + }, + "outputHash": { + "type": "string" + }, + "derivation": { + "$ref": "#/$defs/NixDerivation" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash" + ] + }, + "OpamPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "url": { + "type": "string" + }, + "checksum": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "licenses", + "url", + "checksum", + "homepage", + "dependencies" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "$ref": "#/$defs/cpes" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmDbEntry" + }, + { + "$ref": "#/$defs/ApkDbEntry" + }, + { + "$ref": "#/$defs/BinarySignature" + }, + { + "$ref": "#/$defs/BitnamiSbomEntry" + }, + { + "$ref": "#/$defs/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CConanLockV2Entry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/CondaMetadataEntry" + }, + { + "$ref": "#/$defs/DartPubspec" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPackagesLockEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgArchiveEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElfBinaryPackageNoteJsonPayload" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GithubActionsUseStatement" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$ref": "#/$defs/HomebrewFormula" + }, + { + "$ref": "#/$defs/JavaArchive" + }, + { + "$ref": "#/$defs/JavaJvmInstallation" + }, + { + "$ref": "#/$defs/JavascriptNpmPackage" + }, + { + "$ref": "#/$defs/JavascriptNpmPackageLockEntry" + }, + { + "$ref": "#/$defs/JavascriptYarnLockEntry" + }, + { + "$ref": "#/$defs/LinuxKernelArchive" + }, + { + "$ref": "#/$defs/LinuxKernelModule" + }, + { + "$ref": "#/$defs/LuarocksPackage" + }, + { + "$ref": "#/$defs/MicrosoftKbPatch" + }, + { + "$ref": "#/$defs/NixStoreEntry" + }, + { + "$ref": "#/$defs/OpamPackage" + }, + { + "$ref": "#/$defs/PeBinary" + }, + { + "$ref": "#/$defs/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PhpPearEntry" + }, + { + "$ref": "#/$defs/PhpPeclEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/PythonPoetryLockEntry" + }, + { + "$ref": "#/$defs/PythonUvLockEntry" + }, + { + "$ref": "#/$defs/RDescription" + }, + { + "$ref": "#/$defs/RpmArchive" + }, + { + "$ref": "#/$defs/RpmDbEntry" + }, + { + "$ref": "#/$defs/RubyGemspec" + }, + { + "$ref": "#/$defs/RustCargoAuditEntry" + }, + { + "$ref": "#/$defs/RustCargoLockEntry" + }, + { + "$ref": "#/$defs/SwiftPackageManagerLockEntry" + }, + { + "$ref": "#/$defs/SwiplpackPackage" + }, + { + "$ref": "#/$defs/TerraformLockProviderEntry" + }, + { + "$ref": "#/$defs/WordpressPluginEntry" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PeBinary": { + "properties": { + "VersionResources": { + "$ref": "#/$defs/KeyValues" + } + }, + "type": "object", + "required": [ + "VersionResources" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerInstalledEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpComposerLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PhpPearEntry": { + "properties": { + "name": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "PhpPeclEntry": { + "properties": { + "name": { + "type": "string" + }, + "channel": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "licenses": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + }, + "requiresPython": { + "type": "string" + }, + "requiresDist": { + "items": { + "type": "string" + }, + "type": "array" + }, + "providesExtra": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipRequirementsEntry": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "PythonPipfileLockEntry": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonPoetryLockDependencyEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "optional": { + "type": "boolean" + }, + "markers": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "optional" + ] + }, + "PythonPoetryLockEntry": { + "properties": { + "index": { + "type": "string" + }, + "dependencies": { + "items": { + "$ref": "#/$defs/PythonPoetryLockDependencyEntry" + }, + "type": "array" + }, + "extras": { + "items": { + "$ref": "#/$defs/PythonPoetryLockExtraEntry" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "index", + "dependencies" + ] + }, + "PythonPoetryLockExtraEntry": { + "properties": { + "name": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "dependencies" + ] + }, + "PythonUvLockDependencyEntry": { + "properties": { + "name": { + "type": "string" + }, + "optional": { + "type": "boolean" + }, + "markers": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "optional" + ] + }, + "PythonUvLockEntry": { + "properties": { + "index": { + "type": "string" + }, + "dependencies": { + "items": { + "$ref": "#/$defs/PythonUvLockDependencyEntry" + }, + "type": "array" + }, + "extras": { + "items": { + "$ref": "#/$defs/PythonUvLockExtraEntry" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "index", + "dependencies" + ] + }, + "PythonUvLockExtraEntry": { + "properties": { + "name": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "dependencies" + ] + }, + "RDescription": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmArchive": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "signatures": { + "items": { + "$ref": "#/$defs/RpmSignature" + }, + "type": "array" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmDbEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "signatures": { + "items": { + "$ref": "#/$defs/RpmSignature" + }, + "type": "array" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "files" + ] + }, + "RpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "RpmSignature": { + "properties": { + "algo": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "created": { + "type": "string" + }, + "issuer": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algo", + "hash", + "created", + "issuer" + ] + }, + "RubyGemspec": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "RustCargoAuditEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source" + ] + }, + "RustCargoLockEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerLockEntry": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "SwiplpackPackage": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "packager": { + "type": "string" + }, + "packagerEmail": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "packager", + "packagerEmail", + "homepage", + "dependencies" + ] + }, + "TerraformLockProviderEntry": { + "properties": { + "url": { + "type": "string" + }, + "constraints": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "url", + "constraints", + "version", + "hashes" + ] + }, + "WordpressPluginEntry": { + "properties": { + "pluginInstallDirectory": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorUri": { + "type": "string" + } + }, + "type": "object", + "required": [ + "pluginInstallDirectory" + ] + }, + "cpes": { + "items": { + "$ref": "#/$defs/CPE" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/schema/json/schema-latest.json b/schema/json/schema-latest.json index 1e0a395cf9f..5e326135aaa 100644 --- a/schema/json/schema-latest.json +++ b/schema/json/schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "anchore.io/schema/syft/json/16.0.36/document", + "$id": "anchore.io/schema/syft/json/16.0.38/document", "$ref": "#/$defs/Document", "$defs": { "AlpmDbEntry": { @@ -394,6 +394,146 @@ "checksum" ] }, + "CondaLink": { + "properties": { + "source": { + "type": "string" + }, + "type": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "source", + "type" + ] + }, + "CondaMetadataEntry": { + "properties": { + "arch": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "build": { + "type": "string" + }, + "build_number": { + "type": "integer" + }, + "channel": { + "type": "string" + }, + "subdir": { + "type": "string" + }, + "noarch": { + "type": "string" + }, + "license": { + "type": "string" + }, + "license_family": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "sha256": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "timestamp": { + "type": "integer" + }, + "fn": { + "type": "string" + }, + "url": { + "type": "string" + }, + "extracted_package_dir": { + "type": "string" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "paths_data": { + "$ref": "#/$defs/CondaPathsData" + }, + "link": { + "$ref": "#/$defs/CondaLink" + } + }, + "type": "object", + "required": [ + "name", + "version", + "build", + "build_number" + ] + }, + "CondaPathData": { + "properties": { + "_path": { + "type": "string" + }, + "path_type": { + "type": "string" + }, + "sha256": { + "type": "string" + }, + "sha256_in_prefix": { + "type": "string" + }, + "size_in_bytes": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "_path", + "path_type", + "sha256", + "sha256_in_prefix", + "size_in_bytes" + ] + }, + "CondaPathsData": { + "properties": { + "paths_version": { + "type": "integer" + }, + "paths": { + "items": { + "$ref": "#/$defs/CondaPathData" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "paths_version", + "paths" + ] + }, "Coordinates": { "properties": { "path": { @@ -1893,6 +2033,9 @@ { "$ref": "#/$defs/CocoaPodfileLockEntry" }, + { + "$ref": "#/$defs/CondaMetadataEntry" + }, { "$ref": "#/$defs/DartPubspec" }, diff --git a/syft/internal/packagemetadata/generated.go b/syft/internal/packagemetadata/generated.go index 3d209199351..a8dc44aa5cf 100644 --- a/syft/internal/packagemetadata/generated.go +++ b/syft/internal/packagemetadata/generated.go @@ -16,6 +16,7 @@ func AllTypes() []any { pkg.ConanV2LockEntry{}, pkg.ConanfileEntry{}, pkg.ConaninfoEntry{}, + pkg.CondaMetaPackage{}, pkg.DartPubspec{}, pkg.DartPubspecLockEntry{}, pkg.DotnetDepsEntry{}, diff --git a/syft/internal/packagemetadata/names.go b/syft/internal/packagemetadata/names.go index c4ea482d6e4..b7cea55e8b6 100644 --- a/syft/internal/packagemetadata/names.go +++ b/syft/internal/packagemetadata/names.go @@ -119,6 +119,7 @@ var jsonTypes = makeJSONTypes( jsonNames(pkg.LuaRocksPackage{}, "luarocks-package"), jsonNames(pkg.TerraformLockProviderEntry{}, "terraform-lock-provider-entry"), jsonNames(pkg.DotnetPackagesLockEntry{}, "dotnet-packages-lock-entry"), + jsonNames(pkg.CondaMetaPackage{}, "conda-metadata-entry", "CondaPackageMetadata"), ) func expandLegacyNameVariants(names ...string) []string { From 6f7af12f7614f7c4f025fb51fb80e0c3543f4c5d Mon Sep 17 00:00:00 2001 From: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Date: Tue, 19 Aug 2025 22:05:37 -0400 Subject: [PATCH 10/11] pr: fix static-analysis; update unit tests Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> --- schema/json/schema-16.0.38.json | 3 +++ schema/json/schema-latest.json | 5 ++++- .../helpers/originator_supplier_test.go | 1 + .../internal/spdxutil/helpers/source_info.go | 2 ++ .../spdxutil/helpers/source_info_test.go | 8 +++++++ syft/pkg/cataloger/conda/cataloger.go | 2 +- syft/pkg/cataloger/conda/cataloger_test.go | 21 ++++++++++++------- syft/pkg/type_test.go | 5 +++++ 8 files changed, 37 insertions(+), 10 deletions(-) diff --git a/schema/json/schema-16.0.38.json b/schema/json/schema-16.0.38.json index 5e326135aaa..ef868a71250 100644 --- a/schema/json/schema-16.0.38.json +++ b/schema/json/schema-16.0.38.json @@ -3190,6 +3190,9 @@ "version": { "type": "string" }, + "supplier": { + "type": "string" + }, "type": { "type": "string" }, diff --git a/schema/json/schema-latest.json b/schema/json/schema-latest.json index 56405ed324f..ef868a71250 100644 --- a/schema/json/schema-latest.json +++ b/schema/json/schema-latest.json @@ -3190,6 +3190,9 @@ "version": { "type": "string" }, + "supplier": { + "type": "string" + }, "type": { "type": "string" }, @@ -3313,4 +3316,4 @@ "type": "array" } } -} \ No newline at end of file +} diff --git a/syft/format/internal/spdxutil/helpers/originator_supplier_test.go b/syft/format/internal/spdxutil/helpers/originator_supplier_test.go index cfdbdb6e78c..03710c3ea75 100644 --- a/syft/format/internal/spdxutil/helpers/originator_supplier_test.go +++ b/syft/format/internal/spdxutil/helpers/originator_supplier_test.go @@ -18,6 +18,7 @@ func Test_OriginatorSupplier(t *testing.T) { pkg.ConanV2LockEntry{}, // the field Username might be the username of either the package originator or the supplier (unclear currently) pkg.ConanfileEntry{}, pkg.ConaninfoEntry{}, + pkg.CondaMetaPackage{}, pkg.DartPubspecLockEntry{}, pkg.DartPubspec{}, pkg.DotnetDepsEntry{}, diff --git a/syft/format/internal/spdxutil/helpers/source_info.go b/syft/format/internal/spdxutil/helpers/source_info.go index 5232acbc3bb..5d36a600df4 100644 --- a/syft/format/internal/spdxutil/helpers/source_info.go +++ b/syft/format/internal/spdxutil/helpers/source_info.go @@ -48,6 +48,8 @@ func SourceInfo(p pkg.Package) string { answer = "acquired package info from installed cocoapods manifest file" case pkg.ConanPkg: answer = "acquired package info from conan manifest" + case pkg.CondaPkg: + answer = "acquired package info from conda metadata" case pkg.PortagePkg: answer = "acquired package info from portage DB" case pkg.HackagePkg: diff --git a/syft/format/internal/spdxutil/helpers/source_info_test.go b/syft/format/internal/spdxutil/helpers/source_info_test.go index 726ebcb61cd..2502dfe8c9a 100644 --- a/syft/format/internal/spdxutil/helpers/source_info_test.go +++ b/syft/format/internal/spdxutil/helpers/source_info_test.go @@ -191,6 +191,14 @@ func Test_SourceInfo(t *testing.T) { "from conan manifest", }, }, + { + input: pkg.Package{ + Type: pkg.CondaPkg, + }, + expected: []string{ + "from conda metadata", + }, + }, { input: pkg.Package{ Type: pkg.PortagePkg, diff --git a/syft/pkg/cataloger/conda/cataloger.go b/syft/pkg/cataloger/conda/cataloger.go index af32b1d0d48..39667c8c070 100644 --- a/syft/pkg/cataloger/conda/cataloger.go +++ b/syft/pkg/cataloger/conda/cataloger.go @@ -24,7 +24,7 @@ func parseCondaMetaJSON(ctx context.Context, _ file.Resolver, _ *generic.Environ dec := json.NewDecoder(reader) var meta pkg.CondaMetaPackage if err := dec.Decode(&meta); err != nil { - return nil, nil, fmt.Errorf("failed to parse conda-meta package file at %s: %w", reader.Location, err) + return nil, nil, fmt.Errorf("failed to parse conda-meta package file at %s: %w", reader.RealPath, err) } p := pkg.Package{ diff --git a/syft/pkg/cataloger/conda/cataloger_test.go b/syft/pkg/cataloger/conda/cataloger_test.go index 35e2f1525cd..0c25db32653 100644 --- a/syft/pkg/cataloger/conda/cataloger_test.go +++ b/syft/pkg/cataloger/conda/cataloger_test.go @@ -5,10 +5,12 @@ import ( "strings" "testing" + "github.com/go-test/deep" + "github.com/stretchr/testify/require" + "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" - "github.com/go-test/deep" ) func Test_CondaCataloger(t *testing.T) { @@ -18,10 +20,12 @@ func Test_CondaCataloger(t *testing.T) { name string fixture string expectedPackages []pkg.Package + wantErr require.ErrorAssertionFunc }{ { name: "multiple packages in conda meta (python, c binaries, ...)", fixture: "test-fixtures/conda-meta-python-c-etc", + wantErr: require.NoError, expectedPackages: []pkg.Package{ { Name: "jupyterlab", @@ -191,20 +195,21 @@ func Test_CondaCataloger(t *testing.T) { name: "badly formatted conda meta json file", fixture: "test-fixtures/conda-meta-bad-json", expectedPackages: nil, - }, - { - name: "nonexistent conda meta folder", - fixture: "test-fixtures/conda-meta-nonexistent", - expectedPackages: nil, + wantErr: func(t require.TestingT, err error, msgAndArgs ...interface{}) { + require.Error(t, err) + require.Contains(t, err.Error(), "failed to parse conda-meta package file at conda-meta/package-1.2.3-pyhd8ed1ab_0.json") + require.Contains(t, err.Error(), "invalid character") + }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - (pkgtest.NewCatalogTester(). + pkgtest.NewCatalogTester(). FromDirectory(t, test.fixture). Expects(test.expectedPackages, nil). - TestCataloger(t, NewCondaMetaCataloger())) + WithErrorAssertion(test.wantErr). + TestCataloger(t, NewCondaMetaCataloger()) }) } } diff --git a/syft/pkg/type_test.go b/syft/pkg/type_test.go index abb1ba569e8..a0695b5bf04 100644 --- a/syft/pkg/type_test.go +++ b/syft/pkg/type_test.go @@ -130,6 +130,11 @@ func TestTypeFromPURL(t *testing.T) { purl: "pkg:opam/ocaml-base-compiler@5.2.0", expected: OpamPkg, }, + { + name: "conda", + purl: "pkg:generic/conda@1.2.3", + expected: CondaPkg, + }, } var pkgTypes = strset.New() From 9396a3fce38d84d33517acf199d4904e11c597b3 Mon Sep 17 00:00:00 2001 From: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Date: Tue, 19 Aug 2025 22:16:38 -0400 Subject: [PATCH 11/11] pr: remove conda from current catalog expections in integration Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> --- cmd/syft/internal/test/integration/catalog_packages_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/syft/internal/test/integration/catalog_packages_test.go b/cmd/syft/internal/test/integration/catalog_packages_test.go index a949090eeb8..9fc65fdb02a 100644 --- a/cmd/syft/internal/test/integration/catalog_packages_test.go +++ b/cmd/syft/internal/test/integration/catalog_packages_test.go @@ -87,6 +87,7 @@ func TestPkgCoverageImage(t *testing.T) { definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg)) definedPkgs.Remove(string(pkg.TerraformPkg)) definedPkgs.Remove(string(pkg.PhpPeclPkg)) // we have coverage for pear instead + definedPkgs.Remove(string(pkg.CondaPkg)) var cases []testCase cases = append(cases, commonTestCases...) @@ -159,6 +160,7 @@ func TestPkgCoverageDirectory(t *testing.T) { definedPkgs.Remove(string(pkg.LinuxKernelModulePkg)) definedPkgs.Remove(string(pkg.Rpkg)) definedPkgs.Remove(string(pkg.UnknownPkg)) + definedPkgs.Remove(string(pkg.CondaPkg)) definedPkgs.Remove(string(pkg.PhpPeclPkg)) // this is covered as pear packages // for directory scans we should not expect to see any of the following package types