From 2e6e3b0c74027266d35b246e7fa9a00f1fd1a5bb Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Wed, 1 Mar 2023 10:08:29 -0500 Subject: [PATCH] fix: rebar lock file decoding panic (#1628) --- syft/pkg/cataloger/erlang/erlang_parser.go | 235 ++++++++++++ .../cataloger/erlang/erlang_parser_test.go | 72 ++++ syft/pkg/cataloger/erlang/parse_rebar_lock.go | 121 +++--- .../cataloger/erlang/parse_rebar_lock_test.go | 344 +++++++++++++----- .../erlang/test-fixtures/rebar-2.lock | 32 ++ 5 files changed, 651 insertions(+), 153 deletions(-) create mode 100644 syft/pkg/cataloger/erlang/erlang_parser.go create mode 100644 syft/pkg/cataloger/erlang/erlang_parser_test.go create mode 100644 syft/pkg/cataloger/erlang/test-fixtures/rebar-2.lock diff --git a/syft/pkg/cataloger/erlang/erlang_parser.go b/syft/pkg/cataloger/erlang/erlang_parser.go new file mode 100644 index 000000000..52bf763c4 --- /dev/null +++ b/syft/pkg/cataloger/erlang/erlang_parser.go @@ -0,0 +1,235 @@ +package erlang + +import ( + "bytes" + "fmt" + "io" + "strings" + "unicode" +) + +type erlangNode struct { + value interface{} +} + +func (e erlangNode) Slice() []erlangNode { + out, ok := e.value.([]erlangNode) + if ok { + return out + } + return []erlangNode{} +} + +func (e erlangNode) String() string { + out, ok := e.value.(string) + if ok { + return out + } + return "" +} + +func (e erlangNode) Get(index int) erlangNode { + s := e.Slice() + if len(s) > index { + return s[index] + } + return erlangNode{} +} + +func node(value interface{}) erlangNode { + return erlangNode{ + value: value, + } +} + +// parseErlang basic parser for erlang, used by rebar.lock +func parseErlang(reader io.Reader) (erlangNode, error) { + data, err := io.ReadAll(reader) + if err != nil { + return node(nil), err + } + + out := erlangNode{ + value: []erlangNode{}, + } + + i := 0 + for i < len(data) { + item, err := parseErlangBlock(data, &i) + if err != nil { + return node(nil), fmt.Errorf("%w\n%s", err, printError(data, i)) + } + + skipWhitespace(data, &i) + + if i, ok := item.value.(string); ok && i == "." { + continue + } + + out.value = append(out.value.([]erlangNode), item) + } + return out, nil +} + +func printError(data []byte, i int) string { + line := 1 + char := 1 + + prev := []string{} + curr := bytes.Buffer{} + + for idx, c := range data { + if c == '\n' { + prev = append(prev, curr.String()) + curr.Reset() + if idx >= i { + break + } else { + line++ + } + char = 1 + continue + } + if idx < i { + char++ + } + curr.WriteByte(c) + } + + l1 := fmt.Sprintf("%d", line-1) + l2 := fmt.Sprintf("%d", line) + + if len(l1) < len(l2) { + l1 = " " + l1 + } + + sep := ": " + + lines := "" + if len(prev) > 1 { + lines += fmt.Sprintf("%s%s%s\n", l1, sep, prev[len(prev)-2]) + } + if len(prev) > 0 { + lines += fmt.Sprintf("%s%s%s\n", l2, sep, prev[len(prev)-1]) + } + + pointer := strings.Repeat(" ", len(l2)+len(sep)+char-1) + "^" + + return fmt.Sprintf("line: %v, char: %v\n%s%s", line, char, lines, pointer) +} + +func skipWhitespace(data []byte, i *int) { + for *i < len(data) && isWhitespace(data[*i]) { + *i++ + } +} + +func parseErlangBlock(data []byte, i *int) (erlangNode, error) { + block, err := parseErlangNode(data, i) + if err != nil { + return node(nil), err + } + + skipWhitespace(data, i) + *i++ // skip the trailing . + return block, nil +} + +func parseErlangNode(data []byte, i *int) (erlangNode, error) { + skipWhitespace(data, i) + c := data[*i] + switch c { + case '[', '{': + return parseErlangList(data, i) + case '"': + return parseErlangString(data, i) + case '<': + return parseErlangAngleString(data, i) + } + + if isLiteral(c) { + return parseErlangLiteral(data, i) + } + + return erlangNode{}, fmt.Errorf("invalid literal character: %s", string(c)) +} + +func isWhitespace(c byte) bool { + return unicode.IsSpace(rune(c)) +} + +func isLiteral(c byte) bool { + r := rune(c) + return unicode.IsNumber(r) || unicode.IsLetter(r) || r == '.' || r == '_' +} + +func parseErlangLiteral(data []byte, i *int) (erlangNode, error) { + var buf bytes.Buffer + for *i < len(data) { + c := data[*i] + if isLiteral(c) { + buf.WriteByte(c) + } else { + break + } + *i++ + } + return node(buf.String()), nil +} + +func parseErlangAngleString(data []byte, i *int) (erlangNode, error) { + *i += 2 + out, err := parseErlangString(data, i) + *i += 2 + return out, err +} + +func parseErlangString(data []byte, i *int) (erlangNode, error) { + delim := data[*i] + *i++ + var buf bytes.Buffer + for *i < len(data) { + c := data[*i] + if c == delim { + *i++ + return node(buf.String()), nil + } + if c == '\\' { + *i++ + if len(data) >= *i { + return node(nil), fmt.Errorf("invalid escape without closed string at %d", *i) + } + c = data[*i] + } + buf.WriteByte(c) + *i++ + } + return node(buf.String()), nil +} + +func parseErlangList(data []byte, i *int) (erlangNode, error) { + *i++ + out := erlangNode{ + value: []erlangNode{}, + } + for *i < len(data) { + item, err := parseErlangNode(data, i) + if err != nil { + return node(nil), err + } + out.value = append(out.value.([]erlangNode), item) + skipWhitespace(data, i) + c := data[*i] + switch c { + case ',': + *i++ + continue + case ']', '}': + *i++ + return out, nil + default: + return node(nil), fmt.Errorf("unexpected character: %s", string(c)) + } + } + return out, nil +} diff --git a/syft/pkg/cataloger/erlang/erlang_parser_test.go b/syft/pkg/cataloger/erlang/erlang_parser_test.go new file mode 100644 index 000000000..68225fb9b --- /dev/null +++ b/syft/pkg/cataloger/erlang/erlang_parser_test.go @@ -0,0 +1,72 @@ +package erlang + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_parseErlang(t *testing.T) { + tests := []struct { + name string + content string + wantErr require.ErrorAssertionFunc + }{ + { + name: "basic valid content", + content: ` +{"1.2.0", +[{<<"bcrypt">>,{pkg,<<"bcrypt">>,<<"1.1.5">>},0}, + {<<"bson">>, + {git,"https://github.com/comtihon/bson-erlang", + {ref,"14308ab927cfa69324742c3de720578094e0bb19"}}, + 1}, + {<<"syslog">>,{pkg,<<"syslog">>,<<"1.1.0">>},0}, + {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, + {<<"vernemq_dev">>, + {git,"https://github.com/vernemq/vernemq_dev.git", + {ref,"6d622aa8c901ae7777433aef2bd049e380c474a6"}}, + 0}] +}. +[ +{pkg_hash,[ + {<<"bcrypt">>, <<"A6763BD4E1AF46D34776F85B7995E63A02978DE110C077E9570ED17006E03386">>}, + {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]}, +{pkg_hash_ext,[ + {<<"bcrypt">>, <<"3418821BC17CE6E96A4A77D1A88D7485BF783E212069FACFC79510AFBFF95352">>}, + {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]} +].`, + }, + { + name: "invalid string content", + wantErr: require.Error, + content: ` +{"1.2.0 +">>}, +].`, + }, + { + name: "invalid content", + wantErr: require.Error, + content: ` +{"1.2.0"}. +].`, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + value, err := parseErlang(bytes.NewReader([]byte(test.content))) + + if test.wantErr == nil { + require.NoError(t, err) + } else { + test.wantErr(t, err) + } + + assert.IsType(t, erlangNode{}, value) + }) + } +} diff --git a/syft/pkg/cataloger/erlang/parse_rebar_lock.go b/syft/pkg/cataloger/erlang/parse_rebar_lock.go index fc0331b3a..8ff1dc5f9 100644 --- a/syft/pkg/cataloger/erlang/parse_rebar_lock.go +++ b/syft/pkg/cataloger/erlang/parse_rebar_lock.go @@ -1,12 +1,6 @@ package erlang import ( - "bufio" - "errors" - "fmt" - "io" - "regexp" - "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/pkg" @@ -14,57 +8,44 @@ import ( "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. +// parseRebarLock parses a rebar.lock and returns the discovered Elixir packages. +// +//nolint:funlen func parseRebarLock(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { - r := bufio.NewReader(reader) + doc, err := parseErlang(reader) + if err != nil { + return nil, nil, err + } 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, ok := sourcePkg.Metadata.(pkg.RebarLockMetadata) - if !ok { - log.WithFields("package", name).Warn("unable to extract rebar.lock metadata to add hash metadata") - continue - } + // rebar.lock structure is: + // [ + // ["version", [ + // [<<"package-name">>, ["version-type", "version"]... + // ], + // [ + // [pkg_hash, [ + // [<<"package-name">>, <<"package-hash">>] + // ], + // [pkg_hash_ext, [ + // [<<"package-name">>, <<"package-hash">>] + // ] + // ] + // ] - if metadata.PkgHash == "" { - metadata.PkgHash = hash - } else { - metadata.PkgHashExt = hash - } - sourcePkg.Metadata = metadata - continue - } - name, version := tokens[1], tokens[4] + versions := doc.Get(0) + deps := versions.Get(1) - sourcePkg := pkg.Package{ - Name: name, - Version: version, - Language: pkg.Erlang, - Type: pkg.HexPkg, - MetadataType: pkg.RebarLockMetadataType, + for _, dep := range deps.Slice() { + name := dep.Get(0).String() + versionNode := dep.Get(1) + versionType := versionNode.Get(0).String() + version := versionNode.Get(2).String() + + // capture git hashes if no version specified + if versionType == "git" { + version = versionNode.Get(2).Get(1).String() } p := newPackage(pkg.RebarLockMetadata{ @@ -72,15 +53,45 @@ loop: Version: version, }) - names = append(names, name) - pkgMap[sourcePkg.Name] = &p + pkgMap[name] = &p + } + + hashes := doc.Get(1) + for _, hashStruct := range hashes.Slice() { + hashType := hashStruct.Get(0).String() + + for _, hashValue := range hashStruct.Get(1).Slice() { + name := hashValue.Get(0).String() + hash := hashValue.Get(1).String() + + sourcePkg := pkgMap[name] + if sourcePkg == nil { + log.WithFields("package", name).Warn("unable find source package") + continue + } + metadata, ok := sourcePkg.Metadata.(pkg.RebarLockMetadata) + if !ok { + log.WithFields("package", name).Warn("unable to extract rebar.lock metadata to add hash metadata") + continue + } + + switch hashType { + case "pkg_hash": + metadata.PkgHash = hash + case "pkg_hash_ext": + metadata.PkgHashExt = hash + } + sourcePkg.Metadata = metadata + } } var packages []pkg.Package - for _, name := range names { - p := pkgMap[name] + for _, p := range pkgMap { p.SetID() packages = append(packages, *p) } return packages, nil, nil } + +// integrity check +var _ generic.Parser = parseRebarLock diff --git a/syft/pkg/cataloger/erlang/parse_rebar_lock_test.go b/syft/pkg/cataloger/erlang/parse_rebar_lock_test.go index db3e40d8b..7986f5b5f 100644 --- a/syft/pkg/cataloger/erlang/parse_rebar_lock_test.go +++ b/syft/pkg/cataloger/erlang/parse_rebar_lock_test.go @@ -9,111 +9,259 @@ import ( ) func TestParseRebarLock(t *testing.T) { - expected := []pkg.Package{ + tests := []struct { + fixture string + 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", + fixture: "test-fixtures/rebar.lock", + 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", + }, + }, }, }, { - 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-2.lock", + expected: []pkg.Package{ + //[{<<"bcrypt">>,{pkg,<<"bcrypt">>,<<"1.1.5">>},0}, + // {<<"bcrypt">>, <<"A6763BD4E1AF46D34776F85B7995E63A02978DE110C077E9570ED17006E03386">>}, + // {<<"bcrypt">>, <<"3418821BC17CE6E96A4A77D1A88D7485BF783E212069FACFC79510AFBFF95352">>}, + { + Name: "bcrypt", + Version: "1.1.5", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/bcrypt@1.1.5", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "bcrypt", + Version: "1.1.5", + PkgHash: "A6763BD4E1AF46D34776F85B7995E63A02978DE110C077E9570ED17006E03386", + PkgHashExt: "3418821BC17CE6E96A4A77D1A88D7485BF783E212069FACFC79510AFBFF95352", + }, + }, + // {<<"bson">>, + // {git,"https://github.com/comtihon/bson-erlang", + // {ref,"14308ab927cfa69324742c3de720578094e0bb19"}}, + // 1}, + { + Name: "bson", + Version: "14308ab927cfa69324742c3de720578094e0bb19", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/bson@14308ab927cfa69324742c3de720578094e0bb19", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "bson", + Version: "14308ab927cfa69324742c3de720578094e0bb19", + }, + }, + // {<<"certifi">>,{pkg,<<"certifi">>,<<"2.9.0">>},1}, + // {<<"certifi">>, <<"6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21">>}, {<<"stdout_formatter">>, <<"EC24868D8619757A68F0798357C7190807A1CFC42CE90C18C23760E59249A21A">>}, + // {<<"certifi">>, <<"266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641">>}, + { + 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", + }, + }, + // {<<"stdout_formatter">>,{pkg,<<"stdout_formatter">>,<<"0.2.3">>},0}, + // {<<"stdout_formatter">>, <<"EC24868D8619757A68F0798357C7190807A1CFC42CE90C18C23760E59249A21A">>}, + // {<<"stdout_formatter">>, <<"6B9CAAD8930006F9BB35680C5D3311917AC67690C3AF1BA018623324C015ABE5">>}, + { + Name: "stdout_formatter", + Version: "0.2.3", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/stdout_formatter@0.2.3", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "stdout_formatter", + Version: "0.2.3", + PkgHash: "EC24868D8619757A68F0798357C7190807A1CFC42CE90C18C23760E59249A21A", + PkgHashExt: "6B9CAAD8930006F9BB35680C5D3311917AC67690C3AF1BA018623324C015ABE5", + }, + }, + // {<<"swc">>, + // {git,"https://github.com/vernemq/ServerWideClocks.git", + // {ref,"4835239dca5a5f4ac7202dd94d7effcaa617d575"}}, + // 0}, + { + Name: "swc", + Version: "4835239dca5a5f4ac7202dd94d7effcaa617d575", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/swc@4835239dca5a5f4ac7202dd94d7effcaa617d575", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "swc", + Version: "4835239dca5a5f4ac7202dd94d7effcaa617d575", + }, + }, + // {<<"syslog">>,{pkg,<<"syslog">>,<<"1.1.0">>},0}, + // {<<"syslog">>, <<"6419A232BEA84F07B56DC575225007FFE34D9FDC91ABE6F1B2F254FD71D8EFC2">>}, + // {<<"syslog">>, <<"4C6A41373C7E20587BE33EF841D3DE6F3BEBA08519809329ECC4D27B15B659E1">>}, + { + Name: "syslog", + Version: "1.1.0", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/syslog@1.1.0", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "syslog", + Version: "1.1.0", + PkgHash: "6419A232BEA84F07B56DC575225007FFE34D9FDC91ABE6F1B2F254FD71D8EFC2", + PkgHashExt: "4C6A41373C7E20587BE33EF841D3DE6F3BEBA08519809329ECC4D27B15B659E1", + }, + }, + // {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, + // {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]}, + // {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]} + { + 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", + }, + }, + // {<<"vernemq_dev">>, + // {git,"https://github.com/vernemq/vernemq_dev.git", + // {ref,"6d622aa8c901ae7777433aef2bd049e380c474a6"}}, + // 0}]}. + { + Name: "vernemq_dev", + Version: "6d622aa8c901ae7777433aef2bd049e380c474a6", + Language: pkg.Erlang, + Type: pkg.HexPkg, + PURL: "pkg:hex/vernemq_dev@6d622aa8c901ae7777433aef2bd049e380c474a6", + MetadataType: pkg.RebarLockMetadataType, + Metadata: pkg.RebarLockMetadata{ + Name: "vernemq_dev", + Version: "6d622aa8c901ae7777433aef2bd049e380c474a6", + }, + }, }, }, } - fixture := "test-fixtures/rebar.lock" + for _, test := range tests { + t.Run(test.fixture, func(t *testing.T) { + // TODO: relationships are not under test + var expectedRelationships []artifact.Relationship - // TODO: relationships are not under test - var expectedRelationships []artifact.Relationship - - pkgtest.TestFileParser(t, fixture, parseRebarLock, expected, expectedRelationships) + pkgtest.TestFileParser(t, test.fixture, parseRebarLock, test.expected, expectedRelationships) + }) + } } diff --git a/syft/pkg/cataloger/erlang/test-fixtures/rebar-2.lock b/syft/pkg/cataloger/erlang/test-fixtures/rebar-2.lock new file mode 100644 index 000000000..a04808929 --- /dev/null +++ b/syft/pkg/cataloger/erlang/test-fixtures/rebar-2.lock @@ -0,0 +1,32 @@ +{"1.2.0", +[{<<"bcrypt">>,{pkg,<<"bcrypt">>,<<"1.1.5">>},0}, + {<<"bson">>, + {git,"https://github.com/comtihon/bson-erlang", + {ref,"14308ab927cfa69324742c3de720578094e0bb19"}}, + 1}, + {<<"certifi">>,{pkg,<<"certifi">>,<<"2.9.0">>},1}, + {<<"stdout_formatter">>,{pkg,<<"stdout_formatter">>,<<"0.2.3">>},0}, + {<<"swc">>, + {git,"https://github.com/vernemq/ServerWideClocks.git", + {ref,"4835239dca5a5f4ac7202dd94d7effcaa617d575"}}, + 0}, + {<<"syslog">>,{pkg,<<"syslog">>,<<"1.1.0">>},0}, + {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, + {<<"vernemq_dev">>, + {git,"https://github.com/vernemq/vernemq_dev.git", + {ref,"6d622aa8c901ae7777433aef2bd049e380c474a6"}}, + 0}]}. +[ +{pkg_hash,[ + {<<"bcrypt">>, <<"A6763BD4E1AF46D34776F85B7995E63A02978DE110C077E9570ED17006E03386">>}, + {<<"certifi">>, <<"6F2A475689DD47F19FB74334859D460A2DC4E3252A3324BD2111B8F0429E7E21">>}, + {<<"stdout_formatter">>, <<"EC24868D8619757A68F0798357C7190807A1CFC42CE90C18C23760E59249A21A">>}, + {<<"syslog">>, <<"6419A232BEA84F07B56DC575225007FFE34D9FDC91ABE6F1B2F254FD71D8EFC2">>}, + {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]}, +{pkg_hash_ext,[ + {<<"bcrypt">>, <<"3418821BC17CE6E96A4A77D1A88D7485BF783E212069FACFC79510AFBFF95352">>}, + {<<"certifi">>, <<"266DA46BDB06D6C6D35FDE799BCB28D36D985D424AD7C08B5BB48F5B5CDD4641">>}, + {<<"stdout_formatter">>, <<"6B9CAAD8930006F9BB35680C5D3311917AC67690C3AF1BA018623324C015ABE5">>}, + {<<"syslog">>, <<"4C6A41373C7E20587BE33EF841D3DE6F3BEBA08519809329ECC4D27B15B659E1">>}, + {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]} +].