Skip to content

Commit 8d3d1c2

Browse files
committed
feat: replace yarn.lock parser
1 parent 49214d7 commit 8d3d1c2

File tree

1 file changed

+2
-191
lines changed

1 file changed

+2
-191
lines changed

pkg/lockfile/parse-yarn-lock.go

Lines changed: 2 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -1,200 +1,11 @@
11
package lockfile
22

33
import (
4-
"bufio"
5-
"fmt"
6-
"net/url"
7-
"os"
8-
"strings"
9-
10-
"github.com/g-rath/osv-detector/internal/cachedregexp"
4+
"github.com/google/osv-scalibr/extractor/filesystem/language/javascript/yarnlock"
115
)
126

137
const YarnEcosystem = NpmEcosystem
148

15-
func shouldSkipYarnLine(line string) bool {
16-
return line == "" || strings.HasPrefix(line, "#")
17-
}
18-
19-
func groupPackageLines(scanner *bufio.Scanner) [][]string {
20-
var groups [][]string
21-
var group []string
22-
23-
for scanner.Scan() {
24-
line := scanner.Text()
25-
26-
if shouldSkipYarnLine(line) {
27-
continue
28-
}
29-
30-
// represents the start of a new dependency
31-
if !strings.HasPrefix(line, " ") {
32-
if len(group) > 0 {
33-
groups = append(groups, group)
34-
}
35-
group = make([]string, 0)
36-
}
37-
38-
group = append(group, line)
39-
}
40-
41-
if len(group) > 0 {
42-
groups = append(groups, group)
43-
}
44-
45-
return groups
46-
}
47-
48-
func extractYarnPackageName(str string) string {
49-
str = strings.TrimPrefix(str, "\"")
50-
str, _, _ = strings.Cut(str, ",")
51-
str, isScoped := strings.CutPrefix(str, "@")
52-
53-
name, right, _ := strings.Cut(str, "@")
54-
55-
if strings.HasPrefix(right, "npm:") && strings.Contains(right, "@") {
56-
return extractYarnPackageName(strings.TrimPrefix(right, "npm:"))
57-
}
58-
59-
if isScoped {
60-
name = "@" + name
61-
}
62-
63-
return name
64-
}
65-
66-
func determineYarnPackageVersion(group []string) string {
67-
re := cachedregexp.MustCompile(`^ {2}"?version"?:? "?([\w-.+]+)"?$`)
68-
69-
for _, s := range group {
70-
matched := re.FindStringSubmatch(s)
71-
72-
if matched != nil {
73-
return matched[1]
74-
}
75-
}
76-
77-
// todo: decide what to do here - maybe panic...?
78-
return ""
79-
}
80-
81-
func determineYarnPackageResolution(group []string) string {
82-
re := cachedregexp.MustCompile(`^ {2}"?(?:resolution:|resolved)"? "([^ '"]+)"$`)
83-
84-
for _, s := range group {
85-
matched := re.FindStringSubmatch(s)
86-
87-
if matched != nil {
88-
return matched[1]
89-
}
90-
}
91-
92-
// todo: decide what to do here - maybe panic...?
93-
return ""
94-
}
95-
96-
func tryExtractCommit(resolution string) string {
97-
// language=GoRegExp
98-
matchers := []string{
99-
// ssh://...
100-
// git://...
101-
// git+ssh://...
102-
// git+https://...
103-
`(?:^|.+@)(?:git(?:\+(?:ssh|https))?|ssh)://.+#(\w+)$`,
104-
// https://....git/...
105-
`(?:^|.+@)https://.+\.git#(\w+)$`,
106-
`https://codeload\.github\.com(?:/[\w-.]+){2}/tar\.gz/(\w+)$`,
107-
`.+#commit[:=](\w+)$`,
108-
// github:...
109-
// gitlab:...
110-
// bitbucket:...
111-
`^(?:github|gitlab|bitbucket):.+#(\w+)$`,
112-
}
113-
114-
for _, matcher := range matchers {
115-
re := cachedregexp.MustCompile(matcher)
116-
matched := re.FindStringSubmatch(resolution)
117-
118-
if matched != nil {
119-
return matched[1]
120-
}
121-
}
122-
123-
u, err := url.Parse(resolution)
124-
125-
if err == nil {
126-
gitRepoHosts := []string{
127-
"bitbucket.org",
128-
"github.com",
129-
"gitlab.com",
130-
}
131-
132-
for _, host := range gitRepoHosts {
133-
if u.Host != host {
134-
continue
135-
}
136-
137-
if u.RawQuery != "" {
138-
queries := u.Query()
139-
140-
if queries.Has("ref") {
141-
return queries.Get("ref")
142-
}
143-
}
144-
145-
return u.Fragment
146-
}
147-
}
148-
149-
return ""
150-
}
151-
152-
func parsePackageGroup(group []string) PackageDetails {
153-
name := extractYarnPackageName(group[0])
154-
version := determineYarnPackageVersion(group)
155-
resolution := determineYarnPackageResolution(group)
156-
157-
if version == "" {
158-
_, _ = fmt.Fprintf(
159-
os.Stderr,
160-
"Failed to determine version of %s while parsing a yarn.lock - please report this!\n",
161-
name,
162-
)
163-
}
164-
165-
return PackageDetails{
166-
Name: name,
167-
Version: version,
168-
Ecosystem: YarnEcosystem,
169-
CompareAs: YarnEcosystem,
170-
Commit: tryExtractCommit(resolution),
171-
}
172-
}
173-
1749
func ParseYarnLock(pathToLockfile string) ([]PackageDetails, error) {
175-
file, err := os.Open(pathToLockfile)
176-
if err != nil {
177-
return []PackageDetails{}, fmt.Errorf("could not open %s: %w", pathToLockfile, err)
178-
}
179-
defer file.Close()
180-
181-
scanner := bufio.NewScanner(file)
182-
183-
packageGroups := groupPackageLines(scanner)
184-
185-
if err := scanner.Err(); err != nil {
186-
return []PackageDetails{}, fmt.Errorf("error while scanning %s: %w", pathToLockfile, err)
187-
}
188-
189-
packages := make([]PackageDetails, 0, len(packageGroups))
190-
191-
for _, group := range packageGroups {
192-
if group[0] == "__metadata:" || strings.HasSuffix(group[0], "@workspace:.\":") {
193-
continue
194-
}
195-
196-
packages = append(packages, parsePackageGroup(group))
197-
}
198-
199-
return packages, nil
10+
return extract(pathToLockfile, yarnlock.New(), YarnEcosystem)
20011
}

0 commit comments

Comments
 (0)