@@ -8,36 +8,20 @@ private import codeql.ruby.Concepts
8
8
private import codeql.ruby.controlflow.CfgNodes
9
9
private import codeql.ruby.DataFlow
10
10
private import codeql.ruby.dataflow.RemoteFlowSources
11
- private import ActionController
11
+ private import codeql.ruby.frameworks.internal.Rails
12
+ private import codeql.ruby.frameworks.Rails
12
13
13
14
/**
14
15
* Holds if this AST node is in a context where `ActionView` methods are available.
15
16
*/
16
- predicate inActionViewContext ( AstNode n ) {
17
+ private predicate inActionViewContext ( AstNode n ) {
17
18
// Within a template
18
19
n .getLocation ( ) .getFile ( ) instanceof ErbFile
19
20
}
20
21
21
- /**
22
- * A method call on a string to mark it as HTML safe for Rails.
23
- * Strings marked as such will not be automatically escaped when inserted into
24
- * HTML.
25
- */
26
- abstract class HtmlSafeCall extends MethodCall {
27
- HtmlSafeCall ( ) { this .getMethodName ( ) = "html_safe" }
28
- }
29
-
30
- // A call to `html_safe` from within a template.
31
- private class ActionViewHtmlSafeCall extends HtmlSafeCall {
32
- ActionViewHtmlSafeCall ( ) { inActionViewContext ( this ) }
33
- }
34
-
35
- /**
36
- * A call to a method named "html_escape", "html_escape_once", or "h".
37
- */
38
- abstract class HtmlEscapeCall extends MethodCall {
39
- // "h" is aliased to "html_escape" in ActiveSupport
40
- HtmlEscapeCall ( ) { this .getMethodName ( ) = [ "html_escape" , "html_escape_once" , "h" , "sanitize" ] }
22
+ /** A call to `html_safe` from within a template. */
23
+ private class ActionViewHtmlSafeCall extends HtmlSafeCallImpl {
24
+ ActionViewHtmlSafeCall ( ) { this .getMethodName ( ) = "html_safe" and inActionViewContext ( this ) }
41
25
}
42
26
43
27
/**
@@ -53,12 +37,16 @@ class RailsHtmlEscaping extends Escaping::Range, DataFlow::CallNode {
53
37
override string getKind ( ) { result = Escaping:: getHtmlKind ( ) }
54
38
}
55
39
56
- // A call to `html_escape` from within a template.
57
- private class ActionViewHtmlEscapeCall extends HtmlEscapeCall {
58
- ActionViewHtmlEscapeCall ( ) { inActionViewContext ( this ) }
40
+ /** A call to `html_escape` from within a template. */
41
+ private class ActionViewHtmlEscapeCall extends HtmlEscapeCallImpl {
42
+ ActionViewHtmlEscapeCall ( ) {
43
+ // "h" is aliased to "html_escape" in ActiveSupport
44
+ this .getMethodName ( ) = [ "html_escape" , "html_escape_once" , "h" , "sanitize" ] and
45
+ inActionViewContext ( this )
46
+ }
59
47
}
60
48
61
- // A call in a context where some commonly used `ActionView` methods are available.
49
+ /** A call in a context where some commonly used `ActionView` methods are available. */
62
50
private class ActionViewContextCall extends MethodCall {
63
51
ActionViewContextCall ( ) {
64
52
this .getReceiver ( ) instanceof SelfVariableAccess and
@@ -76,51 +64,14 @@ class RawCall extends ActionViewContextCall {
76
64
RawCall ( ) { this .getMethodName ( ) = "raw" }
77
65
}
78
66
79
- // A call to the `params` method within the context of a template.
80
- private class ActionViewParamsCall extends ActionViewContextCall , ParamsCall { }
67
+ /** A call to the `params` method within the context of a template. */
68
+ private class ActionViewParamsCall extends ActionViewContextCall , ParamsCallImpl {
69
+ ActionViewParamsCall ( ) { this .getMethodName ( ) = "params" }
70
+ }
81
71
82
72
// A call to the `cookies` method within the context of a template.
83
- private class ActionViewCookiesCall extends ActionViewContextCall , CookiesCall { }
84
-
85
- /**
86
- * A call to a `render` method that will populate the response body with the
87
- * rendered content.
88
- */
89
- abstract class RenderCall extends MethodCall {
90
- RenderCall ( ) { this .getMethodName ( ) = "render" }
91
-
92
- private Expr getTemplatePathArgument ( ) {
93
- // TODO: support other ways of specifying paths (e.g. `file`)
94
- result = [ this .getKeywordArgument ( [ "partial" , "template" , "action" ] ) , this .getArgument ( 0 ) ]
95
- }
96
-
97
- private string getTemplatePathValue ( ) {
98
- result = this .getTemplatePathArgument ( ) .getConstantValue ( ) .getStringlikeValue ( )
99
- }
100
-
101
- // everything up to and including the final slash, but ignoring any leading slash
102
- private string getSubPath ( ) {
103
- result = this .getTemplatePathValue ( ) .regexpCapture ( "^/?(.*/)?(?:[^/]*?)$" , 1 )
104
- }
105
-
106
- // everything after the final slash, or the whole string if there is no slash
107
- private string getBaseName ( ) {
108
- result = this .getTemplatePathValue ( ) .regexpCapture ( "^/?(?:.*/)?([^/]*?)$" , 1 )
109
- }
110
-
111
- /**
112
- * Gets the template file to be rendered by this call, if any.
113
- */
114
- ErbFile getTemplateFile ( ) {
115
- result .getTemplateName ( ) = this .getBaseName ( ) and
116
- result .getRelativePath ( ) .matches ( "%app/views/" + this .getSubPath ( ) + "%" )
117
- }
118
-
119
- /**
120
- * Get the local variables passed as context to the renderer
121
- */
122
- HashLiteral getLocals ( ) { result = this .getKeywordArgument ( "locals" ) }
123
- // TODO: implicit renders in controller actions
73
+ private class ActionViewCookiesCall extends ActionViewContextCall , CookiesCallImpl {
74
+ ActionViewCookiesCall ( ) { this .getMethodName ( ) = "cookies" }
124
75
}
125
76
126
77
/**
@@ -172,17 +123,14 @@ private class RenderCallAsHttpResponse extends DataFlow::CallNode, Http::Server:
172
123
}
173
124
174
125
/** A call to the `render` method within the context of a template. */
175
- private class ActionViewRenderCall extends RenderCall , ActionViewContextCall { }
176
-
177
- /**
178
- * A render call that does not automatically set the HTTP response body.
179
- */
180
- abstract class RenderToCall extends MethodCall {
181
- RenderToCall ( ) { this .getMethodName ( ) = [ "render_to_body" , "render_to_string" ] }
126
+ private class ActionViewRenderCall extends ActionViewContextCall , RenderCallImpl {
127
+ ActionViewRenderCall ( ) { this .getMethodName ( ) = "render" }
182
128
}
183
129
184
- // A call to `render_to` from within a template.
185
- private class ActionViewRenderToCall extends ActionViewContextCall , RenderToCall { }
130
+ /** A call to `render_to` from within a template. */
131
+ private class ActionViewRenderToCall extends ActionViewContextCall , RenderToCallImpl {
132
+ ActionViewRenderToCall ( ) { this .getMethodName ( ) = [ "render_to_body" , "render_to_string" ] }
133
+ }
186
134
187
135
/**
188
136
* A call to the ActionView `link_to` helper method.
@@ -224,24 +172,26 @@ module ActionView {
224
172
* Action view helper methods which are XSS sinks.
225
173
*/
226
174
module Helpers {
175
+ abstract private class RawHelperCallImpl extends MethodCall {
176
+ abstract Expr getRawArgument ( ) ;
177
+ }
178
+
227
179
/**
228
180
* A call to an ActionView helper which renders its argument without escaping.
229
181
* The argument should be treated as an XSS sink. In the documentation for
230
182
* classes in this module, the vulnerable argument is named `x`.
231
183
*/
232
- abstract class RawHelperCall extends MethodCall {
233
- /**
234
- * Get an argument which is rendered without escaping.
235
- */
236
- abstract Expr getRawArgument ( ) ;
184
+ class RawHelperCall extends MethodCall instanceof RawHelperCallImpl {
185
+ /** Gets an argument that is rendered without escaping. */
186
+ Expr getRawArgument ( ) { result = super .getRawArgument ( ) }
237
187
}
238
188
239
189
/**
240
190
* `ActionView::Helpers::TextHelper#simple_format`.
241
191
*
242
192
* `simple_format(x, y, sanitize: false)`.
243
193
*/
244
- private class SimpleFormat extends ActionViewContextCall , RawHelperCall {
194
+ private class SimpleFormat extends ActionViewContextCall , RawHelperCallImpl {
245
195
SimpleFormat ( ) {
246
196
this .getMethodName ( ) = "simple_format" and
247
197
this .getKeywordArgument ( "sanitize" ) .getConstantValue ( ) .isBoolean ( false )
@@ -255,7 +205,7 @@ module ActionView {
255
205
*
256
206
* `truncate(x, escape: false)`.
257
207
*/
258
- private class Truncate extends ActionViewContextCall , RawHelperCall {
208
+ private class Truncate extends ActionViewContextCall , RawHelperCallImpl {
259
209
Truncate ( ) {
260
210
this .getMethodName ( ) = "truncate" and
261
211
this .getKeywordArgument ( "escape" ) .getConstantValue ( ) .isBoolean ( false )
@@ -269,7 +219,7 @@ module ActionView {
269
219
*
270
220
* `highlight(x, y, sanitize: false)`.
271
221
*/
272
- private class Highlight extends ActionViewContextCall , RawHelperCall {
222
+ private class Highlight extends ActionViewContextCall , RawHelperCallImpl {
273
223
Highlight ( ) {
274
224
this .getMethodName ( ) = "highlight" and
275
225
this .getKeywordArgument ( "sanitize" ) .getConstantValue ( ) .isBoolean ( false )
@@ -283,7 +233,7 @@ module ActionView {
283
233
*
284
234
* `javascript_tag(x)`.
285
235
*/
286
- private class JavascriptTag extends ActionViewContextCall , RawHelperCall {
236
+ private class JavascriptTag extends ActionViewContextCall , RawHelperCallImpl {
287
237
JavascriptTag ( ) { this .getMethodName ( ) = "javascript_tag" }
288
238
289
239
override Expr getRawArgument ( ) { result = this .getArgument ( 0 ) }
@@ -294,7 +244,7 @@ module ActionView {
294
244
*
295
245
* `content_tag(x, x, y, false)`.
296
246
*/
297
- private class ContentTag extends ActionViewContextCall , RawHelperCall {
247
+ private class ContentTag extends ActionViewContextCall , RawHelperCallImpl {
298
248
ContentTag ( ) {
299
249
this .getMethodName ( ) = "content_tag" and
300
250
this .getArgument ( 3 ) .getConstantValue ( ) .isBoolean ( false )
@@ -308,7 +258,7 @@ module ActionView {
308
258
*
309
259
* `tag(x, x, y, false)`.
310
260
*/
311
- private class Tag extends ActionViewContextCall , RawHelperCall {
261
+ private class Tag extends ActionViewContextCall , RawHelperCallImpl {
312
262
Tag ( ) {
313
263
this .getMethodName ( ) = "tag" and
314
264
this .getArgument ( 3 ) .getConstantValue ( ) .isBoolean ( false )
@@ -322,7 +272,7 @@ module ActionView {
322
272
*
323
273
* `tag.h1(x, escape: false)`.
324
274
*/
325
- private class TagMethod extends MethodCall , RawHelperCall {
275
+ private class TagMethod extends MethodCall , RawHelperCallImpl {
326
276
TagMethod ( ) {
327
277
inActionViewContext ( this ) and
328
278
this .getReceiver ( ) .( MethodCall ) .getMethodName ( ) = "tag" and
0 commit comments