1717package detect
1818
1919import (
20+ "bufio"
2021 "fmt"
22+ "io"
23+ "strconv"
2124 "strings"
2225
2326 "github.com/anchore/syft/syft/source"
2427 "github.com/docker/index-cli-plugin/types"
2528)
2629
27- func nodePackageDetector (_ []types.Package , image source.Source , lm types.LayerMapping ) []types.Package {
30+ func nodePackageDetector (pkgs []types.Package , image source.Source , lm types.LayerMapping ) []types.Package {
31+ // Already found nodejs via package manager
32+ for _ , p := range pkgs {
33+ if purl , err := types .ToPackageUrl (p .Purl ); err == nil && purl .Name == "nodejs" {
34+ return []types.Package {}
35+ }
36+ }
37+
2838 var path []string
2939 var nodeVersion string
3040
@@ -40,12 +50,25 @@ func nodePackageDetector(_ []types.Package, image source.Source, lm types.LayerM
4050 }
4151 }
4252
43- if nodeVersion != "" && len (path ) > 0 {
53+ if len (path ) > 0 {
4454 res , _ := image .FileResolver (source .SquashedScope )
4555 for _ , p := range path {
4656 fp := fmt .Sprintf ("%s/node" , p )
4757 if locations , err := res .FilesByPath (fp ); err == nil && len (locations ) > 0 {
4858 loc := locations [0 ]
59+
60+ if nodeVersion == "" {
61+ f , _ := res .FileContentsByLocation (loc )
62+ values := readStrings (f , "node.js/v" )
63+ if len (values ) == 1 {
64+ nodeVersion = values [0 ][9 :]
65+ }
66+ }
67+
68+ if nodeVersion == "" {
69+ continue
70+ }
71+
4972 return []types.Package {{
5073 Type : "github" ,
5174 Namespace : "nodejs" ,
@@ -68,3 +91,48 @@ func nodePackageDetector(_ []types.Package, image source.Source, lm types.LayerM
6891
6992 return []types.Package {}
7093}
94+
95+ var (
96+ min = 6
97+ max = 256
98+ ascii = true
99+ )
100+
101+ func readStrings (file io.ReadCloser , prefix string ) []string {
102+ detected := make ([]string , 0 )
103+ in := bufio .NewReader (file )
104+ str := make ([]rune , 0 , max )
105+ filePos := int64 (0 )
106+ print := func () {
107+ if len (str ) >= min {
108+ s := string (str )
109+ if strings .HasPrefix (s , prefix ) {
110+ detected = append (detected , s )
111+ }
112+ }
113+ str = str [0 :0 ]
114+ }
115+ for {
116+ var (
117+ r rune
118+ wid int
119+ err error
120+ )
121+ for ; ; filePos += int64 (wid ) {
122+ r , wid , err = in .ReadRune ()
123+ if err != nil {
124+ return detected
125+ }
126+ if ! strconv .IsPrint (r ) || ascii && r >= 0xFF {
127+ print ()
128+ continue
129+ }
130+ // It's printable. Keep it.
131+ if len (str ) >= max {
132+ print ()
133+ }
134+ str = append (str , r )
135+ }
136+ }
137+ return detected
138+ }
0 commit comments