@@ -201,7 +201,18 @@ private class RecursiveReadDir extends FileSystemAccess, FileNameProducer, DataF
201201
202202 override DataFlow:: Node getAPathArgument ( ) { result = getArgument ( 0 ) }
203203
204- override DataFlow:: Node getAFileName ( ) { result = getCallback ( [ 1 .. 2 ] ) .getParameter ( 1 ) }
204+ override DataFlow:: Node getAFileName ( ) { result = trackFileSource ( DataFlow:: TypeTracker:: end ( ) ) }
205+
206+ private DataFlow:: SourceNode trackFileSource ( DataFlow:: TypeTracker t ) {
207+ t .start ( ) and result = getCallback ( [ 1 .. 2 ] ) .getParameter ( 1 )
208+ or
209+ t .startInPromise ( ) and not exists ( getCallback ( [ 1 .. 2 ] ) ) and result = this
210+ or
211+ // Tracking out of a promise
212+ exists ( DataFlow:: TypeTracker t2 |
213+ result = PromiseTypeTracking:: promiseStep ( trackFileSource ( t2 ) , t , t2 )
214+ )
215+ }
205216}
206217
207218/**
@@ -220,10 +231,24 @@ private module JSONFile {
220231
221232 override DataFlow:: Node getAPathArgument ( ) { result = getArgument ( 0 ) }
222233
223- override DataFlow:: Node getADataNode ( ) {
224- this .getCalleeName ( ) = "readFile" and result = getCallback ( [ 1 .. 2 ] ) .getParameter ( 1 )
234+ override DataFlow:: Node getADataNode ( ) { result = trackRead ( DataFlow:: TypeTracker:: end ( ) ) }
235+
236+ private DataFlow:: SourceNode trackRead ( DataFlow:: TypeTracker t ) {
237+ this .getCalleeName ( ) = "readFile" and
238+ (
239+ t .start ( ) and result = getCallback ( [ 1 .. 2 ] ) .getParameter ( 1 )
240+ or
241+ t .startInPromise ( ) and not exists ( getCallback ( [ 1 .. 2 ] ) ) and result = this
242+ )
225243 or
226- this .getCalleeName ( ) = "readFileSync" and result = this
244+ t .start ( ) and
245+ this .getCalleeName ( ) = "readFileSync" and
246+ result = this
247+ or
248+ // Tracking out of a promise
249+ exists ( DataFlow:: TypeTracker t2 |
250+ result = PromiseTypeTracking:: promiseStep ( trackRead ( t2 ) , t , t2 )
251+ )
227252 }
228253 }
229254
@@ -243,6 +268,122 @@ private module JSONFile {
243268 }
244269}
245270
271+ /**
272+ * A call to the library `load-json-file`.
273+ */
274+ private class LoadJsonFile extends FileSystemReadAccess , DataFlow:: CallNode {
275+ LoadJsonFile ( ) {
276+ this = DataFlow:: moduleImport ( "load-json-file" ) .getACall ( )
277+ or
278+ this = DataFlow:: moduleMember ( "load-json-file" , "sync" ) .getACall ( )
279+ }
280+
281+ override DataFlow:: Node getAPathArgument ( ) { result = getArgument ( 0 ) }
282+
283+ override DataFlow:: Node getADataNode ( ) { result = trackRead ( DataFlow:: TypeTracker:: end ( ) ) }
284+
285+ private DataFlow:: SourceNode trackRead ( DataFlow:: TypeTracker t ) {
286+ this .getCalleeName ( ) = "sync" and t .start ( ) and result = this
287+ or
288+ not this .getCalleeName ( ) = "sync" and t .startInPromise ( ) and result = this
289+ or
290+ // Tracking out of a promise
291+ exists ( DataFlow:: TypeTracker t2 |
292+ result = PromiseTypeTracking:: promiseStep ( trackRead ( t2 ) , t , t2 )
293+ )
294+ }
295+ }
296+
297+ /**
298+ * A call to the library `write-json-file`.
299+ */
300+ private class WriteJsonFile extends FileSystemWriteAccess , DataFlow:: CallNode {
301+ WriteJsonFile ( ) {
302+ this = DataFlow:: moduleImport ( "write-json-file" ) .getACall ( )
303+ or
304+ this = DataFlow:: moduleMember ( "write-json-file" , "sync" ) .getACall ( )
305+ }
306+
307+ override DataFlow:: Node getAPathArgument ( ) { result = getArgument ( 0 ) }
308+
309+ override DataFlow:: Node getADataNode ( ) { result = getArgument ( 1 ) }
310+ }
311+
312+ /**
313+ * A call to the library `walkdir`.
314+ */
315+ private class WalkDir extends FileNameProducer , FileSystemAccess , DataFlow:: CallNode {
316+ WalkDir ( ) {
317+ this = DataFlow:: moduleImport ( "walkdir" ) .getACall ( )
318+ or
319+ this = DataFlow:: moduleMember ( "walkdir" , "sync" ) .getACall ( )
320+ or
321+ this = DataFlow:: moduleMember ( "walkdir" , "async" ) .getACall ( )
322+ }
323+
324+ override DataFlow:: Node getAPathArgument ( ) { result = getArgument ( 0 ) }
325+
326+ override DataFlow:: Node getAFileName ( ) { result = trackFileSource ( DataFlow:: TypeTracker:: end ( ) ) }
327+
328+ private DataFlow:: SourceNode trackFileSource ( DataFlow:: TypeTracker t ) {
329+ not this .getCalleeName ( ) = any ( string s | s = "sync" or s = "async" ) and
330+ t .start ( ) and
331+ (
332+ result = getCallback ( getNumArgument ( ) - 1 ) .getParameter ( 0 )
333+ or
334+ result = getAMethodCall ( EventEmitter:: on ( ) ) .getCallback ( 1 ) .getParameter ( 0 )
335+ )
336+ or
337+ t .start ( ) and this .getCalleeName ( ) = "sync" and result = this
338+ or
339+ t .startInPromise ( ) and this .getCalleeName ( ) = "async" and result = this
340+ or
341+ // Tracking out of a promise
342+ exists ( DataFlow:: TypeTracker t2 |
343+ result = PromiseTypeTracking:: promiseStep ( trackFileSource ( t2 ) , t , t2 )
344+ )
345+ }
346+ }
347+
348+ /**
349+ * A call to the library `globule`.
350+ */
351+ private class Globule extends FileNameProducer , FileSystemAccess , DataFlow:: CallNode {
352+ Globule ( ) {
353+ this = DataFlow:: moduleMember ( "globule" , "find" ) .getACall ( )
354+ or
355+ this = DataFlow:: moduleMember ( "globule" , "match" ) .getACall ( )
356+ or
357+ this = DataFlow:: moduleMember ( "globule" , "isMatch" ) .getACall ( )
358+ or
359+ this = DataFlow:: moduleMember ( "globule" , "mapping" ) .getACall ( )
360+ or
361+ this = DataFlow:: moduleMember ( "globule" , "findMapping" ) .getACall ( )
362+ }
363+
364+ override DataFlow:: Node getAPathArgument ( ) {
365+ ( this .getCalleeName ( ) = "match" or this .getCalleeName ( ) = "isMatch" ) and
366+ result = getArgument ( 1 )
367+ or
368+ this .getCalleeName ( ) = "mapping" and
369+ (
370+ result = getAnArgument ( ) and not exists ( result .getALocalSource ( ) .getAPropertyWrite ( "src" ) )
371+ or
372+ result = getAnArgument ( ) .getALocalSource ( ) .getAPropertyWrite ( "src" ) .getRhs ( )
373+ )
374+ }
375+
376+ override DataFlow:: Node getAFileName ( ) {
377+ result = this and
378+ (
379+ this .getCalleeName ( ) = "find" or
380+ this .getCalleeName ( ) = "match" or
381+ this .getCalleeName ( ) = "findMapping" or
382+ this .getCalleeName ( ) = "mapping"
383+ )
384+ }
385+ }
386+
246387/**
247388 * A file system access made by a NodeJS library.
248389 * This class models multiple NodeJS libraries that access files.
@@ -257,6 +398,10 @@ private class LibraryAccess extends FileSystemAccess, DataFlow::InvokeNode {
257398 or
258399 this = DataFlow:: moduleImport ( "rimraf" ) .getACall ( )
259400 or
401+ this = DataFlow:: moduleImport ( "readdirp" ) .getACall ( )
402+ or
403+ this = DataFlow:: moduleImport ( "walker" ) .getACall ( )
404+ or
260405 this =
261406 DataFlow:: moduleMember ( "node-dir" ,
262407 any ( string s |
0 commit comments