Add the Ocaml ecosystem (#3112)

Signed-off-by: Laurent Goderre <laurent.goderre@docker.com>
This commit is contained in:
Laurent Goderre 2024-09-10 10:35:18 -04:00 committed by GitHub
parent dafc6ad034
commit 9c2799e379
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 3454 additions and 3 deletions

View file

@ -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{

View file

@ -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))

View file

@ -0,0 +1,93 @@
opam-version: "2.0"
synopsis: "Official release 4.14.0"
maintainer: [
"David Allsopp <david@tarides.com>"
"Florian Angeletti <florian.angeletti@inria.fr>"
]
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"]
]

View file

@ -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"
)

View file

@ -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"),

File diff suppressed because it is too large Load diff

View file

@ -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"
},

View file

@ -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
}

View file

@ -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{

View file

@ -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

View file

@ -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) {

View file

@ -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:

View file

@ -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,

View file

@ -34,6 +34,7 @@ func AllTypes() []any {
pkg.NixStoreEntry{},
pkg.NpmPackage{},
pkg.NpmPackageLockEntry{},
pkg.OpamPackage{},
pkg.PhpComposerInstalledEntry{},
pkg.PhpComposerLockEntry{},
pkg.PhpPeclEntry{},

View file

@ -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"),

View file

@ -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")
}

View file

@ -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())
})
}
}

View file

@ -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()
}

View file

@ -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))
})
}
}

View file

@ -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<version>[^"]*)"`)
licenseRe := regexp.MustCompile(`(?m)^license:\s*(?P<license>(?:"[^"]*")|(?:\[[^\]]*\]))`)
homepageRe := regexp.MustCompile(`(?m)homepage:\s*"(?P<url>[^"]+)"`)
urlRe := regexp.MustCompile(`(?m)url\s*{(?P<url>[^}]+)}`)
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<url>.*)"`)
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
}

View file

@ -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)
})
}
}

View file

@ -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"

View file

@ -0,0 +1 @@
bogus

View file

@ -0,0 +1,93 @@
opam-version: "2.0"
synopsis: "Official release 4.14.0"
maintainer: [
"David Allsopp <david@tarides.com>"
"Florian Angeletti <florian.angeletti@inria.fr>"
]
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"]
]

View file

@ -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):

View file

@ -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,

11
syft/pkg/ocaml.go Normal file
View file

@ -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"`
}

View file

@ -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:

View file

@ -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