@@ -336,6 +336,23 @@ module NodeJSLib {
336336 )
337337 }
338338
339+ /**
340+ * Holds if the `i`th parameter of method `methodName` of the Node.js
341+ * `fs` module might represent a data parameter or buffer or a callback
342+ * that receives the data.
343+ *
344+ * We determine this by looking for an externs declaration for
345+ * `fs.methodName` where the `i`th parameter's name is `data` or
346+ * `buffer` or a 'callback'.
347+ */
348+ private predicate fsDataParam ( string methodName , int i , string n ) {
349+ exists ( ExternalMemberDecl decl , Function f , JSDocParamTag p |
350+ decl .hasQualifiedName ( "fs" , methodName ) and f = decl .getInit ( ) and
351+ p .getDocumentedParameter ( ) = f .getParameter ( i ) .getAVariable ( ) and
352+ n = p .getName ( ) .toLowerCase ( ) |
353+ n = "data" or n = "buffer" or n = "callback"
354+ )
355+ }
339356 /**
340357 * A member `member` from module `fs` or its drop-in replacements `graceful-fs` or `fs-extra`.
341358 */
@@ -348,21 +365,161 @@ module NodeJSLib {
348365 )
349366 }
350367
368+
351369 /**
352370 * A call to a method from module `fs`, `graceful-fs` or `fs-extra`.
353371 */
354- private class NodeJSFileSystemAccess extends FileSystemAccess , DataFlow:: CallNode {
372+ private class NodeJSFileSystemAccessCall extends FileSystemAccess , DataFlow:: CallNode {
355373 string methodName ;
356374
357- NodeJSFileSystemAccess ( ) {
375+ NodeJSFileSystemAccessCall ( ) {
358376 this = fsModuleMember ( methodName ) .getACall ( )
359377 }
360378
379+ string getMethodName ( ) {
380+ result = methodName
381+ }
382+
383+ override DataFlow:: Node getDataNode ( ) {
384+ (
385+ methodName = "readFileSync" and
386+ result = this
387+ )
388+ or
389+ exists ( int i , string paramName | fsDataParam ( methodName , i , paramName ) |
390+ (
391+ paramName = "callback" and
392+ exists ( DataFlow:: ParameterNode p , string n |
393+ p = getCallback ( i ) .getAParameter ( ) and
394+ n = p .getName ( ) .toLowerCase ( ) and
395+ result = p |
396+ n = "data" or n = "buffer" or n = "string"
397+ )
398+ )
399+ or
400+ result = getArgument ( i ) )
401+ }
402+
361403 override DataFlow:: Node getAPathArgument ( ) {
362- exists ( int i | fsFileParam ( methodName , i ) |
363- result = getArgument ( i )
404+ exists ( int i | fsFileParam ( methodName , i ) |
405+ result = getArgument ( i ) )
406+ }
407+ }
408+
409+ /** Only NodeJSSystemFileAccessCalls that write data to 'fs' */
410+ private class NodeJSFileSystemAccessWriteCall extends FileSystemWriteAccess , NodeJSFileSystemAccessCall {
411+ NodeJSFileSystemAccessWriteCall ( ) {
412+ this .getMethodName ( ) = "appendFile" or
413+ this .getMethodName ( ) = "appendFileSync" or
414+ this .getMethodName ( ) = "write" or
415+ this .getMethodName ( ) = "writeFile" or
416+ this .getMethodName ( ) = "writeFileSync" or
417+ this .getMethodName ( ) = "writeSync"
418+ }
419+ }
420+
421+ /** Only NodeJSSystemFileAccessCalls that read data from 'fs' */
422+ private class NodeJSFileSystemAccessReadCall extends FileSystemReadAccess , NodeJSFileSystemAccessCall {
423+ NodeJSFileSystemAccessReadCall ( ) {
424+ this .getMethodName ( ) = "read" or
425+ this .getMethodName ( ) = "readSync" or
426+ this .getMethodName ( ) = "readFile" or
427+ this .getMethodName ( ) = "readFileSync"
428+ }
429+ }
430+
431+ /**
432+ * A call to write corresponds to a pattern where file stream is open first with 'createWriteStream', followed by 'write' or 'end' call
433+ */
434+ private class NodeJSFileSystemWrite extends FileSystemWriteAccess , DataFlow:: CallNode {
435+
436+ NodeJSFileSystemAccessCall init ;
437+
438+ NodeJSFileSystemWrite ( ) {
439+ exists ( NodeJSFileSystemAccessCall n |
440+ n .getCalleeName ( ) = "createWriteStream" and init = n |
441+ this = n .getAMemberCall ( "write" ) or
442+ this = n .getAMemberCall ( "end" )
443+ )
444+ }
445+
446+ override DataFlow:: Node getDataNode ( ) {
447+ result = this .getArgument ( 0 )
448+ }
449+
450+ override DataFlow:: Node getAPathArgument ( ) {
451+ result = init .getAPathArgument ( )
452+ }
453+ }
454+
455+ /**
456+ * A call to read corresponds to a pattern where file stream is open first with createReadStream, followed by 'read' call
457+ */
458+ private class NodeJSFileSystemRead extends FileSystemReadAccess , DataFlow:: CallNode {
459+
460+ NodeJSFileSystemAccessCall init ;
461+
462+ NodeJSFileSystemRead ( ) {
463+ exists ( NodeJSFileSystemAccessCall n |
464+ n .getCalleeName ( ) = "createReadStream" and init = n |
465+ this = n .getAMemberCall ( "read" )
466+ )
467+ }
468+
469+ override DataFlow:: Node getDataNode ( ) {
470+ result = this
471+ }
472+
473+ override DataFlow:: Node getAPathArgument ( ) {
474+ result = init .getAPathArgument ( )
475+ }
476+ }
477+
478+ /**
479+ * A call to read corresponds to a pattern where file stream is open first with createReadStream, followed by 'pipe' call
480+ */
481+ private class NodeJSFileSystemPipe extends FileSystemReadAccess , DataFlow:: CallNode {
482+
483+ NodeJSFileSystemAccessCall init ;
484+
485+ NodeJSFileSystemPipe ( ) {
486+ exists ( NodeJSFileSystemAccessCall n |
487+ n .getCalleeName ( ) = "createReadStream" and init = n |
488+ this = n .getAMemberCall ( "pipe" )
489+ )
490+ }
491+
492+ override DataFlow:: Node getDataNode ( ) {
493+ result = this .getArgument ( 0 )
494+ }
495+
496+ override DataFlow:: Node getAPathArgument ( ) {
497+ result = init .getAPathArgument ( )
498+ }
499+ }
500+
501+ /**
502+ * An 'on' event where data comes in as a parameter (usage: readstream.on('data', chunk))
503+ */
504+ private class NodeJSFileSystemReadDataEvent extends FileSystemReadAccess , DataFlow:: CallNode {
505+
506+ NodeJSFileSystemAccessCall init ;
507+
508+ NodeJSFileSystemReadDataEvent ( ) {
509+ exists ( NodeJSFileSystemAccessCall n |
510+ n .getCalleeName ( ) = "createReadStream" and init = n |
511+ this = n .getAMethodCall ( "on" ) and
512+ this .getArgument ( 0 ) .mayHaveStringValue ( "data" )
364513 )
365514 }
515+
516+ override DataFlow:: Node getDataNode ( ) {
517+ result = this .getCallback ( 1 ) .getParameter ( 0 )
518+ }
519+
520+ override DataFlow:: Node getAPathArgument ( ) {
521+ result = init .getAPathArgument ( )
522+ }
366523 }
367524
368525 /**
@@ -602,7 +759,18 @@ module NodeJSLib {
602759 }
603760 }
604761
605-
762+ /**
763+ * An argument to client request.write () method, can be used to write body to a HTTP or HTTPS POST/PUT request,
764+ * or request option (like headers, cookies, even url)
765+ */
766+ class HttpRequestWriteArgument extends HTTP:: RequestBody , DataFlow:: Node {
767+ HttpRequestWriteArgument ( ) {
768+ exists ( CustomClientRequest req |
769+ this = req .getAMethodCall ( "write" ) .getArgument ( 0 ) or
770+ this = req .getArgument ( 0 ) )
771+ }
772+ }
773+
606774 /**
607775 * A data flow node that is registered as a callback for an HTTP or HTTPS request made by a Node.js process, for example the function `handler` in `http.request(url).on(message, handler)`.
608776 */
0 commit comments