Skip to content

Commit a6f6936

Browse files
authored
Merge pull request github#11058 from hmac/actioncontroller-logger
Ruby: Model various ActionController methods
2 parents baaafad + ed3270f commit a6f6936

File tree

17 files changed

+565
-414
lines changed

17 files changed

+565
-414
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Calls to `logger` in `ActiveSupport` actions are now recognised as logger instances.
5+
* Calls to `send_data` in `ActiveSupport` actions are recognised as HTTP responses.
6+
* Calls to `body_stream` in `ActiveSupport` actions are recognised as HTTP request accesses.

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

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ private import codeql.ruby.frameworks.ActionDispatch
1212
private import codeql.ruby.frameworks.ActionView
1313
private import codeql.ruby.frameworks.Rails
1414
private import codeql.ruby.frameworks.internal.Rails
15+
private import codeql.ruby.dataflow.internal.DataFlowDispatch
1516

1617
/**
1718
* DEPRECATED: Import `codeql.ruby.frameworks.Rails` and use `Rails::ParamsCall` instead.
@@ -295,7 +296,7 @@ private module Request {
295296

296297
/** A method call on `request` which returns the request body. */
297298
private class BodyCall extends RequestInputAccess {
298-
BodyCall() { this.getMethodName() = ["body", "raw_post"] }
299+
BodyCall() { this.getMethodName() = ["body", "raw_post", "body_stream"] }
299300

300301
override Http::Server::RequestInputKind getKind() { result = Http::Server::bodyInputKind() }
301302
}
@@ -538,12 +539,34 @@ private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetti
538539
/**
539540
* A call to `send_file`, which sends the file at the given path to the client.
540541
*/
541-
private class SendFile extends FileSystemAccess::Range, DataFlow::CallNode {
542+
private class SendFile extends FileSystemAccess::Range, Http::Server::HttpResponse::Range,
543+
DataFlow::CallNode {
542544
SendFile() {
543545
this = [actionControllerInstance(), Response::response()].getAMethodCall("send_file")
544546
}
545547

546548
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
549+
550+
override DataFlow::Node getBody() { result = this.getArgument(0) }
551+
552+
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
553+
554+
override string getMimetypeDefault() { result = "application/octet-stream" }
555+
}
556+
557+
/**
558+
* A call to `send_data`, which sends the given data to the client.
559+
*/
560+
class SendDataCall extends DataFlow::CallNode, Http::Server::HttpResponse::Range {
561+
SendDataCall() {
562+
this = [actionControllerInstance(), Response::response()].getAMethodCall("send_data")
563+
}
564+
565+
override DataFlow::Node getBody() { result = this.getArgument(0) }
566+
567+
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
568+
569+
override string getMimetypeDefault() { result = "application/octet-stream" }
547570
}
548571

549572
private module ParamsSummaries {
@@ -733,3 +756,28 @@ private module Response {
733756
override DataFlow::Node getValue() { result = this.getArgument(0) }
734757
}
735758
}
759+
760+
private class ActionControllerLoggerInstance extends DataFlow::Node {
761+
ActionControllerLoggerInstance() {
762+
this = actionControllerInstance().getAMethodCall("logger")
763+
or
764+
any(ActionControllerLoggerInstance i).(DataFlow::LocalSourceNode).flowsTo(this)
765+
}
766+
}
767+
768+
private class ActionControllerLoggingCall extends DataFlow::CallNode, Logging::Range {
769+
ActionControllerLoggingCall() {
770+
this.getReceiver() instanceof ActionControllerLoggerInstance and
771+
this.getMethodName() = ["debug", "error", "fatal", "info", "unknown", "warn"]
772+
}
773+
774+
// Note: this is identical to the definition `stdlib.Logger.LoggerInfoStyleCall`.
775+
override DataFlow::Node getAnInput() {
776+
// `msg` from `Logger#info(msg)`,
777+
// or `progname` from `Logger#info(progname) <block>`
778+
result = this.getArgument(0)
779+
or
780+
// a return value from the block in `Logger#info(progname) <block>`
781+
exprNodeReturnedFrom(result, this.getBlock().asExpr().getExpr())
782+
}
783+
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ class StringReplaceSanitizer extends Sanitizer {
6060
}
6161
}
6262

63+
/**
64+
* A call to `Object#inspect`, considered as a sanitizer.
65+
* This is because `inspect` will replace newlines in strings with `\n`.
66+
*/
67+
class InspectSanitizer extends Sanitizer {
68+
InspectSanitizer() { this.(DataFlow::CallNode).getMethodName() = "inspect" }
69+
}
70+
6371
/**
6472
* A call to an HTML escape method is considered to sanitize its input.
6573
*/

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

Lines changed: 0 additions & 410 deletions
This file was deleted.

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,26 @@ actionDispatchRoutes
3434
| app/config/routes.rb:49:5:49:95 | call to delete | delete | users/:user/notifications | users/notifications | destroy |
3535
| app/config/routes.rb:50:5:50:94 | call to post | post | users/:user/notifications/:notification_id/mark_as_read | users/notifications | mark_as_read |
3636
actionDispatchControllerMethods
37+
| app/config/routes.rb:2:3:8:5 | call to resources | action_controller/controllers/posts_controller.rb:2:3:3:5 | index |
38+
| app/config/routes.rb:2:3:8:5 | call to resources | action_controller/controllers/posts_controller.rb:5:3:6:5 | show |
3739
| app/config/routes.rb:2:3:8:5 | call to resources | app/controllers/posts_controller.rb:2:3:3:5 | index |
3840
| app/config/routes.rb:2:3:8:5 | call to resources | app/controllers/posts_controller.rb:5:3:6:5 | show |
41+
| app/config/routes.rb:3:5:6:7 | call to resources | action_controller/controllers/comments_controller.rb:2:3:36:5 | index |
42+
| app/config/routes.rb:3:5:6:7 | call to resources | action_controller/controllers/comments_controller.rb:38:3:44:5 | show |
43+
| app/config/routes.rb:3:5:6:7 | call to resources | action_controller/controllers/comments_controller.rb:50:3:52:5 | destroy |
3944
| app/config/routes.rb:3:5:6:7 | call to resources | app/controllers/comments_controller.rb:2:3:36:5 | index |
4045
| app/config/routes.rb:3:5:6:7 | call to resources | app/controllers/comments_controller.rb:38:3:39:5 | show |
46+
| app/config/routes.rb:7:5:7:37 | call to post | action_controller/controllers/posts_controller.rb:8:3:9:5 | upvote |
4147
| app/config/routes.rb:7:5:7:37 | call to post | app/controllers/posts_controller.rb:8:3:9:5 | upvote |
48+
| app/config/routes.rb:27:3:27:48 | call to match | action_controller/controllers/photos_controller.rb:2:3:3:5 | show |
4249
| app/config/routes.rb:27:3:27:48 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
50+
| app/config/routes.rb:28:3:28:50 | call to match | action_controller/controllers/photos_controller.rb:2:3:3:5 | show |
4351
| app/config/routes.rb:28:3:28:50 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
52+
| app/config/routes.rb:29:3:29:69 | call to match | action_controller/controllers/photos_controller.rb:2:3:3:5 | show |
4453
| app/config/routes.rb:29:3:29:69 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
54+
| app/config/routes.rb:30:3:30:50 | call to match | action_controller/controllers/photos_controller.rb:2:3:3:5 | show |
4555
| app/config/routes.rb:30:3:30:50 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
56+
| app/config/routes.rb:50:5:50:94 | call to post | action_controller/controllers/users/notifications_controller.rb:3:5:4:7 | mark_as_read |
4657
| app/config/routes.rb:50:5:50:94 | call to post | app/controllers/users/notifications_controller.rb:3:5:4:7 | mark_as_read |
4758
underscore
4859
| Foo | foo |

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,35 @@ rawCalls
99
| app/views/foo/bars/show.html.erb:5:5:5:21 | call to raw |
1010
| app/views/foo/bars/show.html.erb:7:5:7:19 | call to raw |
1111
renderCalls
12+
| action_controller/controllers/comments_controller.rb:42:21:42:64 | call to render |
13+
| action_controller/controllers/foo/bars_controller.rb:6:5:6:37 | call to render |
14+
| action_controller/controllers/foo/bars_controller.rb:23:5:23:76 | call to render |
15+
| action_controller/controllers/foo/bars_controller.rb:35:5:35:33 | call to render |
16+
| action_controller/controllers/foo/bars_controller.rb:38:5:38:50 | call to render |
17+
| action_controller/controllers/foo/bars_controller.rb:44:5:44:17 | call to render |
1218
| app/controllers/foo/bars_controller.rb:6:5:6:37 | call to render |
1319
| app/controllers/foo/bars_controller.rb:23:5:23:76 | call to render |
1420
| app/controllers/foo/bars_controller.rb:35:5:35:33 | call to render |
1521
| app/controllers/foo/bars_controller.rb:38:5:38:50 | call to render |
1622
| app/controllers/foo/bars_controller.rb:44:5:44:17 | call to render |
1723
| app/views/foo/bars/show.html.erb:31:5:31:89 | call to render |
1824
renderToCalls
25+
| action_controller/controllers/foo/bars_controller.rb:15:16:15:97 | call to render_to_string |
26+
| action_controller/controllers/foo/bars_controller.rb:36:12:36:67 | call to render_to_string |
1927
| app/controllers/foo/bars_controller.rb:15:16:15:97 | call to render_to_string |
2028
| app/controllers/foo/bars_controller.rb:36:12:36:67 | call to render_to_string |
2129
linkToCalls
2230
| app/views/foo/bars/show.html.erb:33:5:33:41 | call to link_to |
2331
httpResponses
32+
| action_controller/controllers/comments_controller.rb:11:5:11:17 | call to body= | action_controller/controllers/comments_controller.rb:11:21:11:34 | ... = ... | text/http |
33+
| action_controller/controllers/comments_controller.rb:21:5:21:37 | call to send_file | action_controller/controllers/comments_controller.rb:21:24:21:36 | "my-file.ext" | application/octet-stream |
34+
| action_controller/controllers/comments_controller.rb:47:5:47:20 | call to send_data | action_controller/controllers/comments_controller.rb:47:15:47:20 | @photo | application/octet-stream |
35+
| action_controller/controllers/foo/bars_controller.rb:15:16:15:97 | call to render_to_string | action_controller/controllers/foo/bars_controller.rb:15:33:15:47 | "foo/bars/show" | text/html |
36+
| action_controller/controllers/foo/bars_controller.rb:23:5:23:76 | call to render | action_controller/controllers/foo/bars_controller.rb:23:12:23:26 | "foo/bars/show" | text/html |
37+
| action_controller/controllers/foo/bars_controller.rb:35:5:35:33 | call to render | action_controller/controllers/foo/bars_controller.rb:35:18:35:33 | call to [] | application/json |
38+
| action_controller/controllers/foo/bars_controller.rb:36:12:36:67 | call to render_to_string | action_controller/controllers/foo/bars_controller.rb:36:29:36:33 | @user | application/json |
39+
| action_controller/controllers/foo/bars_controller.rb:38:5:38:50 | call to render | action_controller/controllers/foo/bars_controller.rb:38:12:38:22 | call to backtrace | text/plain |
40+
| action_controller/controllers/foo/bars_controller.rb:44:5:44:17 | call to render | action_controller/controllers/foo/bars_controller.rb:44:12:44:17 | "show" | text/html |
2441
| app/controllers/comments_controller.rb:11:5:11:17 | call to body= | app/controllers/comments_controller.rb:11:21:11:34 | ... = ... | text/http |
2542
| app/controllers/comments_controller.rb:21:5:21:37 | call to send_file | app/controllers/comments_controller.rb:21:24:21:36 | "my-file.ext" | application/octet-stream |
2643
| app/controllers/foo/bars_controller.rb:15:16:15:97 | call to render_to_string | app/controllers/foo/bars_controller.rb:15:33:15:47 | "foo/bars/show" | text/html |

0 commit comments

Comments
 (0)