-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: move detection of dev/direct deps to dep parser
- Loading branch information
1 parent
cdebe92
commit 6e34f8b
Showing
2 changed files
with
75 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,8 @@ import ( | |
"bytes" | ||
"io" | ||
"regexp" | ||
"slices" | ||
"sort" | ||
"strings" | ||
|
||
"github.com/samber/lo" | ||
|
@@ -127,7 +129,7 @@ func ignoreProtocol(protocol string) bool { | |
return false | ||
} | ||
|
||
func parseResults(patternIDs map[string]string, dependsOn map[string][]string) (deps []ftypes.Dependency) { | ||
func parseResults(patternIDs map[string]string, dependsOn map[string][]string) (deps ftypes.Dependencies) { | ||
// find dependencies by patterns | ||
for pkgID, depPatterns := range dependsOn { | ||
depIDs := lo.Map(depPatterns, func(pattern string, index int) string { | ||
|
@@ -269,9 +271,15 @@ func parseDependency(line string) (string, error) { | |
} | ||
} | ||
|
||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) { | ||
func (p *Parser) Parse(r xio.ReadSeekerAt, pkgJsonDirect, pkgJsonDirectDev map[string]string) ([]ftypes.Package, []ftypes.Dependency, error) { | ||
lineNumber := 1 | ||
var pkgs []ftypes.Package | ||
var pkgs = make(map[string]ftypes.Package) | ||
var directPkgs, directDevPkgs []string | ||
|
||
// Package.json file of project contains direct/direct Dev deps in key-value format (`name`->`version constraint` e.g. `"js-tokens": "^2.0.0"`) | ||
// We need to get pattern to match packageID and pattern when parsing packages | ||
directPatterns := lo.MapToSlice(pkgJsonDirect, func(name string, ver string) string { return packageID(name, ver) }) | ||
directDevPatterns := lo.MapToSlice(pkgJsonDirectDev, func(name string, ver string) string { return packageID(name, ver) }) | ||
|
||
// patternIDs holds mapping between patterns and library IDs | ||
// e.g. ajv@^6.5.5 => [email protected] | ||
|
@@ -291,12 +299,19 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc | |
} | ||
|
||
pkgID := packageID(lib.Name, lib.Version) | ||
pkg := ftypes.Package{ | ||
ID: pkgID, | ||
Name: lib.Name, | ||
Version: lib.Version, | ||
Locations: []ftypes.Location{lib.Location}, | ||
} | ||
for _, pattern := range lib.Patterns { | ||
// Use `<pkg_name>@latest` ID for packages with `pattern` that uses `latest` version. | ||
// This is necessary to find direct dependencies when matching against the associated `package.json` file. | ||
// pkg.ID will be updated to Trivy ID format (`<pkgName>@<pkgVersion>`) later after checking `package.json` file. | ||
if _, ver, _ := strings.Cut(pattern, "@"); ver == "latest" { | ||
pkgID = pattern | ||
// Update `Relationship` and `Dev` fields for Direct pkgs | ||
if slices.Contains(directDevPatterns, pattern) { | ||
directDevPkgs = append(directDevPkgs, pkgID) | ||
} | ||
if slices.Contains(directPatterns, pattern) { | ||
directPkgs = append(directPkgs, pkgID) | ||
} | ||
// e.g. | ||
// combined-stream@^1.0.6 => [email protected] | ||
|
@@ -307,12 +322,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc | |
} | ||
} | ||
|
||
pkgs = append(pkgs, ftypes.Package{ | ||
ID: pkgID, | ||
Name: lib.Name, | ||
Version: lib.Version, | ||
Locations: []ftypes.Location{lib.Location}, | ||
}) | ||
pkgs[pkgID] = pkg | ||
} | ||
|
||
if err := scanner.Err(); err != nil { | ||
|
@@ -322,7 +332,35 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc | |
// Replace dependency patterns with library IDs | ||
// e.g. ajv@^6.5.5 => [email protected] | ||
deps := parseResults(patternIDs, dependsOn) | ||
return pkgs, deps, nil | ||
|
||
// Walk to dependsOn and update `relationship` and `Dev` fields | ||
depsMap := lo.SliceToMap(deps, func(dep ftypes.Dependency) (string, []string) { return dep.ID, dep.DependsOn }) | ||
for _, pkgID := range directDevPkgs { | ||
walkDependencies(pkgs, pkgID, depsMap, ftypes.RelationshipDirect, true) | ||
} | ||
for _, pkgID := range directPkgs { | ||
walkDependencies(pkgs, pkgID, depsMap, ftypes.RelationshipDirect, false) | ||
} | ||
|
||
pkgSlice := lo.Values(pkgs) | ||
sort.Sort(ftypes.Packages(pkgSlice)) | ||
sort.Sort(deps) | ||
|
||
return pkgSlice, deps, nil | ||
} | ||
|
||
func walkDependencies(pkgs map[string]ftypes.Package, pkgID string, deps map[string][]string, relationship ftypes.Relationship, dev bool) { | ||
pkg := pkgs[pkgID] | ||
// Update pkg fields | ||
pkg.Relationship = relationship | ||
pkg.Indirect = lo.Ternary(relationship == ftypes.RelationshipDirect, false, true) | ||
pkg.Dev = dev | ||
pkgs[pkgID] = pkg | ||
|
||
// Update child dependencies | ||
for _, depID := range deps[pkgID] { | ||
walkDependencies(pkgs, depID, deps, ftypes.RelationshipIndirect, dev) | ||
} | ||
} | ||
|
||
func packageID(name, version string) string { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters