Skip to content

Commit 50b0bb8

Browse files
committed
Restrict rb/reflected-xss instance variable taint edges
1 parent 5cfefb1 commit 50b0bb8

File tree

4 files changed

+31
-11
lines changed

4 files changed

+31
-11
lines changed

ql/lib/codeql/ruby/controlflow/CfgNodes.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,13 @@ module ExprNodes {
355355
final override VariableReadAccess getExpr() { result = ExprCfgNode.super.getExpr() }
356356
}
357357

358+
/** A control-flow node that wraps a `InstanceVariableWriteAccess` AST expression. */
359+
class InstanceVariableWriteAccessCfgNode extends ExprCfgNode {
360+
override InstanceVariableWriteAccess e;
361+
362+
final override InstanceVariableWriteAccess getExpr() { result = ExprCfgNode.super.getExpr() }
363+
}
364+
358365
/** A control-flow node that wraps a `StringInterpolationComponent` AST expression. */
359366
class StringInterpolationComponentCfgNode extends StmtSequenceCfgNode {
360367
StringInterpolationComponentCfgNode() { this.getNode() instanceof StringInterpolationComponent }

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,27 @@ module ReflectedXSS {
101101
class StringConstArrayInclusionCallAsSanitizerGuard extends SanitizerGuard,
102102
StringConstArrayInclusionCall { }
103103

104+
/**
105+
* A `VariableWriteAccessCfgNode` that is not succeeded (locally) by another
106+
* write to that variable.
107+
*/
108+
private class FinalInstanceVarWrite extends CfgNodes::ExprNodes::InstanceVariableWriteAccessCfgNode {
109+
private InstanceVariable var;
110+
111+
FinalInstanceVarWrite() {
112+
var = this.getExpr().getVariable() and
113+
not exists(CfgNodes::ExprNodes::InstanceVariableWriteAccessCfgNode succWrite |
114+
succWrite.getExpr().getVariable() = var |
115+
succWrite = this.getASuccessor+()
116+
)
117+
}
118+
119+
InstanceVariable getVariable() { result = var }
120+
121+
AssignExpr getAnAssignExpr() { result.getLeftOperand() = this.getExpr() }
122+
}
123+
124+
104125
/**
105126
* An additional step that is taint-preserving in the context of reflected XSS.
106127
*/
@@ -136,17 +157,16 @@ module ReflectedXSS {
136157
or
137158
// instance variables in the controller
138159
exists(
139-
ActionControllerActionMethod action, VariableReadAccess viewVarRead, AssignExpr ae,
140-
InstanceVariableWriteAccess controllerVarWrite
160+
ActionControllerActionMethod action, VariableReadAccess viewVarRead,
161+
AssignExpr ae, FinalInstanceVarWrite controllerVarWrite
141162
|
142163
viewVarRead = node2.asExpr().(CfgNodes::ExprNodes::VariableReadAccessCfgNode).getExpr() and
143164
action.getDefaultTemplateFile() = viewVarRead.getLocation().getFile() and
144165
// match read to write on variable name
145166
viewVarRead.getVariable().getName() = controllerVarWrite.getVariable().getName() and
146-
// TODO: include only final assignment along a path
147167
// propagate taint from assignment RHS expr to variable read access in view
168+
ae = controllerVarWrite.getAnAssignExpr() and
148169
node1.asExpr().getExpr() = ae.getRightOperand() and
149-
ae.getLeftOperand() = controllerVarWrite and
150170
ae.getParent+() = action
151171
)
152172
or

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ edges
66
| app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/controllers/foo/bars_controller.rb:19:22:19:23 | dt : |
77
| app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : |
88
| app/controllers/foo/bars_controller.rb:19:22:19:23 | dt : | app/views/foo/bars/show.html.erb:41:3:41:16 | @instance_text |
9-
| app/controllers/foo/bars_controller.rb:20:17:20:22 | call to params : | app/controllers/foo/bars_controller.rb:20:17:20:29 | ...[...] : |
10-
| app/controllers/foo/bars_controller.rb:20:17:20:29 | ...[...] : | app/views/foo/bars/show.html.erb:63:5:63:13 | @safe_foo |
119
| app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : | app/views/foo/bars/show.html.erb:5:9:5:20 | call to display_text |
1210
| app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : | app/views/foo/bars/show.html.erb:8:9:8:36 | ...[...] |
1311
| app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : | app/views/foo/bars/show.html.erb:12:9:12:26 | ...[...] |
@@ -25,8 +23,6 @@ nodes
2523
| app/controllers/foo/bars_controller.rb:17:21:17:36 | ...[...] : | semmle.label | ...[...] : |
2624
| app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | semmle.label | call to params : |
2725
| app/controllers/foo/bars_controller.rb:19:22:19:23 | dt : | semmle.label | dt : |
28-
| app/controllers/foo/bars_controller.rb:20:17:20:22 | call to params : | semmle.label | call to params : |
29-
| app/controllers/foo/bars_controller.rb:20:17:20:29 | ...[...] : | semmle.label | ...[...] : |
3026
| app/controllers/foo/bars_controller.rb:23:53:23:54 | dt : | semmle.label | dt : |
3127
| app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | semmle.label | call to display_text |
3228
| app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | semmle.label | ...[...] |
@@ -43,7 +39,6 @@ nodes
4339
| app/views/foo/bars/show.html.erb:54:29:54:44 | ...[...] | semmle.label | ...[...] |
4440
| app/views/foo/bars/show.html.erb:57:13:57:18 | call to params : | semmle.label | call to params : |
4541
| app/views/foo/bars/show.html.erb:57:13:57:28 | ...[...] | semmle.label | ...[...] |
46-
| app/views/foo/bars/show.html.erb:63:5:63:13 | @safe_foo | semmle.label | @safe_foo |
4742
#select
4843
| 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 $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | a user-provided value |
4944
| 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 $@. | app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params | a user-provided value |
@@ -56,4 +51,3 @@ nodes
5651
| app/views/foo/bars/show.html.erb:47:5:47:13 | call to user_name | app/controllers/foo/bars_controller.rb:9:12:9:17 | call to params : | app/views/foo/bars/show.html.erb:47:5:47:13 | call to user_name | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:9:12:9:17 | call to params | a user-provided value |
5752
| app/views/foo/bars/show.html.erb:54:29:54:44 | ...[...] | app/views/foo/bars/show.html.erb:54:29:54:34 | call to params : | app/views/foo/bars/show.html.erb:54:29:54:44 | ...[...] | Cross-site scripting vulnerability due to $@. | app/views/foo/bars/show.html.erb:54:29:54:34 | call to params | a user-provided value |
5853
| app/views/foo/bars/show.html.erb:57:13:57:28 | ...[...] | app/views/foo/bars/show.html.erb:57:13:57:18 | call to params : | app/views/foo/bars/show.html.erb:57:13:57:28 | ...[...] | Cross-site scripting vulnerability due to $@. | app/views/foo/bars/show.html.erb:57:13:57:18 | call to params | a user-provided value |
59-
| app/views/foo/bars/show.html.erb:63:5:63:13 | @safe_foo | app/controllers/foo/bars_controller.rb:20:17:20:22 | call to params : | app/views/foo/bars/show.html.erb:63:5:63:13 | @safe_foo | Cross-site scripting vulnerability due to $@. | app/controllers/foo/bars_controller.rb:20:17:20:22 | call to params | a user-provided value |

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
<% end %>
6060

6161
<%# GOOD: @safe_foo is a hardcoded string here at runtime %>
62-
<%# TODO: we mistakenly flag this up due to overly broad flow through instance variables %>
6362
<%= @safe_foo.html_safe %>
6463

6564
<%# GOOD: @html_escaped is manually escaped in the controller %>

0 commit comments

Comments
 (0)