Skip to content

Commit 0e6322d

Browse files
committed
Ruby: Restrict XSS header sinks
Not all header writes are relevant to XSS. Restrict these to just content-type and access-control-allow-origin.
1 parent 8ae86cf commit 0e6322d

File tree

5 files changed

+22
-15
lines changed

5 files changed

+22
-15
lines changed

ruby/ql/lib/codeql/ruby/Concepts.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,8 @@ module Http {
474474
* extend `HeaderWriteAccess::Range` instead.
475475
*/
476476
class HeaderWriteAccess extends DataFlow::Node instanceof HeaderWriteAccess::Range {
477-
/** Gets the name of the header that is written to. */
478-
string getName() { result = super.getName() }
477+
/** Gets the (lower case) name of the header that is written to. */
478+
string getName() { result = super.getName().toLowerCase() }
479479

480480
/** Gets the value that is written to the header. */
481481
DataFlow::Node getValue() { result = super.getValue() }

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,13 @@ private module Shared {
107107

108108
/** A write to an HTTP response header, considered as a flow sink. */
109109
class HeaderWriteAsSink extends Sink {
110-
HeaderWriteAsSink() { this = any(Http::Server::HeaderWriteAccess a).getValue() }
110+
HeaderWriteAsSink() {
111+
exists(Http::Server::HeaderWriteAccess a |
112+
a.getName() = ["content-type", "access-control-allow-origin"]
113+
|
114+
this = a.getValue()
115+
)
116+
}
111117
}
112118

113119
/**

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,11 @@ controllerTemplateFiles
371371
| app/controllers/foo/bars_controller.rb:3:1:46:3 | BarsController | app/views/foo/bars/_widget.html.erb:0:0:0:0 | app/views/foo/bars/_widget.html.erb |
372372
| app/controllers/foo/bars_controller.rb:3:1:46:3 | BarsController | app/views/foo/bars/show.html.erb:0:0:0:0 | app/views/foo/bars/show.html.erb |
373373
headerWriteAccesses
374-
| app/controllers/comments_controller.rb:15:5:15:35 | call to []= | Content-Type | app/controllers/comments_controller.rb:15:39:15:49 | ... = ... |
375-
| app/controllers/comments_controller.rb:16:5:16:46 | call to set_header | Content-Length | app/controllers/comments_controller.rb:16:43:16:45 | 100 |
376-
| app/controllers/comments_controller.rb:17:5:17:39 | call to []= | X-Custom-Header | app/controllers/comments_controller.rb:17:43:17:46 | ... = ... |
377-
| app/controllers/comments_controller.rb:18:5:18:39 | call to []= | X-Another-Custom-Header | app/controllers/comments_controller.rb:18:43:18:47 | ... = ... |
378-
| app/controllers/comments_controller.rb:19:5:19:49 | call to add_header | X-Yet-Another | app/controllers/comments_controller.rb:19:42:19:49 | "indeed" |
374+
| app/controllers/comments_controller.rb:15:5:15:35 | call to []= | content-type | app/controllers/comments_controller.rb:15:39:15:49 | ... = ... |
375+
| app/controllers/comments_controller.rb:16:5:16:46 | call to set_header | content-length | app/controllers/comments_controller.rb:16:43:16:45 | 100 |
376+
| app/controllers/comments_controller.rb:17:5:17:39 | call to []= | x-custom-header | app/controllers/comments_controller.rb:17:43:17:46 | ... = ... |
377+
| app/controllers/comments_controller.rb:18:5:18:39 | call to []= | x-another-custom-header | app/controllers/comments_controller.rb:18:43:18:47 | ... = ... |
378+
| app/controllers/comments_controller.rb:19:5:19:49 | call to add_header | x-yet-another | app/controllers/comments_controller.rb:19:42:19:49 | "indeed" |
379379
| app/controllers/comments_controller.rb:25:5:25:21 | call to location= | location | app/controllers/comments_controller.rb:25:25:25:36 | ... = ... |
380380
| app/controllers/comments_controller.rb:26:5:26:26 | call to cache_control= | cache-control | app/controllers/comments_controller.rb:26:30:26:36 | ... = ... |
381381
| app/controllers/comments_controller.rb:27:5:27:27 | call to _cache_control= | cache-control | app/controllers/comments_controller.rb:27:31:27:37 | ... = ... |

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ edges
1010
| app/controllers/foo/bars_controller.rb:17:21:17:36 | ...[...] : | app/views/foo/bars/show.html.erb:2:18:2:30 | @user_website |
1111
| app/controllers/foo/bars_controller.rb:18:10:18:15 | call to params : | app/controllers/foo/bars_controller.rb:18:10:18:22 | ...[...] : |
1212
| app/controllers/foo/bars_controller.rb:18:10:18:22 | ...[...] : | app/controllers/foo/bars_controller.rb:19:22:19:23 | dt : |
13-
| app/controllers/foo/bars_controller.rb:18:10:18:22 | ...[...] : | app/controllers/foo/bars_controller.rb:25:53:25:54 | dt : |
13+
| app/controllers/foo/bars_controller.rb:18:10:18:22 | ...[...] : | app/controllers/foo/bars_controller.rb:26:53:26:54 | dt : |
1414
| app/controllers/foo/bars_controller.rb:19:22:19:23 | dt : | app/views/foo/bars/show.html.erb:41:3:41:16 | @instance_text |
1515
| app/controllers/foo/bars_controller.rb:24:39:24:44 | call to params : | app/controllers/foo/bars_controller.rb:24:39:24:59 | ...[...] : |
1616
| app/controllers/foo/bars_controller.rb:24:39:24:59 | ...[...] : | app/controllers/foo/bars_controller.rb:24:39:24:59 | ... = ... |
17-
| app/controllers/foo/bars_controller.rb:25:53:25:54 | dt : | app/views/foo/bars/show.html.erb:5:9:5:20 | call to display_text |
18-
| app/controllers/foo/bars_controller.rb:25:53:25:54 | dt : | app/views/foo/bars/show.html.erb:8:9:8:36 | ...[...] |
19-
| app/controllers/foo/bars_controller.rb:25:53:25:54 | dt : | app/views/foo/bars/show.html.erb:12:9:12:26 | ...[...] |
20-
| app/controllers/foo/bars_controller.rb:25:53:25:54 | dt : | app/views/foo/bars/show.html.erb:36:3:36:14 | call to display_text |
21-
| app/controllers/foo/bars_controller.rb:25:53:25:54 | dt : | app/views/foo/bars/show.html.erb:44:76:44:87 | call to display_text : |
17+
| app/controllers/foo/bars_controller.rb:26:53:26:54 | dt : | app/views/foo/bars/show.html.erb:5:9:5:20 | call to display_text |
18+
| app/controllers/foo/bars_controller.rb:26:53:26:54 | dt : | app/views/foo/bars/show.html.erb:8:9:8:36 | ...[...] |
19+
| app/controllers/foo/bars_controller.rb:26:53:26:54 | dt : | app/views/foo/bars/show.html.erb:12:9:12:26 | ...[...] |
20+
| 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 |
21+
| 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 : |
2222
| 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 |
2323
| app/views/foo/bars/show.html.erb:44:64:44:87 | ... + ... : | app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] |
2424
| 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 | ... + ... : |
@@ -40,7 +40,7 @@ nodes
4040
| app/controllers/foo/bars_controller.rb:24:39:24:44 | call to params : | semmle.label | call to params : |
4141
| app/controllers/foo/bars_controller.rb:24:39:24:59 | ... = ... | semmle.label | ... = ... |
4242
| app/controllers/foo/bars_controller.rb:24:39:24:59 | ...[...] : | semmle.label | ...[...] : |
43-
| app/controllers/foo/bars_controller.rb:25:53:25:54 | dt : | semmle.label | dt : |
43+
| app/controllers/foo/bars_controller.rb:26:53:26:54 | dt : | semmle.label | dt : |
4444
| app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | semmle.label | call to display_text |
4545
| app/views/foo/bars/_widget.html.erb:8:9:8:36 | ...[...] | semmle.label | ...[...] |
4646
| app/views/foo/bars/show.html.erb:2:18:2:30 | @user_website | semmle.label | @user_website |

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def show
2222
@html_escaped = ERB::Util.html_escape(params[:text])
2323
@header_escaped = ERB::Util.html_escape(cookies[:foo]) # OK - cookies not controllable by 3rd party
2424
response.header["content-type"] = params[:content_type]
25+
response.header["x-customer-header"] = params[:bar] # OK - header not relevant to XSS
2526
render "foo/bars/show", locals: { display_text: dt, safe_text: "hello" }
2627
end
2728
end

0 commit comments

Comments
 (0)