Skip to content

Commit 8086d37

Browse files
authored
Merge pull request github#10840 from erik-krogh/html_safe
RB: simplify html_safe modeling
2 parents 24a8487 + e29bf8c commit 8086d37

File tree

10 files changed

+26
-34
lines changed

10 files changed

+26
-34
lines changed

ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -311,14 +311,6 @@ private class ActionControllerRenderToCall extends ActionControllerContextCall,
311311
ActionControllerRenderToCall() { this.getMethodName() = ["render_to_body", "render_to_string"] }
312312
}
313313

314-
/** A call to `html_safe` from within a controller. */
315-
private class ActionControllerHtmlSafeCall extends HtmlSafeCallImpl {
316-
ActionControllerHtmlSafeCall() {
317-
this.getMethodName() = "html_safe" and
318-
this.getEnclosingModule() instanceof ActionControllerControllerClass
319-
}
320-
}
321-
322314
/** A call to `html_escape` from within a controller. */
323315
private class ActionControllerHtmlEscapeCall extends HtmlEscapeCallImpl {
324316
ActionControllerHtmlEscapeCall() {

ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,6 @@ predicate inActionViewContext(AstNode n) {
3939
n.getLocation().getFile() instanceof ErbFile
4040
}
4141

42-
/** A call to `html_safe` from within a template. */
43-
private class ActionViewHtmlSafeCall extends HtmlSafeCallImpl {
44-
ActionViewHtmlSafeCall() { this.getMethodName() = "html_safe" and inActionViewContext(this) }
45-
}
46-
4742
/**
4843
* A call to a Rails method that escapes HTML.
4944
*/

ruby/ql/lib/codeql/ruby/frameworks/Rails.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ private import codeql.ruby.security.OpenSSL
1616
*/
1717
module Rails {
1818
/**
19+
* DEPRECATED: Any call to `html_safe` is considered an XSS sink.
1920
* A method call on a string to mark it as HTML safe for Rails. Strings marked
2021
* as such will not be automatically escaped when inserted into HTML.
2122
*/
22-
class HtmlSafeCall extends MethodCall instanceof HtmlSafeCallImpl { }
23+
deprecated class HtmlSafeCall extends MethodCall {
24+
HtmlSafeCall() { this.getMethodName() = "html_safe" }
25+
}
2326

2427
/** A call to a Rails method to escape HTML. */
2528
class HtmlEscapeCall extends MethodCall instanceof HtmlEscapeCallImpl { }

ruby/ql/lib/codeql/ruby/frameworks/internal/Rails.qll

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
private import codeql.ruby.AST
22

3-
abstract class HtmlSafeCallImpl extends MethodCall { }
4-
53
abstract class HtmlEscapeCallImpl extends MethodCall { }
64

75
abstract class RenderCallImpl extends MethodCall { }

ruby/ql/lib/codeql/ruby/security/XSS.qll

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,7 @@ private module Shared {
6262
*/
6363
class HtmlSafeCallAsSink extends Sink {
6464
HtmlSafeCallAsSink() {
65-
exists(Rails::HtmlSafeCall c, ErbOutputDirective d |
66-
this.asExpr().getExpr() = c.getReceiver() and
67-
c = d.getTerminalStmt()
68-
)
65+
this = any(DataFlow::CallNode call | call.getMethodName() = "html_safe").getReceiver()
6966
}
7067
}
7168

@@ -342,14 +339,13 @@ private module OrmTracking {
342339

343340
override predicate isSource(DataFlow2::Node source) { source instanceof OrmInstantiation }
344341

345-
// Select any call node and narrow down later
346-
override predicate isSink(DataFlow2::Node sink) { sink instanceof DataFlow2::CallNode }
342+
// Select any call receiver and narrow down later
343+
override predicate isSink(DataFlow2::Node sink) {
344+
sink = any(DataFlow2::CallNode c).getReceiver()
345+
}
347346

348347
override predicate isAdditionalFlowStep(DataFlow2::Node node1, DataFlow2::Node node2) {
349348
Shared::isAdditionalXssFlowStep(node1, node2)
350-
or
351-
// Propagate flow through arbitrary method calls
352-
node2.(DataFlow2::CallNode).getReceiver() = node1
353349
}
354350
}
355351
}
@@ -382,10 +378,9 @@ module StoredXss {
382378

383379
private class OrmFieldAsSource extends Source instanceof DataFlow2::CallNode {
384380
OrmFieldAsSource() {
385-
exists(OrmTracking::Configuration subConfig, DataFlow2::CallNode subSrc, MethodCall call |
386-
subConfig.hasFlow(subSrc, this) and
387-
call = this.asExpr().getExpr() and
388-
subSrc.(OrmInstantiation).methodCallMayAccessField(call.getMethodName())
381+
exists(OrmTracking::Configuration subConfig, DataFlow2::CallNode subSrc |
382+
subConfig.hasFlow(subSrc, this.getReceiver()) and
383+
subSrc.(OrmInstantiation).methodCallMayAccessField(this.getMethodName())
389384
)
390385
}
391386
}

ruby/ql/test/library-tests/frameworks/ActionView.expected

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
htmlSafeCalls
2-
| app/views/foo/bars/show.html.erb:23:3:23:25 | call to html_safe |
3-
| app/views/foo/bars/show.html.erb:27:3:27:25 | call to html_safe |
41
rawCalls
52
| app/views/foo/bars/_widget.html.erb:1:5:1:21 | call to raw |
63
| app/views/foo/bars/_widget.html.erb:2:5:2:20 | call to raw |

ruby/ql/test/library-tests/frameworks/ActionView.ql

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ private import codeql.ruby.frameworks.ActionView
44
private import codeql.ruby.frameworks.Rails
55
private import codeql.ruby.Concepts
66

7-
query predicate htmlSafeCalls(Rails::HtmlSafeCall c) { any() }
8-
97
query predicate rawCalls(RawCall c) { any() }
108

119
query predicate renderCalls(Rails::RenderCall c) { any() }

ruby/ql/test/query-tests/security/cwe-079/ReflectedXSS.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ edges
1919
| app/controllers/foo/bars_controller.rb:26:53:26:54 | dt : | app/views/foo/bars/show.html.erb:12:9:12:26 | ...[...] |
2020
| app/controllers/foo/bars_controller.rb:26:53:26:54 | dt : | app/views/foo/bars/show.html.erb:36:3:36:14 | call to display_text |
2121
| app/controllers/foo/bars_controller.rb:26:53:26:54 | dt : | app/views/foo/bars/show.html.erb:44:76:44:87 | call to display_text : |
22+
| app/controllers/foo/bars_controller.rb:30:11:30:16 | call to params : | app/controllers/foo/bars_controller.rb:30:11:30:28 | ...[...] : |
23+
| app/controllers/foo/bars_controller.rb:30:11:30:28 | ...[...] : | app/controllers/foo/bars_controller.rb:31:5:31:7 | str |
2224
| app/views/foo/bars/show.html.erb:44:64:44:87 | ... + ... : | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text |
2325
| app/views/foo/bars/show.html.erb:44:64:44:87 | ... + ... : | app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] |
2426
| app/views/foo/bars/show.html.erb:44:76:44:87 | call to display_text : | app/views/foo/bars/show.html.erb:44:64:44:87 | ... + ... : |
@@ -41,6 +43,9 @@ nodes
4143
| app/controllers/foo/bars_controller.rb:24:39:24:59 | ... = ... | semmle.label | ... = ... |
4244
| app/controllers/foo/bars_controller.rb:24:39:24:59 | ...[...] : | semmle.label | ...[...] : |
4345
| app/controllers/foo/bars_controller.rb:26:53:26:54 | dt : | semmle.label | dt : |
46+
| app/controllers/foo/bars_controller.rb:30:11:30:16 | call to params : | semmle.label | call to params : |
47+
| app/controllers/foo/bars_controller.rb:30:11:30:28 | ...[...] : | semmle.label | ...[...] : |
48+
| app/controllers/foo/bars_controller.rb:31:5:31:7 | str | semmle.label | str |
4449
| app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | semmle.label | call to display_text |
4550
| app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | semmle.label | ...[...] |
4651
| app/views/foo/bars/show.html.erb:2:18:2:30 | @user_website | semmle.label | @user_website |
@@ -64,6 +69,7 @@ nodes
6469
subpaths
6570
#select
6671
| app/controllers/foo/bars_controller.rb:24:39:24:59 | ... = ... | app/controllers/foo/bars_controller.rb:24:39:24:44 | call to params : | app/controllers/foo/bars_controller.rb:24:39:24:59 | ... = ... | Cross-site scripting vulnerability due to a $@. | app/controllers/foo/bars_controller.rb:24:39:24:44 | call to params | user-provided value |
72+
| app/controllers/foo/bars_controller.rb:31:5:31:7 | str | app/controllers/foo/bars_controller.rb:30:11:30:16 | call to params : | app/controllers/foo/bars_controller.rb:31:5:31:7 | str | Cross-site scripting vulnerability due to a $@. | app/controllers/foo/bars_controller.rb:30:11:30:16 | call to params | user-provided value |
6773
| app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | Cross-site scripting vulnerability due to a $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | user-provided value |
6874
| app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | Cross-site scripting vulnerability due to a $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | user-provided value |
6975
| app/views/foo/bars/show.html.erb:2:18:2:30 | @user_website | app/controllers/foo/bars_controller.rb:17:21:17:26 | call to params : | app/views/foo/bars/show.html.erb:2:18:2:30 | @user_website | Cross-site scripting vulnerability due to a $@. | app/controllers/foo/bars_controller.rb:17:21:17:26 | call to params | user-provided value |

ruby/ql/test/query-tests/security/cwe-079/app/controllers/foo/bars_controller.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,9 @@ def show
2525
response.header["x-customer-header"] = params[:bar] # OK - header not relevant to XSS
2626
render "foo/bars/show", locals: { display_text: dt, safe_text: "hello" }
2727
end
28+
29+
def make_safe_html
30+
str = params[:user_name]
31+
str.html_safe
32+
end
2833
end

ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,6 @@
8181

8282
<%# BAD: Indirect to a database value without escaping %>
8383
<%= @other_user_raw_name.html_safe %>
84+
85+
<%# GOOD: The `foo.bar.baz` is not recognized as a source %>
86+
<%= @other_user_raw_name.foo.bar.baz.html_safe %>

0 commit comments

Comments
 (0)