1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
|
package yarn
import (
"bufio"
"fmt"
"io"
"regexp"
"strings"
"github.com/aquasecurity/go-dep-parser/pkg/types"
"golang.org/x/xerrors"
)
var (
yarnLocatorRegexp = regexp.MustCompile(`"?(?P<package>.+?)@(?:(?P<protocol>.+?):)?.+`)
yarnVersionRegexp = regexp.MustCompile(`\s+"?version:?"?\s+"?(?P<version>[^"]+)"?`)
)
type LockFile struct {
Dependencies map[string]Dependency
}
type Dependency struct {
Version string
// TODO : currently yarn can't recognize Dev flag.
// That need to parse package.json for Dev flag
Dev bool
Dependencies map[string]Dependency
}
func parsePackageLocator(target string) (packagename, protocol string, err error) {
capture := yarnLocatorRegexp.FindStringSubmatch(target)
if len(capture) < 2 {
return "", "", xerrors.New("not package format")
}
for i, group := range yarnLocatorRegexp.SubexpNames() {
switch group {
case "package":
packagename = capture[i]
case "protocol":
protocol = capture[i]
}
}
return
}
func getVersion(target string) (version string, err error) {
capture := yarnVersionRegexp.FindStringSubmatch(target)
if len(capture) < 2 {
return "", xerrors.New("not version")
}
return capture[len(capture)-1], nil
}
func validProtocol(protocol string) (valid bool) {
switch protocol {
// only scan npm packages
case "npm", "":
return true
}
return false
}
func Parse(r io.Reader) (libs []types.Library, err error) {
scanner := bufio.NewScanner(r)
unique := map[string]struct{}{}
var lib types.Library
var skipPackage bool
for scanner.Scan() {
line := scanner.Text()
if len(line) < 1 {
continue
}
// parse version
var version string
if version, err = getVersion(line); err == nil {
if skipPackage {
continue
}
if lib.Name == "" {
return nil, xerrors.New("Invalid yarn.lock format")
}
// fetch between version prefix and last double-quote
symbol := fmt.Sprintf("%s@%s", lib.Name, version)
if _, ok := unique[symbol]; ok {
lib = types.Library{}
continue
}
lib.Version = version
libs = append(libs, lib)
lib = types.Library{}
unique[symbol] = struct{}{}
continue
}
// skip __metadata block
if skipPackage = strings.HasPrefix(line, "__metadata"); skipPackage {
continue
}
// packagename line start 1 char
if line[:1] != " " && line[:1] != "#" {
var name string
var protocol string
if name, protocol, err = parsePackageLocator(line); err != nil {
continue
}
if skipPackage = !validProtocol(protocol); skipPackage {
continue
}
lib.Name = name
}
}
return libs, nil
}
|