Skip to content

Commit

Permalink
fix(npm): npm v3 lock parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
iseki0 committed Aug 2, 2024
1 parent bbae3b6 commit 9378b4b
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 79 deletions.
146 changes: 70 additions & 76 deletions module/npm/lockfile_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import (
"encoding/json"
"fmt"
"github.com/murphysecurity/murphysec/model"
"path"
"github.com/murphysecurity/murphysec/utils"
"strings"
)

type v3Lockfile struct {
Expand All @@ -22,13 +23,7 @@ type v3Package struct {
Dev bool `json:"dev"`
}

type v3ParsedLockfile struct {
Name string
Version string
Deps []model.DependencyItem
}

func processLockfileV3(data []byte) (r *v3ParsedLockfile, e error) {
func processLockfileV3(data []byte) (r *model.DependencyItem, e error) {
var lockfile v3Lockfile
if e := json.Unmarshal(data, &lockfile); e != nil {
return nil, fmt.Errorf("parse lockfile failed: %w", e)
Expand All @@ -39,83 +34,82 @@ func processLockfileV3(data []byte) (r *v3ParsedLockfile, e error) {
if lockfile.Packages == nil {
lockfile.Packages = make(map[string]v3Package)
}
parsedLockfile := v3ParsedLockfile{
Name: lockfile.Name,
Version: lockfile.Version,
Deps: make([]model.DependencyItem, 0),
}
for i := range parsedLockfile.Deps {
parsedLockfile.Deps[i].IsDirectDependency = true
}
root := lockfile._v3Conv("", "", make(map[string]struct{}))
if root != nil {
parsedLockfile.Deps = root.Dependencies
}
return &parsedLockfile, nil
var handler visitV3Handler[*model.DependencyItem] = func(theValue *model.DependencyItem, pred, succ [2]string, isDev bool, doNext func(nextValue *model.DependencyItem)) {
var dep = model.DependencyItem{
Component: model.Component{CompName: succ[0], CompVersion: succ[1], EcoRepo: EcoRepo},
}
dep.IsOnline.SetOnline(!isDev)
doNext(&dep)
theValue.Dependencies = append(theValue.Dependencies, dep)
}
var rootNode model.DependencyItem
_visitV3[*model.DependencyItem](&lockfile, nil, nil, make(map[string]struct{}), &rootNode, handler, true)
rootNode.CompName = lockfile.Name
rootNode.CompVersion = lockfile.Version
for i := range rootNode.Dependencies {
rootNode.Dependencies[i].IsDirectDependency = true
}
return &rootNode, nil
}

func (v *v3Lockfile) _v3Conv(rp string, name string, visited map[string]struct{}) *model.DependencyItem {
if _, ok := visited[name]; ok {
return nil
}
visited[name] = struct{}{}
defer func() {
delete(visited, name)
}()
key := path.Join(rp, "node_modules", name)
pkg, ok := v.Packages[key]
if !ok {
key = path.Join(rp, name)
pkg, ok = v.Packages[key]
}
if !ok {
key = path.Join("node_modules", name)
pkg, ok = v.Packages[key]
}
if !ok {
key = name
pkg, ok = v.Packages[name]
}
if !ok {
return nil
}
if pkg.Dev {
return nil
}
var item = &model.DependencyItem{
Component: model.Component{
CompName: pkg.Name,
CompVersion: pkg.Version,
EcoRepo: EcoRepo,
},
}
if item.CompName == "" {
item.CompName = name
type visitV3Handler[T any] func(theValue T, pred, succ [2]string, isDev bool, doNext func(nextValue T))

func _visitV3[T any](lockfile *v3Lockfile, pred *v3Package, rPath []string, pathVisited map[string]struct{}, theValue T, handler visitV3Handler[T], pruneTree bool) {
if pred == nil {
var root, ok = lockfile.Packages[""]
if ok {
pred = &root
} else {
return
}
}
if pkg.Dependencies != nil {
for s := range pkg.Dependencies {
if s == "" {
var predV = [2]string{pred.Name, pred.Version}
var traversalDependencies = func(isDev bool, m map[string]string) {
for succName := range m {
var succPath, succSegments, succ, ok = npmFindCorrectPair(lockfile.Packages, rPath, succName)
if !ok {
continue
}
r := v._v3Conv(key, s, visited)
if r == nil {
if _, ok := pathVisited[succPath]; ok {
continue
}
item.Dependencies = append(item.Dependencies, *r)
}
}
if pkg.DevDependencies != nil {
for s := range pkg.DevDependencies {
if s == "" {
continue
pathVisited[succPath] = struct{}{}
var succV = [2]string{succ.Name, succ.Version}
if succV[0] == "" {
succV[0] = succName
}
r := v._v3Conv(key, s, visited)
if r == nil {
continue
var doNext = func(v T) { _visitV3(lockfile, &succ, succSegments, pathVisited, v, handler, pruneTree) }
handler(theValue, predV, succV, isDev, doNext)
if !pruneTree {
delete(pathVisited, succPath)
}
r.IsOnline.SetOnline(false)
item.Dependencies = append(item.Dependencies, *r)
}
}
return item
traversalDependencies(false, pred.Dependencies)
traversalDependencies(true, pred.DevDependencies)
}

func npmFindCorrectPair[U any](m map[string]U, rp []string, name string) (key string, segments []string, value U, ok bool) {
rp = utils.CopySlice(rp)
for {
segments = append(rp, "node_modules", name)
key = strings.Join(segments, "/")
value, ok = m[key]
if ok {
return
}
segments = append(rp, name)
key = strings.Join(segments, "/")
value, ok = m[key]
if ok {
return
}
if len(rp) == 0 {
break
}
rp = rp[:len(rp)-1]
}
key = ""
segments = nil
return
}
6 changes: 3 additions & 3 deletions module/npm/npm.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ func ScanNpmProject(ctx context.Context) ([]model.Module, error) {
if e != nil {
return nil, fmt.Errorf("v3lockfile: %w", e)
}
module.ModuleName = parsed.Name
module.ModuleVersion = parsed.Version
module.Dependencies = parsed.Deps
module.ModuleName = parsed.CompName
module.ModuleVersion = parsed.CompVersion
module.Dependencies = parsed.Dependencies
return []model.Module{module}, nil
}

Expand Down

0 comments on commit 9378b4b

Please sign in to comment.