11var childProcess = require ( 'child_process' )
2+ var fs = require ( 'fs' )
23var debug = require ( 'debug' ) ( 'risingstack/trace' )
4+ var yaml = require ( 'js-yaml' )
5+ var forEach = require ( 'lodash.foreach' )
6+ var uniq = require ( 'lodash.uniq' )
37
48function Security ( options ) {
59 this . name = 'Security'
@@ -14,7 +18,96 @@ function Security (options) {
1418 }
1519}
1620
17- Security . prototype . collectDependencies = function ( callback ) {
21+ Security . prototype . collectSnykFlags = function collectSnykFlags ( callback ) {
22+ fs . readFile ( '.snyk' , function ( error , data ) {
23+ if ( error ) {
24+ // .snyk file is not present, not an error
25+ callback ( null , [ ] )
26+ return
27+ }
28+
29+ var patch
30+ try {
31+ patch = yaml . safeLoad ( data ) . patch
32+ } catch ( ex ) {
33+ debug ( 'error collecting snyk flags' , ex )
34+ callback ( null , [ ] )
35+ return
36+ }
37+
38+ // no patches
39+ if ( ! patch || ! Object . keys ( patch ) . length ) {
40+ callback ( null , [ ] )
41+ return
42+ }
43+
44+ var patchedVulnIds = Object . keys ( patch )
45+ var packageNames = patchedVulnIds
46+ . map ( function ( patch ) {
47+ return patch . split ( ':' ) [ 1 ]
48+ } )
49+
50+ childProcess . execFile ( 'npm' , [ 'ls' ] . concat ( uniq ( packageNames ) ) . concat ( [ '--parseable' , '--long' ] ) , function ( _error , stdout , stderr ) {
51+ if ( _error ) {
52+ // ignore
53+ debug ( '`npm ls <package_names> --long --parseable` returned error' , _error )
54+ }
55+
56+ var pkgs = stdout . split ( '\n' )
57+ . filter ( function ( string ) {
58+ return string !== ''
59+ } )
60+ . map ( function ( pkg ) {
61+ return {
62+ nameWithVersion : pkg . split ( ':' ) [ 1 ] . trim ( ) ,
63+ path : pkg . split ( ':' ) [ 0 ] . trim ( )
64+ }
65+ } )
66+
67+ var flags = { }
68+ var pending = pkgs . length
69+ pkgs . forEach ( function ( pkg ) {
70+ flags [ pkg . nameWithVersion ] = [ ]
71+
72+ // read dependency directory
73+ fs . readdir ( pkg . path , function ( __error , files ) {
74+ if ( __error ) {
75+ pending -= 1
76+ return
77+ }
78+
79+ var vulnIds = files
80+ // filter snyk flag files
81+ . filter ( function ( file ) {
82+ return / .s n y k - .* - \d * .f l a g / . test ( file )
83+ } )
84+ // turn them into vulnIds
85+ . map ( function ( file ) {
86+ return file . split ( / - | \. / g)
87+ . filter ( function ( string ) {
88+ return string !== ''
89+ } )
90+ . slice ( 1 , 4 )
91+ . join ( ':' )
92+ } )
93+ // filter for patched vulnIds
94+ . filter ( function ( fileVulnId ) {
95+ return patchedVulnIds . indexOf ( fileVulnId ) > - 1
96+ } )
97+
98+ Array . prototype . push . apply ( flags [ pkg . nameWithVersion ] , vulnIds )
99+
100+ pending -= 1
101+ if ( ! pending ) {
102+ callback ( null , flags )
103+ }
104+ } )
105+ } )
106+ } )
107+ } )
108+ }
109+
110+ Security . prototype . collectDependencies = function collectDependencies ( callback ) {
18111 var maxBuffer = 10 * 1024 * 1024 // 10mb
19112 childProcess . execFile ( 'npm' , [ 'ls' , '--json' , '--production' ] , {
20113 maxBuffer : maxBuffer
@@ -28,26 +121,54 @@ Security.prototype.collectDependencies = function (callback) {
28121 parsedDependencies = JSON . parse ( stdout ) . dependencies
29122
30123 if ( ! parsedDependencies ) {
31- return callback ( new Error ( '`npm ls --json --production` returned with no dependencies' ) )
124+ callback ( new Error ( '`npm ls --json --production` returned with no dependencies' ) )
125+ return
32126 }
33127 } catch ( ex ) {
34- return callback ( ex )
128+ callback ( ex )
129+ return
35130 }
36131
37- return callback ( null , parsedDependencies )
132+ callback ( null , parsedDependencies )
38133 } )
39134}
40135
41- Security . prototype . sendDependencies = function ( ) {
136+ Security . prototype . sendDependencies = function sendDependencies ( ) {
42137 var _this = this
43138 this . collectDependencies ( function ( error , dependencies ) {
44139 if ( error ) {
45140 debug ( 'error collecting dependencies' , error )
46141 return
47142 }
48143
49- _this . collectorApi . sendDependencies ( dependencies )
144+ _this . collectSnykFlags ( function ( _error , flags ) {
145+ if ( _error ) {
146+ debug ( 'error collecting snyk flags' , _error )
147+ // send dependencies without patches
148+ _this . collectorApi . sendDependencies ( dependencies )
149+ return
150+ }
151+
152+ forEach ( dependencies , function ( dependency , packageName ) {
153+ addPatchesToDependency ( dependency , packageName , flags )
154+ } )
155+
156+ _this . collectorApi . sendDependencies ( dependencies )
157+ } )
50158 } )
159+
160+ function addPatchesToDependency ( dependency , packageName , flags ) {
161+ if ( dependency . dependencies ) {
162+ forEach ( dependency . dependencies , function ( subDependency , subPackageName ) {
163+ addPatchesToDependency ( subDependency , subPackageName , flags )
164+ } )
165+ }
166+
167+ var patches = flags [ packageName + '@' + dependency . version ]
168+ if ( patches ) {
169+ dependency . patches = patches
170+ }
171+ }
51172}
52173
53174function create ( options ) {
0 commit comments