Skip to content

Commit 30eff9c

Browse files
feat(nodejs): add yarn alias support (#5818)
Signed-off-by: knqyf263 <knqyf263@gmail.com> Co-authored-by: knqyf263 <knqyf263@gmail.com>
1 parent 013df4c commit 30eff9c

File tree

5 files changed

+201
-11
lines changed

5 files changed

+201
-11
lines changed

docs/docs/coverage/language/nodejs.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ By default, Trivy doesn't report development dependencies. Use the `--include-de
4242

4343
### Yarn
4444
Trivy parses `yarn.lock`, which doesn't contain information about development dependencies.
45-
To exclude devDependencies, `package.json` also needs to be present next to `yarn.lock`.
45+
Trivy also uses `package.json` file to handle [aliases](https://classic.yarnpkg.com/lang/en/docs/cli/add/#toc-yarn-add-alias).
46+
47+
To exclude devDependencies and allow aliases, `package.json` also needs to be present next to `yarn.lock`.
48+
4649
Trivy analyzes `.yarn` (Yarn 2+) or `node_modules` (Yarn Classic) folder next to the yarn.lock file to detect licenses.
4750

4851
By default, Trivy doesn't report development dependencies. Use the `--include-dev-deps` flag to include them.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "test",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"license": "MIT",
6+
"devDependencies": {
7+
"foo-json": "npm:@types/jsonstream@0.8.33",
8+
"foo-uuid": "npm:@types/uuid"
9+
},
10+
"dependencies": {
11+
"foo-debug": "npm:debug@^4.3",
12+
"foo-ms": "npm:ms"
13+
}
14+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+
# yarn lockfile v1
3+
4+
5+
"@types/node@*":
6+
version "20.10.5"
7+
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.5.tgz#47ad460b514096b7ed63a1dae26fad0914ed3ab2"
8+
integrity sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==
9+
dependencies:
10+
undici-types "~5.26.4"
11+
12+
"foo-debug@npm:debug@^4.3":
13+
version "4.3.4"
14+
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
15+
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
16+
dependencies:
17+
ms "2.1.2"
18+
19+
"foo-json@npm:@types/jsonstream@0.8.33":
20+
version "0.8.33"
21+
resolved "https://registry.yarnpkg.com/@types/jsonstream/-/jsonstream-0.8.33.tgz#7d37a16a78cf68a67858110dc1767023436fca23"
22+
integrity sha512-yhg1SNOgJ8y2nOkvAQ1zZ1Z2xibxgFs7984+EeBPuWgo/TbuYo79+rj2wUVch3KF4GhhcwAi/AlJcehmLCXb3g==
23+
dependencies:
24+
"@types/node" "*"
25+
26+
"foo-ms@npm:ms":
27+
version "2.1.3"
28+
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
29+
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
30+
31+
"foo-uuid@npm:@types/uuid":
32+
version "9.0.7"
33+
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.7.tgz#b14cebc75455eeeb160d5fe23c2fcc0c64f724d8"
34+
integrity sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==
35+
36+
ms@2.1.2:
37+
version "2.1.2"
38+
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
39+
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
40+
41+
undici-types@~5.26.4:
42+
version "5.26.5"
43+
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
44+
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==

pkg/fanal/analyzer/language/nodejs/yarn/yarn.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"path"
1111
"path/filepath"
12+
"regexp"
1213
"sort"
1314
"strings"
1415

@@ -36,6 +37,10 @@ func init() {
3637

3738
const version = 2
3839

40+
// Taken from Yarn
41+
// cf. https://github.com/yarnpkg/yarn/blob/328fd596de935acc6c3e134741748fcc62ec3739/src/resolvers/exotics/registry-resolver.js#L12
42+
var fragmentRegexp = regexp.MustCompile(`(\S+):(@?.*?)(@(.*?)|)$`)
43+
3944
type yarnAnalyzer struct {
4045
packageJsonParser *packagejson.Parser
4146
lockParser godeptypes.Parser
@@ -193,17 +198,30 @@ func (a yarnAnalyzer) walkDependencies(libs []types.Package, pkgIDs map[string]t
193198
// Identify direct dependencies
194199
pkgs := make(map[string]types.Package)
195200
for _, pkg := range libs {
196-
if constraint, ok := directDeps[pkg.Name]; ok {
197-
// npm has own comparer to compare versions
198-
if match, err := a.comparer.MatchVersion(pkg.Version, constraint); err != nil {
199-
return nil, xerrors.Errorf("unable to match version for %s", pkg.Name)
200-
} else if match {
201-
// Mark as a direct dependency
202-
pkg.Indirect = false
203-
pkg.Dev = dev
204-
pkgs[pkg.ID] = pkg
205-
}
201+
constraint, ok := directDeps[pkg.Name]
202+
if !ok {
203+
continue
204+
}
205+
206+
// Handle aliases
207+
// cf. https://classic.yarnpkg.com/lang/en/docs/cli/add/#toc-yarn-add-alias
208+
if m := fragmentRegexp.FindStringSubmatch(constraint); len(m) == 5 {
209+
pkg.Name = m[2] // original name
210+
constraint = m[4]
206211
}
212+
213+
// npm has own comparer to compare versions
214+
if match, err := a.comparer.MatchVersion(pkg.Version, constraint); err != nil {
215+
return nil, xerrors.Errorf("unable to match version for %s", pkg.Name)
216+
} else if !match {
217+
continue
218+
}
219+
220+
// Mark as a direct dependency
221+
pkg.Indirect = false
222+
pkg.Dev = dev
223+
pkgs[pkg.ID] = pkg
224+
207225
}
208226

209227
// Walk indirect dependencies

pkg/fanal/analyzer/language/nodejs/yarn/yarn_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,117 @@ func Test_yarnLibraryAnalyzer_Analyze(t *testing.T) {
318318
},
319319
},
320320
},
321+
{
322+
name: "happy path with alias rewrite",
323+
dir: "testdata/alias",
324+
want: &analyzer.AnalysisResult{
325+
Applications: []types.Application{
326+
{
327+
Type: types.Yarn,
328+
FilePath: "yarn.lock",
329+
Libraries: types.Packages{
330+
{
331+
ID: "foo-json@0.8.33",
332+
Name: "@types/jsonstream",
333+
Version: "0.8.33",
334+
Indirect: false,
335+
Dev: true,
336+
Locations: []types.Location{
337+
{
338+
StartLine: 19,
339+
EndLine: 24,
340+
},
341+
},
342+
DependsOn: []string{
343+
"@types/node@20.10.5",
344+
},
345+
},
346+
{
347+
ID: "@types/node@20.10.5",
348+
Name: "@types/node",
349+
Version: "20.10.5",
350+
Indirect: true,
351+
Dev: true,
352+
Locations: []types.Location{
353+
{
354+
StartLine: 5,
355+
EndLine: 10,
356+
},
357+
},
358+
DependsOn: []string{
359+
"undici-types@5.26.5",
360+
},
361+
},
362+
{
363+
ID: "foo-uuid@9.0.7",
364+
Name: "@types/uuid",
365+
Version: "9.0.7",
366+
Indirect: false,
367+
Dev: true,
368+
Locations: []types.Location{
369+
{
370+
StartLine: 31,
371+
EndLine: 34,
372+
},
373+
},
374+
},
375+
{
376+
ID: "foo-debug@4.3.4",
377+
Name: "debug",
378+
Version: "4.3.4",
379+
Indirect: false,
380+
Locations: []types.Location{
381+
{
382+
StartLine: 12,
383+
EndLine: 17,
384+
},
385+
},
386+
DependsOn: []string{
387+
"ms@2.1.2",
388+
},
389+
},
390+
{
391+
ID: "ms@2.1.2",
392+
Name: "ms",
393+
Version: "2.1.2",
394+
Indirect: true,
395+
Locations: []types.Location{
396+
{
397+
StartLine: 36,
398+
EndLine: 39,
399+
},
400+
},
401+
},
402+
{
403+
ID: "foo-ms@2.1.3",
404+
Name: "ms",
405+
Version: "2.1.3",
406+
Indirect: false,
407+
Locations: []types.Location{
408+
{
409+
StartLine: 26,
410+
EndLine: 29,
411+
},
412+
},
413+
},
414+
{
415+
ID: "undici-types@5.26.5",
416+
Name: "undici-types",
417+
Version: "5.26.5",
418+
Indirect: true,
419+
Dev: true,
420+
Locations: []types.Location{
421+
{
422+
StartLine: 41,
423+
EndLine: 44,
424+
},
425+
},
426+
},
427+
},
428+
},
429+
},
430+
},
431+
},
321432
{
322433
name: "monorepo",
323434
dir: "testdata/monorepo",

0 commit comments

Comments
 (0)