Skip to content

Commit f85c9fa

Browse files
fix(nodejs): add support for parsing workspaces from package.json as an object (aquasecurity#6231)
Co-authored-by: Teppei Fukuda <[email protected]>
1 parent 9d7f5c9 commit f85c9fa

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

pkg/dependency/parser/nodejs/packagejson/parse.go

+26-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"io"
66
"regexp"
77

8+
"github.com/samber/lo"
89
"golang.org/x/xerrors"
910

1011
"github.com/aquasecurity/trivy/pkg/dependency"
@@ -21,7 +22,7 @@ type packageJSON struct {
2122
Dependencies map[string]string `json:"dependencies"`
2223
OptionalDependencies map[string]string `json:"optionalDependencies"`
2324
DevDependencies map[string]string `json:"devDependencies"`
24-
Workspaces []string `json:"workspaces"`
25+
Workspaces any `json:"workspaces"`
2526
}
2627

2728
type Package struct {
@@ -65,7 +66,7 @@ func (p *Parser) Parse(r io.Reader) (Package, error) {
6566
Dependencies: pkgJSON.Dependencies,
6667
OptionalDependencies: pkgJSON.OptionalDependencies,
6768
DevDependencies: pkgJSON.DevDependencies,
68-
Workspaces: pkgJSON.Workspaces,
69+
Workspaces: parseWorkspaces(pkgJSON.Workspaces),
6970
}, nil
7071
}
7172

@@ -82,6 +83,29 @@ func parseLicense(val interface{}) string {
8283
return ""
8384
}
8485

86+
// parseWorkspaces returns slice of workspaces
87+
func parseWorkspaces(val any) []string {
88+
// Workspaces support 2 types - https://github.com/SchemaStore/schemastore/blob/d9516961f8a5b0e65a457808070147b5a866f60b/src/schemas/json/package.json#L777
89+
switch ws := val.(type) {
90+
// Workspace as object (map[string][]string)
91+
// e.g. "workspaces": {"packages": ["packages/*", "plugins/*"]},
92+
case map[string]interface{}:
93+
// Take only workspaces for `packages` - https://classic.yarnpkg.com/blog/2018/02/15/nohoist/
94+
if pkgsWorkspaces, ok := ws["packages"]; ok {
95+
return lo.Map(pkgsWorkspaces.([]interface{}), func(workspace interface{}, _ int) string {
96+
return workspace.(string)
97+
})
98+
}
99+
// Workspace as string array
100+
// e.g. "workspaces": ["packages/*", "backend"],
101+
case []interface{}:
102+
return lo.Map(ws, func(workspace interface{}, _ int) string {
103+
return workspace.(string)
104+
})
105+
}
106+
return nil
107+
}
108+
85109
func IsValidName(name string) bool {
86110
// Name is optional field
87111
// https://docs.npmjs.com/cli/v9/configuring-npm/package-json#name

pkg/dependency/parser/nodejs/packagejson/parse_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,20 @@ func TestParse(t *testing.T) {
7676
},
7777
},
7878
},
79+
{
80+
name: "happy path - workspace as struct",
81+
inputFile: "testdata/workspace_as_map_package.json",
82+
want: packagejson.Package{
83+
Library: types.Library{
84+
85+
Name: "example",
86+
Version: "1.0.0",
87+
},
88+
Workspaces: []string{
89+
"packages/*",
90+
},
91+
},
92+
},
7993
{
8094
name: "invalid package name",
8195
inputFile: "testdata/invalid_name.json",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "example",
3+
"version": "1.0.0",
4+
"workspaces": {
5+
"packages": ["packages/*"],
6+
"nohoist": ["**/react-native", "**/react-native/**"]
7+
}
8+
}

0 commit comments

Comments
 (0)