feat(nodejs): add yarn alias support (#5818)

Signed-off-by: knqyf263 <knqyf263@gmail.com>
Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
DmitriyLewen
2024-01-04 11:16:35 +06:00
committed by GitHub
parent 013df4c6b8
commit 30eff9c83e
5 changed files with 201 additions and 11 deletions

View File

@@ -42,7 +42,10 @@ By default, Trivy doesn't report development dependencies. Use the `--include-de
### Yarn
Trivy parses `yarn.lock`, which doesn't contain information about development dependencies.
To exclude devDependencies, `package.json` also needs to be present next to `yarn.lock`.
Trivy also uses `package.json` file to handle [aliases](https://classic.yarnpkg.com/lang/en/docs/cli/add/#toc-yarn-add-alias).
To exclude devDependencies and allow aliases, `package.json` also needs to be present next to `yarn.lock`.
Trivy analyzes `.yarn` (Yarn 2+) or `node_modules` (Yarn Classic) folder next to the yarn.lock file to detect licenses.
By default, Trivy doesn't report development dependencies. Use the `--include-dev-deps` flag to include them.

View File

@@ -0,0 +1,14 @@
{
"name": "test",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"foo-json": "npm:@types/jsonstream@0.8.33",
"foo-uuid": "npm:@types/uuid"
},
"dependencies": {
"foo-debug": "npm:debug@^4.3",
"foo-ms": "npm:ms"
}
}

View File

@@ -0,0 +1,44 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/node@*":
version "20.10.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.5.tgz#47ad460b514096b7ed63a1dae26fad0914ed3ab2"
integrity sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==
dependencies:
undici-types "~5.26.4"
"foo-debug@npm:debug@^4.3":
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
"foo-json@npm:@types/jsonstream@0.8.33":
version "0.8.33"
resolved "https://registry.yarnpkg.com/@types/jsonstream/-/jsonstream-0.8.33.tgz#7d37a16a78cf68a67858110dc1767023436fca23"
integrity sha512-yhg1SNOgJ8y2nOkvAQ1zZ1Z2xibxgFs7984+EeBPuWgo/TbuYo79+rj2wUVch3KF4GhhcwAi/AlJcehmLCXb3g==
dependencies:
"@types/node" "*"
"foo-ms@npm:ms":
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
"foo-uuid@npm:@types/uuid":
version "9.0.7"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.7.tgz#b14cebc75455eeeb160d5fe23c2fcc0c64f724d8"
integrity sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
undici-types@~5.26.4:
version "5.26.5"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==

View File

@@ -9,6 +9,7 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"sort"
"strings"
@@ -36,6 +37,10 @@ func init() {
const version = 2
// Taken from Yarn
// cf. https://github.com/yarnpkg/yarn/blob/328fd596de935acc6c3e134741748fcc62ec3739/src/resolvers/exotics/registry-resolver.js#L12
var fragmentRegexp = regexp.MustCompile(`(\S+):(@?.*?)(@(.*?)|)$`)
type yarnAnalyzer struct {
packageJsonParser *packagejson.Parser
lockParser godeptypes.Parser
@@ -193,17 +198,30 @@ func (a yarnAnalyzer) walkDependencies(libs []types.Package, pkgIDs map[string]t
// Identify direct dependencies
pkgs := make(map[string]types.Package)
for _, pkg := range libs {
if constraint, ok := directDeps[pkg.Name]; ok {
// npm has own comparer to compare versions
if match, err := a.comparer.MatchVersion(pkg.Version, constraint); err != nil {
return nil, xerrors.Errorf("unable to match version for %s", pkg.Name)
} else if match {
// Mark as a direct dependency
pkg.Indirect = false
pkg.Dev = dev
pkgs[pkg.ID] = pkg
}
constraint, ok := directDeps[pkg.Name]
if !ok {
continue
}
// Handle aliases
// cf. https://classic.yarnpkg.com/lang/en/docs/cli/add/#toc-yarn-add-alias
if m := fragmentRegexp.FindStringSubmatch(constraint); len(m) == 5 {
pkg.Name = m[2] // original name
constraint = m[4]
}
// npm has own comparer to compare versions
if match, err := a.comparer.MatchVersion(pkg.Version, constraint); err != nil {
return nil, xerrors.Errorf("unable to match version for %s", pkg.Name)
} else if !match {
continue
}
// Mark as a direct dependency
pkg.Indirect = false
pkg.Dev = dev
pkgs[pkg.ID] = pkg
}
// Walk indirect dependencies

View File

@@ -318,6 +318,117 @@ func Test_yarnLibraryAnalyzer_Analyze(t *testing.T) {
},
},
},
{
name: "happy path with alias rewrite",
dir: "testdata/alias",
want: &analyzer.AnalysisResult{
Applications: []types.Application{
{
Type: types.Yarn,
FilePath: "yarn.lock",
Libraries: types.Packages{
{
ID: "foo-json@0.8.33",
Name: "@types/jsonstream",
Version: "0.8.33",
Indirect: false,
Dev: true,
Locations: []types.Location{
{
StartLine: 19,
EndLine: 24,
},
},
DependsOn: []string{
"@types/node@20.10.5",
},
},
{
ID: "@types/node@20.10.5",
Name: "@types/node",
Version: "20.10.5",
Indirect: true,
Dev: true,
Locations: []types.Location{
{
StartLine: 5,
EndLine: 10,
},
},
DependsOn: []string{
"undici-types@5.26.5",
},
},
{
ID: "foo-uuid@9.0.7",
Name: "@types/uuid",
Version: "9.0.7",
Indirect: false,
Dev: true,
Locations: []types.Location{
{
StartLine: 31,
EndLine: 34,
},
},
},
{
ID: "foo-debug@4.3.4",
Name: "debug",
Version: "4.3.4",
Indirect: false,
Locations: []types.Location{
{
StartLine: 12,
EndLine: 17,
},
},
DependsOn: []string{
"ms@2.1.2",
},
},
{
ID: "ms@2.1.2",
Name: "ms",
Version: "2.1.2",
Indirect: true,
Locations: []types.Location{
{
StartLine: 36,
EndLine: 39,
},
},
},
{
ID: "foo-ms@2.1.3",
Name: "ms",
Version: "2.1.3",
Indirect: false,
Locations: []types.Location{
{
StartLine: 26,
EndLine: 29,
},
},
},
{
ID: "undici-types@5.26.5",
Name: "undici-types",
Version: "5.26.5",
Indirect: true,
Dev: true,
Locations: []types.Location{
{
StartLine: 41,
EndLine: 44,
},
},
},
},
},
},
},
},
{
name: "monorepo",
dir: "testdata/monorepo",