diff --git a/README.md b/README.md index 14ccbf40fd5..f4553c682fc 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ For commercial support options with Syft or Grype, please [contact Anchore](http - Debian (dpkg) - Dotnet (deps.json) - Objective-C (cocoapods) +- Elixir (mix) +- Erlang (rebar3) - Go (go.mod, Go binaries) - Haskell (cabal, stack) - Java (jar, ear, war, par, sar, native-image) diff --git a/syft/formats/common/spdxhelpers/source_info.go b/syft/formats/common/spdxhelpers/source_info.go index 0e5391f7afe..f13187e5e57 100644 --- a/syft/formats/common/spdxhelpers/source_info.go +++ b/syft/formats/common/spdxhelpers/source_info.go @@ -43,6 +43,8 @@ func SourceInfo(p pkg.Package) string { answer = "acquired package info from portage DB" case pkg.HackagePkg: answer = "acquired package info from cabal or stack manifest files" + case pkg.HexPkg: + answer = "acquired package info from rebar3 or mix manifest file" default: answer = "acquired package info from the following paths" } diff --git a/syft/formats/common/spdxhelpers/source_info_test.go b/syft/formats/common/spdxhelpers/source_info_test.go index 6de1f461a21..f26d0744de6 100644 --- a/syft/formats/common/spdxhelpers/source_info_test.go +++ b/syft/formats/common/spdxhelpers/source_info_test.go @@ -191,6 +191,14 @@ func Test_SourceInfo(t *testing.T) { "acquired package info from the following paths", }, }, + { + input: pkg.Package{ + Type: pkg.HexPkg, + }, + expected: []string{ + "from rebar3 or mix manifest file", + }, + }, } var pkgTypes []pkg.Type for _, test := range tests { diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index 86db095ad9b..58e455d570e 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -9,7 +9,7 @@ "locations": [ { "path": "/somefile-1.txt", - "layerID": "sha256:62058900d4ce269c900160b8dd255fe310c3a459dda236d041102fa070f84406" + "layerID": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59" } ], "licenses": [ @@ -40,7 +40,7 @@ "locations": [ { "path": "/somefile-2.txt", - "layerID": "sha256:623ad97366f39ae279f1925673cdacb4851ddf2e3266f04e63010ec080a098c1" + "layerID": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec" } ], "licenses": [], @@ -64,11 +64,11 @@ ], "artifactRelationships": [], "source": { - "id": "6de18113d4a87f3503d13d10d1e69ebe2f5b41880aec96fb506b2113fd8df2f8", + "id": "1a678f111c8ddc66fd82687bb024e0dd6af61314404937a80e810c0cf317b796", "type": "image", "target": { "userInput": "user-image-input", - "imageID": "sha256:fdd8b25b9402be0c8cf99e1edde6da614310d6bacd2e45a03781a3ef4a59025f", + "imageID": "sha256:3c51b06feb0cda8ee62d0e3755ef2a8496a6b71f8a55b245f07f31c4bb813d31", "manifestDigest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368", "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "tags": [ @@ -78,17 +78,17 @@ "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:62058900d4ce269c900160b8dd255fe310c3a459dda236d041102fa070f84406", + "digest": "sha256:fb6beecb75b39f4bb813dbf177e501edd5ddb3e69bb45cedeb78c676ee1b7a59", "size": 22 }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", - "digest": "sha256:623ad97366f39ae279f1925673cdacb4851ddf2e3266f04e63010ec080a098c1", + "digest": "sha256:319b588ce64253a87b533c8ed01cf0025e0eac98e7b516e12532957e1244fdec", "size": 16 } ], - "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1NjpmZGQ4YjI1Yjk0MDJiZTBjOGNmOTllMWVkZGU2ZGE2MTQzMTBkNmJhY2QyZTQ1YTAzNzgxYTNlZjRhNTkwMjVmIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1Njo2MjA1ODkwMGQ0Y2UyNjljOTAwMTYwYjhkZDI1NWZlMzEwYzNhNDU5ZGRhMjM2ZDA0MTEwMmZhMDcwZjg0NDA2In0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjYyM2FkOTczNjZmMzlhZTI3OWYxOTI1NjczY2RhY2I0ODUxZGRmMmUzMjY2ZjA0ZTYzMDEwZWMwODBhMDk4YzEifV19", - "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjItMTItMjFUMTk6MDc6NDQuMDcyMTgyMTk0WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTEyLTIxVDE5OjA3OjQ0LjA0NjkyNTc5M1oiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjItMTItMjFUMTk6MDc6NDQuMDcyMTgyMTk0WiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6NjIwNTg5MDBkNGNlMjY5YzkwMDE2MGI4ZGQyNTVmZTMxMGMzYTQ1OWRkYTIzNmQwNDExMDJmYTA3MGY4NDQwNiIsInNoYTI1Njo2MjNhZDk3MzY2ZjM5YWUyNzlmMTkyNTY3M2NkYWNiNDg1MWRkZjJlMzI2NmYwNGU2MzAxMGVjMDgwYTA5OGMxIl19fQ==", + "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzMsImRpZ2VzdCI6InNoYTI1NjozYzUxYjA2ZmViMGNkYThlZTYyZDBlMzc1NWVmMmE4NDk2YTZiNzFmOGE1NWIyNDVmMDdmMzFjNGJiODEzZDMxIn0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpmYjZiZWVjYjc1YjM5ZjRiYjgxM2RiZjE3N2U1MDFlZGQ1ZGRiM2U2OWJiNDVjZWRlYjc4YzY3NmVlMWI3YTU5In0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjMxOWI1ODhjZTY0MjUzYTg3YjUzM2M4ZWQwMWNmMDAyNWUwZWFjOThlN2I1MTZlMTI1MzI5NTdlMTI0NGZkZWMifV19", + "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjItMDgtMDFUMjA6MDk6MjIuNTA5NDIxNzEyWiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIyLTA4LTAxVDIwOjA5OjIyLjQ4Nzg5NTUxOVoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjItMDgtMDFUMjA6MDk6MjIuNTA5NDIxNzEyWiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6ZmI2YmVlY2I3NWIzOWY0YmI4MTNkYmYxNzdlNTAxZWRkNWRkYjNlNjliYjQ1Y2VkZWI3OGM2NzZlZTFiN2E1OSIsInNoYTI1NjozMTliNTg4Y2U2NDI1M2E4N2I1MzNjOGVkMDFjZjAwMjVlMGVhYzk4ZTdiNTE2ZTEyNTMyOTU3ZTEyNDRmZGVjIl19fQ==", "repoDigests": [], "architecture": "", "os": "" diff --git a/syft/pkg/cataloger/binary/classifier.go b/syft/pkg/cataloger/binary/classifier.go index 959a151fa4b..4e89483233d 100644 --- a/syft/pkg/cataloger/binary/classifier.go +++ b/syft/pkg/cataloger/binary/classifier.go @@ -125,7 +125,6 @@ func singlePackage(classifier classifier, reader source.LocationReadCloser, matc p := pkg.Package{ Name: classifier.Package, Version: version, - Language: pkg.Binary, Locations: source.NewLocationSet(reader.Location), Type: pkg.BinaryPkg, CPEs: cpes, diff --git a/syft/pkg/cataloger/cataloger.go b/syft/pkg/cataloger/cataloger.go index a18d0d32f33..bfc2e1e9cd6 100644 --- a/syft/pkg/cataloger/cataloger.go +++ b/syft/pkg/cataloger/cataloger.go @@ -17,6 +17,8 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/dart" "github.com/anchore/syft/syft/pkg/cataloger/deb" "github.com/anchore/syft/syft/pkg/cataloger/dotnet" + "github.com/anchore/syft/syft/pkg/cataloger/elixir" + "github.com/anchore/syft/syft/pkg/cataloger/erlang" "github.com/anchore/syft/syft/pkg/cataloger/golang" "github.com/anchore/syft/syft/pkg/cataloger/haskell" "github.com/anchore/syft/syft/pkg/cataloger/java" @@ -81,6 +83,8 @@ func DirectoryCatalogers(cfg Config) []pkg.Cataloger { haskell.NewHackageCataloger(), sbom.NewSBOMCataloger(), binary.NewCataloger(), + elixir.NewMixLockCataloger(), + erlang.NewRebarLockCataloger(), }, cfg.Catalogers) } @@ -115,6 +119,8 @@ func AllCatalogers(cfg Config) []pkg.Cataloger { haskell.NewHackageCataloger(), sbom.NewSBOMCataloger(), binary.NewCataloger(), + elixir.NewMixLockCataloger(), + erlang.NewRebarLockCataloger(), }, cfg.Catalogers) } diff --git a/syft/pkg/cataloger/elixir/cataloger.go b/syft/pkg/cataloger/elixir/cataloger.go new file mode 100644 index 00000000000..f4e62effd59 --- /dev/null +++ b/syft/pkg/cataloger/elixir/cataloger.go @@ -0,0 +1,16 @@ +/* +Package elixir provides a concrete Cataloger implementation for elixir specific package manger files. +*/ +package elixir + +import ( + "github.com/anchore/syft/syft/pkg/cataloger/generic" +) + +const catalogerName = "elixir-mix-lock-cataloger" + +// NewMixLockCataloger returns parses mix.lock files and returns packages +func NewMixLockCataloger() *generic.Cataloger { + return generic.NewCataloger(catalogerName). + WithParserByGlobs(parseMixLock, "**/mix.lock") +} diff --git a/syft/pkg/cataloger/elixir/package.go b/syft/pkg/cataloger/elixir/package.go new file mode 100644 index 00000000000..fc1ca514736 --- /dev/null +++ b/syft/pkg/cataloger/elixir/package.go @@ -0,0 +1,37 @@ +package elixir + +import ( + "github.com/anchore/packageurl-go" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +func newPackage(d pkg.MixLockMetadata, locations ...source.Location) pkg.Package { + p := pkg.Package{ + Name: d.Name, + Version: d.Version, + Language: pkg.Elixir, + Locations: source.NewLocationSet(locations...), + PURL: packageURL(d), + Type: pkg.HexPkg, + MetadataType: pkg.MixLockMetadataType, + Metadata: d, + } + + p.SetID() + + return p +} + +func packageURL(m pkg.MixLockMetadata) string { + var qualifiers packageurl.Qualifiers + + return packageurl.NewPackageURL( + packageurl.TypeHex, + "", + m.Name, + m.Version, + qualifiers, + "", + ).ToString() +} diff --git a/syft/pkg/cataloger/elixir/parse_mix_lock.go b/syft/pkg/cataloger/elixir/parse_mix_lock.go new file mode 100644 index 00000000000..075e5fa5e9d --- /dev/null +++ b/syft/pkg/cataloger/elixir/parse_mix_lock.go @@ -0,0 +1,53 @@ +package elixir + +import ( + "bufio" + "errors" + "fmt" + "io" + "regexp" + + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" + "github.com/anchore/syft/syft/source" +) + +// integrity check +var _ generic.Parser = parseMixLock + +var mixLockDelimiter = regexp.MustCompile(`[%{}\n" ,:]+`) + +// parseMixLock parses a mix.lock and returns the discovered Elixir packages. +func parseMixLock(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + r := bufio.NewReader(reader) + + var packages []pkg.Package + for { + line, err := r.ReadString('\n') + switch { + case errors.Is(io.EOF, err): + return packages, nil, nil + case err != nil: + return nil, nil, fmt.Errorf("failed to parse mix.lock file: %w", err) + } + tokens := mixLockDelimiter.Split(line, -1) + if len(tokens) < 6 { + continue + } + name, version, hash, hashExt := tokens[1], tokens[4], tokens[5], tokens[len(tokens)-2] + + if name == "" { + log.WithFields("path", reader.RealPath).Debug("skipping empty package name from mix.lock file") + continue + } + + packages = append(packages, newPackage(pkg.MixLockMetadata{ + Name: name, + Version: version, + PkgHash: hash, + PkgHashExt: hashExt, + })) + } +} diff --git a/syft/pkg/cataloger/elixir/parse_mix_lock_test.go b/syft/pkg/cataloger/elixir/parse_mix_lock_test.go new file mode 100644 index 00000000000..11de894ac07 --- /dev/null +++ b/syft/pkg/cataloger/elixir/parse_mix_lock_test.go @@ -0,0 +1,231 @@ +package elixir + +import ( + "testing" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func TestParseMixLock(t *testing.T) { + expected := []pkg.Package{ + { + Name: "castore", + Version: "0.1.17", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/castore@0.1.17", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "castore", + Version: "0.1.17", + PkgHash: "ba672681de4e51ed8ec1f74ed624d104c0db72742ea1a5e74edbc770c815182f", + PkgHashExt: "d9844227ed52d26e7519224525cb6868650c272d4a3d327ce3ca5570c12163f9", + }, + }, + { + Name: "connection", + Version: "1.1.0", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/connection@1.1.0", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "connection", + Version: "1.1.0", + PkgHash: "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", + PkgHashExt: "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c", + }, + }, + { + Name: "cowboy", + Version: "2.9.0", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/cowboy@2.9.0", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "cowboy", + Version: "2.9.0", + PkgHash: "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", + PkgHashExt: "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde", + }, + }, + { + Name: "cowboy_telemetry", + Version: "0.4.0", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/cowboy_telemetry@0.4.0", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "cowboy_telemetry", + Version: "0.4.0", + PkgHash: "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", + PkgHashExt: "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de", + }, + }, + { + Name: "cowlib", + Version: "2.11.0", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/cowlib@2.11.0", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "cowlib", + Version: "2.11.0", + PkgHash: "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", + PkgHashExt: "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9", + }, + }, + { + Name: "db_connection", + Version: "2.4.2", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/db_connection@2.4.2", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "db_connection", + Version: "2.4.2", + PkgHash: "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", + PkgHashExt: "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668", + }, + }, + { + Name: "decimal", + Version: "2.0.0", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/decimal@2.0.0", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "decimal", + Version: "2.0.0", + PkgHash: "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", + PkgHashExt: "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577", + }, + }, + { + Name: "earmark_parser", + Version: "1.4.25", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/earmark_parser@1.4.25", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "earmark_parser", + Version: "1.4.25", + PkgHash: "2024618731c55ebfcc5439d756852ec4e85978a39d0d58593763924d9a15916f", + PkgHashExt: "56749c5e1c59447f7b7a23ddb235e4b3defe276afc220a6227237f3efe83f51e", + }, + }, + { + Name: "ecto", + Version: "3.8.1", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/ecto@3.8.1", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "ecto", + Version: "3.8.1", + PkgHash: "35e0bd8c8eb772e14a5191a538cd079706ecb45164ea08a7523b4fc69ab70f56", + PkgHashExt: "f1b68f8d5fe3ab89e24f57c03db5b5d0aed3602077972098b3a6006a1be4b69b", + }, + }, + { + Name: "ecto_sql", + Version: "3.8.1", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/ecto_sql@3.8.1", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "ecto_sql", + Version: "3.8.1", + PkgHash: "1acaaba32ca0551fd19e492fc7c80414e72fc1a7140fc9395aaa53c2e8629798", + PkgHashExt: "ba7fc75882edce6f2ceca047315d5db27ead773cafea47f1724e35f1e7964525", + }, + }, + { + Name: "esbuild", + Version: "0.5.0", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/esbuild@0.5.0", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "esbuild", + Version: "0.5.0", + PkgHash: "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", + PkgHashExt: "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5", + }, + }, + { + Name: "ex_doc", + Version: "0.28.4", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/ex_doc@0.28.4", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "ex_doc", + Version: "0.28.4", + PkgHash: "001a0ea6beac2f810f1abc3dbf4b123e9593eaa5f00dd13ded024eae7c523298", + PkgHashExt: "bf85d003dd34911d89c8ddb8bda1a958af3471a274a4c2150a9c01c78ac3f8ed", + }, + }, + { + Name: "gettext", + Version: "0.19.1", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/gettext@0.19.1", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "gettext", + Version: "0.19.1", + PkgHash: "564953fd21f29358e68b91634799d9d26989f8d039d7512622efb3c3b1c97892", + PkgHashExt: "10c656c0912b8299adba9b061c06947511e3f109ab0d18b44a866a4498e77222", + }, + }, + { + Name: "hpax", + Version: "0.1.1", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/hpax@0.1.1", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "hpax", + Version: "0.1.1", + PkgHash: "2396c313683ada39e98c20a75a82911592b47e5c24391363343bde74f82396ca", + PkgHashExt: "0ae7d5a0b04a8a60caf7a39fcf3ec476f35cc2cc16c05abea730d3ce6ac6c826", + }, + }, + { + Name: "jason", + Version: "1.3.0", + Language: pkg.Elixir, + Type: pkg.HexPkg, + PURL: "pkg:hex/jason@1.3.0", + MetadataType: pkg.MixLockMetadataType, + Metadata: pkg.MixLockMetadata{ + Name: "jason", + Version: "1.3.0", + PkgHash: "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", + PkgHashExt: "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac", + }, + }, + } + + fixture := "test-fixtures/mix.lock" + + // TODO: relationships are not under test + var expectedRelationships []artifact.Relationship + + pkgtest.TestFileParser(t, fixture, parseMixLock, expected, expectedRelationships) +} diff --git a/syft/pkg/cataloger/elixir/test-fixtures/mix.lock b/syft/pkg/cataloger/elixir/test-fixtures/mix.lock new file mode 100644 index 00000000000..8d9806c1b80 --- /dev/null +++ b/syft/pkg/cataloger/elixir/test-fixtures/mix.lock @@ -0,0 +1,17 @@ +%{ + "castore": {:hex, :castore, "0.1.17", "ba672681de4e51ed8ec1f74ed624d104c0db72742ea1a5e74edbc770c815182f", [:mix], [], "hexpm", "d9844227ed52d26e7519224525cb6868650c272d4a3d327ce3ca5570c12163f9"}, + "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, + "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, + "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, + "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, + "db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"}, + "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.25", "2024618731c55ebfcc5439d756852ec4e85978a39d0d58593763924d9a15916f", [:mix], [], "hexpm", "56749c5e1c59447f7b7a23ddb235e4b3defe276afc220a6227237f3efe83f51e"}, + "ecto": {:hex, :ecto, "3.8.1", "35e0bd8c8eb772e14a5191a538cd079706ecb45164ea08a7523b4fc69ab70f56", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f1b68f8d5fe3ab89e24f57c03db5b5d0aed3602077972098b3a6006a1be4b69b"}, + "ecto_sql": {:hex, :ecto_sql, "3.8.1", "1acaaba32ca0551fd19e492fc7c80414e72fc1a7140fc9395aaa53c2e8629798", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.8.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ba7fc75882edce6f2ceca047315d5db27ead773cafea47f1724e35f1e7964525"}, + "esbuild": {:hex, :esbuild, "0.5.0", "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5"}, + "ex_doc": {:hex, :ex_doc, "0.28.4", "001a0ea6beac2f810f1abc3dbf4b123e9593eaa5f00dd13ded024eae7c523298", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bf85d003dd34911d89c8ddb8bda1a958af3471a274a4c2150a9c01c78ac3f8ed"}, + "gettext": {:hex, :gettext, "0.19.1", "564953fd21f29358e68b91634799d9d26989f8d039d7512622efb3c3b1c97892", [:mix], [], "hexpm", "10c656c0912b8299adba9b061c06947511e3f109ab0d18b44a866a4498e77222"}, + "hpax": {:hex, :hpax, "0.1.1", "2396c313683ada39e98c20a75a82911592b47e5c24391363343bde74f82396ca", [:mix], [], "hexpm", "0ae7d5a0b04a8a60caf7a39fcf3ec476f35cc2cc16c05abea730d3ce6ac6c826"}, + "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"}, +} diff --git a/syft/pkg/cataloger/erlang/cataloger.go b/syft/pkg/cataloger/erlang/cataloger.go new file mode 100644 index 00000000000..3e3b54f88e7 --- /dev/null +++ b/syft/pkg/cataloger/erlang/cataloger.go @@ -0,0 +1,16 @@ +/* +Package erlang provides a concrete Cataloger implementation for erlang specific package manger files. +*/ +package erlang + +import ( + "github.com/anchore/syft/syft/pkg/cataloger/generic" +) + +const catalogerName = "erlang-rebar-lock-cataloger" + +// NewRebarLockCataloger returns parses rebar.lock files and returns packages. +func NewRebarLockCataloger() *generic.Cataloger { + return generic.NewCataloger(catalogerName). + WithParserByGlobs(parseRebarLock, "**/rebar.lock") +} diff --git a/syft/pkg/cataloger/erlang/package.go b/syft/pkg/cataloger/erlang/package.go new file mode 100644 index 00000000000..2eb89053819 --- /dev/null +++ b/syft/pkg/cataloger/erlang/package.go @@ -0,0 +1,37 @@ +package erlang + +import ( + "github.com/anchore/packageurl-go" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +func newPackage(d pkg.RebarLockMetadata, locations ...source.Location) pkg.Package { + p := pkg.Package{ + Name: d.Name, + Version: d.Version, + Language: pkg.Erlang, + Locations: source.NewLocationSet(locations...), + PURL: packageURL(d), + Type: pkg.HexPkg, + MetadataType: pkg.RebarLockMetadataType, + Metadata: d, + } + + p.SetID() + + return p +} + +func packageURL(m pkg.RebarLockMetadata) string { + var qualifiers packageurl.Qualifiers + + return packageurl.NewPackageURL( + packageurl.TypeHex, + "", + m.Name, + m.Version, + qualifiers, + "", + ).ToString() +} diff --git a/syft/pkg/cataloger/erlang/parse_rebar_lock.go b/syft/pkg/cataloger/erlang/parse_rebar_lock.go new file mode 100644 index 00000000000..97e0929473b --- /dev/null +++ b/syft/pkg/cataloger/erlang/parse_rebar_lock.go @@ -0,0 +1,80 @@ +package erlang + +import ( + "bufio" + "errors" + "fmt" + "io" + "regexp" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" + "github.com/anchore/syft/syft/source" +) + +// integrity check +var _ generic.Parser = parseRebarLock + +var rebarLockDelimiter = regexp.MustCompile(`[\[{<">},: \]\n]+`) + +// parseMixLock parses a mix.lock and returns the discovered Elixir packages. +func parseRebarLock(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + r := bufio.NewReader(reader) + + pkgMap := make(map[string]*pkg.Package) + + var names []string +loop: + for { + line, err := r.ReadString('\n') + switch { + case errors.Is(io.EOF, err): + break loop + case err != nil: + // TODO: return partial result and warn + return nil, nil, fmt.Errorf("failed to parse rebar.lock file: %w", err) + } + tokens := rebarLockDelimiter.Split(line, -1) + if len(tokens) < 4 { + continue + } + if len(tokens) < 5 { + name, hash := tokens[1], tokens[2] + sourcePkg := pkgMap[name] + metadata := sourcePkg.Metadata.(pkg.RebarLockMetadata) + if metadata.PkgHash == "" { + metadata.PkgHash = hash + } else { + metadata.PkgHashExt = hash + } + sourcePkg.Metadata = metadata + continue + } + name, version := tokens[1], tokens[4] + + sourcePkg := pkg.Package{ + Name: name, + Version: version, + Language: pkg.Erlang, + Type: pkg.HexPkg, + MetadataType: pkg.RebarLockMetadataType, + } + + p := newPackage(pkg.RebarLockMetadata{ + Name: name, + Version: version, + }) + + names = append(names, name) + pkgMap[sourcePkg.Name] = &p + } + + var packages []pkg.Package + for _, name := range names { + p := pkgMap[name] + p.SetID() + packages = append(packages, *p) + } + return packages, nil, nil +} diff --git a/syft/pkg/cataloger/erlang/parse_rebar_lock_test.go b/syft/pkg/cataloger/erlang/parse_rebar_lock_test.go new file mode 100644 index 00000000000..db3e40d8bf9 --- /dev/null +++ b/syft/pkg/cataloger/erlang/parse_rebar_lock_test.go @@ -0,0 +1,119 @@ +package erlang + +import ( + "testing" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func TestParseRebarLock(t *testing.T) { + expected := []pkg.Package{ + { + Name: "certifi", + Version: "2.9.0", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/certifi@2.9.0", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "certifi", + Version: "2.9.0", + PkgHash: "6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21", + PkgHashExt: "266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641", + }, + }, + { + Name: "idna", + Version: "6.1.1", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/idna@6.1.1", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "idna", + Version: "6.1.1", + PkgHash: "8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D", + PkgHashExt: "92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA", + }, + }, + { + Name: "metrics", + Version: "1.0.1", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/metrics@1.0.1", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "metrics", + Version: "1.0.1", + PkgHash: "25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486", + PkgHashExt: "69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16", + }, + }, + { + Name: "mimerl", + Version: "1.2.0", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/mimerl@1.2.0", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "mimerl", + Version: "1.2.0", + PkgHash: "67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3", + PkgHashExt: "F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323", + }, + }, + { + Name: "parse_trans", + Version: "3.3.1", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/parse_trans@3.3.1", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "parse_trans", + Version: "3.3.1", + PkgHash: "16328AB840CC09919BD10DAB29E431DA3AF9E9E7E7E6F0089DD5A2D2820011D8", + PkgHashExt: "07CD9577885F56362D414E8C4C4E6BDF10D43A8767ABB92D24CBE8B24C54888B", + }, + }, + { + Name: "ssl_verify_fun", + Version: "1.1.6", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/ssl_verify_fun@1.1.6", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "ssl_verify_fun", + Version: "1.1.6", + PkgHash: "CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0", + PkgHashExt: "BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680", + }, + }, + { + Name: "unicode_util_compat", + Version: "0.7.0", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/unicode_util_compat@0.7.0", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "unicode_util_compat", + Version: "0.7.0", + PkgHash: "BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78", + PkgHashExt: "25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521", + }, + }, + } + + fixture := "test-fixtures/rebar.lock" + + // TODO: relationships are not under test + var expectedRelationships []artifact.Relationship + + pkgtest.TestFileParser(t, fixture, parseRebarLock, expected, expectedRelationships) +} diff --git a/syft/pkg/cataloger/erlang/test-fixtures/rebar.lock b/syft/pkg/cataloger/erlang/test-fixtures/rebar.lock new file mode 100644 index 00000000000..34397ef86d1 --- /dev/null +++ b/syft/pkg/cataloger/erlang/test-fixtures/rebar.lock @@ -0,0 +1,26 @@ +{"1.2.0", +[{<<"certifi">>,{pkg,<<"certifi">>,<<"2.9.0">>},0}, + {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, + {<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},0}, + {<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.2.0">>},0}, + {<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.1">>},0}, + {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.6">>},0}, + {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},0}]}. +[ +{pkg_hash,[ + {<<"certifi">>, <<"6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21">>}, + {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, + {<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>}, + {<<"mimerl">>, <<"67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3">>}, + {<<"parse_trans">>, <<"16328AB840CC09919BD10DAB29E431DA3AF9E9E7E7E6F0089DD5A2D2820011D8">>}, + {<<"ssl_verify_fun">>, <<"CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0">>}, + {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]}, +{pkg_hash_ext,[ + {<<"certifi">>, <<"266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641">>}, + {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, + {<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>}, + {<<"mimerl">>, <<"F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323">>}, + {<<"parse_trans">>, <<"07CD9577885F56362D414E8C4C4E6BDF10D43A8767ABB92D24CBE8B24C54888B">>}, + {<<"ssl_verify_fun">>, <<"BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680">>}, + {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]} +]. diff --git a/syft/pkg/language.go b/syft/pkg/language.go index 1adc0d3a698..2ce12163e4e 100644 --- a/syft/pkg/language.go +++ b/syft/pkg/language.go @@ -12,36 +12,38 @@ type Language string const ( // the full set of supported programming languages UnknownLanguage Language = "" + CPP Language = "c++" + Dart Language = "dart" + Dotnet Language = "dotnet" + Elixir Language = "elixir" + Erlang Language = "erlang" + Go Language = "go" + Haskell Language = "haskell" Java Language = "java" JavaScript Language = "javascript" - Python Language = "python" PHP Language = "php" + Python Language = "python" Ruby Language = "ruby" - Go Language = "go" Rust Language = "rust" - Dart Language = "dart" - Dotnet Language = "dotnet" Swift Language = "swift" - CPP Language = "c++" - Haskell Language = "haskell" - Binary Language = "binary" - File Language = "file" ) // AllLanguages is a set of all programming languages detected by syft. var AllLanguages = []Language{ + CPP, + Dart, + Dotnet, + Elixir, + Erlang, + Go, + Haskell, Java, JavaScript, - Python, PHP, + Python, Ruby, - Go, Rust, - Dart, - Dotnet, Swift, - CPP, - Haskell, } // String returns the string representation of the language. @@ -84,6 +86,11 @@ func LanguageByName(name string) Language { return CPP case packageurl.TypeHackage, string(Haskell): return Haskell + case packageurl.TypeHex, "beam", "elixir", "erlang": + // should we support returning multiple languages to support this case? + // answer: no. We want this to definitively answer "which language does this package represent?" + // which might not be possible in all cases. See for more context: https://github.com/package-url/purl-spec/pull/178 + return UnknownLanguage default: return UnknownLanguage } diff --git a/syft/pkg/language_test.go b/syft/pkg/language_test.go index 4acf8c37996..8e84b975eb1 100644 --- a/syft/pkg/language_test.go +++ b/syft/pkg/language_test.go @@ -62,6 +62,10 @@ func TestLanguageFromPURL(t *testing.T) { purl: "pkg:hackage/HTTP@4000.3.16", want: Haskell, }, + { + purl: "pkg:hex/hpax/hpax@0.1.1", + want: UnknownLanguage, + }, } var languages []string @@ -70,6 +74,10 @@ func TestLanguageFromPURL(t *testing.T) { expectedLanguages.Add(string(ty)) } + // we cannot determine the language from these purl ecosystems (yet?) + expectedLanguages.Remove(Elixir.String()) + expectedLanguages.Remove(Erlang.String()) + for _, tt := range tests { t.Run(tt.purl, func(t *testing.T) { actual := LanguageFromPURL(tt.purl) diff --git a/syft/pkg/metadata.go b/syft/pkg/metadata.go index adcb5b6a8ce..a5072b61005 100644 --- a/syft/pkg/metadata.go +++ b/syft/pkg/metadata.go @@ -9,74 +9,79 @@ type MetadataType string const ( // this is the full set of data shapes that can be represented within the pkg.Package.Metadata field - - UnknownMetadataType MetadataType = "UnknownMetadata" - ApkMetadataType MetadataType = "ApkMetadata" AlpmMetadataType MetadataType = "AlpmMetadata" + ApkMetadataType MetadataType = "ApkMetadata" BinaryMetadataType MetadataType = "BinaryMetadata" + CocoapodsMetadataType MetadataType = "CocoapodsMetadataType" + ConanLockMetadataType MetadataType = "ConanLockMetadataType" + ConanMetadataType MetadataType = "ConanMetadataType" + DartPubMetadataType MetadataType = "DartPubMetadata" + DotnetDepsMetadataType MetadataType = "DotnetDepsMetadata" DpkgMetadataType MetadataType = "DpkgMetadata" GemMetadataType MetadataType = "GemMetadata" + GolangMetadataType MetadataType = "GolangMetadata" + HackageMetadataType MetadataType = "HackageMetadataType" JavaMetadataType MetadataType = "JavaMetadata" - NpmPackageJSONMetadataType MetadataType = "NpmPackageJsonMetadata" - RpmMetadataType MetadataType = "RpmMetadata" - DartPubMetadataType MetadataType = "DartPubMetadata" - DotnetDepsMetadataType MetadataType = "DotnetDepsMetadata" - PythonPackageMetadataType MetadataType = "PythonPackageMetadata" - RustCargoPackageMetadataType MetadataType = "RustCargoPackageMetadata" KbPackageMetadataType MetadataType = "KbPackageMetadata" - GolangMetadataType MetadataType = "GolangMetadata" + MixLockMetadataType MetadataType = "MixLockMetadataType" + NpmPackageJSONMetadataType MetadataType = "NpmPackageJsonMetadata" PhpComposerJSONMetadataType MetadataType = "PhpComposerJsonMetadata" - CocoapodsMetadataType MetadataType = "CocoapodsMetadataType" - ConanMetadataType MetadataType = "ConanMetadataType" - ConanLockMetadataType MetadataType = "ConanLockMetadataType" PortageMetadataType MetadataType = "PortageMetadata" - HackageMetadataType MetadataType = "HackageMetadataType" + PythonPackageMetadataType MetadataType = "PythonPackageMetadata" + RebarLockMetadataType MetadataType = "RebarLockMetadataType" + RpmMetadataType MetadataType = "RpmMetadata" + RustCargoPackageMetadataType MetadataType = "RustCargoPackageMetadata" + UnknownMetadataType MetadataType = "UnknownMetadata" ) var AllMetadataTypes = []MetadataType{ - ApkMetadataType, AlpmMetadataType, + ApkMetadataType, BinaryMetadataType, + CocoapodsMetadataType, + ConanLockMetadataType, + ConanMetadataType, + DartPubMetadataType, + DotnetDepsMetadataType, DpkgMetadataType, GemMetadataType, + GolangMetadataType, + HackageMetadataType, JavaMetadataType, - NpmPackageJSONMetadataType, - RpmMetadataType, - DartPubMetadataType, - DotnetDepsMetadataType, - PythonPackageMetadataType, - RustCargoPackageMetadataType, KbPackageMetadataType, - GolangMetadataType, + MixLockMetadataType, + NpmPackageJSONMetadataType, PhpComposerJSONMetadataType, - CocoapodsMetadataType, - ConanMetadataType, - ConanLockMetadataType, PortageMetadataType, - HackageMetadataType, + PythonPackageMetadataType, + RebarLockMetadataType, + RpmMetadataType, + RustCargoPackageMetadataType, } var MetadataTypeByName = map[MetadataType]reflect.Type{ - ApkMetadataType: reflect.TypeOf(ApkMetadata{}), AlpmMetadataType: reflect.TypeOf(AlpmMetadata{}), + ApkMetadataType: reflect.TypeOf(ApkMetadata{}), BinaryMetadataType: reflect.TypeOf(BinaryMetadata{}), + CocoapodsMetadataType: reflect.TypeOf(CocoapodsMetadata{}), + ConanLockMetadataType: reflect.TypeOf(ConanLockMetadata{}), + ConanMetadataType: reflect.TypeOf(ConanMetadata{}), + DartPubMetadataType: reflect.TypeOf(DartPubMetadata{}), + DotnetDepsMetadataType: reflect.TypeOf(DotnetDepsMetadata{}), DpkgMetadataType: reflect.TypeOf(DpkgMetadata{}), GemMetadataType: reflect.TypeOf(GemMetadata{}), + GolangMetadataType: reflect.TypeOf(GolangMetadata{}), + HackageMetadataType: reflect.TypeOf(HackageMetadata{}), JavaMetadataType: reflect.TypeOf(JavaMetadata{}), - NpmPackageJSONMetadataType: reflect.TypeOf(NpmPackageJSONMetadata{}), - RpmMetadataType: reflect.TypeOf(RpmMetadata{}), - DartPubMetadataType: reflect.TypeOf(DartPubMetadata{}), - DotnetDepsMetadataType: reflect.TypeOf(DotnetDepsMetadata{}), - PythonPackageMetadataType: reflect.TypeOf(PythonPackageMetadata{}), - RustCargoPackageMetadataType: reflect.TypeOf(CargoPackageMetadata{}), KbPackageMetadataType: reflect.TypeOf(KbPackageMetadata{}), - GolangMetadataType: reflect.TypeOf(GolangMetadata{}), + MixLockMetadataType: reflect.TypeOf(MixLockMetadata{}), + NpmPackageJSONMetadataType: reflect.TypeOf(NpmPackageJSONMetadata{}), PhpComposerJSONMetadataType: reflect.TypeOf(PhpComposerJSONMetadata{}), - CocoapodsMetadataType: reflect.TypeOf(CocoapodsMetadata{}), - ConanMetadataType: reflect.TypeOf(ConanMetadata{}), - ConanLockMetadataType: reflect.TypeOf(ConanLockMetadata{}), PortageMetadataType: reflect.TypeOf(PortageMetadata{}), - HackageMetadataType: reflect.TypeOf(HackageMetadata{}), + PythonPackageMetadataType: reflect.TypeOf(PythonPackageMetadata{}), + RebarLockMetadataType: reflect.TypeOf(RebarLockMetadata{}), + RpmMetadataType: reflect.TypeOf(RpmMetadata{}), + RustCargoPackageMetadataType: reflect.TypeOf(CargoPackageMetadata{}), } func CleanMetadataType(typ MetadataType) MetadataType { diff --git a/syft/pkg/mix_lock_metadata.go b/syft/pkg/mix_lock_metadata.go new file mode 100644 index 00000000000..1677aae05a6 --- /dev/null +++ b/syft/pkg/mix_lock_metadata.go @@ -0,0 +1,8 @@ +package pkg + +type MixLockMetadata struct { + Name string `mapstructure:"name" json:"name"` + Version string `mapstructure:"version" json:"version"` + PkgHash string `mapstructure:"pkgHash" json:"pkgHash"` + PkgHashExt string `mapstructure:"pkgHashExt" json:"pkgHashExt"` +} diff --git a/syft/pkg/rebar_lock_metadata.go b/syft/pkg/rebar_lock_metadata.go new file mode 100644 index 00000000000..581105cb708 --- /dev/null +++ b/syft/pkg/rebar_lock_metadata.go @@ -0,0 +1,8 @@ +package pkg + +type RebarLockMetadata struct { + Name string `mapstructure:"name" json:"name"` + Version string `mapstructure:"version" json:"version"` + PkgHash string `mapstructure:"pkgHash" json:"pkgHash"` + PkgHashExt string `mapstructure:"pkgHashExt" json:"pkgHashExt"` +} diff --git a/syft/pkg/type.go b/syft/pkg/type.go index 984f4077283..a275ed14035 100644 --- a/syft/pkg/type.go +++ b/syft/pkg/type.go @@ -10,90 +10,94 @@ type Type string const ( // the full set of supported packages UnknownPkg Type = "UnknownPackage" - BinaryPkg Type = "binary" - ApkPkg Type = "apk" AlpmPkg Type = "alpm" - GemPkg Type = "gem" + ApkPkg Type = "apk" + BinaryPkg Type = "binary" + CocoapodsPkg Type = "pod" + ConanPkg Type = "conan" + DartPubPkg Type = "dart-pub" DebPkg Type = "deb" - RpmPkg Type = "rpm" - NpmPkg Type = "npm" - PythonPkg Type = "python" - PhpComposerPkg Type = "php-composer" - JavaPkg Type = "java-archive" + DotnetPkg Type = "dotnet" + GemPkg Type = "gem" + GoModulePkg Type = "go-module" GraalVMNativeImagePkg Type = "graalvm-native-image" + HackagePkg Type = "hackage" + HexPkg Type = "hex" + JavaPkg Type = "java-archive" JenkinsPluginPkg Type = "jenkins-plugin" - GoModulePkg Type = "go-module" - RustPkg Type = "rust-crate" KbPkg Type = "msrc-kb" - DartPubPkg Type = "dart-pub" - DotnetPkg Type = "dotnet" - CocoapodsPkg Type = "pod" - ConanPkg Type = "conan" + NpmPkg Type = "npm" + PhpComposerPkg Type = "php-composer" PortagePkg Type = "portage" - HackagePkg Type = "hackage" + PythonPkg Type = "python" + RpmPkg Type = "rpm" + RustPkg Type = "rust-crate" ) // AllPkgs represents all supported package types var AllPkgs = []Type{ - ApkPkg, AlpmPkg, + ApkPkg, BinaryPkg, - GemPkg, + CocoapodsPkg, + ConanPkg, + DartPubPkg, DebPkg, - RpmPkg, - NpmPkg, - PythonPkg, - PhpComposerPkg, + DotnetPkg, + GemPkg, + GoModulePkg, + HackagePkg, + HexPkg, JavaPkg, JenkinsPluginPkg, - GoModulePkg, - RustPkg, KbPkg, - DartPubPkg, - DotnetPkg, - CocoapodsPkg, - ConanPkg, + NpmPkg, + PhpComposerPkg, PortagePkg, - HackagePkg, + PythonPkg, + RpmPkg, + RustPkg, } // PackageURLType returns the PURL package type for the current package. func (t Type) PackageURLType() string { switch t { - case ApkPkg: - return packageurl.TypeAlpine case AlpmPkg: return "alpm" - case GemPkg: - return packageurl.TypeGem + case ApkPkg: + return packageurl.TypeAlpine + case CocoapodsPkg: + return packageurl.TypeCocoapods + case ConanPkg: + return packageurl.TypeConan + case DartPubPkg: + return packageurl.TypePub case DebPkg: return "deb" - case PythonPkg: - return packageurl.TypePyPi + case DotnetPkg: + return packageurl.TypeDotnet + case GemPkg: + return packageurl.TypeGem + case HexPkg: + return packageurl.TypeHex + case GoModulePkg: + return packageurl.TypeGolang + case HackagePkg: + return packageurl.TypeHackage + case JavaPkg, JenkinsPluginPkg: + return packageurl.TypeMaven case PhpComposerPkg: return packageurl.TypeComposer + case PythonPkg: + return packageurl.TypePyPi + case PortagePkg: + return "portage" case NpmPkg: return packageurl.TypeNPM - case JavaPkg, JenkinsPluginPkg: - return packageurl.TypeMaven case RpmPkg: return packageurl.TypeRPM - case GoModulePkg: - return packageurl.TypeGolang case RustPkg: return "cargo" - case DartPubPkg: - return packageurl.TypePub - case DotnetPkg: - return packageurl.TypeDotnet - case CocoapodsPkg: - return packageurl.TypeCocoapods - case ConanPkg: - return packageurl.TypeConan - case PortagePkg: - return "portage" - case HackagePkg: - return packageurl.TypeHackage default: // TODO: should this be a "generic" purl type instead? return "" @@ -111,7 +115,7 @@ func TypeFromPURL(p string) Type { func TypeByName(name string) Type { switch name { - case packageurl.TypeDebian, "deb": + case packageurl.TypeDebian: return DebPkg case packageurl.TypeRPM: return RpmPkg @@ -145,6 +149,8 @@ func TypeByName(name string) Type { return HackagePkg case "portage": return PortagePkg + case packageurl.TypeHex: + return HexPkg default: return UnknownPkg } diff --git a/syft/pkg/type_test.go b/syft/pkg/type_test.go index 3afc2eb1d75..7f2afb33ac4 100644 --- a/syft/pkg/type_test.go +++ b/syft/pkg/type_test.go @@ -79,6 +79,10 @@ func TestTypeFromPURL(t *testing.T) { purl: "pkg:hackage/HTTP@4000.3.16", expected: HackagePkg, }, + { + purl: "pkg:hex/hpax/hpax@0.1.1", + expected: HexPkg, + }, } var pkgTypes []string diff --git a/test/integration/catalog_packages_cases_test.go b/test/integration/catalog_packages_cases_test.go index 512461105a2..aef3ab23c34 100644 --- a/test/integration/catalog_packages_cases_test.go +++ b/test/integration/catalog_packages_cases_test.go @@ -315,6 +315,38 @@ var dirOnlyTestCases = []testCase{ "ptr": "0.16.8.2", }, }, + { + name: "find hex packages", + pkgType: pkg.HexPkg, + pkgLanguage: pkg.Elixir + "," + pkg.Erlang, + pkgInfo: map[string]string{ + // elixir + "castore": "0.1.17", + "connection": "1.1.0", + "cowboy": "2.9.0", + "cowboy_telemetry": "0.4.0", + "cowlib": "2.11.0", + "db_connection": "2.4.2", + "decimal": "2.0.0", + "earmark_parser": "1.4.25", + "ecto": "3.8.1", + "ecto_sql": "3.8.1", + "esbuild": "0.5.0", + "ex_doc": "0.28.4", + "gettext": "0.19.1", + "hpax": "0.1.1", + "jason": "1.3.0", + + // erlang + "certifi": "2.9.0", + "idna": "6.1.1", + "metrics": "1.0.1", + "mimerl": "1.2.0", + "parse_trans": "3.3.1", + "ssl_verify_fun": "1.1.6", + "unicode_util_compat": "0.7.0", + }, + }, } var commonTestCases = []testCase{ diff --git a/test/integration/catalog_packages_test.go b/test/integration/catalog_packages_test.go index 84cb86ad2ee..df73d14ed1e 100644 --- a/test/integration/catalog_packages_test.go +++ b/test/integration/catalog_packages_test.go @@ -1,6 +1,7 @@ package integration import ( + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -69,6 +70,8 @@ func TestPkgCoverageImage(t *testing.T) { definedLanguages.Remove(string(pkg.Swift.String())) definedLanguages.Remove(pkg.CPP.String()) definedLanguages.Remove(pkg.Haskell.String()) + definedLanguages.Remove(pkg.Erlang.String()) + definedLanguages.Remove(pkg.Elixir.String()) observedPkgs := internal.NewStringSet() definedPkgs := internal.NewStringSet() @@ -86,6 +89,7 @@ func TestPkgCoverageImage(t *testing.T) { definedPkgs.Remove(string(pkg.ConanPkg)) definedPkgs.Remove(string(pkg.HackagePkg)) definedPkgs.Remove(string(pkg.BinaryPkg)) + definedPkgs.Remove(string(pkg.HexPkg)) var cases []testCase cases = append(cases, commonTestCases...) @@ -184,7 +188,14 @@ func TestPkgCoverageDirectory(t *testing.T) { t.Errorf("unexpected package version (pkg=%s): %s", actualPkg.Name, actualPkg.Version) } - if actualPkg.Language != test.pkgLanguage { + var foundLang bool + for _, lang := range strings.Split(test.pkgLanguage.String(), ",") { + if actualPkg.Language.String() == lang { + foundLang = true + break + } + } + if !foundLang { t.Errorf("bad language (pkg=%+v): %+v", actualPkg.Name, actualPkg.Language) } diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/elixir/mix.lock b/test/integration/test-fixtures/image-pkg-coverage/pkgs/elixir/mix.lock new file mode 100644 index 00000000000..8d9806c1b80 --- /dev/null +++ b/test/integration/test-fixtures/image-pkg-coverage/pkgs/elixir/mix.lock @@ -0,0 +1,17 @@ +%{ + "castore": {:hex, :castore, "0.1.17", "ba672681de4e51ed8ec1f74ed624d104c0db72742ea1a5e74edbc770c815182f", [:mix], [], "hexpm", "d9844227ed52d26e7519224525cb6868650c272d4a3d327ce3ca5570c12163f9"}, + "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, + "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, + "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, + "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, + "db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"}, + "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.25", "2024618731c55ebfcc5439d756852ec4e85978a39d0d58593763924d9a15916f", [:mix], [], "hexpm", "56749c5e1c59447f7b7a23ddb235e4b3defe276afc220a6227237f3efe83f51e"}, + "ecto": {:hex, :ecto, "3.8.1", "35e0bd8c8eb772e14a5191a538cd079706ecb45164ea08a7523b4fc69ab70f56", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f1b68f8d5fe3ab89e24f57c03db5b5d0aed3602077972098b3a6006a1be4b69b"}, + "ecto_sql": {:hex, :ecto_sql, "3.8.1", "1acaaba32ca0551fd19e492fc7c80414e72fc1a7140fc9395aaa53c2e8629798", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.8.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ba7fc75882edce6f2ceca047315d5db27ead773cafea47f1724e35f1e7964525"}, + "esbuild": {:hex, :esbuild, "0.5.0", "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5"}, + "ex_doc": {:hex, :ex_doc, "0.28.4", "001a0ea6beac2f810f1abc3dbf4b123e9593eaa5f00dd13ded024eae7c523298", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bf85d003dd34911d89c8ddb8bda1a958af3471a274a4c2150a9c01c78ac3f8ed"}, + "gettext": {:hex, :gettext, "0.19.1", "564953fd21f29358e68b91634799d9d26989f8d039d7512622efb3c3b1c97892", [:mix], [], "hexpm", "10c656c0912b8299adba9b061c06947511e3f109ab0d18b44a866a4498e77222"}, + "hpax": {:hex, :hpax, "0.1.1", "2396c313683ada39e98c20a75a82911592b47e5c24391363343bde74f82396ca", [:mix], [], "hexpm", "0ae7d5a0b04a8a60caf7a39fcf3ec476f35cc2cc16c05abea730d3ce6ac6c826"}, + "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"}, +} diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/erlang/rebar.lock b/test/integration/test-fixtures/image-pkg-coverage/pkgs/erlang/rebar.lock new file mode 100644 index 00000000000..34397ef86d1 --- /dev/null +++ b/test/integration/test-fixtures/image-pkg-coverage/pkgs/erlang/rebar.lock @@ -0,0 +1,26 @@ +{"1.2.0", +[{<<"certifi">>,{pkg,<<"certifi">>,<<"2.9.0">>},0}, + {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, + {<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},0}, + {<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.2.0">>},0}, + {<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.1">>},0}, + {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.6">>},0}, + {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},0}]}. +[ +{pkg_hash,[ + {<<"certifi">>, <<"6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21">>}, + {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, + {<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>}, + {<<"mimerl">>, <<"67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3">>}, + {<<"parse_trans">>, <<"16328AB840CC09919BD10DAB29E431DA3AF9E9E7E7E6F0089DD5A2D2820011D8">>}, + {<<"ssl_verify_fun">>, <<"CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0">>}, + {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]}, +{pkg_hash_ext,[ + {<<"certifi">>, <<"266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641">>}, + {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, + {<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>}, + {<<"mimerl">>, <<"F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323">>}, + {<<"parse_trans">>, <<"07CD9577885F56362D414E8C4C4E6BDF10D43A8767ABB92D24CBE8B24C54888B">>}, + {<<"ssl_verify_fun">>, <<"BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680">>}, + {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]} +].