From 9c2799e379681eb94f904c020fe5734019032a85 Mon Sep 17 00:00:00 2001 From: Laurent Goderre Date: Tue, 10 Sep 2024 10:35:18 -0400 Subject: [PATCH] Add the Ocaml ecosystem (#3112) Signed-off-by: Laurent Goderre --- .../catalog_packages_cases_test.go | 8 + .../test/integration/catalog_packages_test.go | 2 + .../pkgs/opam/ocaml-base-compiler.4.14.0/opam | 93 + internal/constants.go | 2 +- internal/task/package_tasks.go | 2 + schema/json/schema-16.0.16.json | 2629 +++++++++++++++++ schema/json/schema-latest.json | 49 +- .../common/spdxhelpers/to_format_model.go | 8 + .../spdxhelpers/to_format_model_test.go | 25 + .../spdxutil/helpers/download_location.go | 2 + .../helpers/originator_supplier_test.go | 9 + .../internal/spdxutil/helpers/source_info.go | 2 + .../spdxutil/helpers/source_info_test.go | 8 + syft/internal/packagemetadata/generated.go | 1 + syft/internal/packagemetadata/names.go | 1 + syft/pkg/cataloger/ocaml/cataloger.go | 15 + syft/pkg/cataloger/ocaml/cataloger_test.go | 33 + syft/pkg/cataloger/ocaml/package.go | 37 + syft/pkg/cataloger/ocaml/package_test.go | 34 + syft/pkg/cataloger/ocaml/parse_opam.go | 150 + syft/pkg/cataloger/ocaml/parse_opam_test.go | 166 ++ .../ocaml/test-fixtures/alcotest.opam | 51 + .../glob-paths/opam/alcotest.opam | 1 + .../opam/ocaml-base-compiler.4.14.0/opam | 1 + .../ocaml-base-compiler.4.14.0/opam | 93 + syft/pkg/language.go | 4 + syft/pkg/language_test.go | 8 + syft/pkg/ocaml.go | 11 + syft/pkg/type.go | 8 +- syft/pkg/type_test.go | 4 + 30 files changed, 3454 insertions(+), 3 deletions(-) create mode 100644 cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/opam/ocaml-base-compiler.4.14.0/opam create mode 100644 schema/json/schema-16.0.16.json create mode 100644 syft/pkg/cataloger/ocaml/cataloger.go create mode 100644 syft/pkg/cataloger/ocaml/cataloger_test.go create mode 100644 syft/pkg/cataloger/ocaml/package.go create mode 100644 syft/pkg/cataloger/ocaml/package_test.go create mode 100644 syft/pkg/cataloger/ocaml/parse_opam.go create mode 100644 syft/pkg/cataloger/ocaml/parse_opam_test.go create mode 100644 syft/pkg/cataloger/ocaml/test-fixtures/alcotest.opam create mode 100644 syft/pkg/cataloger/ocaml/test-fixtures/glob-paths/opam/alcotest.opam create mode 100644 syft/pkg/cataloger/ocaml/test-fixtures/glob-paths/opam/ocaml-base-compiler.4.14.0/opam create mode 100644 syft/pkg/cataloger/ocaml/test-fixtures/ocaml-base-compiler.4.14.0/opam create mode 100644 syft/pkg/ocaml.go diff --git a/cmd/syft/internal/test/integration/catalog_packages_cases_test.go b/cmd/syft/internal/test/integration/catalog_packages_cases_test.go index 55822043e..a8ea39d09 100644 --- a/cmd/syft/internal/test/integration/catalog_packages_cases_test.go +++ b/cmd/syft/internal/test/integration/catalog_packages_cases_test.go @@ -410,6 +410,14 @@ var dirOnlyTestCases = []testCase{ "octo-org/this-repo/.github/workflows/workflow-1.yml": "172239021f7ba04fe7327647b213799853a9eb89", }, }, + { + name: "find opam package", + pkgType: pkg.OpamPkg, + pkgLanguage: pkg.OCaml, + pkgInfo: map[string]string{ + "ocaml-base-compiler": "4.14.0", + }, + }, } var commonTestCases = []testCase{ diff --git a/cmd/syft/internal/test/integration/catalog_packages_test.go b/cmd/syft/internal/test/integration/catalog_packages_test.go index e84d57839..371ddb4e5 100644 --- a/cmd/syft/internal/test/integration/catalog_packages_test.go +++ b/cmd/syft/internal/test/integration/catalog_packages_test.go @@ -52,6 +52,7 @@ func TestPkgCoverageImage(t *testing.T) { definedLanguages.Remove(pkg.Dart.String()) definedLanguages.Remove(pkg.Swift.String()) definedLanguages.Remove(pkg.Swipl.String()) + definedLanguages.Remove(pkg.OCaml.String()) definedLanguages.Remove(pkg.CPP.String()) definedLanguages.Remove(pkg.Haskell.String()) definedLanguages.Remove(pkg.Elixir.String()) @@ -78,6 +79,7 @@ func TestPkgCoverageImage(t *testing.T) { definedPkgs.Remove(string(pkg.LinuxKernelModulePkg)) definedPkgs.Remove(string(pkg.SwiftPkg)) definedPkgs.Remove(string(pkg.SwiplPackPkg)) + definedPkgs.Remove(string(pkg.OpamPkg)) definedPkgs.Remove(string(pkg.GithubActionPkg)) definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg)) diff --git a/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/opam/ocaml-base-compiler.4.14.0/opam b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/opam/ocaml-base-compiler.4.14.0/opam new file mode 100644 index 000000000..15e874ec8 --- /dev/null +++ b/cmd/syft/internal/test/integration/test-fixtures/image-pkg-coverage/pkgs/opam/ocaml-base-compiler.4.14.0/opam @@ -0,0 +1,93 @@ +opam-version: "2.0" +synopsis: "Official release 4.14.0" +maintainer: [ + "David Allsopp " + "Florian Angeletti " +] +authors: "Xavier Leroy and many contributors" +license: "LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception" +homepage: "https://ocaml.org" +bug-reports: "https://github.com/ocaml/opam-repository/issues" +depends: [ + "ocaml" {= "4.14.0" & post} + "base-unix" {post} + "base-bigarray" {post} + "base-threads" {post} + "host-arch-arm32" {arch = "arm32" & post} + "host-arch-arm64" {arch = "arm64" & post} + "host-arch-ppc64" {arch = "ppc64" & post} + "host-arch-riscv64" {arch = "riscv64" & post} + "host-arch-s390x" {arch = "s390x" & post} + "host-arch-x86_32" {os != "win32" & arch = "x86_32" & post} + "host-arch-x86_64" {os != "win32" & arch = "x86_64" & post} + "host-arch-unknown" + {os != "win32" & arch != "arm32" & arch != "arm64" & arch != "ppc64" & + arch != "riscv64" & + arch != "s390x" & + arch != "x86_32" & + arch != "x86_64" & + post} + (("arch-x86_64" {os = "win32" & arch = "x86_64"} & + (("system-mingw" & "mingw-w64-shims" {os-distribution = "cygwin" & post}) | + "system-msvc")) | + ("arch-x86_32" {os = "win32"} & + (("system-mingw" & "mingw-w64-shims" {os-distribution = "cygwin" & post}) | + "system-msvc")) | + "host-system-other" {os != "win32" & post}) + "ocaml-options-vanilla" {post} + "flexdll" {>= "0.36" & os = "win32"} +] +conflict-class: "ocaml-core-compiler" +flags: compiler +setenv: CAML_LD_LIBRARY_PATH = "%{lib}%/stublibs" +build: [ + [ + "./configure" + "--host=x86_64-pc-windows" + {system-msvc:installed & arch-x86_64:installed} + "--host=x86_64-w64-mingw32" + {os-distribution = "cygwin" & system-mingw:installed & + arch-x86_64:installed} + "--host=i686-pc-windows" {system-msvc:installed & arch-x86_32:installed} + "--host=i686-w64-mingw32" + {os-distribution = "cygwin" & system-mingw:installed & + arch-x86_32:installed} + "--prefix=%{prefix}%" + "--docdir=%{doc}%/ocaml" + "--with-flexdll=%{flexdll:share}%" {os = "win32" & flexdll:installed} + "-C" + "CC=cc" {os = "openbsd" | os = "macos"} + "ASPP=cc -c" {os = "openbsd" | os = "macos"} + ] + [make "-j%{jobs}%"] +] +install: [make "install"] +build-env: MSYS2_ARG_CONV_EXCL = "*" +post-messages: [ + """\ +A failure in the middle of the build may be caused by build parallelism + (enabled by default). + Please file a bug report at https://github.com/ocaml/opam-repository/issues""" + {failure & jobs > "1"} + """\ +You can try installing again including --jobs=1 + to force a sequential build instead.""" + {failure & jobs > "1" & opam-version >= "2.0.5"} +] +dev-repo: "git+https://github.com/ocaml/ocaml#4.14" +url { + src: "https://github.com/ocaml/ocaml/archive/4.14.0.tar.gz" + checksum: + "sha256=39f44260382f28d1054c5f9d8bf4753cb7ad64027da792f7938344544da155e8" +} +extra-source "ocaml-base-compiler.install" { + src: + "https://raw.githubusercontent.com/ocaml/opam-source-archives/main/patches/ocaml-base-compiler/ocaml-base-compiler.install" + checksum: [ + "sha256=79f2a1a5044a91350a0eb6ce12e261a72a2855c094c425cddf3860e58c486678" + "md5=3e969b841df1f51ca448e6e6295cb451" + ] +} +x-env-path-rewrite: [ + [CAML_LD_LIBRARY_PATH (";" {os = "win32"} ":" {os != "win32"}) "target"] +] diff --git a/internal/constants.go b/internal/constants.go index 6d1dd1974..93b0093ab 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.15" + JSONSchemaVersion = "16.0.16" ) diff --git a/internal/task/package_tasks.go b/internal/task/package_tasks.go index 88ce35345..4d56dc88a 100644 --- a/internal/task/package_tasks.go +++ b/internal/task/package_tasks.go @@ -21,6 +21,7 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/kernel" "github.com/anchore/syft/syft/pkg/cataloger/lua" "github.com/anchore/syft/syft/pkg/cataloger/nix" + "github.com/anchore/syft/syft/pkg/cataloger/ocaml" "github.com/anchore/syft/syft/pkg/cataloger/php" "github.com/anchore/syft/syft/pkg/cataloger/python" "github.com/anchore/syft/syft/pkg/cataloger/r" @@ -95,6 +96,7 @@ func DefaultPackageTaskFactories() PackageTaskFactories { newSimplePackageTaskFactory(swift.NewCocoapodsCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "swift", "cocoapods"), newSimplePackageTaskFactory(swift.NewSwiftPackageManagerCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "swift", "spm"), newSimplePackageTaskFactory(swipl.NewSwiplPackCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "swipl", "pack"), + newSimplePackageTaskFactory(ocaml.NewOpamPackageManagerCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "ocaml", "opam"), // language-specific package for both image and directory scans (but not necessarily declared) //////////////////////////////////////// newSimplePackageTaskFactory(dotnet.NewDotnetPortableExecutableCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "dotnet", "c#", "binary"), diff --git a/schema/json/schema-16.0.16.json b/schema/json/schema-16.0.16.json new file mode 100644 index 000000000..a88197859 --- /dev/null +++ b/schema/json/schema-16.0.16.json @@ -0,0 +1,2629 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/16.0.16/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" + ] + }, + "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" + ] + }, + "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" + } + }, + "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" + ] + }, + "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" + ] + }, + "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" + } + }, + "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/CConanFileEntry" + }, + { + "$ref": "#/$defs/CConanInfoEntry" + }, + { + "$ref": "#/$defs/CConanLockEntry" + }, + { + "$ref": "#/$defs/CConanLockV2Entry" + }, + { + "$ref": "#/$defs/CocoaPodfileLockEntry" + }, + { + "$ref": "#/$defs/DartPubspecLockEntry" + }, + { + "$ref": "#/$defs/DotnetDepsEntry" + }, + { + "$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/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/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" + ] + }, + "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 1bab78aa6..a88197859 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.15/document", + "$id": "anchore.io/schema/syft/json/16.0.16/document", "$ref": "#/$defs/Document", "$defs": { "AlpmDbEntry": { @@ -1436,6 +1436,50 @@ "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": { @@ -1563,6 +1607,9 @@ { "$ref": "#/$defs/NixStoreEntry" }, + { + "$ref": "#/$defs/OpamPackage" + }, { "$ref": "#/$defs/PhpComposerInstalledEntry" }, diff --git a/syft/format/common/spdxhelpers/to_format_model.go b/syft/format/common/spdxhelpers/to_format_model.go index 3fd136e84..3f93883cf 100644 --- a/syft/format/common/spdxhelpers/to_format_model.go +++ b/syft/format/common/spdxhelpers/to_format_model.go @@ -505,6 +505,14 @@ func toPackageChecksums(p pkg.Package) ([]spdx.Checksum, bool) { Algorithm: spdx.ChecksumAlgorithm(algo), Value: hexStr, }) + case pkg.OpamPackage: + for _, checksum := range meta.Checksums { + parts := strings.Split(checksum, "=") + checksums = append(checksums, spdx.Checksum{ + Algorithm: spdx.ChecksumAlgorithm(strings.ToUpper(parts[0])), + Value: parts[1], + }) + } } return checksums, filesAnalyzed } diff --git a/syft/format/common/spdxhelpers/to_format_model_test.go b/syft/format/common/spdxhelpers/to_format_model_test.go index e986069bb..934fc95dd 100644 --- a/syft/format/common/spdxhelpers/to_format_model_test.go +++ b/syft/format/common/spdxhelpers/to_format_model_test.go @@ -335,6 +335,31 @@ func Test_toPackageChecksums(t *testing.T) { }, filesAnalyzed: false, }, + { + name: "Opam Package", + pkg: pkg.Package{ + Name: "test", + Version: "1.0.0", + Language: pkg.Go, + Metadata: pkg.OpamPackage{ + Checksums: []string{ + "sha256=f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c", + "sha512=05a359dc8400d4ca200ff255dbd030acd33d2c4acb5020838f772c02cdb5f243f3dbafbc43a8cd51e6b5923a140f84c9e7ea25b2c0fa277bb68b996190d36e3b", + }, + }, + }, + expected: []spdx.Checksum{ + { + Algorithm: "SHA256", + Value: "f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c", + }, + { + Algorithm: "SHA512", + Value: "05a359dc8400d4ca200ff255dbd030acd33d2c4acb5020838f772c02cdb5f243f3dbafbc43a8cd51e6b5923a140f84c9e7ea25b2c0fa277bb68b996190d36e3b", + }, + }, + filesAnalyzed: false, + }, { name: "Package with no metadata type", pkg: pkg.Package{ diff --git a/syft/format/internal/spdxutil/helpers/download_location.go b/syft/format/internal/spdxutil/helpers/download_location.go index 3ed1c9ed6..b2bae8f96 100644 --- a/syft/format/internal/spdxutil/helpers/download_location.go +++ b/syft/format/internal/spdxutil/helpers/download_location.go @@ -26,6 +26,8 @@ func DownloadLocation(p pkg.Package) string { return NoneIfEmpty(metadata.Dist.URL) case pkg.PhpComposerInstalledEntry: return NoneIfEmpty(metadata.Dist.URL) + case pkg.OpamPackage: + return NoneIfEmpty(metadata.URL) } } return NOASSERTION diff --git a/syft/format/internal/spdxutil/helpers/originator_supplier_test.go b/syft/format/internal/spdxutil/helpers/originator_supplier_test.go index 51965925d..67abf1dc5 100644 --- a/syft/format/internal/spdxutil/helpers/originator_supplier_test.go +++ b/syft/format/internal/spdxutil/helpers/originator_supplier_test.go @@ -41,6 +41,7 @@ func Test_OriginatorSupplier(t *testing.T) { pkg.RustCargoLockEntry{}, pkg.SwiftPackageManagerResolvedEntry{}, pkg.SwiplPackEntry{}, + pkg.OpamPackage{}, pkg.YarnLockEntry{}, ) tests := []struct { @@ -350,6 +351,14 @@ func Test_OriginatorSupplier(t *testing.T) { originator: "Person: auth (auth@auth.gov)", supplier: "Person: me (me@auth.com)", }, + { + name: "from ocaml opam", + input: pkg.Package{ + Metadata: pkg.OpamPackage{}, + }, + originator: "", + supplier: "", + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/syft/format/internal/spdxutil/helpers/source_info.go b/syft/format/internal/spdxutil/helpers/source_info.go index 770577112..bf8b27218 100644 --- a/syft/format/internal/spdxutil/helpers/source_info.go +++ b/syft/format/internal/spdxutil/helpers/source_info.go @@ -64,6 +64,8 @@ func SourceInfo(p pkg.Package) string { answer = "acquired package info from resolved Swift package manifest" case pkg.SwiplPackPkg: answer = "acquired package info from SWI Prolo pack package file" + case pkg.OpamPkg: + answer = "acquired package info from OCaml opam package file" case pkg.GithubActionPkg, pkg.GithubActionWorkflowPkg: answer = "acquired package info from GitHub Actions workflow file or composite action file" case pkg.WordpressPluginPkg: diff --git a/syft/format/internal/spdxutil/helpers/source_info_test.go b/syft/format/internal/spdxutil/helpers/source_info_test.go index df76ac7eb..d7da0eab1 100644 --- a/syft/format/internal/spdxutil/helpers/source_info_test.go +++ b/syft/format/internal/spdxutil/helpers/source_info_test.go @@ -271,6 +271,14 @@ func Test_SourceInfo(t *testing.T) { "acquired package info from SWI Prolo pack package file", }, }, + { + input: pkg.Package{ + Type: pkg.OpamPkg, + }, + expected: []string{ + "acquired package info from OCaml opam package file", + }, + }, { input: pkg.Package{ Type: pkg.GithubActionPkg, diff --git a/syft/internal/packagemetadata/generated.go b/syft/internal/packagemetadata/generated.go index db843261c..24b961753 100644 --- a/syft/internal/packagemetadata/generated.go +++ b/syft/internal/packagemetadata/generated.go @@ -34,6 +34,7 @@ func AllTypes() []any { pkg.NixStoreEntry{}, pkg.NpmPackage{}, pkg.NpmPackageLockEntry{}, + pkg.OpamPackage{}, pkg.PhpComposerInstalledEntry{}, pkg.PhpComposerLockEntry{}, pkg.PhpPeclEntry{}, diff --git a/syft/internal/packagemetadata/names.go b/syft/internal/packagemetadata/names.go index 52deebce0..8f8390c29 100644 --- a/syft/internal/packagemetadata/names.go +++ b/syft/internal/packagemetadata/names.go @@ -103,6 +103,7 @@ var jsonTypes = makeJSONTypes( jsonNamesWithoutLookup(pkg.RpmArchive{}, "rpm-archive", "RpmMetadata"), // the legacy value is split into two types, where the other is preferred jsonNames(pkg.SwiftPackageManagerResolvedEntry{}, "swift-package-manager-lock-entry", "SwiftPackageManagerMetadata"), jsonNames(pkg.SwiplPackEntry{}, "swiplpack-package"), + jsonNames(pkg.OpamPackage{}, "opam-package"), jsonNames(pkg.RustCargoLockEntry{}, "rust-cargo-lock-entry", "RustCargoPackageMetadata"), jsonNamesWithoutLookup(pkg.RustBinaryAuditEntry{}, "rust-cargo-audit-entry", "RustCargoPackageMetadata"), // the legacy value is split into two types, where the other is preferred jsonNames(pkg.WordpressPluginEntry{}, "wordpress-plugin-entry", "WordpressMetadata"), diff --git a/syft/pkg/cataloger/ocaml/cataloger.go b/syft/pkg/cataloger/ocaml/cataloger.go new file mode 100644 index 000000000..c41ef477b --- /dev/null +++ b/syft/pkg/cataloger/ocaml/cataloger.go @@ -0,0 +1,15 @@ +/* +Package ocaml provides a concrete Cataloger implementation for packages relating to the OCaml language ecosystem. +*/ +package ocaml + +import ( + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" +) + +// NewOpamPackageManagerCataloger returns a new cataloger object for OCaml opam. +func NewOpamPackageManagerCataloger() pkg.Cataloger { + return generic.NewCataloger("opam-cataloger"). + WithParserByGlobs(parseOpamPackage, "*opam") +} diff --git a/syft/pkg/cataloger/ocaml/cataloger_test.go b/syft/pkg/cataloger/ocaml/cataloger_test.go new file mode 100644 index 000000000..b17f13c78 --- /dev/null +++ b/syft/pkg/cataloger/ocaml/cataloger_test.go @@ -0,0 +1,33 @@ +package ocaml + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func Test_PackageCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + expected []string + }{ + { + name: "obtain package files", + fixture: "test-fixtures/glob-paths", + expected: []string{ + "opam/alcotest.opam", + "opam/ocaml-base-compiler.4.14.0/opam", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsResolverContentQueries(test.expected). + TestCataloger(t, NewOpamPackageManagerCataloger()) + }) + } +} diff --git a/syft/pkg/cataloger/ocaml/package.go b/syft/pkg/cataloger/ocaml/package.go new file mode 100644 index 000000000..5144add41 --- /dev/null +++ b/syft/pkg/cataloger/ocaml/package.go @@ -0,0 +1,37 @@ +package ocaml + +import ( + "github.com/anchore/packageurl-go" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" +) + +func newOpamPackage(m pkg.OpamPackage, fileLocation file.Location) pkg.Package { + p := pkg.Package{ + Name: m.Name, + Version: m.Version, + Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(fileLocation, m.Licenses...)...), + PURL: opamPackageURL(m.Name, m.Version), + Locations: file.NewLocationSet(fileLocation), + Type: pkg.OpamPkg, + Language: pkg.OCaml, + Metadata: m, + } + + p.SetID() + + return p +} + +func opamPackageURL(name, version string) string { + var qualifiers packageurl.Qualifiers + + return packageurl.NewPackageURL( + "opam", + "", + name, + version, + qualifiers, + "", + ).ToString() +} diff --git a/syft/pkg/cataloger/ocaml/package_test.go b/syft/pkg/cataloger/ocaml/package_test.go new file mode 100644 index 000000000..551c04524 --- /dev/null +++ b/syft/pkg/cataloger/ocaml/package_test.go @@ -0,0 +1,34 @@ +package ocaml + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_packageURL(t *testing.T) { + + type args struct { + name string + version string + } + tests := []struct { + name string + args args + want string + }{ + { + name: "go case", + args: args{ + name: "ocaml-base-compiler", + version: "5.2.0", + }, + want: "pkg:opam/ocaml-base-compiler@5.2.0", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, opamPackageURL(tt.args.name, tt.args.version)) + }) + } +} diff --git a/syft/pkg/cataloger/ocaml/parse_opam.go b/syft/pkg/cataloger/ocaml/parse_opam.go new file mode 100644 index 000000000..6e56db757 --- /dev/null +++ b/syft/pkg/cataloger/ocaml/parse_opam.go @@ -0,0 +1,150 @@ +package ocaml + +import ( + "context" + "encoding/json" + "io" + "path" + "regexp" + "strings" + + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" +) + +//nolint:funlen +func parseOpamPackage(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + var pkgs []pkg.Package + + opamVersionRe := regexp.MustCompile(`(?m)opam-version:\s*"[0-9]+\.[0-9]+"`) + versionRe := regexp.MustCompile(`(?m)^version:\s*"(?P[^"]*)"`) + licenseRe := regexp.MustCompile(`(?m)^license:\s*(?P(?:"[^"]*")|(?:\[[^\]]*\]))`) + homepageRe := regexp.MustCompile(`(?m)homepage:\s*"(?P[^"]+)"`) + urlRe := regexp.MustCompile(`(?m)url\s*{(?P[^}]+)}`) + + data, err := io.ReadAll(reader) + if err != nil { + log.WithFields("error", err).Trace("unable to read opam package") + return nil, nil, nil + } + + if opamVersionRe.FindSubmatch(data) == nil { + log.WithFields("warning", err).Trace("opam version not found") + return nil, nil, nil + } + + // If name is inferred from file name/path + var name, version string + var licenses []string + loc := reader.Location.LocationData.AccessPath + dir, file := path.Split(loc) + + if file == "opam" { + // folder name is the package name and version + s := strings.SplitN(path.Base(dir), ".", 2) + name = s[0] + + if len(s) > 1 { + version = s[1] + } + } else { + // filename is the package name and version is in the content + name = strings.Replace(file, ".opam", "", 1) + + m := versionRe.FindSubmatch(data) + + if m != nil { + version = string(m[1]) + } + } + + entry := pkg.OpamPackage{ + Name: name, + Version: version, + } + + licenseMatch := licenseRe.FindSubmatch(data) + if licenseMatch != nil { + licenses = parseLicenses(string(licenseMatch[1])) + + entry.Licenses = licenses + } + + urlMatch := urlRe.FindSubmatch(data) + if urlMatch != nil { + url, checksums := parseURL(urlMatch[1]) + + if url != "" { + entry.URL = url + } + + if checksums != nil { + entry.Checksums = checksums + } + } + + homepageMatch := homepageRe.FindSubmatch(data) + if homepageMatch != nil { + entry.Homepage = string(homepageMatch[1]) + } + + pkgs = append( + pkgs, + newOpamPackage( + entry, + reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ), + ) + + return pkgs, nil, nil +} + +func parseLicenses(licensesStr string) []string { + licenses := []string{} + + if licensesStr[:1] == `"` { + content := licensesStr[1 : len(licensesStr)-1] + licenses = append(licenses, content) + } else { + var d []string + err := json.Unmarshal([]byte(licensesStr), &d) + + if err == nil { + licenses = append(licenses, d...) + } + } + + return licenses +} + +func parseURL(data []byte) (string, []string) { + urlRe := regexp.MustCompile(`(?m)src:\s*"(?P.*)"`) + checksumsRe := regexp.MustCompile(`(?m)checksum:\s*("[^"]*"|\[\s*((?:"[^"]*"\s*)+)\])`) + + urlMatch := urlRe.FindSubmatch(data) + if urlMatch == nil { + return "", nil + } + + url := urlMatch[1] + + var checksum []string + checksumMatch := checksumsRe.FindSubmatch(data) + if checksumMatch != nil { + var fields []string + if checksumMatch[2] != nil { + fields = strings.Fields(string(checksumMatch[2])) + } else { + fields = strings.Fields(string(checksumMatch[1])) + } + + for _, f := range fields { + checksum = append(checksum, strings.ReplaceAll(f, `"`, "")) + } + } + + return string(url), checksum +} diff --git a/syft/pkg/cataloger/ocaml/parse_opam_test.go b/syft/pkg/cataloger/ocaml/parse_opam_test.go new file mode 100644 index 000000000..f25563be4 --- /dev/null +++ b/syft/pkg/cataloger/ocaml/parse_opam_test.go @@ -0,0 +1,166 @@ +package ocaml + +import ( + "testing" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" + "github.com/stretchr/testify/assert" +) + +func TestParseOpamPackage(t *testing.T) { + fixture1 := "test-fixtures/ocaml-base-compiler.4.14.0/opam" + location1 := file.NewLocation(fixture1) + + fixture2 := "test-fixtures/alcotest.opam" + location2 := file.NewLocation(fixture2) + + tests := []struct { + fixture string + want []pkg.Package + }{ + { + fixture: fixture1, + want: []pkg.Package{ + { + Name: "ocaml-base-compiler", + Version: "4.14.0", + PURL: "pkg:opam/ocaml-base-compiler@4.14.0", + Locations: file.NewLocationSet(location1), + Licenses: pkg.NewLicenseSet( + pkg.NewLicensesFromLocation( + location1, + "LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception", + )..., + ), + Language: pkg.OCaml, + Type: pkg.OpamPkg, + Metadata: pkg.OpamPackage{ + Name: "ocaml-base-compiler", + Version: "4.14.0", + Licenses: []string{"LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception"}, + URL: "https://github.com/ocaml/ocaml/archive/4.14.0.tar.gz", + Checksums: []string{ + "sha256=39f44260382f28d1054c5f9d8bf4753cb7ad64027da792f7938344544da155e8", + }, + Homepage: "https://ocaml.org", + }, + }, + }, + }, + { + fixture: fixture2, + want: []pkg.Package{ + { + Name: "alcotest", + Version: "1.5.0", + PURL: "pkg:opam/alcotest@1.5.0", + Locations: file.NewLocationSet(location2), + Licenses: pkg.NewLicenseSet( + pkg.NewLicensesFromLocation( + location2, + "ISC", + )..., + ), + Language: pkg.OCaml, + Type: pkg.OpamPkg, + Metadata: pkg.OpamPackage{ + Name: "alcotest", + Version: "1.5.0", + Licenses: []string{"ISC"}, + Homepage: "https://github.com/mirage/alcotest", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.fixture, func(t *testing.T) { + // TODO: no relationships are under test yet + var expectedRelationships []artifact.Relationship + + pkgtest.TestFileParser(t, tt.fixture, parseOpamPackage, tt.want, expectedRelationships) + }) + } +} + +func TestParseLicense(t *testing.T) { + tests := []struct { + name string + input string + want []string + }{ + { + name: "single license", + input: `"MIT"`, + want: []string{ + "MIT", + }, + }, + { + name: "multiple license", + input: `[ + "MIT", "IST" + ]`, + want: []string{ + "MIT", + "IST", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, parseLicenses(tt.input)) + }) + } +} + +func TestParseUrl(t *testing.T) { + tests := []struct { + name string + input string + wantUrl string + wantChecksums []string + }{ + { + name: "single checksums", + input: ` +src: + "https://github.com/mirage/mirage-clock/releases/download/v4.2.0/mirage-clock-4.2.0.tbz" + checksum: + "sha256=fa17d15d5be23c79ba741f5f7cb88ed7112de16a4410cea81c71b98086889847" + `, + wantUrl: "https://github.com/mirage/mirage-clock/releases/download/v4.2.0/mirage-clock-4.2.0.tbz", + wantChecksums: []string{ + "sha256=fa17d15d5be23c79ba741f5f7cb88ed7112de16a4410cea81c71b98086889847", + }, + }, + { + name: "multiple checksums", + input: ` +src: + "https://github.com/mirage/mirage-clock/releases/download/v4.2.0/mirage-clock-4.2.0.tbz" + checksum: [ + "sha256=fa17d15d5be23c79ba741f5f7cb88ed7112de16a4410cea81c71b98086889847" + "sha512=05a359dc8400d4ca200ff255dbd030acd33d2c4acb5020838f772c02cdb5f243f3dbafbc43a8cd51e6b5923a140f84c9e7ea25b2c0fa277bb68b996190d36e3b" + "sha1024=05a359dc8400d4ca200ff255dbd030acd33d2c4acb5020838f772c02cdb5f243f3dbafbc43a8cd51e6b5923a140f84c9e7ea25b2c0fa277bb68b996190d36e3b" + ] + `, + wantUrl: "https://github.com/mirage/mirage-clock/releases/download/v4.2.0/mirage-clock-4.2.0.tbz", + wantChecksums: []string{ + "sha256=fa17d15d5be23c79ba741f5f7cb88ed7112de16a4410cea81c71b98086889847", + "sha512=05a359dc8400d4ca200ff255dbd030acd33d2c4acb5020838f772c02cdb5f243f3dbafbc43a8cd51e6b5923a140f84c9e7ea25b2c0fa277bb68b996190d36e3b", + "sha1024=05a359dc8400d4ca200ff255dbd030acd33d2c4acb5020838f772c02cdb5f243f3dbafbc43a8cd51e6b5923a140f84c9e7ea25b2c0fa277bb68b996190d36e3b", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + url, checksums := parseURL([]byte(tt.input)) + assert.Equal(t, tt.wantUrl, url) + assert.Equal(t, tt.wantChecksums, checksums) + }) + } +} diff --git a/syft/pkg/cataloger/ocaml/test-fixtures/alcotest.opam b/syft/pkg/cataloger/ocaml/test-fixtures/alcotest.opam new file mode 100644 index 000000000..f1ef6f7a3 --- /dev/null +++ b/syft/pkg/cataloger/ocaml/test-fixtures/alcotest.opam @@ -0,0 +1,51 @@ +version: "1.5.0" +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "Alcotest is a lightweight and colourful test framework" +description: """ +Alcotest exposes simple interface to perform unit tests. It exposes +a simple TESTABLE module type, a check function to assert test +predicates and a run function to perform a list of unit -> unit +test callbacks. + +Alcotest provides a quiet and colorful output where only faulty runs +are fully displayed at the end of the run (with the full logs ready to +inspect), with a simple (yet expressive) query language to select the +tests to run. +""" +maintainer: ["thomas@gazagnaire.org"] +authors: ["Thomas Gazagnaire"] +license: "ISC" +homepage: "https://github.com/mirage/alcotest" +doc: "https://mirage.github.io/alcotest" +bug-reports: "https://github.com/mirage/alcotest/issues" +depends: [ + "dune" {>= "2.8"} + "ocaml" {>= "4.03.0"} + "fmt" {>= "0.8.7"} + "astring" + "cmdliner" {>= "1.0.0"} + "re" + "stdlib-shims" + "uutf" + "ocaml-syntax-shims" + "odoc" {with-doc} +] +conflicts: [ + "result" {< "1.5"} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/mirage/alcotest.git" \ No newline at end of file diff --git a/syft/pkg/cataloger/ocaml/test-fixtures/glob-paths/opam/alcotest.opam b/syft/pkg/cataloger/ocaml/test-fixtures/glob-paths/opam/alcotest.opam new file mode 100644 index 000000000..882b6040c --- /dev/null +++ b/syft/pkg/cataloger/ocaml/test-fixtures/glob-paths/opam/alcotest.opam @@ -0,0 +1 @@ +bogus \ No newline at end of file diff --git a/syft/pkg/cataloger/ocaml/test-fixtures/glob-paths/opam/ocaml-base-compiler.4.14.0/opam b/syft/pkg/cataloger/ocaml/test-fixtures/glob-paths/opam/ocaml-base-compiler.4.14.0/opam new file mode 100644 index 000000000..882b6040c --- /dev/null +++ b/syft/pkg/cataloger/ocaml/test-fixtures/glob-paths/opam/ocaml-base-compiler.4.14.0/opam @@ -0,0 +1 @@ +bogus \ No newline at end of file diff --git a/syft/pkg/cataloger/ocaml/test-fixtures/ocaml-base-compiler.4.14.0/opam b/syft/pkg/cataloger/ocaml/test-fixtures/ocaml-base-compiler.4.14.0/opam new file mode 100644 index 000000000..15e874ec8 --- /dev/null +++ b/syft/pkg/cataloger/ocaml/test-fixtures/ocaml-base-compiler.4.14.0/opam @@ -0,0 +1,93 @@ +opam-version: "2.0" +synopsis: "Official release 4.14.0" +maintainer: [ + "David Allsopp " + "Florian Angeletti " +] +authors: "Xavier Leroy and many contributors" +license: "LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception" +homepage: "https://ocaml.org" +bug-reports: "https://github.com/ocaml/opam-repository/issues" +depends: [ + "ocaml" {= "4.14.0" & post} + "base-unix" {post} + "base-bigarray" {post} + "base-threads" {post} + "host-arch-arm32" {arch = "arm32" & post} + "host-arch-arm64" {arch = "arm64" & post} + "host-arch-ppc64" {arch = "ppc64" & post} + "host-arch-riscv64" {arch = "riscv64" & post} + "host-arch-s390x" {arch = "s390x" & post} + "host-arch-x86_32" {os != "win32" & arch = "x86_32" & post} + "host-arch-x86_64" {os != "win32" & arch = "x86_64" & post} + "host-arch-unknown" + {os != "win32" & arch != "arm32" & arch != "arm64" & arch != "ppc64" & + arch != "riscv64" & + arch != "s390x" & + arch != "x86_32" & + arch != "x86_64" & + post} + (("arch-x86_64" {os = "win32" & arch = "x86_64"} & + (("system-mingw" & "mingw-w64-shims" {os-distribution = "cygwin" & post}) | + "system-msvc")) | + ("arch-x86_32" {os = "win32"} & + (("system-mingw" & "mingw-w64-shims" {os-distribution = "cygwin" & post}) | + "system-msvc")) | + "host-system-other" {os != "win32" & post}) + "ocaml-options-vanilla" {post} + "flexdll" {>= "0.36" & os = "win32"} +] +conflict-class: "ocaml-core-compiler" +flags: compiler +setenv: CAML_LD_LIBRARY_PATH = "%{lib}%/stublibs" +build: [ + [ + "./configure" + "--host=x86_64-pc-windows" + {system-msvc:installed & arch-x86_64:installed} + "--host=x86_64-w64-mingw32" + {os-distribution = "cygwin" & system-mingw:installed & + arch-x86_64:installed} + "--host=i686-pc-windows" {system-msvc:installed & arch-x86_32:installed} + "--host=i686-w64-mingw32" + {os-distribution = "cygwin" & system-mingw:installed & + arch-x86_32:installed} + "--prefix=%{prefix}%" + "--docdir=%{doc}%/ocaml" + "--with-flexdll=%{flexdll:share}%" {os = "win32" & flexdll:installed} + "-C" + "CC=cc" {os = "openbsd" | os = "macos"} + "ASPP=cc -c" {os = "openbsd" | os = "macos"} + ] + [make "-j%{jobs}%"] +] +install: [make "install"] +build-env: MSYS2_ARG_CONV_EXCL = "*" +post-messages: [ + """\ +A failure in the middle of the build may be caused by build parallelism + (enabled by default). + Please file a bug report at https://github.com/ocaml/opam-repository/issues""" + {failure & jobs > "1"} + """\ +You can try installing again including --jobs=1 + to force a sequential build instead.""" + {failure & jobs > "1" & opam-version >= "2.0.5"} +] +dev-repo: "git+https://github.com/ocaml/ocaml#4.14" +url { + src: "https://github.com/ocaml/ocaml/archive/4.14.0.tar.gz" + checksum: + "sha256=39f44260382f28d1054c5f9d8bf4753cb7ad64027da792f7938344544da155e8" +} +extra-source "ocaml-base-compiler.install" { + src: + "https://raw.githubusercontent.com/ocaml/opam-source-archives/main/patches/ocaml-base-compiler/ocaml-base-compiler.install" + checksum: [ + "sha256=79f2a1a5044a91350a0eb6ce12e261a72a2855c094c425cddf3860e58c486678" + "md5=3e969b841df1f51ca448e6e6295cb451" + ] +} +x-env-path-rewrite: [ + [CAML_LD_LIBRARY_PATH (";" {os = "win32"} ":" {os != "win32"}) "target"] +] diff --git a/syft/pkg/language.go b/syft/pkg/language.go index 24e2de35f..d9289b2da 100644 --- a/syft/pkg/language.go +++ b/syft/pkg/language.go @@ -22,6 +22,7 @@ const ( Java Language = "java" JavaScript Language = "javascript" Lua Language = "lua" + OCaml Language = "ocaml" PHP Language = "php" Python Language = "python" R Language = "R" @@ -43,6 +44,7 @@ var AllLanguages = []Language{ Java, JavaScript, Lua, + OCaml, PHP, Python, R, @@ -92,6 +94,8 @@ func LanguageByName(name string) Language { return Swift case "swipl", string(SwiplPackPkg): return Swipl + case "ocaml", string(OpamPkg): + return OCaml case packageurl.TypeConan, string(CPP): return CPP case packageurl.TypeHackage, string(Haskell): diff --git a/syft/pkg/language_test.go b/syft/pkg/language_test.go index 5519a332b..d5ce5af26 100644 --- a/syft/pkg/language_test.go +++ b/syft/pkg/language_test.go @@ -86,6 +86,10 @@ func TestLanguageFromPURL(t *testing.T) { purl: "pkg:luarocks/kong@3.7.0", want: Lua, }, + { + purl: "pkg:opam/ocaml-base-compiler@ 5.2.0", + want: OCaml, + }, } var languages = strset.New() @@ -227,6 +231,10 @@ func TestLanguageByName(t *testing.T) { name: "swiplpack", language: Swipl, }, + { + name: "opam", + language: OCaml, + }, { name: "pod", language: Swift, diff --git a/syft/pkg/ocaml.go b/syft/pkg/ocaml.go new file mode 100644 index 000000000..350d04339 --- /dev/null +++ b/syft/pkg/ocaml.go @@ -0,0 +1,11 @@ +package pkg + +type OpamPackage struct { + Name string `toml:"name" json:"name"` + Version string `toml:"version" json:"version"` + Licenses []string `mapstructure:"licenses" json:"licenses"` + URL string `mapstructure:"url" json:"url"` + Checksums []string `mapstructure:"checksums" json:"checksum"` + Homepage string `json:"homepage"` + Dependencies []string `toml:"dependencies" json:"dependencies"` +} diff --git a/syft/pkg/type.go b/syft/pkg/type.go index cdb063828..7cebaa8e1 100644 --- a/syft/pkg/type.go +++ b/syft/pkg/type.go @@ -43,6 +43,7 @@ const ( RustPkg Type = "rust-crate" SwiftPkg Type = "swift" SwiplPackPkg Type = "swiplpack" + OpamPkg Type = "opam" WordpressPluginPkg Type = "wordpress-plugin" ) @@ -80,12 +81,13 @@ var AllPkgs = []Type{ RustPkg, SwiftPkg, SwiplPackPkg, + OpamPkg, WordpressPluginPkg, } // PackageURLType returns the PURL package type for the current package. // -//nolint:funlen +//nolint:funlen, gocyclo func (t Type) PackageURLType() string { switch t { case AlpmPkg: @@ -145,6 +147,8 @@ func (t Type) PackageURLType() string { return packageurl.TypeSwift case SwiplPackPkg: return "swiplpack" + case OpamPkg: + return "opam" case WordpressPluginPkg: return "wordpress-plugin" default: @@ -223,6 +227,8 @@ func TypeByName(name string) Type { return SwiftPkg case "swiplpack": return SwiplPackPkg + case "opam": + return OpamPkg case "wordpress-plugin": return WordpressPluginPkg default: diff --git a/syft/pkg/type_test.go b/syft/pkg/type_test.go index b79ad772d..6607abd06 100644 --- a/syft/pkg/type_test.go +++ b/syft/pkg/type_test.go @@ -115,6 +115,10 @@ func TestTypeFromPURL(t *testing.T) { purl: "pkg:swiplpack/condition@0.1.1", expected: SwiplPackPkg, }, + { + purl: "pkg:opam/ocaml-base-compiler@5.2.0", + expected: OpamPkg, + }, } var pkgTypes []string