diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 3f773335e..30c2d98f2 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -8,4 +8,8 @@ A cataloger contributed by Oracle Corporation that extracts packages given withi ## Swift Package Manager -A cataloger contributed by Axis Communications that catalogs packages resolved by Swift Package Manager. \ No newline at end of file +A cataloger contributed by Axis Communications that catalogs packages resolved by Swift Package Manager. + +## Bitnami Packages + +A cataloger contributed by Bitnami developer that catalogs packages described in Bitnami SBOMs. diff --git a/README.md b/README.md index f997c2174..789798045 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ Note that flags using the @ can be used for earlier versions of each sp ### Supported Ecosystems - Alpine (apk) +- Bitnami packages - C (conan) - C++ (conan) - Dart (pubs) diff --git a/cmd/syft/internal/test/integration/catalog_packages_test.go b/cmd/syft/internal/test/integration/catalog_packages_test.go index 6a1a9dbb3..e1be4ee5a 100644 --- a/cmd/syft/internal/test/integration/catalog_packages_test.go +++ b/cmd/syft/internal/test/integration/catalog_packages_test.go @@ -74,6 +74,8 @@ func TestPkgCoverageImage(t *testing.T) { definedPkgs.Remove(string(pkg.ConanPkg)) definedPkgs.Remove(string(pkg.HackagePkg)) definedPkgs.Remove(string(pkg.BinaryPkg)) + definedPkgs.Remove(string(pkg.BitnamiPkg)) + definedPkgs.Remove(string(pkg.GraalVMNativeImagePkg)) definedPkgs.Remove(string(pkg.HexPkg)) definedPkgs.Remove(string(pkg.LinuxKernelPkg)) definedPkgs.Remove(string(pkg.LinuxKernelModulePkg)) @@ -219,6 +221,8 @@ func TestPkgCoverageDirectory(t *testing.T) { definedLanguages.Remove(pkg.R.String()) observedPkgs.Remove(string(pkg.UnknownPkg)) definedPkgs.Remove(string(pkg.BinaryPkg)) + definedPkgs.Remove(string(pkg.BitnamiPkg)) + definedPkgs.Remove(string(pkg.GraalVMNativeImagePkg)) definedPkgs.Remove(string(pkg.LinuxKernelPkg)) definedPkgs.Remove(string(pkg.LinuxKernelModulePkg)) definedPkgs.Remove(string(pkg.Rpkg)) diff --git a/go.mod b/go.mod index e3bde93b0..10aacea6c 100644 --- a/go.mod +++ b/go.mod @@ -90,6 +90,7 @@ require ( github.com/OneOfOne/xxhash v1.2.8 github.com/adrg/xdg v0.5.3 github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 + github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef github.com/hashicorp/hcl/v2 v2.23.0 github.com/magiconair/properties v1.8.9 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 diff --git a/go.sum b/go.sum index cdebcdae9..cbddf0fb8 100644 --- a/go.sum +++ b/go.sum @@ -149,6 +149,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef h1:TSFnfbbu2oAOuWbeDDTtwXWE6z+PmpgbSsMBeV7l0ww= +github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo= github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38= github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= diff --git a/internal/constants.go b/internal/constants.go index 7fc5f1350..bfee284e8 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.21" + JSONSchemaVersion = "16.0.22" ) diff --git a/internal/relationship/binary/binary_dependencies.go b/internal/relationship/binary/binary_dependencies.go index 210f88114..552f767e7 100644 --- a/internal/relationship/binary/binary_dependencies.go +++ b/internal/relationship/binary/binary_dependencies.go @@ -58,11 +58,10 @@ func generateRelationships(resolver file.Resolver, accessor sbomsync.Accessor, i // PackagesToRemove returns a list of binary packages (resolved by the ELF cataloger) that should be removed from the SBOM // These packages are removed because they are already represented by a higher order packages in the SBOM. -func PackagesToRemove(resolver file.Resolver, accessor sbomsync.Accessor) []artifact.ID { +func PackagesToRemove(accessor sbomsync.Accessor) []artifact.ID { pkgsToDelete := make([]artifact.ID, 0) accessor.ReadFromSBOM(func(s *sbom.SBOM) { - // OTHER package type > ELF package type > Binary package type - pkgsToDelete = append(pkgsToDelete, getBinaryPackagesToDelete(resolver, s)...) + // ELF package type > Binary package type pkgsToDelete = append(pkgsToDelete, compareElfBinaryPackages(s)...) }) return pkgsToDelete @@ -114,33 +113,6 @@ func isElfPackage(p pkg.Package) bool { return ok } -func getBinaryPackagesToDelete(resolver file.Resolver, s *sbom.SBOM) []artifact.ID { - pkgsToDelete := make([]artifact.ID, 0) - for p := range s.Artifacts.Packages.Enumerate() { - if p.Type == pkg.BinaryPkg { - continue - } - fileOwner, ok := p.Metadata.(pkg.FileOwner) - if !ok { - continue - } - ownedFiles := fileOwner.OwnedFiles() - locations, err := resolver.FilesByPath(ownedFiles...) - if err != nil { - log.WithFields("error", err).Trace("unable to find path for owned file") - continue - } - for _, loc := range locations { - for _, pathPkg := range s.Artifacts.Packages.PackagesByPath(loc.RealPath) { - if pathPkg.Type == pkg.BinaryPkg { - pkgsToDelete = append(pkgsToDelete, pathPkg.ID()) - } - } - } - } - return pkgsToDelete -} - func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver file.Resolver, addRelationship func(artifact.Relationship), index *sharedLibraryIndex) { for _, libReference := range exec.ImportedLibraries { // for each library reference, check s.Artifacts.Packages.Sorted(pkg.BinaryPkg) for a binary package that represents that library diff --git a/internal/relationship/binary/binary_dependencies_test.go b/internal/relationship/binary/binary_dependencies_test.go index ea524fa1f..494a41170 100644 --- a/internal/relationship/binary/binary_dependencies_test.go +++ b/internal/relationship/binary/binary_dependencies_test.go @@ -21,7 +21,7 @@ func TestPackagesToRemove(t *testing.T) { Name: "glibc", Version: "2.28-236.el8_9.12", Locations: file.NewLocationSet( - file.NewLocation(glibcCoordinate.RealPath), + file.NewLocation("path/to/rpmdb"), ), Type: pkg.RpmPkg, Metadata: pkg.RpmDBEntry{ @@ -88,50 +88,45 @@ func TestPackagesToRemove(t *testing.T) { tests := []struct { name string - resolver file.Resolver accessor sbomsync.Accessor want []artifact.ID }{ { name: "remove packages that are overlapping rpm --> binary", - resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath), accessor: newAccessor([]pkg.Package{glibCPackage, glibCBinaryELFPackage}, map[file.Coordinates]file.Executable{}, nil), - want: []artifact.ID{glibCBinaryELFPackage.ID()}, + // this is surprising, right? the calling function reasons about if any generic binary package (regardless of it being an ELF package or not) + // should be deleted or kept based on the user configuration to do so. + want: []artifact.ID{}, }, { name: "keep packages that are overlapping rpm --> binary when the binary self identifies as an RPM", - resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath), accessor: newAccessor([]pkg.Package{glibCPackage, glibCBinaryELFPackageAsRPM}, map[file.Coordinates]file.Executable{}, nil), want: []artifact.ID{}, }, { name: "remove no packages when there is a single binary package (or self identifying RPM)", - resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath), accessor: newAccessor([]pkg.Package{glibCBinaryELFPackage, glibCBinaryELFPackageAsRPM}, map[file.Coordinates]file.Executable{}, nil), want: []artifact.ID{}, }, { name: "remove packages when there is a single binary package and a classifier package", - resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath), accessor: newAccessor([]pkg.Package{glibCBinaryELFPackage, glibCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil), want: []artifact.ID{glibCBinaryClassifierPackage.ID()}, }, { name: "ensure we're considering ELF packages, not just binary packages (supporting evidence)", - resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath), accessor: newAccessor([]pkg.Package{glibCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil), want: []artifact.ID{}, }, { name: "ensure we're considering ELF packages, not just binary packages (primary evidence)", - resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath), accessor: newAccessor([]pkg.Package{libCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil), want: []artifact.ID{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - pkgsToDelete := PackagesToRemove(tt.resolver, tt.accessor) + pkgsToDelete := PackagesToRemove(tt.accessor) if diff := cmp.Diff(tt.want, pkgsToDelete); diff != "" { t.Errorf("unexpected packages to delete (-want, +got): %s", diff) } diff --git a/internal/relationship/exclude_binaries_by_file_ownership_overlap.go b/internal/relationship/exclude_binaries_by_file_ownership_overlap.go index 648646657..646ffec22 100644 --- a/internal/relationship/exclude_binaries_by_file_ownership_overlap.go +++ b/internal/relationship/exclude_binaries_by_file_ownership_overlap.go @@ -22,6 +22,9 @@ var ( binaryCatalogerTypes = []pkg.Type{ pkg.BinaryPkg, } + bitnamiCatalogerTypes = []pkg.Type{ + pkg.BitnamiPkg, + } binaryMetadataTypes = []string{ reflect.TypeOf(pkg.ELFBinaryPackageNoteJSONPayload{}).Name(), reflect.TypeOf(pkg.BinarySignature{}).Name(), @@ -65,6 +68,10 @@ func excludeByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collection) a return idToRemove } + if idToRemove := identifyOverlappingBitnamiRelationship(parent, child); idToRemove != "" { + return idToRemove + } + return "" } @@ -124,3 +131,24 @@ func identifyOverlappingOSRelationship(parent *pkg.Package, child *pkg.Package) return child.ID() } + +// identifyOverlappingBitnamiRelationship indicates the package ID to remove if this is a Bitnami pkg -> bin pkg relationship. +func identifyOverlappingBitnamiRelationship(parent *pkg.Package, child *pkg.Package) artifact.ID { + if !slices.Contains(bitnamiCatalogerTypes, parent.Type) { + return "" + } + + if slices.Contains(binaryCatalogerTypes, child.Type) { + return child.ID() + } + + if child.Metadata == nil { + return "" + } + + if !slices.Contains(binaryMetadataTypes, reflect.TypeOf(child.Metadata).Name()) { + return "" + } + + return child.ID() +} diff --git a/internal/relationship/exclude_binaries_by_file_ownership_overlap_test.go b/internal/relationship/exclude_binaries_by_file_ownership_overlap_test.go index c0b9865ef..def556cf5 100644 --- a/internal/relationship/exclude_binaries_by_file_ownership_overlap_test.go +++ b/internal/relationship/exclude_binaries_by_file_ownership_overlap_test.go @@ -13,7 +13,8 @@ func TestExcludeByFileOwnershipOverlap(t *testing.T) { packageA := pkg.Package{Name: "package-a", Type: pkg.ApkPkg} packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg, Metadata: pkg.JavaVMInstallation{}} packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{Type: "rpm"}} - for _, p := range []*pkg.Package{&packageA, &packageB, &packageC} { + packageD := pkg.Package{Name: "package-d", Type: pkg.BitnamiPkg} + for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD} { p := p p.SetID() } @@ -46,6 +47,17 @@ func TestExcludeByFileOwnershipOverlap(t *testing.T) { packages: pkg.NewCollection(packageC, packageB), shouldExclude: true, }, + { + // prove that Bitnami -> bin exclusions are wired + name: "exclusions from bitnami -> binary", + relationship: artifact.Relationship{ + Type: artifact.OwnershipByFileOverlapRelationship, + From: packageD, // Bitnami + To: packageC, // ELF binary + }, + packages: pkg.NewCollection(packageD, packageC), + shouldExclude: true, + }, } for _, test := range tests { @@ -117,6 +129,63 @@ func TestIdentifyOverlappingOSRelationship(t *testing.T) { } } +func TestIdentifyOverlappingBitnamiRelationship(t *testing.T) { + packageA := pkg.Package{Name: "package-a", Type: pkg.BitnamiPkg} // Bitnami package + packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg} + packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.BinarySignature{}} + packageD := pkg.Package{Name: "package-d", Type: pkg.PythonPkg} // Language package + packageE := pkg.Package{Name: "package-e", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{}} + + for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE} { + p.SetID() + } + + tests := []struct { + name string + parent *pkg.Package + child *pkg.Package + expectedID artifact.ID + }{ + { + name: "Bitnami -> binary without metadata", + parent: &packageA, + child: &packageB, + expectedID: packageB.ID(), // Bitnami package to binary package, should return child ID + }, + { + name: "Bitnami -> binary with binary metadata", + parent: &packageA, + child: &packageC, + expectedID: packageC.ID(), // Bitnami package to binary package with binary metadata, should return child ID + }, + { + name: "Bitnami -> non-binary package", + parent: &packageA, + child: &packageD, + expectedID: "", // Bitnami package to non-binary package, no exclusion + }, + { + name: "Bitnami -> binary with ELF metadata", + parent: &packageA, + child: &packageE, + expectedID: packageE.ID(), // Bitnami package to binary package with ELF metadata, should return child ID + }, + { + name: "non-Bitnami parent", + parent: &packageD, // non-Bitnami package + child: &packageC, + expectedID: "", // non-Bitnami parent, no exclusion + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resultID := identifyOverlappingBitnamiRelationship(tt.parent, tt.child) + assert.Equal(t, tt.expectedID, resultID) + }) + } +} + func TestIdentifyOverlappingJVMRelationship(t *testing.T) { packageA := pkg.Package{Name: "package-a", Type: pkg.BinaryPkg} diff --git a/internal/task/package_tasks.go b/internal/task/package_tasks.go index b6da66980..64a3e476c 100644 --- a/internal/task/package_tasks.go +++ b/internal/task/package_tasks.go @@ -6,6 +6,7 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/alpine" "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/cpp" "github.com/anchore/syft/syft/pkg/cataloger/dart" "github.com/anchore/syft/syft/pkg/cataloger/debian" @@ -152,6 +153,7 @@ func DefaultPackageTaskFactories() Factories { pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "linux", "kernel", ), newSimplePackageTaskFactory(sbomCataloger.NewCataloger, "sbom"), // note: not evidence of installed packages + newSimplePackageTaskFactory(bitnamiSbomCataloger.NewCataloger, "bitnami", pkgcataloging.InstalledTag, pkgcataloging.ImageTag), newSimplePackageTaskFactory(wordpress.NewWordpressPluginCataloger, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "wordpress"), newSimplePackageTaskFactory(terraform.NewLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "terraform"), } diff --git a/internal/task/relationship_tasks.go b/internal/task/relationship_tasks.go index 54c366287..9d4656906 100644 --- a/internal/task/relationship_tasks.go +++ b/internal/task/relationship_tasks.go @@ -42,7 +42,7 @@ func finalizeRelationships(resolver file.Resolver, builder sbomsync.Builder, cfg // remove ELF packages and Binary packages that are already // represented by a source package (e.g. a package that is evident by some package manager) - builder.DeletePackages(binary.PackagesToRemove(resolver, accessor)...) + builder.DeletePackages(binary.PackagesToRemove(accessor)...) // add relationships showing packages that are evident by a file which is owned by another package (package-to-package) if cfg.PackageFileOwnershipOverlap { diff --git a/schema/json/schema-16.0.22.json b/schema/json/schema-16.0.22.json new file mode 100644 index 000000000..b3c5e4983 --- /dev/null +++ b/schema/json/schema-16.0.22.json @@ -0,0 +1,2826 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.22/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" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "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" + } + }, + "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" + ] + }, + "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" + ] + }, + "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" + }, + "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" + } + }, + "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" + ] + }, + "NixStoreEntry": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "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/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$ref": "#/$defs/DotnetPackagesLockEntry" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableEntry" + }, + { + "$ref": "#/$defs/DpkgDbEntry" + }, + { + "$ref": "#/$defs/ElfBinaryPackageNoteJsonPayload" + }, + { + "$ref": "#/$defs/ElixirMixLockEntry" + }, + { + "$ref": "#/$defs/ErlangRebarLockEntry" + }, + { + "$ref": "#/$defs/GoModuleBuildinfoEntry" + }, + { + "$ref": "#/$defs/GoModuleEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackEntry" + }, + { + "$ref": "#/$defs/HaskellHackageStackLockEntry" + }, + { + "$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/PhpComposerInstalledEntry" + }, + { + "$ref": "#/$defs/PhpComposerLockEntry" + }, + { + "$ref": "#/$defs/PhpPeclEntry" + }, + { + "$ref": "#/$defs/PortageDbEntry" + }, + { + "$ref": "#/$defs/PythonPackage" + }, + { + "$ref": "#/$defs/PythonPipRequirementsEntry" + }, + { + "$ref": "#/$defs/PythonPipfileLockEntry" + }, + { + "$ref": "#/$defs/PythonPoetryLockEntry" + }, + { + "$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" + ] + }, + "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" + ] + }, + "PhpPeclEntry": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "PortageDbEntry": { + "properties": { + "installedSize": { + "type": "integer" + }, + "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" + ] + }, + "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" + }, + "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" + }, + "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" + ] + }, + "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 c53a978be..b3c5e4983 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.21/document", + "$id": "anchore.io/schema/syft/json/16.0.22/document", "$ref": "#/$defs/Document", "$defs": { "AlpmDbEntry": { @@ -218,6 +218,44 @@ "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": { @@ -1653,6 +1691,9 @@ { "$ref": "#/$defs/BinarySignature" }, + { + "$ref": "#/$defs/BitnamiSbomEntry" + }, { "$ref": "#/$defs/CConanFileEntry" }, diff --git a/syft/format/internal/spdxutil/helpers/originator_supplier.go b/syft/format/internal/spdxutil/helpers/originator_supplier.go index 18fc7f798..15407e45e 100644 --- a/syft/format/internal/spdxutil/helpers/originator_supplier.go +++ b/syft/format/internal/spdxutil/helpers/originator_supplier.go @@ -43,6 +43,10 @@ func Originator(p pkg.Package) (typ string, author string) { //nolint: funlen case pkg.ApkDBEntry: author = metadata.Maintainer + case pkg.BitnamiSBOMEntry: + typ = orgType + author = "Bitnami" + case pkg.DotnetPortableExecutableEntry: typ = orgType author = metadata.CompanyName diff --git a/syft/format/internal/spdxutil/helpers/originator_supplier_test.go b/syft/format/internal/spdxutil/helpers/originator_supplier_test.go index 9f35aefe0..34dd55750 100644 --- a/syft/format/internal/spdxutil/helpers/originator_supplier_test.go +++ b/syft/format/internal/spdxutil/helpers/originator_supplier_test.go @@ -12,6 +12,7 @@ import ( func Test_OriginatorSupplier(t *testing.T) { completionTester := packagemetadata.NewCompletionTester(t, pkg.BinarySignature{}, + pkg.BitnamiSBOMEntry{}, pkg.CocoaPodfileLockEntry{}, pkg.ConanV1LockEntry{}, pkg.ConanV2LockEntry{}, // the field Username might be the username of either the package originator or the supplier (unclear currently) @@ -89,6 +90,14 @@ func Test_OriginatorSupplier(t *testing.T) { originator: "", supplier: "Person: someone", }, + { + name: "from bitnami", + input: pkg.Package{ + Metadata: pkg.BitnamiSBOMEntry{}, + }, + originator: "Organization: Bitnami", + supplier: "Organization: Bitnami", + }, { name: "from dotnet -- PE binary", input: pkg.Package{ diff --git a/syft/format/internal/spdxutil/helpers/source_info.go b/syft/format/internal/spdxutil/helpers/source_info.go index 6ea2a67f9..f9aebad65 100644 --- a/syft/format/internal/spdxutil/helpers/source_info.go +++ b/syft/format/internal/spdxutil/helpers/source_info.go @@ -16,6 +16,8 @@ func SourceInfo(p pkg.Package) string { answer = "acquired package info from RPM DB" case pkg.ApkPkg: answer = "acquired package info from APK DB" + case pkg.BitnamiPkg: + answer = "acquired package info from a Bitnami SBOM" case pkg.DartPubPkg: answer = "acquired package info from pubspec manifest" case pkg.DebPkg: @@ -32,6 +34,8 @@ func SourceInfo(p pkg.Package) string { answer = "acquired package info from installed gem metadata file" case pkg.GoModulePkg: answer = "acquired package info from go module information" + case pkg.GraalVMNativeImagePkg: + answer = "acquired package info from GraalVM native image" case pkg.RustPkg: answer = "acquired package info from rust cargo manifest" case pkg.PhpComposerPkg: diff --git a/syft/format/internal/spdxutil/helpers/source_info_test.go b/syft/format/internal/spdxutil/helpers/source_info_test.go index 309d79bbf..02b074482 100644 --- a/syft/format/internal/spdxutil/helpers/source_info_test.go +++ b/syft/format/internal/spdxutil/helpers/source_info_test.go @@ -111,6 +111,14 @@ func Test_SourceInfo(t *testing.T) { "from go module information", }, }, + { + input: pkg.Package{ + Type: pkg.GraalVMNativeImagePkg, + }, + expected: []string{ + "from GraalVM native image", + }, + }, { input: pkg.Package{ Type: pkg.RustPkg, @@ -199,6 +207,14 @@ func Test_SourceInfo(t *testing.T) { "acquired package info from the following paths", }, }, + { + input: pkg.Package{ + Type: pkg.BitnamiPkg, + }, + expected: []string{ + "acquired package info from a Bitnami SBOM", + }, + }, { input: pkg.Package{ Type: pkg.HexPkg, diff --git a/syft/internal/packagemetadata/generated.go b/syft/internal/packagemetadata/generated.go index e08e09ab4..2512c581c 100644 --- a/syft/internal/packagemetadata/generated.go +++ b/syft/internal/packagemetadata/generated.go @@ -10,6 +10,7 @@ func AllTypes() []any { pkg.AlpmDBEntry{}, pkg.ApkDBEntry{}, pkg.BinarySignature{}, + pkg.BitnamiSBOMEntry{}, pkg.CocoaPodfileLockEntry{}, pkg.ConanV1LockEntry{}, pkg.ConanV2LockEntry{}, diff --git a/syft/internal/packagemetadata/names.go b/syft/internal/packagemetadata/names.go index ef5005feb..fa9f18ed9 100644 --- a/syft/internal/packagemetadata/names.go +++ b/syft/internal/packagemetadata/names.go @@ -65,6 +65,7 @@ var jsonTypes = makeJSONTypes( jsonNames(pkg.AlpmDBEntry{}, "alpm-db-entry", "AlpmMetadata"), jsonNames(pkg.ApkDBEntry{}, "apk-db-entry", "ApkMetadata"), jsonNames(pkg.BinarySignature{}, "binary-signature", "BinaryMetadata"), + jsonNames(pkg.BitnamiSBOMEntry{}, "bitnami-sbom-entry"), jsonNames(pkg.CocoaPodfileLockEntry{}, "cocoa-podfile-lock-entry", "CocoapodsMetadataType"), jsonNames(pkg.ConanV1LockEntry{}, "c-conan-lock-entry", "ConanLockMetadataType"), jsonNames(pkg.ConanV2LockEntry{}, "c-conan-lock-v2-entry"), diff --git a/syft/pkg/bitnami.go b/syft/pkg/bitnami.go new file mode 100644 index 000000000..195a4ba50 --- /dev/null +++ b/syft/pkg/bitnami.go @@ -0,0 +1,17 @@ +package pkg + +// BitnamiSBOMEntry represents all captured data from Bitnami packages +// described in Bitnami' SPDX files. +type BitnamiSBOMEntry struct { + Name string `mapstructure:"name" json:"name"` + Architecture string `mapstructure:"arch" json:"arch"` + Distro string `mapstructure:"distro" json:"distro"` + Revision string `mapstructure:"revision" json:"revision"` + Version string `mapstructure:"version" json:"version"` + Path string `mapstructure:"path" json:"path"` + Files []string `mapstructure:"files" json:"files"` +} + +func (b BitnamiSBOMEntry) OwnedFiles() (result []string) { + return b.Files +} diff --git a/syft/pkg/cataloger/bitnami/cataloger.go b/syft/pkg/cataloger/bitnami/cataloger.go new file mode 100644 index 000000000..510d526d8 --- /dev/null +++ b/syft/pkg/cataloger/bitnami/cataloger.go @@ -0,0 +1,173 @@ +/* +Package bitnami provides a concrete Cataloger implementation for capturing packages embedded within Bitnami SBOM files. +*/ +package bitnami + +import ( + "context" + "path/filepath" + "strings" + + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/format" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" +) + +const catalogerName = "bitnami-cataloger" + +// NewCataloger returns a new SBOM cataloger object loaded from saved SBOM JSON. +func NewCataloger() pkg.Cataloger { + return generic.NewCataloger(catalogerName). + WithParserByGlobs(parseSBOM, + "/opt/bitnami/**/.spdx-*.spdx", + ) +} + +func parseSBOM(_ context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + s, sFormat, _, err := format.Decode(reader) + if err != nil { + return nil, nil, err + } + + if s == nil { + log.WithFields("path", reader.Location.RealPath).Trace("file is not an SBOM") + return nil, nil, nil + } + + // Bitnami exclusively uses SPDX JSON SBOMs + if sFormat != "spdx-json" { + log.WithFields("path", reader.Location.RealPath).Trace("file is not an SPDX JSON SBOM") + return nil, nil, nil + } + + var pkgs []pkg.Package + var secondaryPkgsFiles []string + mainPkgID := findMainPkgID(s.Relationships) + for _, p := range s.Artifacts.Packages.Sorted() { + // We only want to report Bitnami packages + if !strings.HasPrefix(p.PURL, "pkg:bitnami") { + continue + } + + p.FoundBy = catalogerName + p.Type = pkg.BitnamiPkg + // replace all locations on the package with the location of the SBOM file. + // Why not keep the original list of locations? Since the "locations" field is meant to capture + // where there is evidence of this file, and the catalogers have not run against any file other than, + // the SBOM, this is the only location that is relevant for this cataloger. + p.Locations = file.NewLocationSet( + reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ) + + // Parse the Bitnami-specific metadata + metadata, err := parseBitnamiPURL(p.PURL) + if err != nil { + return nil, nil, err + } + + // Bitnami packages reported in a SPDX file are shipped under the same directory + // as the SPDX file itself. + metadata.Path = filepath.Dir(reader.Location.RealPath) + if p.ID() != mainPkgID { + metadata.Files = packageFiles(s.Relationships, p, metadata.Path) + secondaryPkgsFiles = append(secondaryPkgsFiles, metadata.Files...) + } + + p.Metadata = metadata + + pkgs = append(pkgs, p) + } + + // Resolve all files owned by the main package in the SBOM and update the metadata + if mainPkgFiles, err := mainPkgFiles(resolver, reader.Location.RealPath, secondaryPkgsFiles); err == nil { + for i, p := range pkgs { + if p.ID() == mainPkgID { + metadata, ok := p.Metadata.(*pkg.BitnamiSBOMEntry) + if !ok { + log.WithFields("spdx-filepath", reader.Location.RealPath).Trace("main package in SBOM does not have Bitnami metadata") + continue + } + + metadata.Files = mainPkgFiles + pkgs[i].Metadata = metadata + } + } + } else { + log.WithFields("spdx-filepath", reader.Location.RealPath, "error", err).Trace("unable to resolve owned files for main package in SBOM") + } + + return pkgs, filterRelationships(s.Relationships, pkgs), nil +} + +// filterRelationships filters out relationships that are not related to Bitnami packages +// and replaces the package information with the one with completed info +func filterRelationships(relationships []artifact.Relationship, pkgs []pkg.Package) []artifact.Relationship { + var result []artifact.Relationship + for _, r := range relationships { + if value, ok := r.From.(pkg.Package); ok { + found := false + for _, p := range pkgs { + if value.PURL == p.PURL { + r.From = p + found = true + break + } + } + if !found { + continue + } + } + if value, ok := r.To.(pkg.Package); ok { + found := false + for _, p := range pkgs { + if value.PURL == p.PURL { + r.To = p + found = true + break + } + } + if !found { + continue + } + } + result = append(result, r) + } + + return result +} + +// findMainPkgID goes through the list of relationships and finds the main package ID +// which is the one that contains other packages but is not contained by any other package +func findMainPkgID(relationships []artifact.Relationship) artifact.ID { + containedByAnother := func(candidateID artifact.ID) bool { + for _, r := range relationships { + if r.Type != artifact.ContainsRelationship { + continue + } + + if to, ok := r.To.(pkg.Package); ok { + if to.ID() == candidateID { + return true + } + } + } + + return false + } + + for _, r := range relationships { + if from, ok := r.From.(pkg.Package); ok { + if !strings.HasPrefix(from.PURL, "pkg:bitnami") { + continue + } + if !containedByAnother(from.ID()) { + return from.ID() + } + } + } + + return "" +} diff --git a/syft/pkg/cataloger/bitnami/cataloger_test.go b/syft/pkg/cataloger/bitnami/cataloger_test.go new file mode 100644 index 000000000..948b266cd --- /dev/null +++ b/syft/pkg/cataloger/bitnami/cataloger_test.go @@ -0,0 +1,470 @@ +package bitnami + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/cpe" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func mustCPEs(s ...string) (c []cpe.CPE) { + for _, i := range s { + newCPE := cpe.Must(i, "") + newCPE.Source = cpe.DeclaredSource + c = append(c, newCPE) + } + return +} + +func TestBitnamiCataloger(t *testing.T) { + postgresqlMainPkg := pkg.Package{ + Name: "postgresql", + Version: "17.2.0-8", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("PostgreSQL", license.Concluded), + pkg.NewLicenseFromType("PostgreSQL", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/postgresql@17.2.0-8?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:postgresql:postgresql:17.2.0:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "postgresql", + Version: "17.2.0", + Revision: "8", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + Files: []string{ + "opt/bitnami/postgresql/readme.txt", + }, + }, + } + postgresqlSecondaryPkgs := []pkg.Package{ + { + Name: "geos", + Version: "3.13.0", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("LGPL-2.1-only", license.Concluded), + pkg.NewLicenseFromType("LGPL-2.1-only", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/geos@3.13.0?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:libgeos:geos:3.13.0:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "geos", + Version: "3.13.0", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "proj", + Version: "6.3.2", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("MIT", license.Concluded), + pkg.NewLicenseFromType("MIT", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/proj@6.3.2?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:proj:proj:6.3.2:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "proj", + Version: "6.3.2", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "gdal", + Version: "3.10.1", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("MIT", license.Concluded), + pkg.NewLicenseFromType("MIT", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/gdal@3.10.1?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:osgeo:gdal:3.10.1:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "gdal", + Version: "3.10.1", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "json-c", + Version: "0.16.20220414", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("MIT", license.Concluded), + pkg.NewLicenseFromType("MIT", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/json-c@0.16.20220414?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:json-c_project:json-c:0.16.20220414:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "json-c", + Version: "0.16.20220414", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "orafce", + Version: "4.14.1", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("0BSD", license.Concluded), + pkg.NewLicenseFromType("0BSD", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/orafce@4.14.1?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:orafce:orafce:4.14.1:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "orafce", + Version: "4.14.1", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "pljava", + Version: "1.6.8", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("BSD-3-Clause", license.Concluded), + pkg.NewLicenseFromType("BSD-3-Clause", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/pljava@1.6.8?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:pl/java_project:pl/java:1.6.8:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "pljava", + Version: "1.6.8", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + Files: []string{ + "opt/bitnami/postgresql/share/pljava/pljava-api-1.6.8.jar", + "opt/bitnami/postgresql/share/pljava/pljava-1.6.8.jar", + "opt/bitnami/postgresql/share/pljava/pljava-examples-1.6.8.jar", + }, + }, + }, + { + Name: "unixodbc", + Version: "2.3.12", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("LGPL-2.1-only", license.Concluded), + pkg.NewLicenseFromType("LGPL-2.1-only", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/unixodbc@2.3.12?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:unixodbc:unixodbc:2.3.12:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "unixodbc", + Version: "2.3.12", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "psqlodbc", + Version: "16.0.0", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("LGPL-3.0-only", license.Concluded), + pkg.NewLicenseFromType("LGPL-3.0-only", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/psqlodbc@16.0.0?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:postgresql:psqlodbc:16.0.0:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "psqlodbc", + Version: "16.0.0", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "protobuf", + Version: "3.21.12", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("BSD-3-Clause", license.Concluded), + pkg.NewLicenseFromType("BSD-3-Clause", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/protobuf@3.21.12?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:golang:protobuf:3.21.12:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "protobuf", + Version: "3.21.12", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "protobuf-c", + Version: "1.5.1", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("BSD-2-Clause", license.Concluded), + pkg.NewLicenseFromType("BSD-2-Clause", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/protobuf-c@1.5.1?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:protobuf-c:protobuf-c:1.5.1:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "protobuf-c", + Version: "1.5.1", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "postgis", + Version: "3.4.4", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("GPL-2.0-or-later", license.Concluded), + pkg.NewLicenseFromType("GPL-2.0-or-later", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/postgis@3.4.4?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:postgis:postgis:3.4.4:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "postgis", + Version: "3.4.4", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "pgaudit", + Version: "17.0.0", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("PostgreSQL", license.Concluded), + pkg.NewLicenseFromType("PostgreSQL", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/pgaudit@17.0.0?arch=arm64&distro=debian-12", + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "pgaudit", + Version: "17.0.0", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "pgbackrest", + Version: "2.54.2", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("MIT", license.Concluded), + pkg.NewLicenseFromType("MIT", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/pgbackrest@2.54.2?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:pgbackrest:pgbackrest:2.54.2:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "pgbackrest", + Version: "2.54.2", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "wal2json", + Version: "2.6.0", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("BSD-3-Clause", license.Concluded), + pkg.NewLicenseFromType("BSD-3-Clause", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/wal2json@2.6.0?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:wal2json:wal2json:2.6.0:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "wal2json", + Version: "2.6.0", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + { + Name: "nss-wrapper", + Version: "1.1.16", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/postgresql/.spdx-postgresql.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("BSD-3-Clause", license.Concluded), + pkg.NewLicenseFromType("BSD-3-Clause", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/nss_wrapper@1.1.16?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:nss_wrapper:nss_wrapper:1.1.16:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "nss_wrapper", + Version: "1.1.16", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/postgresql", + }, + }, + } + + postgresqlExpectedPkgs := []pkg.Package{postgresqlMainPkg} + postgresqlExpectedPkgs = append(postgresqlExpectedPkgs, postgresqlSecondaryPkgs...) + pkg.Sort(postgresqlExpectedPkgs) + var postgresqlExpectedRelationships []artifact.Relationship + for _, p := range postgresqlSecondaryPkgs { + postgresqlExpectedRelationships = append(postgresqlExpectedRelationships, artifact.Relationship{ + From: postgresqlMainPkg, + To: p, + Type: artifact.ContainsRelationship, + }) + } + + renderTemplateMainPkg := pkg.Package{ + Name: "render-template", + Version: "1.0.7-4", + Type: pkg.BitnamiPkg, + Locations: file.NewLocationSet(file.NewLocation("opt/bitnami/render-template/.spdx-render-template.spdx")), + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromType("Apache-2.0", license.Concluded), + pkg.NewLicenseFromType("Apache-2.0", license.Declared), + ), + FoundBy: catalogerName, + PURL: "pkg:bitnami/render-template@1.0.7-4?arch=arm64&distro=debian-12", + CPEs: mustCPEs( + "cpe:2.3:*:render-template:render-template:1.0.7:*:*:*:*:*:*:*", + ), + Metadata: &pkg.BitnamiSBOMEntry{ + Name: "render-template", + Version: "1.0.7", + Revision: "4", + Architecture: "arm64", + Distro: "debian-12", + Path: "opt/bitnami/render-template", + Files: []string{}, + }, + } + + tests := []struct { + name string + fixture string + wantPkgs []pkg.Package + wantRelationships []artifact.Relationship + wantErr require.ErrorAssertionFunc + }{ + { + name: "parse valid PostgreSQL SBOM", + fixture: "test-fixtures/json", + wantPkgs: postgresqlExpectedPkgs, + wantRelationships: postgresqlExpectedRelationships, + wantErr: require.NoError, + }, + { + name: "parse valid SBOM that includes both Bitnami and non-Bitnami packages", + fixture: "test-fixtures/mix", + wantPkgs: []pkg.Package{renderTemplateMainPkg}, + wantRelationships: nil, + wantErr: require.NoError, + }, + { + name: "Redis SBOM with not allowed tag-value format", + fixture: "test-fixtures/tag-value", + wantPkgs: nil, + wantRelationships: nil, + wantErr: require.NoError, + }, + { + name: "Invalid SBOM", + fixture: "test-fixtures/invalid", + wantPkgs: nil, + wantRelationships: nil, + wantErr: require.Error, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, tt.fixture). + Expects(tt.wantPkgs, tt.wantRelationships). + WithErrorAssertion(tt.wantErr). + TestCataloger(t, NewCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/bitnami/package.go b/syft/pkg/cataloger/bitnami/package.go new file mode 100644 index 000000000..354dbe320 --- /dev/null +++ b/syft/pkg/cataloger/bitnami/package.go @@ -0,0 +1,92 @@ +package bitnami + +import ( + "fmt" + "path" + "path/filepath" + "slices" + "strings" + + version "github.com/bitnami/go-version/pkg/version" + + "github.com/anchore/packageurl-go" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" +) + +func parseBitnamiPURL(p string) (*pkg.BitnamiSBOMEntry, error) { + purl, err := packageurl.FromString(p) + if err != nil { + return nil, err + } + + v, err := version.Parse(purl.Version) + if err != nil { + return nil, err + } + + entry := pkg.BitnamiSBOMEntry{ + Name: purl.Name, + Version: strings.TrimSuffix(v.String(), fmt.Sprintf("-%s", v.Revision().String())), + Revision: v.Revision().String(), + } + + for _, q := range purl.Qualifiers { + switch q.Key { + case "arch": + entry.Architecture = q.Value + case "distro": + entry.Distro = q.Value + } + } + + return &entry, nil +} + +// packageFiles goes through the list of relationships and finds the files that +// are owned by the given package +func packageFiles(relationships []artifact.Relationship, p pkg.Package, baseDirectory string) []string { + var result []string + for _, r := range relationships { + if r.Type != artifact.ContainsRelationship { + continue + } + + if from, ok := r.From.(pkg.Package); ok { + if from.PURL == p.PURL { + if to, ok := r.To.(pkg.Package); ok { + result = append(result, packageFiles(relationships, to, baseDirectory)...) + } + if value, ok := r.To.(file.Location); ok { + // note: the file.Location is from the SBOM, and all files within the Bitnami SBOM by convention + // are relative to the /opt/bitnami/PRODUCT directory, so we need to prepend the base directory + // so that it's relative to the path found within the container image. + result = append(result, path.Join(baseDirectory, value.RealPath)) + } + } + } + } + + return result +} + +// mainPkgFiles returns the files owned by the main package in the SPDX file. +func mainPkgFiles(resolver file.Resolver, spdxFilePath string, secondaryPkgsFiles []string) ([]string, error) { + ownedPathGlob := fmt.Sprintf("%s/**", filepath.Dir(spdxFilePath)) + ownedLocations, err := resolver.FilesByGlob(ownedPathGlob) + if err != nil { + return nil, err + } + + ownedLocationSet := file.NewLocationSet(ownedLocations...) + ownedFiles := ownedLocationSet.CoordinateSet().Paths() + + // Remove the SPDX file and the files already assigned to other packages + // from the list of owned files + files := slices.DeleteFunc(ownedFiles, func(f string) bool { + return f == spdxFilePath || slices.Contains(secondaryPkgsFiles, f) + }) + + return files, nil +} diff --git a/syft/pkg/cataloger/bitnami/package_test.go b/syft/pkg/cataloger/bitnami/package_test.go new file mode 100644 index 000000000..f7b4234e4 --- /dev/null +++ b/syft/pkg/cataloger/bitnami/package_test.go @@ -0,0 +1,57 @@ +package bitnami + +import ( + "reflect" + "testing" + + "github.com/anchore/syft/syft/pkg" +) + +func Test_parseBitnamiPURL(t *testing.T) { + tests := []struct { + name string + purl string + want *pkg.BitnamiSBOMEntry + wantErr bool + }{ + { + name: "Valid Bitnami pURL", + purl: "pkg:bitnami/redis@7.4.1-0?arch=arm64&distro=debian-12", + want: &pkg.BitnamiSBOMEntry{ + Name: "redis", + Version: "7.4.1", + Revision: "0", + Architecture: "arm64", + Distro: "debian-12", + }, + wantErr: false, + }, + { + name: "Invalid pURL", + purl: "this/is/not/a/purl", + want: nil, + wantErr: true, + }, + { + name: "Invalid version", + purl: "pkg:bitnami/redis@7.4.1.0?arch=arm64&distro=debian-12", + want: nil, + wantErr: true, + }, + } + t.Parallel() + for _, testToRun := range tests { + test := testToRun + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + got, err := parseBitnamiPURL(test.purl) + if (err != nil) != test.wantErr { + tt.Errorf("parseBitnamiPURL() error = %v, wantErr %v", err, test.wantErr) + return + } + if !reflect.DeepEqual(got, test.want) { + tt.Errorf("parseBitnamiPURL() = %v, want %v", got, test.want) + } + }) + } +} diff --git a/syft/pkg/cataloger/bitnami/test-fixtures/invalid/opt/bitnami/redis/.spdx-redis.spdx b/syft/pkg/cataloger/bitnami/test-fixtures/invalid/opt/bitnami/redis/.spdx-redis.spdx new file mode 100644 index 000000000..78d9776b5 --- /dev/null +++ b/syft/pkg/cataloger/bitnami/test-fixtures/invalid/opt/bitnami/redis/.spdx-redis.spdx @@ -0,0 +1,5 @@ +{ + "id": "invalid-id", + "name": "invalid-name", + "description: "This is not a SPDX file" +} diff --git a/syft/pkg/cataloger/bitnami/test-fixtures/json/opt/bitnami/postgresql/.spdx-postgresql.spdx b/syft/pkg/cataloger/bitnami/test-fixtures/json/opt/bitnami/postgresql/.spdx-postgresql.spdx new file mode 100644 index 000000000..3ca256cd3 --- /dev/null +++ b/syft/pkg/cataloger/bitnami/test-fixtures/json/opt/bitnami/postgresql/.spdx-postgresql.spdx @@ -0,0 +1,573 @@ +{ + "SPDXID": "SPDXRef-postgresql", + "spdxVersion": "SPDX-2.3", + "creationInfo": { + "created": "2025-02-02T05:18:41.934Z", + "creators": [ + "Organization: Broadcom Inc. and/or its subsidiaries." + ] + }, + "name": "SPDX document for PostgreSQL 17.2.0", + "dataLicense": "CC0-1.0", + "documentDescribes": [ + "SPDXRef-postgresql" + ], + "documentNamespace": "postgresql-17.2.0", + "packages": [ + { + "SPDXID": "SPDXRef-postgresql", + "name": "postgresql", + "versionInfo": "17.2.0-8", + "downloadLocation": "https://ftp.postgresql.org/pub/source/v17.2/postgresql-17.2.tar.gz", + "licenseConcluded": "PostgreSQL", + "licenseDeclared": "PostgreSQL", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:postgresql:postgresql:17.2.0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/postgresql@17.2.0-8?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-geos", + "name": "geos", + "versionInfo": "3.13.0", + "downloadLocation": "https://github.com/libgeos/geos/archive/3.13.0.tar.gz", + "licenseConcluded": "LGPL-2.1-only", + "licenseDeclared": "LGPL-2.1-only", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:libgeos:geos:3.13.0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/geos@3.13.0?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-proj", + "name": "proj", + "versionInfo": "6.3.2", + "downloadLocation": "https://github.com/OSGeo/PROJ/archive/6.3.2.tar.gz", + "licenseConcluded": "MIT", + "licenseDeclared": "MIT", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:proj:proj:6.3.2:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/proj@6.3.2?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-gdal", + "name": "gdal", + "versionInfo": "3.10.1", + "downloadLocation": "https://github.com/OSGeo/gdal/releases/download/v3.10.1/gdal-3.10.1.tar.gz", + "licenseConcluded": "MIT", + "licenseDeclared": "MIT", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:osgeo:gdal:3.10.1:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/gdal@3.10.1?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-json-c", + "name": "json-c", + "versionInfo": "0.16.20220414", + "downloadLocation": "https://github.com/json-c/json-c/archive/refs/tags/json-c-0.16-20220414.tar.gz", + "licenseConcluded": "MIT", + "licenseDeclared": "MIT", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:json-c_project:json-c:0.16.20220414:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/json-c@0.16.20220414?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-orafce", + "name": "orafce", + "versionInfo": "4.14.1", + "downloadLocation": "https://github.com/orafce/orafce/archive/refs/tags/VERSION_4_14_1.tar.gz", + "licenseConcluded": "0BSD", + "licenseDeclared": "0BSD", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:orafce:orafce:4.14.1:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/orafce@4.14.1?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-pljava", + "name": "pljava", + "versionInfo": "1.6.8", + "downloadLocation": "https://github.com/tada/pljava/archive/V1_6_8.tar.gz", + "licenseConcluded": "BSD-3-Clause", + "licenseDeclared": "BSD-3-Clause", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:pl/java_project:pl/java:1.6.8:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/pljava@1.6.8?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "name": "org.postgresql:pljava", + "SPDXID": "SPDXRef-Package-551f76fc50fc5735", + "versionInfo": "1.6.8", + "supplier": "NOASSERTION", + "downloadLocation": "NONE", + "filesAnalyzed": true, + "packageVerificationCode": { + "packageVerificationCodeValue": "8b35292054790088fc1f41fb696a993d584d17cd" + }, + "licenseConcluded": "NONE", + "licenseDeclared": "NONE", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:maven/org.postgresql/pljava@1.6.8" + } + ], + "primaryPackagePurpose": "LIBRARY", + "copyrightText": "NOASSERTION" + }, + { + "name": "org.postgresql:pljava-api", + "SPDXID": "SPDXRef-Package-2dde48fd49927020", + "versionInfo": "1.6.8", + "supplier": "NOASSERTION", + "downloadLocation": "NONE", + "filesAnalyzed": true, + "packageVerificationCode": { + "packageVerificationCodeValue": "3aaf93e288e125481a250d50ab629d890f72e7b8" + }, + "licenseConcluded": "NONE", + "licenseDeclared": "NONE", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:maven/org.postgresql/pljava-api@1.6.8" + } + ], + "primaryPackagePurpose": "LIBRARY", + "copyrightText": "NOASSERTION" + }, + { + "name": "org.postgresql:pljava-examples", + "SPDXID": "SPDXRef-Package-f13571c8a05828cc", + "versionInfo": "1.6.8", + "supplier": "NOASSERTION", + "downloadLocation": "NONE", + "filesAnalyzed": true, + "packageVerificationCode": { + "packageVerificationCodeValue": "6f126f1237ddd2be6196726130c014770864bcc9" + }, + "licenseConcluded": "NONE", + "licenseDeclared": "NONE", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:maven/org.postgresql/pljava-examples@1.6.8" + } + ], + "primaryPackagePurpose": "LIBRARY", + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-unixodbc", + "name": "unixodbc", + "versionInfo": "2.3.12", + "downloadLocation": "http://www.unixodbc.org/unixODBC-2.3.12.tar.gz", + "licenseConcluded": "LGPL-2.1-only", + "licenseDeclared": "LGPL-2.1-only", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:unixodbc:unixodbc:2.3.12:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/unixodbc@2.3.12?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-psqlodbc", + "name": "psqlodbc", + "versionInfo": "16.0.0", + "downloadLocation": "https://ftp.postgresql.org/pub/odbc/versions/src/psqlodbc-16.00.0000.tar.gz", + "licenseConcluded": "LGPL-3.0-only", + "licenseDeclared": "LGPL-3.0-only", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:postgresql:psqlodbc:16.0.0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/psqlodbc@16.0.0?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-protobuf", + "name": "protobuf", + "versionInfo": "3.21.12", + "downloadLocation": "https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.21.12.tar.gz", + "licenseConcluded": "BSD-3-Clause", + "licenseDeclared": "BSD-3-Clause", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:golang:protobuf:3.21.12:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/protobuf@3.21.12?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-protobuf-c", + "name": "protobuf-c", + "versionInfo": "1.5.1", + "downloadLocation": "https://github.com/protobuf-c/protobuf-c/releases/download/v1.5.1/protobuf-c-1.5.1.tar.gz", + "licenseConcluded": "BSD-2-Clause", + "licenseDeclared": "BSD-2-Clause", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:protobuf-c:protobuf-c:1.5.1:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/protobuf-c@1.5.1?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-postgis", + "name": "postgis", + "versionInfo": "3.4.4", + "downloadLocation": "http://download.osgeo.org/postgis/source/postgis-3.4.4.tar.gz", + "licenseConcluded": "GPL-2.0-or-later", + "licenseDeclared": "GPL-2.0-or-later", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:postgis:postgis:3.4.4:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/postgis@3.4.4?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-pgaudit", + "name": "pgaudit", + "versionInfo": "17.0.0", + "downloadLocation": "https://github.com/pgaudit/pgaudit/archive/17.0.tar.gz", + "licenseConcluded": "PostgreSQL", + "licenseDeclared": "PostgreSQL", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/pgaudit@17.0.0?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-pgbackrest", + "name": "pgbackrest", + "versionInfo": "2.54.2", + "downloadLocation": "https://github.com/pgbackrest/pgbackrest/archive/release/2.54.2.tar.gz", + "licenseConcluded": "MIT", + "licenseDeclared": "MIT", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:pgbackrest:pgbackrest:2.54.2:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/pgbackrest@2.54.2?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-wal2json", + "name": "wal2json", + "versionInfo": "2.6.0", + "downloadLocation": "https://github.com/eulerto/wal2json/archive/refs/tags/wal2json_2_6.tar.gz", + "licenseConcluded": "BSD-3-Clause", + "licenseDeclared": "BSD-3-Clause", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:wal2json:wal2json:2.6.0:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/wal2json@2.6.0?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "SPDXID": "SPDXRef-nss-wrapper", + "name": "nss-wrapper", + "versionInfo": "1.1.16", + "downloadLocation": "https://ftp.samba.org/pub/cwrap/nss_wrapper-1.1.16.tar.gz", + "licenseConcluded": "BSD-3-Clause", + "licenseDeclared": "BSD-3-Clause", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:nss_wrapper:nss_wrapper:1.1.16:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/nss_wrapper@1.1.16?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + } + ], + "files": [ + { + "fileName": "share/pljava/pljava-1.6.8.jar", + "SPDXID": "SPDXRef-File-28d7473df5d8af04-pljava", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "b0b8f544ddefbc9ba47ed72ddce13627df919cc9" + } + ], + "copyrightText": "" + }, + { + "fileName": "share/pljava/pljava-api-1.6.8.jar", + "SPDXID": "SPDXRef-File-3531f2e94fdf52e3-pljava", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "a2013f3202afc8dc15a21ef7a796730df08d1a00" + } + ], + "copyrightText": "" + }, + { + "fileName": "share/pljava/pljava-examples-1.6.8.jar", + "SPDXID": "SPDXRef-File-8f3dee04908d7a5a-pljava", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1c61bddc63ce4aaaa803d9c39f0740a15c19d300" + } + ], + "copyrightText": "" + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-geos" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-proj" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-gdal" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-json-c" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-orafce" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-pljava" + }, + { + "spdxElementId": "SPDXRef-pljava", + "relatedSpdxElement": "SPDXRef-Package-2dde48fd49927020", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-pljava", + "relatedSpdxElement": "SPDXRef-Package-551f76fc50fc5735", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-pljava", + "relatedSpdxElement": "SPDXRef-Package-f13571c8a05828cc", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-2dde48fd49927020", + "relatedSpdxElement": "SPDXRef-File-3531f2e94fdf52e3-pljava", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-551f76fc50fc5735", + "relatedSpdxElement": "SPDXRef-File-28d7473df5d8af04-pljava", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-f13571c8a05828cc", + "relatedSpdxElement": "SPDXRef-File-8f3dee04908d7a5a-pljava", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-unixodbc" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-psqlodbc" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-protobuf" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-protobuf-c" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-postgis" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-pgaudit" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-pgbackrest" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-wal2json" + }, + { + "spdxElementId": "SPDXRef-postgresql", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-nss-wrapper" + } + ] +} \ No newline at end of file diff --git a/syft/pkg/cataloger/bitnami/test-fixtures/json/opt/bitnami/postgresql/readme.txt b/syft/pkg/cataloger/bitnami/test-fixtures/json/opt/bitnami/postgresql/readme.txt new file mode 100644 index 000000000..82bc64557 --- /dev/null +++ b/syft/pkg/cataloger/bitnami/test-fixtures/json/opt/bitnami/postgresql/readme.txt @@ -0,0 +1 @@ +This file should be reported as part of the Bitnami PostgreSQL package. diff --git a/syft/pkg/cataloger/bitnami/test-fixtures/mix/opt/bitnami/render-template/.spdx-render-template.spdx b/syft/pkg/cataloger/bitnami/test-fixtures/mix/opt/bitnami/render-template/.spdx-render-template.spdx new file mode 100644 index 000000000..adccbaacd --- /dev/null +++ b/syft/pkg/cataloger/bitnami/test-fixtures/mix/opt/bitnami/render-template/.spdx-render-template.spdx @@ -0,0 +1,212 @@ +{ + "SPDXID": "SPDXRef-render-template", + "spdxVersion": "SPDX-2.3", + "creationInfo": { + "created": "2024-09-05T18:51:42.225Z", + "creators": [ + "Organization: VMware, Inc." + ] + }, + "name": "SPDX document for render-template 1.0.7", + "dataLicense": "CC0-1.0", + "documentDescribes": [ + "SPDXRef-render-template" + ], + "documentNamespace": "render-template-1.0.7", + "packages": [ + { + "SPDXID": "SPDXRef-render-template", + "name": "render-template", + "versionInfo": "1.0.7-4", + "downloadLocation": "https://github.com/bitnami/render-template/archive/refs/tags/v1.0.7.tar.gz", + "licenseConcluded": "Apache-2.0", + "licenseDeclared": "Apache-2.0", + "filesAnalyzed": false, + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:*:render-template:render-template:1.0.7:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:bitnami/render-template@1.0.7-4?arch=arm64&distro=debian-12" + } + ], + "copyrightText": "NOASSERTION" + }, + { + "name": "opt/bitnami/common/bin/render-template", + "SPDXID": "SPDXRef-Application-4b412cf3f25d2574-render-template", + "downloadLocation": "NONE", + "filesAnalyzed": false, + "primaryPackagePurpose": "APPLICATION", + "copyrightText": "NOASSERTION", + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION" + }, + { + "name": "github.com/aymerick/raymond", + "SPDXID": "SPDXRef-Package-c77f44f540ae92a0", + "versionInfo": "v2.0.2+incompatible", + "supplier": "NOASSERTION", + "downloadLocation": "NONE", + "filesAnalyzed": false, + "sourceInfo": "opt/bitnami/common/package found in: opt/bitnami/common/bin/render-template", + "licenseConcluded": "NONE", + "licenseDeclared": "NONE", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/aymerick/raymond@v2.0.2%2Bincompatible" + } + ], + "primaryPackagePurpose": "LIBRARY", + "copyrightText": "NOASSERTION" + }, + { + "name": "github.com/bitnami/render-template", + "SPDXID": "SPDXRef-Package-8213648cad51225d", + "supplier": "NOASSERTION", + "downloadLocation": "NONE", + "filesAnalyzed": false, + "sourceInfo": "opt/bitnami/common/package found in: opt/bitnami/common/bin/render-template", + "licenseConcluded": "NONE", + "licenseDeclared": "NONE", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/bitnami/render-template" + } + ], + "primaryPackagePurpose": "LIBRARY", + "copyrightText": "NOASSERTION" + }, + { + "name": "github.com/jessevdk/go-flags", + "SPDXID": "SPDXRef-Package-be6fde8a3edd7caf", + "versionInfo": "v1.6.1", + "supplier": "NOASSERTION", + "downloadLocation": "NONE", + "filesAnalyzed": false, + "sourceInfo": "opt/bitnami/common/package found in: opt/bitnami/common/bin/render-template", + "licenseConcluded": "NONE", + "licenseDeclared": "NONE", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/jessevdk/go-flags@v1.6.1" + } + ], + "primaryPackagePurpose": "LIBRARY", + "copyrightText": "NOASSERTION" + }, + { + "name": "github.com/mmikulicic/multierror", + "SPDXID": "SPDXRef-Package-b08d834237b92fed", + "versionInfo": "v0.0.0-20170428094957-c1ad6b5ecd26", + "supplier": "NOASSERTION", + "downloadLocation": "NONE", + "filesAnalyzed": false, + "sourceInfo": "opt/bitnami/common/package found in: opt/bitnami/common/bin/render-template", + "licenseConcluded": "NONE", + "licenseDeclared": "NONE", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/github.com/mmikulicic/multierror@v0.0.0-20170428094957-c1ad6b5ecd26" + } + ], + "primaryPackagePurpose": "LIBRARY", + "copyrightText": "NOASSERTION" + }, + { + "name": "golang.org/x/sys", + "SPDXID": "SPDXRef-Package-644a66965f04af7f", + "versionInfo": "v0.21.0", + "supplier": "NOASSERTION", + "downloadLocation": "NONE", + "filesAnalyzed": false, + "sourceInfo": "opt/bitnami/common/package found in: opt/bitnami/common/bin/render-template", + "licenseConcluded": "NONE", + "licenseDeclared": "NONE", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/golang.org/x/sys@v0.21.0" + } + ], + "primaryPackagePurpose": "LIBRARY", + "copyrightText": "NOASSERTION" + }, + { + "name": "stdlib", + "SPDXID": "SPDXRef-Package-7d9b78ebb84f1578", + "versionInfo": "1.22.7", + "supplier": "NOASSERTION", + "downloadLocation": "NONE", + "filesAnalyzed": false, + "sourceInfo": "opt/bitnami/common/package found in: opt/bitnami/common/bin/render-template", + "licenseConcluded": "NONE", + "licenseDeclared": "NONE", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:golang/stdlib@1.22.7" + } + ], + "primaryPackagePurpose": "LIBRARY", + "copyrightText": "NOASSERTION" + } + ], + "files": [], + "relationships": [ + { + "spdxElementId": "SPDXRef-render-template", + "relationshipType": "CONTAINS", + "relatedSpdxElement": "SPDXRef-Application-4b412cf3f25d2574-render-template" + }, + { + "spdxElementId": "SPDXRef-Application-4b412cf3f25d2574-render-template", + "relatedSpdxElement": "SPDXRef-Package-8213648cad51225d", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-render-template", + "relatedSpdxElement": "SPDXRef-Application-4b412cf3f25d2574-render-template", + "relationshipType": "CONTAINS" + }, + { + "spdxElementId": "SPDXRef-Package-8213648cad51225d", + "relatedSpdxElement": "SPDXRef-Package-644a66965f04af7f", + "relationshipType": "DEPENDS_ON" + }, + { + "spdxElementId": "SPDXRef-Package-8213648cad51225d", + "relatedSpdxElement": "SPDXRef-Package-7d9b78ebb84f1578", + "relationshipType": "DEPENDS_ON" + }, + { + "spdxElementId": "SPDXRef-Package-8213648cad51225d", + "relatedSpdxElement": "SPDXRef-Package-b08d834237b92fed", + "relationshipType": "DEPENDS_ON" + }, + { + "spdxElementId": "SPDXRef-Package-8213648cad51225d", + "relatedSpdxElement": "SPDXRef-Package-be6fde8a3edd7caf", + "relationshipType": "DEPENDS_ON" + }, + { + "spdxElementId": "SPDXRef-Package-8213648cad51225d", + "relatedSpdxElement": "SPDXRef-Package-c77f44f540ae92a0", + "relationshipType": "DEPENDS_ON" + } + ] +} diff --git a/syft/pkg/cataloger/bitnami/test-fixtures/tag-value/opt/bitnami/redis/.spdx-apache.spdx b/syft/pkg/cataloger/bitnami/test-fixtures/tag-value/opt/bitnami/redis/.spdx-apache.spdx new file mode 100644 index 000000000..8fc58a44f --- /dev/null +++ b/syft/pkg/cataloger/bitnami/test-fixtures/tag-value/opt/bitnami/redis/.spdx-apache.spdx @@ -0,0 +1,23 @@ +SPDXVersion: SPDX-2.3 +DataLicense: CC0-1.0 +DocumentNamespace: redis-7.4.1 +DocumentName: SPDX document for Redis(R) 7.4.1 +SPDXID: SPDXRef-DOCUMENT + +## Creation Information +Creator: Organization: VMware, Inc. +Created: 2024-10-02T20:17:11.692Z +## Relationships +Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-redis + +## Package Information +PackageName: redis +SPDXID: SPDXRef-redis +PackageVersion: 7.4.1-0 +PackageDownloadLocation: http://download.redis.io/releases/redis-7.4.1.tar.gz +PackageLicenseConcluded: RSALv2 +PackageLicenseDeclared: RSALv2 +PackageCopyrightText: NOASSERTION +ExternalRef: SECURITY cpe23Type cpe:2.3:*:redis:redis:7.4.1:*:*:*:*:*:*:* +ExternalRef: PACKAGE-MANAGER purl pkg:bitnami/redis@7.4.1-0?arch=arm64&distro=debian-12 +FilesAnalyzed: false diff --git a/syft/pkg/type.go b/syft/pkg/type.go index 27b6c773e..749623bdb 100644 --- a/syft/pkg/type.go +++ b/syft/pkg/type.go @@ -13,6 +13,7 @@ const ( AlpmPkg Type = "alpm" ApkPkg Type = "apk" BinaryPkg Type = "binary" + BitnamiPkg Type = "bitnami" CocoapodsPkg Type = "pod" ConanPkg Type = "conan" DartPubPkg Type = "dart-pub" @@ -53,6 +54,7 @@ var AllPkgs = []Type{ AlpmPkg, ApkPkg, BinaryPkg, + BitnamiPkg, CocoapodsPkg, ConanPkg, DartPubPkg, @@ -62,6 +64,7 @@ var AllPkgs = []Type{ GemPkg, GithubActionPkg, GithubActionWorkflowPkg, + GraalVMNativeImagePkg, GoModulePkg, HackagePkg, HexPkg, @@ -96,6 +99,8 @@ func (t Type) PackageURLType() string { return "alpm" case ApkPkg: return packageurl.TypeAlpine + case BitnamiPkg: + return packageurl.TypeBitnami case CocoapodsPkg: return packageurl.TypeCocoapods case ConanPkg: @@ -177,28 +182,20 @@ func TypeFromPURL(p string) Type { //nolint:funlen,gocyclo func TypeByName(name string) Type { switch name { - case packageurl.TypeDebian: - return DebPkg - case packageurl.TypeRPM: - return RpmPkg - case packageurl.TypeLuaRocks: - return LuaRocksPkg case "alpm": return AlpmPkg case packageurl.TypeAlpine, "alpine": return ApkPkg - case packageurl.TypeMaven: - return JavaPkg + case packageurl.TypeBitnami: + return BitnamiPkg + case packageurl.TypeDebian: + return DebPkg case packageurl.TypeComposer: return PhpComposerPkg case "pecl": return PhpPeclPkg case packageurl.TypeGolang: return GoModulePkg - case packageurl.TypeNPM: - return NpmPkg - case packageurl.TypePyPi: - return PythonPkg case packageurl.TypeGem: return GemPkg case "cargo", "crate": @@ -213,10 +210,18 @@ func TypeByName(name string) Type { return ConanPkg case packageurl.TypeHackage: return HackagePkg - case "portage": - return PortagePkg case packageurl.TypeHex: return HexPkg + case packageurl.TypeLuaRocks: + return LuaRocksPkg + case packageurl.TypeMaven: + return JavaPkg + case packageurl.TypeNPM: + return NpmPkg + case packageurl.TypePyPi: + return PythonPkg + case "portage": + return PortagePkg case packageurl.TypeOTP: return ErlangOTPPkg case "linux-kernel": @@ -229,6 +234,8 @@ func TypeByName(name string) Type { return OpamPkg case packageurl.TypeCran: return Rpkg + case packageurl.TypeRPM: + return RpmPkg case packageurl.TypeSwift: return SwiftPkg case "swiplpack": diff --git a/syft/pkg/type_test.go b/syft/pkg/type_test.go index 69370814c..1f0534868 100644 --- a/syft/pkg/type_test.go +++ b/syft/pkg/type_test.go @@ -22,6 +22,10 @@ func TestTypeFromPURL(t *testing.T) { purl: "pkg:apk/alpine/util-linux@2.32.1", expected: ApkPkg, }, + { + purl: "pkg:bitnami/apache@2.4.62-3?arch=arm64&distro=debian-12", + expected: BitnamiPkg, + }, { purl: "pkg:deb/debian/curl@7.50.3-1?arch=i386&distro=jessie", expected: DebPkg, @@ -50,7 +54,6 @@ func TestTypeFromPURL(t *testing.T) { purl: "pkg:pub/util@1.2.34?hosted_url=pub.hosted.org", expected: DartPubPkg, }, - { purl: "pkg:dotnet/Microsoft.CodeAnalysis.Razor@2.2.0", expected: DotnetPkg, @@ -137,6 +140,7 @@ func TestTypeFromPURL(t *testing.T) { expectedTypes.Remove(string(GithubActionPkg), string(GithubActionWorkflowPkg)) expectedTypes.Remove(string(WordpressPluginPkg)) expectedTypes.Remove(string(TerraformPkg)) + expectedTypes.Remove(string(GraalVMNativeImagePkg)) for _, test := range tests { t.Run(string(test.expected), func(t *testing.T) {