Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 33 additions & 18 deletions pkg/nodejs/yarn/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
var (
yarnPatternRegexp = regexp.MustCompile(`^\s?\\?"?(?P<package>\S+?)@(?:(?P<protocol>\S+?):)?(?P<version>.+?)\\?"?:?$`)
yarnVersionRegexp = regexp.MustCompile(`^"?version:?"?\s+"?(?P<version>[^"]+)"?`)
yarnDependencyRegexp = regexp.MustCompile(`\s{4,}"?(?P<package>.+?)"?:?\s"?(?P<version>[^"]+)"?`)
yarnDependencyRegexp = regexp.MustCompile(`\s{4,}"?(?P<package>.+?)"?:?\s"?(?:(?P<protocol>\S+?):)?(?P<version>[^"]+)"?`)
)

type LockFile struct {
Expand Down Expand Up @@ -103,7 +103,12 @@ func getDependency(target string) (name, version string, err error) {
if len(capture) < 3 {
return "", "", xerrors.New("not dependency")
}
return capture[1], capture[2], nil
// skip dependencies with ignored protocols
// We warn about this when parsing this dependency as a library
if ignoreProtocol(capture[2]) {
return "", "", nil
}
return capture[1], capture[3], nil
}

func validProtocol(protocol string) bool {
Expand Down Expand Up @@ -164,8 +169,9 @@ func scanBlocks(data []byte, atEOF bool) (advance int, token []byte, err error)

func parseBlock(block []byte, lineNum int) (lib Library, deps []string, newLine int, err error) {
var (
emptyLines int // lib can start with empty lines first
skipBlock bool
emptyLines int // lib can start with empty lines first
skipBlock bool
packageNameFound bool
)

scanner := NewLineScanner(bytes.NewReader(block))
Expand Down Expand Up @@ -201,21 +207,28 @@ func parseBlock(block []byte, lineNum int) (lib Library, deps []string, newLine
continue
}

// try parse package patterns
if name, protocol, patterns, patternErr := parsePackagePatterns(line); patternErr == nil {
if patterns == nil || !validProtocol(protocol) {
skipBlock = true
if !ignoreProtocol(protocol) {
// we need to calculate the last line of the block in order to correctly determine the line numbers of the next blocks
// store the error. we will handle it later
err = xerrors.Errorf("unknown protocol: '%s', line: %s", protocol, line)
// Lines can use same patterns with packages.
// e.g. dependencies under `dependenciesMeta`:
// dependenciesMeta:
// [email protected]:
// To avoid rewriting package name,
// we need to parse only 1st found package line
if !packageNameFound {
// try parse package patterns
if name, protocol, patterns, patternErr := parsePackagePatterns(line); patternErr == nil {
if patterns == nil || !validProtocol(protocol) {
skipBlock = true
if !ignoreProtocol(protocol) {
// we need to calculate the last line of the block in order to correctly determine the line numbers of the next blocks
// store the error. we will handle it later
err = xerrors.Errorf("unknown protocol: '%s', line: %s", protocol, line)
}
continue
} else {
lib.Patterns = patterns
lib.Name = name
continue
}
continue
} else {
lib.Patterns = patterns
lib.Name = name
continue
}
}
}
Expand Down Expand Up @@ -246,7 +259,9 @@ func parseDependencies(scanner *LineScanner) (deps []string) {
// finished dependencies block
return deps
} else {
deps = append(deps, dep)
if dep != "" {
deps = append(deps, dep)
}
}
}

Expand Down
19 changes: 19 additions & 0 deletions pkg/nodejs/yarn/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,18 @@ func TestGetDependency(t *testing.T) {
expectName: "@types/color-name",
expactVersion: "^1.1.1",
},
{
name: "version with protocol",
target: ` ms: "npm:2.1.2"`,
expectName: "ms",
expactVersion: "2.1.2",
},
{
name: "version with ignore protocol",
target: ` ms: "git:2.1.2"`,
expectName: "",
expactVersion: "",
},
}

for _, v := range vectors {
Expand Down Expand Up @@ -283,6 +295,7 @@ func TestParse(t *testing.T) {
wantDeps: yarnRealWorldDeps,
},
{
name: "yarn with npm",
file: "testdata/yarn_with_npm.lock",
want: yarnWithNpm,
},
Expand Down Expand Up @@ -310,6 +323,12 @@ func TestParse(t *testing.T) {
want: yarnV2Many,
wantDeps: yarnV2ManyDeps,
},
{
name: "yarn v2 with dependenciesMeta",
file: "testdata/yarn_v2_with_depsMeta.lock",
want: yarnV2WithDependenciesMeta,
wantDeps: yarnV2WithDependenciesMetaDeps,
},
{
name: "yarn with local dependency",
file: "testdata/yarn_with_local.lock",
Expand Down
19 changes: 19 additions & 0 deletions pkg/nodejs/yarn/parse_testcase.go
Original file line number Diff line number Diff line change
Expand Up @@ -15871,4 +15871,23 @@ var (
yarnBadProtocol = []types.Library{
{ID: "[email protected]", Name: "jquery", Version: "3.4.1", Locations: []types.Location{{StartLine: 4, EndLine: 7}}},
}

// docker run --name node --rm -it node@sha256:807e66e2bee193961c9642bb1157d77a61747bf76737ca786da45b10749dcb42 sh
// mkdir app && cd app
// yarn init -y
// yarn set version berry
// yarn add [email protected]
// yarn unplug debug
// Libraries are filled manually
yarnV2WithDependenciesMeta = []types.Library{
{ID: "[email protected]", Name: "debug", Version: "4.3.4", Locations: []types.Location{{StartLine: 19, EndLine: 29}}},
{ID: "[email protected]", Name: "ms", Version: "2.1.2", Locations: []types.Location{{StartLine: 31, EndLine: 36}}},
}

yarnV2WithDependenciesMetaDeps = []types.Dependency{
{
ID: "[email protected]",
DependsOn: []string{"[email protected]"},
},
}
)
36 changes: 36 additions & 0 deletions pkg/nodejs/yarn/testdata/yarn_v2_with_depsMeta.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!

__metadata:
version: 8
cacheKey: 10c0

"app@workspace:.":
version: 0.0.0-use.local
resolution: "app@workspace:."
dependencies:
debug: "npm:^4.3.4"
dependenciesMeta:
[email protected]:
unplugged: true
languageName: unknown
linkType: soft

"debug@npm:^4.3.4":
version: 4.3.4
resolution: "debug@npm:4.3.4"
dependencies:
ms: "npm:2.1.2"
peerDependenciesMeta:
supports-color:
optional: true
checksum: cedbec45298dd5c501d01b92b119cd3faebe5438c3917ff11ae1bff86a6c722930ac9c8659792824013168ba6db7c4668225d845c633fbdafbbf902a6389f736
languageName: node
linkType: hard

"ms@npm:2.1.2":
version: 2.1.2
resolution: "ms@npm:2.1.2"
checksum: a437714e2f90dbf881b5191d35a6db792efbca5badf112f87b9e1c712aace4b4b9b742dd6537f3edf90fd6f684de897cec230abde57e87883766712ddda297cc
languageName: node
linkType: hard