Skip to content

Commit 4eee0f3

Browse files
committed
refactor(yarn): split classic and berry lock parsing
Refs: SCA-321 Jira: https://liucheng-jira.murphy-int.com/browse/SCA-321
1 parent e745f39 commit 4eee0f3

4 files changed

Lines changed: 371 additions & 140 deletions

File tree

module/yarn/yarnlock.go

Lines changed: 11 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@ import (
55
"io"
66
"os"
77
"path/filepath"
8-
"regexp"
98
"strings"
109

1110
"github.com/iseki0/go-yarnlock"
1211
"github.com/murphysecurity/murphysec/infra/logctx"
1312
"github.com/murphysecurity/murphysec/module/pkgjs"
14-
"github.com/murphysecurity/murphysec/module/pnpm/shared"
1513
"github.com/pkg/errors"
1614
)
1715

@@ -38,10 +36,7 @@ func yarnFallback(dir string) ([]Dep, error) {
3836
}
3937

4038
for k, v := range distinct {
41-
var di Dep
42-
di.Name = k
43-
di.Version = v
44-
rs = append(rs, di)
39+
rs = append(rs, Dep{Name: k, Version: v})
4540
}
4641
return rs, nil
4742
}
@@ -58,12 +53,16 @@ func analyzeYarnDep(ctx context.Context, dir string) (r []Dep, e error) {
5853
if e != nil {
5954
return nil, errors.Wrap(e, "Read yarn.lock failed.")
6055
}
56+
6157
var lockfile yarnlock.LockFile
62-
var newLock = false
63-
if strings.Contains(string(data), "__metadata:") {
64-
lockfile, e = parseYarnLockYaml(string(data))
65-
delete(lockfile, "__metadata")
66-
newLock = true
58+
var berryData *berryLockData
59+
newLock := strings.Contains(string(data), "__metadata:")
60+
if newLock {
61+
berryData, e = parseYarnLockYamlWithIndex(string(data))
62+
if e == nil {
63+
lockfile = berryData.Lockfile
64+
delete(lockfile, "__metadata")
65+
}
6766
} else {
6867
lockfile, e = yarnlock.ParseLockFileData(data)
6968
}
@@ -81,134 +80,7 @@ func analyzeYarnDep(ctx context.Context, dir string) (r []Dep, e error) {
8180
for name, ver := range pkg.DevDependencies {
8281
pkg.DevDependencies[name] = "npm:" + ver
8382
}
83+
return buildDepTreeBerry(berryData, pkg), nil
8484
}
8585
return buildDepTree(lockfile, pkg), nil
8686
}
87-
88-
func parseYarnLockYaml(text string) (r yarnlock.LockFile, e error) {
89-
type Element struct {
90-
Version string `yaml:"version"`
91-
Dependencies map[string]string `yaml:"dependencies"`
92-
}
93-
var pkgs map[string]Element
94-
e = shared.ParseYaml([]byte(text), &pkgs)
95-
if e != nil {
96-
return
97-
}
98-
r = make(yarnlock.LockFile)
99-
for key, value := range pkgs {
100-
for keyEl := range strings.SplitSeq(key, ", ") {
101-
r[keyEl] = yarnlock.LockFileEntry{
102-
Version: value.Version,
103-
Dependencies: value.Dependencies,
104-
}
105-
}
106-
}
107-
return
108-
}
109-
110-
func buildDepTree(lkFile yarnlock.LockFile, pkg *pkgjs.Pkg) []Dep {
111-
type id struct {
112-
name string
113-
version string
114-
}
115-
var rs []Dep
116-
repeatedElement := map[id]struct{}{}
117-
for n, v := range pkg.Dependencies {
118-
node := _buildDepTree(lkFile, n+"@"+v, map[string]struct{}{}, 5)
119-
if node == nil {
120-
continue
121-
}
122-
key := id{node.Name, node.Version}
123-
if _, ok := repeatedElement[key]; ok {
124-
continue
125-
}
126-
repeatedElement[key] = struct{}{}
127-
rs = append(rs, *node)
128-
}
129-
for n, v := range pkg.DevDependencies {
130-
node := _buildDepTree(lkFile, n+"@"+v, map[string]struct{}{}, 5)
131-
if node == nil {
132-
continue
133-
}
134-
key := id{node.Name, node.Version}
135-
if _, ok := repeatedElement[key]; ok {
136-
continue
137-
}
138-
repeatedElement[key] = struct{}{}
139-
rs = append(rs, *node)
140-
}
141-
return rs
142-
}
143-
144-
var versionNpmPattern = regexp.MustCompile(`^npm:(.+?)@(.+)`)
145-
146-
func _buildDepTree(lkFile yarnlock.LockFile, element string, visitedKey map[string]struct{}, depth int) *Dep {
147-
if depth < 0 {
148-
return nil
149-
}
150-
{
151-
// avoid circle dependency
152-
if _, ok := visitedKey[element]; ok {
153-
return nil
154-
}
155-
visitedKey[element] = struct{}{}
156-
defer delete(visitedKey, element)
157-
}
158-
info, ok := lkFile[element]
159-
if !ok {
160-
return nil
161-
}
162-
pkgName, pkgVer := parsePkgName(element)
163-
if pkgName == "" || pkgVer == "" {
164-
return nil
165-
}
166-
node := &Dep{
167-
Name: pkgName,
168-
Version: info.Version, // use real version
169-
}
170-
type id struct {
171-
name string
172-
version string
173-
}
174-
repeatedElement := map[id]struct{}{}
175-
for childComp, childVer := range lkFile[element].Dependencies {
176-
childKey := childComp + "@" + childVer
177-
c := _buildDepTree(lkFile, childKey, visitedKey, depth-1)
178-
if c == nil {
179-
continue
180-
}
181-
if _, ok := repeatedElement[id{c.Name, c.Version}]; ok {
182-
continue
183-
}
184-
repeatedElement[id{c.Name, c.Version}] = struct{}{}
185-
node.Children = append(node.Children, *c)
186-
}
187-
for childComp, childVer := range lkFile[element].OptionalDependencies {
188-
childKey := childComp + "@" + childVer
189-
c := _buildDepTree(lkFile, childKey, visitedKey, depth-1)
190-
if c == nil {
191-
continue
192-
}
193-
if _, ok := repeatedElement[id{c.Name, c.Version}]; ok {
194-
continue
195-
}
196-
repeatedElement[id{c.Name, c.Version}] = struct{}{}
197-
node.Children = append(node.Children, *c)
198-
}
199-
return node
200-
}
201-
202-
var __parsePkgNamePattern = regexp.MustCompile("(@?[^@]+)@(.+)")
203-
204-
func parsePkgName(input string) (pkgName string, pkgVersion string) {
205-
m := __parsePkgNamePattern.FindStringSubmatch(input)
206-
if m == nil {
207-
return "", ""
208-
} else {
209-
if m := versionNpmPattern.FindStringSubmatch(m[2]); m != nil {
210-
return m[1], m[2]
211-
}
212-
return m[1], m[2]
213-
}
214-
}

0 commit comments

Comments
 (0)