11package pkg
22
33import (
4+ "bufio"
45 "context"
6+ "fmt"
57 "os"
68 "path/filepath"
9+ "strconv"
710 "strings"
811
912 "github.com/samber/lo"
1013
14+ "github.com/aquasecurity/trivy/pkg/dependency"
1115 "github.com/aquasecurity/trivy/pkg/dependency/parser/nodejs/packagejson"
1216 "github.com/aquasecurity/trivy/pkg/fanal/analyzer"
1317 "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
@@ -20,8 +24,9 @@ func init() {
2024}
2125
2226const (
23- version = 1
24- requiredFile = "package.json"
27+ version = 2
28+ requiredFile = "package.json"
29+ requiredNodeVersionFile = "node_version.h"
2530)
2631
2732type parser struct {}
@@ -42,15 +47,64 @@ func (*parser) Parse(_ context.Context, r xio.ReadSeekerAt) ([]types.Package, []
4247 return []types.Package {pkg .Package }, nil , nil
4348}
4449
50+ type nodeVersionParser struct {}
51+
52+ func (* nodeVersionParser ) Parse (_ context.Context , r xio.ReadSeekerAt ) ([]types.Package , []types.Dependency , error ) {
53+ var versions [3 ]int
54+ var found [3 ]bool
55+ scanner := bufio .NewScanner (r )
56+ for scanner .Scan () {
57+ fields := strings .Fields (scanner .Text ())
58+ if len (fields ) != 3 || fields [0 ] != "#define" {
59+ continue
60+ }
61+
62+ var i int
63+ switch fields [1 ] {
64+ case "NODE_MAJOR_VERSION" :
65+ i = 0
66+ case "NODE_MINOR_VERSION" :
67+ i = 1
68+ case "NODE_PATCH_VERSION" :
69+ i = 2
70+ default :
71+ continue
72+ }
73+ v , err := strconv .Atoi (fields [2 ])
74+ if err != nil {
75+ return nil , nil , err
76+ }
77+ versions [i ], found [i ] = v , true
78+ }
79+ if err := scanner .Err (); err != nil {
80+ return nil , nil , err
81+ }
82+ if ! found [0 ] || ! found [1 ] || ! found [2 ] {
83+ return nil , nil , nil
84+ }
85+
86+ version := fmt .Sprintf ("%d.%d.%d" , versions [0 ], versions [1 ], versions [2 ])
87+ return []types.Package {{
88+ ID : dependency .ID (types .NodePkg , "node" , version ),
89+ Name : "node" ,
90+ Version : version ,
91+ }}, nil , nil
92+ }
93+
4594type nodePkgLibraryAnalyzer struct {}
4695
47- // Analyze analyzes package.json for node packages
96+ // Analyze analyzes package.json files and Node.js version headers.
4897func (a nodePkgLibraryAnalyzer ) Analyze (ctx context.Context , input analyzer.AnalysisInput ) (* analyzer.AnalysisResult , error ) {
49- return language .AnalyzePackage (ctx , types .NodePkg , input .FilePath , input .Content , & parser {}, input .Options .FileChecksum )
98+ var p language.Parser = & parser {}
99+ if filepath .Base (input .FilePath ) == requiredNodeVersionFile {
100+ p = & nodeVersionParser {}
101+ }
102+ return language .AnalyzePackage (ctx , types .NodePkg , input .FilePath , input .Content , p , input .Options .FileChecksum )
50103}
51104
52105func (a nodePkgLibraryAnalyzer ) Required (filePath string , _ os.FileInfo ) bool {
53- return requiredFile == filepath .Base (filePath )
106+ return filepath .Base (filePath ) == requiredFile ||
107+ strings .HasSuffix (filepath .ToSlash (filePath ), "include/node/" + requiredNodeVersionFile )
54108}
55109
56110func (a nodePkgLibraryAnalyzer ) Type () analyzer.Type {
0 commit comments