@@ -42,7 +42,83 @@ module TemplateObjectInjection {
42
42
override DataFlow:: FlowLabel getAFlowLabel ( ) { result .isTaint ( ) }
43
43
}
44
44
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
+ )
47
123
}
48
124
}
0 commit comments