@@ -8,6 +8,10 @@ module SQL {
88 /** A string-valued expression that is interpreted as a SQL command. */
99 abstract class SqlString extends Expr { }
1010
11+ private class SqlStringFromModel extends SqlString {
12+ SqlStringFromModel ( ) { this = ModelOutput:: getASinkNode ( "sql-injection" ) .getARhs ( ) .asExpr ( ) }
13+ }
14+
1115 /**
1216 * An expression that sanitizes a string to make it safe to embed into
1317 * a SQL command.
@@ -474,176 +478,65 @@ private module MsSql {
474478 * Provides classes modeling the `sequelize` package.
475479 */
476480private module Sequelize {
477- /** Gets an import of the `sequelize` module or one that re-exports it. */
478- API:: Node sequelize ( ) { result = API:: moduleImport ( [ "sequelize" , "sequelize-typescript" ] ) }
479-
480- /** Gets an expression that creates an instance of the `Sequelize` class. */
481- API:: Node instance ( ) {
482- result = [ sequelize ( ) , sequelize ( ) .getMember ( "Sequelize" ) ] .getInstance ( )
483- or
484- result = API:: Node:: ofType ( [ "sequelize" , "sequelize-typescript" ] , [ "Sequelize" , "default" ] )
485- }
486-
487- /** A call to `Sequelize.query`. */
488- private class QueryCall extends DatabaseAccess , DataFlow:: MethodCallNode {
489- QueryCall ( ) { this = instance ( ) .getMember ( "query" ) .getACall ( ) }
490-
491- override DataFlow:: Node getAQueryArgument ( ) {
492- result = this .getArgument ( 0 )
493- or
494- result = this .getOptionArgument ( 0 , "query" )
481+ class SequelizeModel extends ModelInput:: TypeModelCsv {
482+ override predicate row ( string row ) {
483+ // package1;type1;package2;type2;path
484+ row =
485+ [
486+ "sequelize;;sequelize-typescript;;" , //
487+ "sequelize;Sequelize;sequelize;default;" , //
488+ "sequelize;Sequelize;sequelize;;Instance" ,
489+ "sequelize;Sequelize;sequelize;;Member[Sequelize].Instance" ,
490+ ]
495491 }
496492 }
497493
498- /** An expression that is passed to `Sequelize.query` method and hence interpreted as SQL. */
499- class QueryString extends SQL:: SqlString {
500- QueryString ( ) {
501- this = any ( QueryCall qc ) .getAQueryArgument ( ) .asExpr ( )
502- or
503- this = sequelize ( ) .getMember ( [ "literal" , "asIs" ] ) .getParameter ( 0 ) .getARhs ( ) .asExpr ( )
494+ class SequelizeSink extends ModelInput:: SinkModelCsv {
495+ override predicate row ( string row ) {
496+ row =
497+ [
498+ "sequelize;Sequelize;Member[query].Argument[0];sql-injection" ,
499+ "sequelize;Sequelize;Member[query].Argument[0].Member[query];sql-injection" ,
500+ "sequelize;;Member[literal,asIs].Argument[0];sql-injection" ,
501+ "sequelize;;Argument[1];credentials[user name]" ,
502+ "sequelize;;Argument[2];credentials[password]" ,
503+ "sequelize;;Argument[0..].Member[username];credentials[user name]" ,
504+ "sequelize;;Argument[0..].Member[password];credentials[password]"
505+ ]
504506 }
505507 }
506-
507- /**
508- * An expression that is passed as user name or password when creating an instance of the
509- * `Sequelize` class.
510- */
511- class Credentials extends CredentialsExpr {
512- string kind ;
513-
514- Credentials ( ) {
515- exists ( NewExpr ne , string prop |
516- ne = sequelize ( ) .getAnInstantiation ( ) .asExpr ( ) and
517- (
518- this = ne .getArgument ( 1 ) and prop = "username"
519- or
520- this = ne .getArgument ( 2 ) and prop = "password"
521- or
522- ne .hasOptionArgument ( ne .getNumArgument ( ) - 1 , prop , this )
523- ) and
524- (
525- prop = "username" and kind = "user name"
526- or
527- prop = "password" and kind = prop
528- )
529- )
530- }
531-
532- override string getCredentialsKind ( ) { result = kind }
533- }
534508}
535509
536- /**
537- * Provides classes modeling the Google Cloud Spanner library.
538- */
539- private module Spanner {
540- /**
541- * Gets a node that refers to the `Spanner` class
542- */
543- API:: Node spanner ( ) {
544- // older versions
545- result = API:: moduleImport ( "@google-cloud/spanner" )
546- or
547- // newer versions
548- result = API:: moduleImport ( "@google-cloud/spanner" ) .getMember ( "Spanner" )
549- }
550-
551- /**
552- * Gets a node that refers to an instance of the `Database` class.
553- */
554- API:: Node database ( ) {
555- result =
556- spanner ( ) .getReturn ( ) .getMember ( "instance" ) .getReturn ( ) .getMember ( "database" ) .getReturn ( )
557- or
558- result = API:: Node:: ofType ( "@google-cloud/spanner" , "Database" )
559- }
560-
561- /**
562- * Gets a node that refers to an instance of the `v1.SpannerClient` class.
563- */
564- API:: Node v1SpannerClient ( ) {
565- result = spanner ( ) .getMember ( "v1" ) .getMember ( "SpannerClient" ) .getInstance ( )
566- or
567- result = API:: Node:: ofType ( "@google-cloud/spanner" , "v1.SpannerClient" )
568- }
569-
570- /**
571- * Gets a node that refers to a transaction object.
572- */
573- API:: Node transaction ( ) {
574- result =
575- database ( )
576- .getMember ( [ "runTransaction" , "runTransactionAsync" ] )
577- .getParameter ( [ 0 , 1 ] )
578- .getParameter ( 1 )
579- or
580- result = API:: Node:: ofType ( "@google-cloud/spanner" , "Transaction" )
581- }
582-
583- /** Gets an API node referring to a `BatchTransaction` object. */
584- API:: Node batchTransaction ( ) {
585- result = database ( ) .getMember ( "batchTransaction" ) .getReturn ( )
586- or
587- result = database ( ) .getMember ( "createBatchTransaction" ) .getReturn ( ) .getPromised ( )
588- or
589- result = API:: Node:: ofType ( "@google-cloud/spanner" , "BatchTransaction" )
590- }
591-
592- /**
593- * A call to a Spanner method that executes a SQL query.
594- */
595- abstract class SqlExecution extends DatabaseAccess , DataFlow:: InvokeNode { }
596-
597- /**
598- * A SQL execution that takes the input directly in the first argument or in the `sql` option.
599- */
600- class SqlExecutionDirect extends SqlExecution {
601- SqlExecutionDirect ( ) {
602- this = database ( ) .getMember ( [ "run" , "runPartitionedUpdate" , "runStream" ] ) .getACall ( )
603- or
604- this = transaction ( ) .getMember ( [ "run" , "runStream" , "runUpdate" ] ) .getACall ( )
605- or
606- this = batchTransaction ( ) .getMember ( "createQueryPartitions" ) .getACall ( )
607- }
608-
609- override DataFlow:: Node getAQueryArgument ( ) {
610- result = this .getArgument ( 0 )
611- or
612- result = this .getOptionArgument ( 0 , "sql" )
510+ private module SpannerCsv {
511+ class SpannerTypes extends ModelInput:: TypeModelCsv {
512+ override predicate row ( string row ) {
513+ // package1; type1; package2; type2; path
514+ row =
515+ [
516+ "@google-cloud/spanner;;@google-cloud/spanner;;Member[Spanner]" ,
517+ "@google-cloud/spanner;Database;@google-cloud/spanner;;ReturnValue.Member[instance].ReturnValue.Member[database].ReturnValue" ,
518+ "@google-cloud/spanner;v1.SpannerClient;@google-cloud/spanner;;Member[v1].Member[SpannerClient].Instance" ,
519+ "@google-cloud/spanner;Transaction;@google-cloud/spanner;Database;Member[runTransaction,runTransactionAsync].Argument[0..1].Parameter[1]" ,
520+ "@google-cloud/spanner;BatchTransaction;@google-cloud/spanner;Database;Member[batchTransaction].ReturnValue" ,
521+ "@google-cloud/spanner;BatchTransaction;@google-cloud/spanner;Database;Member[createBatchTransaction].ReturnValue.Awaited" ,
522+ "@google-cloud/spanner;~SqlExecutorDirect;@google-cloud/spanner;Database;Member[run,runPartitionedUpdate,runStream]" ,
523+ "@google-cloud/spanner;~SqlExecutorDirect;@google-cloud/spanner;Transaction;Member[run,runStream,runUpdate]" ,
524+ "@google-cloud/spanner;~SqlExecutorDirect;@google-cloud/spanner;BatchTransaction;Member[createQueryPartitions]" ,
525+ ]
613526 }
614527 }
615528
616- /**
617- * A SQL execution that takes an array of SQL strings or { sql: string } objects.
618- */
619- class SqlExecutionBatch extends SqlExecution , API:: CallNode {
620- SqlExecutionBatch ( ) { this = transaction ( ) .getMember ( "batchUpdate" ) .getACall ( ) }
621-
622- override DataFlow:: Node getAQueryArgument ( ) {
623- // just use the whole array as the query argument, as arrays becomes tainted if one of the elements
624- // are tainted
625- result = this .getArgument ( 0 )
626- or
627- result = this .getParameter ( 0 ) .getUnknownMember ( ) .getMember ( "sql" ) .getARhs ( )
529+ class SpannerSinks extends ModelInput:: SinkModelCsv {
530+ override predicate row ( string row ) {
531+ // package; type; path; kind
532+ row =
533+ [
534+ "@google-cloud/spanner;~SqlExecutorDirect;Argument[0];sql-injection" ,
535+ "@google-cloud/spanner;~SqlExecutorDirect;Argument[0].Member[sql];sql-injection" ,
536+ "@google-cloud/spanner;Transaction;Member[batchUpdate].Argument[0];sql-injection" ,
537+ "@google-cloud/spanner;Transaction;Member[batchUpdate].Argument[0].ArrayElement.Member[sql];sql-injection" ,
538+ "@google-cloud/spanner;v1.SpannerClient;Member[executeSql,executeStreamingSql].Argument[0].Member[sql];sql-injection" ,
539+ ]
628540 }
629541 }
630-
631- /**
632- * A SQL execution that only takes the input in the `sql` option, and do not accept query strings
633- * directly.
634- */
635- class SqlExecutionWithOption extends SqlExecution {
636- SqlExecutionWithOption ( ) {
637- this = v1SpannerClient ( ) .getMember ( [ "executeSql" , "executeStreamingSql" ] ) .getACall ( )
638- }
639-
640- override DataFlow:: Node getAQueryArgument ( ) { result = this .getOptionArgument ( 0 , "sql" ) }
641- }
642-
643- /**
644- * An expression that is interpreted as a SQL string.
645- */
646- class QueryString extends SQL:: SqlString {
647- QueryString ( ) { this = any ( SqlExecution se ) .getAQueryArgument ( ) .asExpr ( ) }
648- }
649542}
0 commit comments