@@ -42,7 +42,83 @@ module TemplateObjectInjection {
4242 override DataFlow:: FlowLabel getAFlowLabel ( ) { result .isTaint ( ) }
4343 }
4444
45- private class TemplateSink extends Sink {
46- TemplateSink ( ) { this .asExpr ( ) instanceof Express:: TemplateObjectInput }
45+ /**
46+ * An argument given to the `render` method on an Express response object,
47+ * where the view engine used by the Express instance is vulnerable to template object injection.
48+ */
49+ private class TemplateSink extends Sink , Express:: TemplateObjectInput {
50+ TemplateSink ( ) {
51+ exists (
52+ Express:: RouteSetup setup , Express:: RouterDefinition router , Express:: RouterDefinition top
53+ |
54+ setup .getARouteHandler ( ) = getRouteHandler ( ) and
55+ setup .getRouter ( ) = router and
56+ usesVulnerableTemplateEngine ( router )
57+ )
58+ }
59+ }
60+
61+ /**
62+ * Gets the package name for a templating library that is vulnerable to template object injection.
63+ */
64+ private string getAVulnerableTemplateEngine ( ) {
65+ result =
66+ [
67+ "eta" , "squirrelly" , "haml-coffee" , "express-hbs" , "ejs" , "hbs" , "whiskers" ,
68+ "express-handlebars"
69+ ]
70+ }
71+
72+ /**
73+ * Holds if the "view engine" of `router` is set to a vulnerable templating engine.
74+ */
75+ predicate usesVulnerableTemplateEngine ( Express:: RouterDefinition router ) {
76+ // option 1: `app.set("view engine", "theEngine")`.
77+ // Express will load the engine automatically.
78+ exists ( MethodCallExpr call |
79+ router .flowsTo ( call .getReceiver ( ) ) and
80+ call .getMethodName ( ) = "set" and
81+ call .getArgument ( 0 ) .getStringValue ( ) = "view engine" and
82+ call .getArgument ( 1 ) .getStringValue ( ) = getAVulnerableTemplateEngine ( )
83+ )
84+ or
85+ // option 2: setup an engine.
86+ // ```app.engine("name", engine); app.set("view engine", "name");```
87+ // ^^^ `registerCall` ^^^ ^^^ `viewEngineCall` ^^^
88+ exists (
89+ DataFlow:: MethodCallNode registerCall , DataFlow:: SourceNode engine ,
90+ DataFlow:: MethodCallNode viewEngineCall
91+ |
92+ // `app.engine("name", engine)
93+ router .flowsTo ( registerCall .getReceiver ( ) .asExpr ( ) ) and
94+ registerCall .getMethodName ( ) = [ "engine" , "register" ] and
95+ engine = registerCall .getArgument ( 1 ) .getALocalSource ( ) and
96+ // app.set("view engine", "name")
97+ router .flowsTo ( viewEngineCall .getReceiver ( ) .asExpr ( ) ) and
98+ viewEngineCall .getMethodName ( ) = "set" and
99+ viewEngineCall .getArgument ( 0 ) .getStringValue ( ) = "view engine" and
100+ // The name set by the `app.engine("name")` call matches `app.set("view engine", "name")`.
101+ viewEngineCall .getArgument ( 1 ) .getStringValue ( ) = registerCall .getArgument ( 0 ) .getStringValue ( )
102+ |
103+ // Different ways of initializing vulnerable template engines.
104+ engine = DataFlow:: moduleImport ( getAVulnerableTemplateEngine ( ) )
105+ or
106+ engine = DataFlow:: moduleImport ( getAVulnerableTemplateEngine ( ) ) .getAPropertyRead ( "__express" )
107+ or
108+ engine = DataFlow:: moduleImport ( "express-handlebars" ) .getACall ( )
109+ or
110+ engine = DataFlow:: moduleImport ( "express-handlebars" ) .getAPropertyRead ( "engine" )
111+ or
112+ exists ( DataFlow:: SourceNode hbs |
113+ hbs = DataFlow:: moduleImport ( "express-hbs" )
114+ or
115+ hbs = DataFlow:: moduleImport ( "express-hbs" ) .getAMemberCall ( "create" )
116+ |
117+ engine = hbs .getAMemberCall ( [ "express3" , "express4" ] )
118+ )
119+ or
120+ engine =
121+ DataFlow:: moduleImport ( "consolidate" ) .getAPropertyRead ( getAVulnerableTemplateEngine ( ) )
122+ )
47123 }
48124}
0 commit comments