Skip to content

Commit

Permalink
feat(nodejs): support yarn workspaces (aquasecurity#4664)
Browse files Browse the repository at this point in the history
* feat(nodejs): add the workspaces field to the package

* fix go.mod

* update go.mod

* compare workspaces by length
  • Loading branch information
nikpivkin committed Jun 30, 2023
1 parent 22463ab commit 73734ea
Show file tree
Hide file tree
Showing 8 changed files with 327 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "c",
"version": "0.0.0",
"dependencies": {
"is-number": "^7.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "yarn-workspace-test",
"version": "1.0.0",
"packageManager": "yarn@3.4.1",
"private": true,
"workspaces": [
"packages/**",
"c"
],
"devDependencies": {
"prettier": "^2.8.8"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "package1",
"version": "0.0.0",
"private": true,
"dependencies": {
"scheduler": "^0.23.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "package2",
"private": true,
"version": "0.0.0",
"type": "module",
"dependencies": {
"is-odd": "^3.0.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "util1",
"version": "0.0.0",
"dependencies": {
"js-tokens": "^8.0.1"
},
"devDependencies": {
"prop-types": "^15.8.1"
}
}
138 changes: 138 additions & 0 deletions pkg/fanal/analyzer/language/nodejs/yarn/testdata/monorepo/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!

__metadata:
version: 6
cacheKey: 8

"c@workspace:c":
version: 0.0.0-use.local
resolution: "c@workspace:c"
dependencies:
is-number: ^7.0.0
languageName: unknown
linkType: soft

"is-number@npm:^6.0.0":
version: 6.0.0
resolution: "is-number@npm:6.0.0"
checksum: f73bfced022128b5684bf77e0266a74e5222522bbc40f81cc1e949170c774a3c14b59a208be025d2d97a9c6b79c7c45fe351ab1c2c780872464fdedde0ae067a
languageName: node
linkType: hard

"is-number@npm:^7.0.0":
version: 7.0.0
resolution: "is-number@npm:7.0.0"
checksum: 456ac6f8e0f3111ed34668a624e45315201dff921e5ac181f8ec24923b99e9f32ca1a194912dc79d539c97d33dba17dc635202ff0b2cf98326f608323276d27a
languageName: node
linkType: hard

"is-odd@npm:^3.0.1":
version: 3.0.1
resolution: "is-odd@npm:3.0.1"
dependencies:
is-number: ^6.0.0
checksum: 4e2b20764dd2296bafe44823d127f281c7039b37d2feaf5caffc1bf162502ef2920bcd4ad171490f371d3f15f52232c763a8ffc0b3633d4c83385fe20f3493af
languageName: node
linkType: hard

"js-tokens@npm:^3.0.0 || ^4.0.0":
version: 4.0.0
resolution: "js-tokens@npm:4.0.0"
checksum: 8a95213a5a77deb6cbe94d86340e8d9ace2b93bc367790b260101d2f36a2eaf4e4e22d9fa9cf459b38af3a32fb4190e638024cf82ec95ef708680e405ea7cc78
languageName: node
linkType: hard

"js-tokens@npm:^8.0.1":
version: 8.0.1
resolution: "js-tokens@npm:8.0.1"
checksum: fb7bcd476c5b902ffb766382ca85aecb86ec66a607e419377026293b5877774e465f6cbe4229c8d85db3776ccc91c3aee518a0e04a005e260e57353f6f9278a8
languageName: node
linkType: hard

"loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
version: 1.4.0
resolution: "loose-envify@npm:1.4.0"
dependencies:
js-tokens: ^3.0.0 || ^4.0.0
bin:
loose-envify: cli.js
checksum: 6517e24e0cad87ec9888f500c5b5947032cdfe6ef65e1c1936a0c48a524b81e65542c9c3edc91c97d5bddc806ee2a985dbc79be89215d613b1de5db6d1cfe6f4
languageName: node
linkType: hard

"object-assign@npm:^4.1.1":
version: 4.1.1
resolution: "object-assign@npm:4.1.1"
checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f
languageName: node
linkType: hard

"package1@workspace:packages/package1":
version: 0.0.0-use.local
resolution: "package1@workspace:packages/package1"
dependencies:
scheduler: ^0.23.0
languageName: unknown
linkType: soft

"package2@workspace:packages/package2":
version: 0.0.0-use.local
resolution: "package2@workspace:packages/package2"
dependencies:
is-odd: ^3.0.1
languageName: unknown
linkType: soft

"prettier@npm:^2.8.8":
version: 2.8.8
resolution: "prettier@npm:2.8.8"
bin:
prettier: bin-prettier.js
checksum: b49e409431bf129dd89238d64299ba80717b57ff5a6d1c1a8b1a28b590d998a34e083fa13573bc732bb8d2305becb4c9a4407f8486c81fa7d55100eb08263cf8
languageName: node
linkType: hard

"prop-types@npm:^15.8.1":
version: 15.8.1
resolution: "prop-types@npm:15.8.1"
dependencies:
loose-envify: ^1.4.0
object-assign: ^4.1.1
react-is: ^16.13.1
checksum: c056d3f1c057cb7ff8344c645450e14f088a915d078dcda795041765047fa080d38e5d626560ccaac94a4e16e3aa15f3557c1a9a8d1174530955e992c675e459
languageName: node
linkType: hard

"react-is@npm:^16.13.1":
version: 16.13.1
resolution: "react-is@npm:16.13.1"
checksum: f7a19ac3496de32ca9ae12aa030f00f14a3d45374f1ceca0af707c831b2a6098ef0d6bdae51bd437b0a306d7f01d4677fcc8de7c0d331eb47ad0f46130e53c5f
languageName: node
linkType: hard

"scheduler@npm:^0.23.0":
version: 0.23.0
resolution: "scheduler@npm:0.23.0"
dependencies:
loose-envify: ^1.1.0
checksum: d79192eeaa12abef860c195ea45d37cbf2bbf5f66e3c4dcd16f54a7da53b17788a70d109ee3d3dde1a0fd50e6a8fc171f4300356c5aee4fc0171de526bf35f8a
languageName: node
linkType: hard

"util1@workspace:packages/utils/util1":
version: 0.0.0-use.local
resolution: "util1@workspace:packages/utils/util1"
dependencies:
js-tokens: ^8.0.1
prop-types: ^15.8.1
languageName: unknown
linkType: soft

"yarn-workspace-test@workspace:.":
version: 0.0.0-use.local
resolution: "yarn-workspace-test@workspace:."
dependencies:
prettier: ^2.8.8
languageName: unknown
linkType: soft
47 changes: 45 additions & 2 deletions pkg/fanal/analyzer/language/nodejs/yarn/yarn.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,54 @@ func (a yarnAnalyzer) parsePackageJsonDependencies(fsys fs.FS, path string) (map
}
defer func() { _ = f.Close() }()

pkg, err := a.packageJsonParser.Parse(f)
rootPkg, err := a.packageJsonParser.Parse(f)
if err != nil {
return nil, xerrors.Errorf("parse error: %w", err)
}

// Merge dependencies and optionalDependencies
return lo.Assign(pkg.Dependencies, pkg.OptionalDependencies), nil
dependencies := lo.Assign(rootPkg.Dependencies, rootPkg.OptionalDependencies)

if len(rootPkg.Workspaces) > 0 {
pkgs, err := a.traverseWorkspaces(fsys, rootPkg.Workspaces)
if err != nil {
return nil, xerrors.Errorf("traverse workspaces error: %w", err)
}
for _, pkg := range pkgs {
dependencies = lo.Assign(dependencies, pkg.Dependencies, pkg.OptionalDependencies)
}
}

return dependencies, nil
}

func (a yarnAnalyzer) traverseWorkspaces(fsys fs.FS, workspaces []string) ([]packagejson.Package, error) {
var pkgs []packagejson.Package

required := func(path string, _ fs.DirEntry) bool {
return filepath.Base(path) == types.NpmPkg
}

walkDirFunc := func(path string, d fs.DirEntry, r dio.ReadSeekerAt) error {
pkg, err := a.packageJsonParser.Parse(r)
if err != nil {
return xerrors.Errorf("unable to parse %q: %w", path, err)
}
pkgs = append(pkgs, pkg)
return nil
}

for _, workspace := range workspaces {
matches, err := fs.Glob(fsys, workspace)
if err != nil {
return nil, err
}
for _, match := range matches {
if err := fsutils.WalkDir(fsys, match, required, walkDirFunc); err != nil {
return nil, xerrors.Errorf("walk error: %w", err)
}
}

}
return pkgs, nil
}
97 changes: 97 additions & 0 deletions pkg/fanal/analyzer/language/nodejs/yarn/yarn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,103 @@ func Test_yarnLibraryAnalyzer_Analyze(t *testing.T) {
dir: "testdata/unsupported_protocol",
want: &analyzer.AnalysisResult{},
},
{
name: "monorepo",
dir: "testdata/monorepo",
want: &analyzer.AnalysisResult{
Applications: []types.Application{
{
Type: types.Yarn,
FilePath: "yarn.lock",
Libraries: []types.Package{
{
ID: "is-number@6.0.0",
Name: "is-number",
Version: "6.0.0",
Indirect: true,
Locations: []types.Location{
{
StartLine: 16,
EndLine: 21,
},
},
},
{
ID: "is-number@7.0.0",
Name: "is-number",
Version: "7.0.0",
Locations: []types.Location{
{
StartLine: 23,
EndLine: 28,
},
},
},
{
ID: "is-odd@3.0.1",
Name: "is-odd",
Version: "3.0.1",
DependsOn: []string{"is-number@6.0.0"},
Locations: []types.Location{
{
StartLine: 30,
EndLine: 37,
},
},
},
{
ID: "js-tokens@4.0.0",
Name: "js-tokens",
Version: "4.0.0",
Indirect: true,
Locations: []types.Location{
{
StartLine: 39,
EndLine: 44,
},
},
},
{
ID: "js-tokens@8.0.1",
Name: "js-tokens",
Version: "8.0.1",
Locations: []types.Location{
{
StartLine: 46,
EndLine: 51,
},
},
},
{
ID: "loose-envify@1.4.0",
Name: "loose-envify",
Version: "1.4.0",
Indirect: true,
DependsOn: []string{"js-tokens@4.0.0"},
Locations: []types.Location{
{
StartLine: 53,
EndLine: 62,
},
},
},
{
ID: "scheduler@0.23.0",
Name: "scheduler",
Version: "0.23.0",
DependsOn: []string{"loose-envify@1.4.0"},
Locations: []types.Location{
{
StartLine: 114,
EndLine: 121,
},
},
},
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit 73734ea

Please sign in to comment.