mirror of
https://github.com/github/codeql.git
synced 2026-04-26 09:15:12 +02:00
Ruby: Recognise Rails render calls as HTTP responses
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Calls to `render` in Rails controllers and views are now recognised as HTTP
|
||||
response bodies.
|
||||
@@ -149,26 +149,26 @@ class CookiesSource extends HTTP::Server::RequestInputAccess::Range {
|
||||
override string getSourceType() { result = "ActionController::Metal#cookies" }
|
||||
}
|
||||
|
||||
// A call to `cookies` from within a controller.
|
||||
/** A call to `cookies` from within a controller. */
|
||||
private class ActionControllerCookiesCall extends ActionControllerContextCall, CookiesCall { }
|
||||
|
||||
// A call to `params` from within a controller.
|
||||
/** A call to `params` from within a controller. */
|
||||
private class ActionControllerParamsCall extends ActionControllerContextCall, ParamsCall { }
|
||||
|
||||
// A call to `render` from within a controller.
|
||||
/** A call to `render` from within a controller. */
|
||||
private class ActionControllerRenderCall extends ActionControllerContextCall, RenderCall { }
|
||||
|
||||
// A call to `render_to` from within a controller.
|
||||
/** A call to `render_to` from within a controller. */
|
||||
private class ActionControllerRenderToCall extends ActionControllerContextCall, RenderToCall { }
|
||||
|
||||
// A call to `html_safe` from within a controller.
|
||||
/** A call to `html_safe` from within a controller. */
|
||||
private class ActionControllerHtmlSafeCall extends HtmlSafeCall {
|
||||
ActionControllerHtmlSafeCall() {
|
||||
this.getEnclosingModule() instanceof ActionControllerControllerClass
|
||||
}
|
||||
}
|
||||
|
||||
// A call to `html_escape` from within a controller.
|
||||
/** A call to `html_escape` from within a controller. */
|
||||
private class ActionControllerHtmlEscapeCall extends HtmlEscapeCall {
|
||||
ActionControllerHtmlEscapeCall() {
|
||||
this.getEnclosingModule() instanceof ActionControllerControllerClass
|
||||
|
||||
@@ -123,7 +123,55 @@ abstract class RenderCall extends MethodCall {
|
||||
// TODO: implicit renders in controller actions
|
||||
}
|
||||
|
||||
// A call to the `render` method within the context of a template.
|
||||
/**
|
||||
* A call to `render`, `render_to_body` or `render_to_string`, seen as an
|
||||
* `HttpResponse`.
|
||||
*/
|
||||
private class RenderCallAsHttpResponse extends DataFlow::CallNode, HTTP::Server::HttpResponse::Range {
|
||||
RenderCallAsHttpResponse() {
|
||||
this.asExpr().getExpr() instanceof RenderCall or
|
||||
this.asExpr().getExpr() instanceof RenderToCall
|
||||
}
|
||||
|
||||
// `render` is a very polymorphic method - all of these are valid calls:
|
||||
// render @user
|
||||
// render "path/to/template"
|
||||
// render html: "<html></html>"
|
||||
// render json: { "some" => "hash" }
|
||||
// render body: "some text"
|
||||
override DataFlow::Node getBody() {
|
||||
// A positional argument, e.g.
|
||||
// render @user
|
||||
// render "path/to/template"
|
||||
result = this.getArgument(_) and
|
||||
not result.asExpr() instanceof ExprNodes::PairCfgNode
|
||||
or
|
||||
result = this.getKeywordArgument(["html", "json", "body", "inline", "plain", "js", "file"])
|
||||
}
|
||||
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() {
|
||||
result = this.getKeywordArgument("content_type")
|
||||
}
|
||||
|
||||
override string getMimetype() {
|
||||
exists(this.getKeywordArgument("json")) and result = "application/json"
|
||||
or
|
||||
exists(this.getKeywordArgument("plain")) and result = "text/plain"
|
||||
or
|
||||
exists(this.getKeywordArgument("html")) and result = "text/html"
|
||||
or
|
||||
exists(this.getKeywordArgument("xml")) and result = "application/xml"
|
||||
or
|
||||
exists(this.getKeywordArgument("js")) and result = "text/javascript"
|
||||
or
|
||||
not exists(this.getKeywordArgument(["json", "plain", "html", "xml", "js"])) and
|
||||
result = super.getMimetype()
|
||||
}
|
||||
|
||||
override string getMimetypeDefault() { result = "text/html" }
|
||||
}
|
||||
|
||||
/** A call to the `render` method within the context of a template. */
|
||||
private class ActionViewRenderCall extends RenderCall, ActionViewContextCall { }
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@ actionControllerControllerClasses
|
||||
| active_record/ActiveRecord.rb:66:1:94:3 | BazController |
|
||||
| active_record/ActiveRecord.rb:96:1:104:3 | AnnotatedController |
|
||||
| app/controllers/comments_controller.rb:1:1:7:3 | CommentsController |
|
||||
| app/controllers/foo/bars_controller.rb:3:1:39:3 | BarsController |
|
||||
| app/controllers/foo/bars_controller.rb:3:1:46:3 | BarsController |
|
||||
| app/controllers/photos_controller.rb:1:1:4:3 | PhotosController |
|
||||
| app/controllers/posts_controller.rb:1:1:10:3 | PostsController |
|
||||
| app/controllers/users/notifications_controller.rb:2:3:5:5 | NotificationsController |
|
||||
@@ -28,6 +28,7 @@ actionControllerActionMethods
|
||||
| app/controllers/foo/bars_controller.rb:20:3:24:5 | show |
|
||||
| app/controllers/foo/bars_controller.rb:26:3:28:5 | go_back |
|
||||
| app/controllers/foo/bars_controller.rb:30:3:32:5 | go_back_2 |
|
||||
| app/controllers/foo/bars_controller.rb:34:3:39:5 | show_2 |
|
||||
| app/controllers/photos_controller.rb:2:3:3:5 | show |
|
||||
| app/controllers/posts_controller.rb:2:3:3:5 | index |
|
||||
| app/controllers/posts_controller.rb:5:3:6:5 | show |
|
||||
@@ -103,8 +104,8 @@ redirectToCalls
|
||||
| app/controllers/foo/bars_controller.rb:31:5:31:56 | call to redirect_back |
|
||||
actionControllerHelperMethods
|
||||
getAssociatedControllerClasses
|
||||
| app/controllers/foo/bars_controller.rb:3:1:39:3 | BarsController | app/views/foo/bars/_widget.html.erb:0:0:0:0 | app/views/foo/bars/_widget.html.erb |
|
||||
| app/controllers/foo/bars_controller.rb:3:1:39:3 | BarsController | app/views/foo/bars/show.html.erb:0:0:0:0 | app/views/foo/bars/show.html.erb |
|
||||
| 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 |
|
||||
| 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 |
|
||||
controllerTemplateFiles
|
||||
| app/controllers/foo/bars_controller.rb:3:1:39:3 | BarsController | app/views/foo/bars/_widget.html.erb:0:0:0:0 | app/views/foo/bars/_widget.html.erb |
|
||||
| app/controllers/foo/bars_controller.rb:3:1:39:3 | BarsController | app/views/foo/bars/show.html.erb:0:0:0:0 | app/views/foo/bars/show.html.erb |
|
||||
| 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 |
|
||||
| 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 |
|
||||
|
||||
@@ -14,9 +14,19 @@ rawCalls
|
||||
renderCalls
|
||||
| app/controllers/foo/bars_controller.rb:6:5:6:37 | call to render |
|
||||
| app/controllers/foo/bars_controller.rb:23:5:23:76 | call to render |
|
||||
| app/controllers/foo/bars_controller.rb:37:5:37:17 | call to render |
|
||||
| app/controllers/foo/bars_controller.rb:35:5:35:33 | call to render |
|
||||
| app/controllers/foo/bars_controller.rb:38:5:38:50 | call to render |
|
||||
| app/controllers/foo/bars_controller.rb:44:5:44:17 | call to render |
|
||||
| app/views/foo/bars/show.html.erb:31:5:31:89 | call to render |
|
||||
renderToCalls
|
||||
| app/controllers/foo/bars_controller.rb:15:16:15:97 | call to render_to_string |
|
||||
| app/controllers/foo/bars_controller.rb:36:12:36:67 | call to render_to_string |
|
||||
linkToCalls
|
||||
| app/views/foo/bars/show.html.erb:33:5:33:41 | call to link_to |
|
||||
httpResponses
|
||||
| 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 |
|
||||
| app/controllers/foo/bars_controller.rb:23:5:23:76 | call to render | app/controllers/foo/bars_controller.rb:23:12:23:26 | "foo/bars/show" | text/html |
|
||||
| app/controllers/foo/bars_controller.rb:35:5:35:33 | call to render | app/controllers/foo/bars_controller.rb:35:18:35:33 | call to [] | application/json |
|
||||
| app/controllers/foo/bars_controller.rb:36:12:36:67 | call to render_to_string | app/controllers/foo/bars_controller.rb:36:29:36:33 | @user | application/json |
|
||||
| app/controllers/foo/bars_controller.rb:38:5:38:50 | call to render | app/controllers/foo/bars_controller.rb:38:12:38:22 | call to backtrace | text/plain |
|
||||
| app/controllers/foo/bars_controller.rb:44:5:44:17 | call to render | app/controllers/foo/bars_controller.rb:44:12:44:17 | "show" | text/html |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import codeql.ruby.frameworks.ActionController
|
||||
import codeql.ruby.frameworks.ActionView
|
||||
private import codeql.ruby.frameworks.ActionController
|
||||
private import codeql.ruby.frameworks.ActionView
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.DataFlow
|
||||
|
||||
query predicate htmlSafeCalls(HtmlSafeCall c) { any() }
|
||||
|
||||
@@ -10,3 +12,7 @@ query predicate renderCalls(RenderCall c) { any() }
|
||||
query predicate renderToCalls(RenderToCall c) { any() }
|
||||
|
||||
query predicate linkToCalls(LinkToCall c) { any() }
|
||||
|
||||
query predicate httpResponses(HTTP::Server::HttpResponse r, DataFlow::Node body, string mimeType) {
|
||||
r.getBody() = body and r.getMimetype() = mimeType
|
||||
}
|
||||
|
||||
@@ -31,6 +31,13 @@ class BarsController < ApplicationController
|
||||
redirect_back fallback_location: { action: "index" }
|
||||
end
|
||||
|
||||
def show_2
|
||||
render json: { some: "data" }
|
||||
body = render_to_string @user, content_type: "application/json"
|
||||
rescue => e
|
||||
render e.backtrace, content_type: "text/plain"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def unreachable_action
|
||||
|
||||
Reference in New Issue
Block a user