@@ -372,6 +372,23 @@ module NodeJSLib {
372372 )
373373 }
374374
375+ /**
376+ * Holds if the `i`th parameter of method `methodName` of the Node.js
377+ * `fs` module might represent a data parameter or buffer or a callback
378+ * that receives the data.
379+ *
380+ * We determine this by looking for an externs declaration for
381+ * `fs.methodName` where the `i`th parameter's name is `data` or
382+ * `buffer` or a 'callback'.
383+ */
384+ private predicate fsDataParam ( string methodName , int i , string n ) {
385+ exists ( ExternalMemberDecl decl , Function f , JSDocParamTag p |
386+ decl .hasQualifiedName ( "fs" , methodName ) and f = decl .getInit ( ) and
387+ p .getDocumentedParameter ( ) = f .getParameter ( i ) .getAVariable ( ) and
388+ n = p .getName ( ) .toLowerCase ( ) |
389+ n = "data" or n = "buffer" or n = "callback"
390+ )
391+ }
375392 /**
376393 * A member `member` from module `fs` or its drop-in replacements `graceful-fs` or `fs-extra`.
377394 */
@@ -384,21 +401,161 @@ module NodeJSLib {
384401 )
385402 }
386403
404+
387405 /**
388406 * A call to a method from module `fs`, `graceful-fs` or `fs-extra`.
389407 */
390- private class NodeJSFileSystemAccess extends FileSystemAccess , DataFlow:: CallNode {
408+ private class NodeJSFileSystemAccessCall extends FileSystemAccess , DataFlow:: CallNode {
391409 string methodName ;
392410
393- NodeJSFileSystemAccess ( ) {
411+ NodeJSFileSystemAccessCall ( ) {
394412 this = fsModuleMember ( methodName ) .getACall ( )
395413 }
396414
415+ string getMethodName ( ) {
416+ result = methodName
417+ }
418+
419+ override DataFlow:: Node getDataNode ( ) {
420+ (
421+ methodName = "readFileSync" and
422+ result = this
423+ )
424+ or
425+ exists ( int i , string paramName | fsDataParam ( methodName , i , paramName ) |
426+ (
427+ paramName = "callback" and
428+ exists ( DataFlow:: ParameterNode p , string n |
429+ p = getCallback ( i ) .getAParameter ( ) and
430+ n = p .getName ( ) .toLowerCase ( ) and
431+ result = p |
432+ n = "data" or n = "buffer" or n = "string"
433+ )
434+ )
435+ or
436+ result = getArgument ( i ) )
437+ }
438+
397439 override DataFlow:: Node getAPathArgument ( ) {
398- exists ( int i | fsFileParam ( methodName , i ) |
399- result = getArgument ( i )
440+ exists ( int i | fsFileParam ( methodName , i ) |
441+ result = getArgument ( i ) )
442+ }
443+ }
444+
445+ /** Only NodeJSSystemFileAccessCalls that write data to 'fs' */
446+ private class NodeJSFileSystemAccessWriteCall extends FileSystemWriteAccess , NodeJSFileSystemAccessCall {
447+ NodeJSFileSystemAccessWriteCall ( ) {
448+ this .getMethodName ( ) = "appendFile" or
449+ this .getMethodName ( ) = "appendFileSync" or
450+ this .getMethodName ( ) = "write" or
451+ this .getMethodName ( ) = "writeFile" or
452+ this .getMethodName ( ) = "writeFileSync" or
453+ this .getMethodName ( ) = "writeSync"
454+ }
455+ }
456+
457+ /** Only NodeJSSystemFileAccessCalls that read data from 'fs' */
458+ private class NodeJSFileSystemAccessReadCall extends FileSystemReadAccess , NodeJSFileSystemAccessCall {
459+ NodeJSFileSystemAccessReadCall ( ) {
460+ this .getMethodName ( ) = "read" or
461+ this .getMethodName ( ) = "readSync" or
462+ this .getMethodName ( ) = "readFile" or
463+ this .getMethodName ( ) = "readFileSync"
464+ }
465+ }
466+
467+ /**
468+ * A call to write corresponds to a pattern where file stream is open first with 'createWriteStream', followed by 'write' or 'end' call
469+ */
470+ private class NodeJSFileSystemWrite extends FileSystemWriteAccess , DataFlow:: CallNode {
471+
472+ NodeJSFileSystemAccessCall init ;
473+
474+ NodeJSFileSystemWrite ( ) {
475+ exists ( NodeJSFileSystemAccessCall n |
476+ n .getCalleeName ( ) = "createWriteStream" and init = n |
477+ this = n .getAMemberCall ( "write" ) or
478+ this = n .getAMemberCall ( "end" )
479+ )
480+ }
481+
482+ override DataFlow:: Node getDataNode ( ) {
483+ result = this .getArgument ( 0 )
484+ }
485+
486+ override DataFlow:: Node getAPathArgument ( ) {
487+ result = init .getAPathArgument ( )
488+ }
489+ }
490+
491+ /**
492+ * A call to read corresponds to a pattern where file stream is open first with createReadStream, followed by 'read' call
493+ */
494+ private class NodeJSFileSystemRead extends FileSystemReadAccess , DataFlow:: CallNode {
495+
496+ NodeJSFileSystemAccessCall init ;
497+
498+ NodeJSFileSystemRead ( ) {
499+ exists ( NodeJSFileSystemAccessCall n |
500+ n .getCalleeName ( ) = "createReadStream" and init = n |
501+ this = n .getAMemberCall ( "read" )
502+ )
503+ }
504+
505+ override DataFlow:: Node getDataNode ( ) {
506+ result = this
507+ }
508+
509+ override DataFlow:: Node getAPathArgument ( ) {
510+ result = init .getAPathArgument ( )
511+ }
512+ }
513+
514+ /**
515+ * A call to read corresponds to a pattern where file stream is open first with createReadStream, followed by 'pipe' call
516+ */
517+ private class NodeJSFileSystemPipe extends FileSystemReadAccess , DataFlow:: CallNode {
518+
519+ NodeJSFileSystemAccessCall init ;
520+
521+ NodeJSFileSystemPipe ( ) {
522+ exists ( NodeJSFileSystemAccessCall n |
523+ n .getCalleeName ( ) = "createReadStream" and init = n |
524+ this = n .getAMemberCall ( "pipe" )
525+ )
526+ }
527+
528+ override DataFlow:: Node getDataNode ( ) {
529+ result = this .getArgument ( 0 )
530+ }
531+
532+ override DataFlow:: Node getAPathArgument ( ) {
533+ result = init .getAPathArgument ( )
534+ }
535+ }
536+
537+ /**
538+ * An 'on' event where data comes in as a parameter (usage: readstream.on('data', chunk))
539+ */
540+ private class NodeJSFileSystemReadDataEvent extends FileSystemReadAccess , DataFlow:: CallNode {
541+
542+ NodeJSFileSystemAccessCall init ;
543+
544+ NodeJSFileSystemReadDataEvent ( ) {
545+ exists ( NodeJSFileSystemAccessCall n |
546+ n .getCalleeName ( ) = "createReadStream" and init = n |
547+ this = n .getAMethodCall ( "on" ) and
548+ this .getArgument ( 0 ) .mayHaveStringValue ( "data" )
400549 )
401550 }
551+
552+ override DataFlow:: Node getDataNode ( ) {
553+ result = this .getCallback ( 1 ) .getParameter ( 0 )
554+ }
555+
556+ override DataFlow:: Node getAPathArgument ( ) {
557+ result = init .getAPathArgument ( )
558+ }
402559 }
403560
404561 /**
@@ -637,9 +794,20 @@ module NodeJSLib {
637794 result = "http.request data parameter"
638795 }
639796 }
640-
641-
797+
642798 /**
799+ * An argument to client request.write () method, can be used to write body to a HTTP or HTTPS POST/PUT request,
800+ * or request option (like headers, cookies, even url)
801+ */
802+ class HttpRequestWriteArgument extends HTTP:: RequestBody , DataFlow:: Node {
803+ HttpRequestWriteArgument ( ) {
804+ exists ( CustomClientRequest req |
805+ this = req .getAMethodCall ( "write" ) .getArgument ( 0 ) or
806+ this = req .getArgument ( 0 ) )
807+ }
808+ }
809+
810+ /**
643811 * 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)`.
644812 */
645813 class ClientRequestHandler extends DataFlow:: FunctionNode {
0 commit comments