diff --git a/ql/Cargo.lock b/ql/Cargo.lock index 6ee11a3ab46..d9961c9d94f 100644 Binary files a/ql/Cargo.lock and b/ql/Cargo.lock differ diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll index 6fd1f77d554..ba365af167d 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll @@ -14,6 +14,15 @@ private import codeql.ruby.frameworks.Rails private import codeql.ruby.frameworks.internal.Rails private import codeql.ruby.dataflow.internal.DataFlowDispatch +/** + * Provides modeling for ActionController, which is part of the `actionpack` gem. + * Version: 7.0. + */ +module ActionController { + // TODO: move the rest of this file inside this module. + import codeql.ruby.frameworks.actioncontroller.Filters +} + /** * DEPRECATED: Import `codeql.ruby.frameworks.Rails` and use `Rails::ParamsCall` instead. */ diff --git a/ruby/ql/lib/codeql/ruby/frameworks/actioncontroller/Filters.qll b/ruby/ql/lib/codeql/ruby/frameworks/actioncontroller/Filters.qll new file mode 100644 index 00000000000..55c7e2e7683 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/actioncontroller/Filters.qll @@ -0,0 +1,390 @@ +/** + * Provides modeling for ActionController filters. + */ + +private import codeql.ruby.AST +private import codeql.ruby.frameworks.ActionController +private import codeql.ruby.controlflow.CfgNodes +private import codeql.ruby.controlflow.CfgNodes::ExprNodes +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate +private import codeql.ruby.ast.internal.Constant + +/** + * Provides modeling for ActionController filters. + */ +module Filters { + private newtype TFilterKind = + TBeforeKind() or + TAfterKind() or + TAroundKind() + + /** + * Represents the kind of filter. + * "before" filters run before the action and "after" filters run after the + * action. "around" filters run around the action, `yield`ing to it at will. + */ + private class FilterKind extends TFilterKind { + string toString() { + this = TBeforeKind() and result = "before" + or + this = TAfterKind() and result = "after" + or + this = TAroundKind() and result = "around" + } + } + + /** + * A call to a class method that adds or removes a filter from the callback chain. + * This class exists to encapsulate common behavior between calls that + * register callbacks (`before_action`, `after_action` etc.) and calls that + * remove callbacks (`skip_before_action`, `skip_after_action` etc.) + */ + private class FilterCall extends MethodCallCfgNode { + private FilterKind kind; + + FilterCall() { + this.getExpr().getEnclosingModule() = any(ActionControllerClass c).getADeclaration() and + this.getMethodName() = ["", "prepend_", "append_", "skip_"] + kind + "_action" + } + + FilterKind getKind() { result = kind } + + /** + * Gets an action which this filter is applied to. + */ + ActionControllerActionMethod getAnAction() { + // A filter cannot apply to another filter + result != any(Filter f).getFilterCallable() and + // Only include routable actions. This can exclude valid actions if we can't parse the `routes.rb` file fully. + exists(result.getARoute()) and + ( + result.getName() = this.getOnlyArgument() + or + not exists(this.getOnlyArgument()) and + forall(string except | except = this.getExceptArgument() | result.getName() != except) + ) and + ( + result = this.getExpr().getEnclosingModule().getAMethod() + or + exists(ModuleBase m | + m.getModule() = this.getExpr().getEnclosingModule().getModule().getADescendent() and + result = m.getAMethod() + ) + ) + } + + private string getOnlyArgument() { + exists(ExprCfgNode only | only = this.getKeywordArgument("only") | + // only: :index + result = only.getConstantValue().getStringlikeValue() + or + // only: [:index, :show] + // only: SOME_CONST_ARRAY + exists(ArrayLiteralCfgNode n | + isArrayConstant(only, n) and + result = n.getAnArgument().getConstantValue().getStringlikeValue() + ) + ) + } + + private string getExceptArgument() { + exists(ExprCfgNode except | except = this.getKeywordArgument("except") | + // except: :create + result = except.getConstantValue().getStringlikeValue() + or + // except: [:create, :update] + // except: SOME_CONST_ARRAY + exists(ArrayLiteralCfgNode n | + isArrayConstant(except, n) and + result = n.getAnArgument().getConstantValue().getStringlikeValue() + ) + ) + } + + StringlikeLiteralCfgNode getFilterArgument() { result = this.getPositionalArgument(_) } + + /** + * Gets the callable that implements the filter with name `name`. + * This currently only finds methods in the local class or superclass. + * It doesn't handle: + * - lambdas + * - blocks + * - classes + * + * In the example below, the callable for the filter `:foo` is the method `foo`. + * ```rb + * class PostsController < ActionController::Base + * before_action :foo + * + * def foo + * end + * end + * ``` + */ + Callable getFilterCallable(string name) { + result.(MethodBase).getName() = name and + result.getEnclosingModule().getModule() = + this.getExpr().getEnclosingModule().getModule().getAnAncestor() + } + } + + /** + * An argument to a call that registers a callback for one or more + * ActionController actions. These are commonly called filters. + * + * In the example below, the `before_action` call registers `set_user` as a + * callback for all actions in the controller. When a request is routed to + * `PostsController#index`, the method `set_user` will be called before + * `index` is executed. + * + * The `after_action` call registers `log_request` as a callback. This behaves + * similarly to `before_action` but the callback will be called _after_ the + * action has finished executing. + * + * The `around_action` call registers `trace_request` as a callback that will + * run _around_ the action. This means that `trace_request` will be called + * before the action, and will run until it `yield`s control. Then the action + * (or another callback) will be run. Once the action has run, control will be + * returned to `trace_request`, which will finish executing. + * + * Due to the complexity of dataflow around `yield` calls, currently only + * `before_action` and `after_action` callbacks are modeled fully here. + * + * ```rb + * class PostsController < ApplicationController + * before_action :set_user + * after_action :log_request + * around_action :trace_request + * + * def index + * @posts = @user.posts.all + * end + * + * private + * + * def set_user + * @user = User.find(session[:user_id]) + * end + * + * def log_request + * Logger.info(request.path) + * end + * + * def trace_request + * start = Time.now + * yield + * Logger.info("Request took #{Time.now - start} seconds") + * end + * end + * ``` + */ + private class Filter extends FilterImpl { + Filter() { not this.isSkipFilter() } + + /** + * Holds if this callback does not run for `action`. This is either because + * it has been explicitly skipped by a `SkipFilter` or because a callback + * with the same name is registered later one, overriding this one. + */ + predicate skipped(ActionControllerActionMethod action) { + action = this.getAnAction() and + ( + this = any(SkipFilter f).getSkippedFilter(action) or + this.overridden() + ) + } + + /** + * Holds if this callback runs before `other`. + */ + predicate runsBefore(Filter other, ActionControllerActionMethod action) { + other.getKind() = this.getKind() and + not this.skipped(action) and + not other.skipped(action) and + action = this.getAnAction() and + action = other.getAnAction() and + ( + not this.isPrepended() and + ( + not other.isPrepended() and + ( + this.getKind() = TBeforeKind() and + this.registeredBefore(other) + or + this.getKind() = TAfterKind() and + other.registeredBefore(this) + ) + or + other.isPrepended() and this.getKind() = TAfterKind() + ) + or + this.isPrepended() and + ( + not other.isPrepended() and + this.getKind() = TBeforeKind() + or + other.isPrepended() and + ( + this.getKind() = TBeforeKind() and this.registeredBefore(other) + or + this.getKind() = TAfterKind() and other.registeredBefore(this) + ) + ) + ) + } + + Filter getNextFilter(ActionControllerActionMethod action) { + result != this and + this.runsBefore(result, action) and + not exists(Filter mid | this.runsBefore(mid, action) | mid.runsBefore(result, action)) + } + } + + /** + * Behavior that is common to filters and `skip_*` calls. + * This is separated just because when we don't want `Filter` to include `skip_*` calls. + */ + private class FilterImpl extends StringlikeLiteralCfgNode { + private FilterCall call; + private FilterKind kind; + + FilterImpl() { + this = call.getFilterArgument() and + kind = call.getKind() and + call.getMethodName() = ["", "prepend_", "append_", "skip_"] + kind + "_action" + } + + predicate isSkipFilter() { call.getMethodName().regexpMatch("^skip_.+$") } + + predicate isPrepended() { call.getMethodName().regexpMatch("^prepend.+$") } + + FilterCall getCall() { result = call } + + FilterKind getKind() { result = kind } + + /** + * Holds if this callback is registered before `other`. This does not + * guarantee that the callback will be executed before `other`. For example, + * `after_action` callbacks are executed in reverse order. + */ + predicate registeredBefore(FilterImpl other) { + exists(FilterCall otherCall | + // predCall -> call + // pred -> this + // succ -> other + other = otherCall.getFilterArgument() and + ( + // before_action :this, :other + // + // before_action :this + // before_action :other + this.getBasicBlock() = other.getBasicBlock() and + this.getASuccessor+() = other + or + call.getExpr().getEnclosingModule() = otherCall.getExpr().getEnclosingModule() and + this.getBasicBlock().strictlyDominates(other.getBasicBlock()) and + this != other + or + // This callback is in a superclass of `other`'s class. + // + // class A + // before_action :this + // + // class B < A + // before_action :other + otherCall.getExpr().getEnclosingModule().getModule() = + call.getExpr().getEnclosingModule().getModule().getASubClass+() + ) + ) + } + + /** + * Holds if this callback is overridden by a callback with the same name. For example: + * ```rb + * class UsersController + * before_action :foo # this filter is override by the subsequent `before_action :foo` call below. + * before_action :bar + * before_action :foo + * end + * ``` + */ + predicate overridden() { + exists(FilterImpl f | + f != this and + f.getFilterCallable() = this.getFilterCallable() and + f.getFilterName() = this.getFilterName() and + f.getKind() = this.getKind() and + this.registeredBefore(f) + ) + } + + string getFilterName() { result = this.getConstantValue().getStringlikeValue() } + + Callable getFilterCallable() { result = call.getFilterCallable(this.getFilterName()) } + + ActionControllerActionMethod getAnAction() { result = call.getAnAction() } + } + + private class BeforeFilter extends Filter { + BeforeFilter() { this.getKind() = TBeforeKind() } + } + + private class AfterFilter extends Filter { + AfterFilter() { this.getKind() = TAfterKind() } + } + + /** + * A call to `skip_before_action`, `skip_after_action` or `skip_around_action`. + * This skips a previously registered callback. + * Like other filter calls, the `except` and `only` keyword arguments can be + * passed to restrict the actions that the callback is skipped for. + */ + private class SkipFilter extends FilterImpl { + SkipFilter() { this.isSkipFilter() } + + Filter getSkippedFilter(ActionControllerActionMethod action) { + action = this.getAnAction() and + action = result.getAnAction() and + result.getKind() = this.getKind() and + result.registeredBefore(this) and + result.getFilterCallable() = this.getFilterCallable() + } + } + + /** + * Holds if `pred` is called before `succ` in the callback chain for action `action`. + * `pred` and `succ` may be methods bound to callbacks or controller actions. + */ + predicate next(ActionControllerActionMethod action, Method pred, Method succ) { + exists(BeforeFilter f | pred = f.getFilterCallable() | + // Non-terminal before filter + succ = f.getNextFilter(action).getFilterCallable() + or + // Final before filter + not exists(f.getNextFilter(action)) and + not f.skipped(action) and + action = f.getAnAction() and + succ = action + ) + or + exists(AfterFilter f | + // First after filter + action = f.getAnAction() and + not f.skipped(action) and + pred = action and + succ = f.getFilterCallable() and + not exists(AfterFilter g | g.getNextFilter(action) = f) + or + // Subsequent after filter + pred = f.getFilterCallable() and + succ = f.getNextFilter(action).getFilterCallable() + ) + } + + /** + * Holds if `pred` is called before `succ` in the callback chain for some action. + * `pred` and `succ` may be methods bound to callbacks or controller actions. + */ + predicate next(Method pred, Method succ) { next(_, pred, succ) } +} diff --git a/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected b/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected index 849ed702151..f09706f05fe 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected +++ b/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected @@ -1,4 +1,46 @@ actionDispatchRoutes +| action_controller/routes.rb:2:5:2:20 | call to resources | delete | users/:id | users | destroy | +| action_controller/routes.rb:2:5:2:20 | call to resources | get | users | users | index | +| action_controller/routes.rb:2:5:2:20 | call to resources | get | users/:id | users | show | +| action_controller/routes.rb:2:5:2:20 | call to resources | get | users/new | users | new | +| action_controller/routes.rb:2:5:2:20 | call to resources | get | users:id/edit | users | edit | +| action_controller/routes.rb:2:5:2:20 | call to resources | patch | users/:id | users | update | +| action_controller/routes.rb:2:5:2:20 | call to resources | post | users | users | create | +| action_controller/routes.rb:2:5:2:20 | call to resources | put | users/:id | users | update | +| action_controller/routes.rb:3:5:5:7 | call to resources | delete | comments/:id | comments | destroy | +| action_controller/routes.rb:3:5:5:7 | call to resources | get | comments | comments | index | +| action_controller/routes.rb:3:5:5:7 | call to resources | get | comments/:id | comments | show | +| action_controller/routes.rb:3:5:5:7 | call to resources | get | comments/new | comments | new | +| action_controller/routes.rb:3:5:5:7 | call to resources | get | comments:id/edit | comments | edit | +| action_controller/routes.rb:3:5:5:7 | call to resources | patch | comments/:id | comments | update | +| action_controller/routes.rb:3:5:5:7 | call to resources | post | comments | comments | create | +| action_controller/routes.rb:3:5:5:7 | call to resources | put | comments/:id | comments | update | +| action_controller/routes.rb:4:9:4:32 | call to get | get | comments/:comment_id/photo | comments | photo | +| action_controller/routes.rb:6:5:6:21 | call to resources | delete | photos/:id | photos | destroy | +| action_controller/routes.rb:6:5:6:21 | call to resources | get | photos | photos | index | +| action_controller/routes.rb:6:5:6:21 | call to resources | get | photos/:id | photos | show | +| action_controller/routes.rb:6:5:6:21 | call to resources | get | photos/new | photos | new | +| action_controller/routes.rb:6:5:6:21 | call to resources | get | photos:id/edit | photos | edit | +| action_controller/routes.rb:6:5:6:21 | call to resources | patch | photos/:id | photos | update | +| action_controller/routes.rb:6:5:6:21 | call to resources | post | photos | photos | create | +| action_controller/routes.rb:6:5:6:21 | call to resources | put | photos/:id | photos | update | +| action_controller/routes.rb:7:5:9:7 | call to resources | delete | posts/:id | posts | destroy | +| action_controller/routes.rb:7:5:9:7 | call to resources | get | posts | posts | index | +| action_controller/routes.rb:7:5:9:7 | call to resources | get | posts/:id | posts | show | +| action_controller/routes.rb:7:5:9:7 | call to resources | get | posts/new | posts | new | +| action_controller/routes.rb:7:5:9:7 | call to resources | get | posts:id/edit | posts | edit | +| action_controller/routes.rb:7:5:9:7 | call to resources | patch | posts/:id | posts | update | +| action_controller/routes.rb:7:5:9:7 | call to resources | post | posts | posts | create | +| action_controller/routes.rb:7:5:9:7 | call to resources | put | posts/:id | posts | update | +| action_controller/routes.rb:8:9:8:34 | call to post | post | posts/:post_id/upvote | posts | upvote | +| action_controller/routes.rb:10:5:10:19 | call to resources | delete | tags/:id | tags | destroy | +| action_controller/routes.rb:10:5:10:19 | call to resources | get | tags | tags | index | +| action_controller/routes.rb:10:5:10:19 | call to resources | get | tags/:id | tags | show | +| action_controller/routes.rb:10:5:10:19 | call to resources | get | tags/new | tags | new | +| action_controller/routes.rb:10:5:10:19 | call to resources | get | tags:id/edit | tags | edit | +| action_controller/routes.rb:10:5:10:19 | call to resources | patch | tags/:id | tags | update | +| action_controller/routes.rb:10:5:10:19 | call to resources | post | tags | tags | create | +| action_controller/routes.rb:10:5:10:19 | call to resources | put | tags/:id | tags | update | | app/config/routes.rb:2:3:8:5 | call to resources | get | posts | posts | index | | app/config/routes.rb:2:3:8:5 | call to resources | get | posts/:id | posts | show | | app/config/routes.rb:3:5:6:7 | call to resources | delete | posts/:post_id/comments/:id | comments | destroy | @@ -34,24 +76,42 @@ actionDispatchRoutes | app/config/routes.rb:49:5:49:95 | call to delete | delete | users/:user/notifications | users/notifications | destroy | | 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 | actionDispatchControllerMethods -| app/config/routes.rb:2:3:8:5 | call to resources | action_controller/controllers/posts_controller.rb:2:3:3:5 | index | -| app/config/routes.rb:2:3:8:5 | call to resources | action_controller/controllers/posts_controller.rb:5:3:6:5 | show | +| action_controller/routes.rb:2:5:2:20 | call to resources | action_controller/input_access.rb:2:3:49:5 | index | +| action_controller/routes.rb:2:5:2:20 | call to resources | action_controller/logging.rb:2:5:8:7 | index | +| action_controller/routes.rb:3:5:5:7 | call to resources | action_controller/controllers/comments_controller.rb:17:3:51:5 | index | +| action_controller/routes.rb:3:5:5:7 | call to resources | action_controller/controllers/comments_controller.rb:53:3:54:5 | create | +| action_controller/routes.rb:3:5:5:7 | call to resources | action_controller/controllers/comments_controller.rb:56:3:62:5 | show | +| action_controller/routes.rb:3:5:5:7 | call to resources | action_controller/controllers/comments_controller.rb:68:3:70:5 | destroy | +| action_controller/routes.rb:3:5:5:7 | call to resources | app/controllers/comments_controller.rb:2:3:36:5 | index | +| action_controller/routes.rb:3:5:5:7 | call to resources | app/controllers/comments_controller.rb:38:3:39:5 | show | +| action_controller/routes.rb:4:9:4:32 | call to get | action_controller/controllers/comments_controller.rb:64:3:66:5 | photo | +| action_controller/routes.rb:6:5:6:21 | call to resources | action_controller/controllers/photos_controller.rb:3:3:6:5 | show | +| action_controller/routes.rb:6:5:6:21 | call to resources | app/controllers/photos_controller.rb:2:3:3:5 | show | +| action_controller/routes.rb:7:5:9:7 | call to resources | action_controller/controllers/posts_controller.rb:12:3:13:5 | index | +| action_controller/routes.rb:7:5:9:7 | call to resources | action_controller/controllers/posts_controller.rb:15:3:16:5 | show | +| action_controller/routes.rb:7:5:9:7 | call to resources | app/controllers/posts_controller.rb:2:3:3:5 | index | +| action_controller/routes.rb:7:5:9:7 | call to resources | app/controllers/posts_controller.rb:5:3:6:5 | show | +| action_controller/routes.rb:8:9:8:34 | call to post | action_controller/controllers/posts_controller.rb:18:3:19:5 | upvote | +| action_controller/routes.rb:8:9:8:34 | call to post | app/controllers/posts_controller.rb:8:3:9:5 | upvote | +| app/config/routes.rb:2:3:8:5 | call to resources | action_controller/controllers/posts_controller.rb:12:3:13:5 | index | +| app/config/routes.rb:2:3:8:5 | call to resources | action_controller/controllers/posts_controller.rb:15:3:16:5 | show | | app/config/routes.rb:2:3:8:5 | call to resources | app/controllers/posts_controller.rb:2:3:3:5 | index | | app/config/routes.rb:2:3:8:5 | call to resources | app/controllers/posts_controller.rb:5:3:6:5 | show | -| app/config/routes.rb:3:5:6:7 | call to resources | action_controller/controllers/comments_controller.rb:2:3:36:5 | index | -| app/config/routes.rb:3:5:6:7 | call to resources | action_controller/controllers/comments_controller.rb:38:3:44:5 | show | -| app/config/routes.rb:3:5:6:7 | call to resources | action_controller/controllers/comments_controller.rb:50:3:52:5 | destroy | +| app/config/routes.rb:3:5:6:7 | call to resources | action_controller/controllers/comments_controller.rb:17:3:51:5 | index | +| app/config/routes.rb:3:5:6:7 | call to resources | action_controller/controllers/comments_controller.rb:53:3:54:5 | create | +| app/config/routes.rb:3:5:6:7 | call to resources | action_controller/controllers/comments_controller.rb:56:3:62:5 | show | +| app/config/routes.rb:3:5:6:7 | call to resources | action_controller/controllers/comments_controller.rb:68:3:70:5 | destroy | | app/config/routes.rb:3:5:6:7 | call to resources | app/controllers/comments_controller.rb:2:3:36:5 | index | | app/config/routes.rb:3:5:6:7 | call to resources | app/controllers/comments_controller.rb:38:3:39:5 | show | -| app/config/routes.rb:7:5:7:37 | call to post | action_controller/controllers/posts_controller.rb:8:3:9:5 | upvote | +| app/config/routes.rb:7:5:7:37 | call to post | action_controller/controllers/posts_controller.rb:18:3:19:5 | upvote | | app/config/routes.rb:7:5:7:37 | call to post | app/controllers/posts_controller.rb:8:3:9:5 | upvote | -| app/config/routes.rb:27:3:27:48 | call to match | action_controller/controllers/photos_controller.rb:2:3:3:5 | show | +| app/config/routes.rb:27:3:27:48 | call to match | action_controller/controllers/photos_controller.rb:3:3:6:5 | show | | app/config/routes.rb:27:3:27:48 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show | -| app/config/routes.rb:28:3:28:50 | call to match | action_controller/controllers/photos_controller.rb:2:3:3:5 | show | +| app/config/routes.rb:28:3:28:50 | call to match | action_controller/controllers/photos_controller.rb:3:3:6:5 | show | | app/config/routes.rb:28:3:28:50 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show | -| app/config/routes.rb:29:3:29:69 | call to match | action_controller/controllers/photos_controller.rb:2:3:3:5 | show | +| app/config/routes.rb:29:3:29:69 | call to match | action_controller/controllers/photos_controller.rb:3:3:6:5 | show | | app/config/routes.rb:29:3:29:69 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show | -| app/config/routes.rb:30:3:30:50 | call to match | action_controller/controllers/photos_controller.rb:2:3:3:5 | show | +| app/config/routes.rb:30:3:30:50 | call to match | action_controller/controllers/photos_controller.rb:3:3:6:5 | show | | app/config/routes.rb:30:3:30:50 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show | | 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 | | app/config/routes.rb:50:5:50:94 | call to post | app/controllers/users/notifications_controller.rb:3:5:4:7 | mark_as_read | diff --git a/ruby/ql/test/library-tests/frameworks/ActionView.expected b/ruby/ql/test/library-tests/frameworks/ActionView.expected index 1ad020a2394..07fb6264633 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionView.expected +++ b/ruby/ql/test/library-tests/frameworks/ActionView.expected @@ -9,7 +9,8 @@ rawCalls | app/views/foo/bars/show.html.erb:5:5:5:21 | call to raw | | app/views/foo/bars/show.html.erb:7:5:7:19 | call to raw | renderCalls -| action_controller/controllers/comments_controller.rb:42:21:42:64 | call to render | +| action_controller/controllers/comments_controller.rb:60:21:60:64 | call to render | +| action_controller/controllers/comments_controller.rb:76:5:76:68 | call to render | | action_controller/controllers/foo/bars_controller.rb:6:5:6:37 | call to render | | action_controller/controllers/foo/bars_controller.rb:23:5:23:76 | call to render | | action_controller/controllers/foo/bars_controller.rb:35:5:35:33 | call to render | @@ -29,9 +30,9 @@ renderToCalls linkToCalls | app/views/foo/bars/show.html.erb:33:5:33:41 | call to link_to | httpResponses -| 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 | -| 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 | -| 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 | +| action_controller/controllers/comments_controller.rb:26:5:26:17 | call to body= | action_controller/controllers/comments_controller.rb:26:21:26:34 | ... = ... | text/http | +| action_controller/controllers/comments_controller.rb:36:5:36:37 | call to send_file | action_controller/controllers/comments_controller.rb:36:24:36:36 | "my-file.ext" | application/octet-stream | +| action_controller/controllers/comments_controller.rb:65:5:65:20 | call to send_data | action_controller/controllers/comments_controller.rb:65:15:65:20 | @photo | application/octet-stream | | 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 | | 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 | | 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 | diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/ActionController.expected b/ruby/ql/test/library-tests/frameworks/action_controller/ActionController.expected index f4a21655355..e17f691b149 100644 --- a/ruby/ql/test/library-tests/frameworks/action_controller/ActionController.expected +++ b/ruby/ql/test/library-tests/frameworks/action_controller/ActionController.expected @@ -1,28 +1,31 @@ actionControllerControllerClasses -| controllers/comments_controller.rb:1:1:53:3 | CommentsController | +| controllers/application_controller.rb:1:1:13:3 | ApplicationController | +| controllers/comments_controller.rb:1:1:104:3 | CommentsController | | controllers/foo/bars_controller.rb:3:1:46:3 | BarsController | -| controllers/photos_controller.rb:1:1:4:3 | PhotosController | -| controllers/posts_controller.rb:1:1:10:3 | PostsController | +| controllers/photos_controller.rb:1:1:10:3 | PhotosController | +| controllers/posts_controller.rb:1:1:30:3 | PostsController | | controllers/tags_controller.rb:1:1:2:3 | TagsController | | controllers/users/notifications_controller.rb:2:3:5:5 | Users::NotificationsController | | input_access.rb:1:1:50:3 | UsersController | | params_flow.rb:1:1:162:3 | MyController | | params_flow.rb:170:1:178:3 | Subclass | actionControllerActionMethods -| controllers/comments_controller.rb:2:3:36:5 | index | -| controllers/comments_controller.rb:38:3:44:5 | show | -| controllers/comments_controller.rb:46:3:48:5 | photo | -| controllers/comments_controller.rb:50:3:52:5 | destroy | +| controllers/comments_controller.rb:17:3:51:5 | index | +| controllers/comments_controller.rb:53:3:54:5 | create | +| controllers/comments_controller.rb:56:3:62:5 | show | +| controllers/comments_controller.rb:64:3:66:5 | photo | +| controllers/comments_controller.rb:68:3:70:5 | destroy | | controllers/foo/bars_controller.rb:5:3:7:5 | index | | controllers/foo/bars_controller.rb:9:3:18:5 | show_debug | | controllers/foo/bars_controller.rb:20:3:24:5 | show | | controllers/foo/bars_controller.rb:26:3:28:5 | go_back | | controllers/foo/bars_controller.rb:30:3:32:5 | go_back_2 | | controllers/foo/bars_controller.rb:34:3:39:5 | show_2 | -| controllers/photos_controller.rb:2:3:3:5 | show | -| controllers/posts_controller.rb:2:3:3:5 | index | -| controllers/posts_controller.rb:5:3:6:5 | show | -| controllers/posts_controller.rb:8:3:9:5 | upvote | +| controllers/photos_controller.rb:3:3:6:5 | show | +| controllers/photos_controller.rb:8:3:9:5 | foo | +| controllers/posts_controller.rb:12:3:13:5 | index | +| controllers/posts_controller.rb:15:3:16:5 | show | +| controllers/posts_controller.rb:18:3:19:5 | upvote | | controllers/users/notifications_controller.rb:3:5:4:7 | mark_as_read | | input_access.rb:2:3:49:5 | index | | logging.rb:2:5:8:7 | index | @@ -63,10 +66,12 @@ actionControllerActionMethods | params_flow.rb:165:3:167:5 | m34 | | params_flow.rb:171:3:173:5 | m35 | paramsCalls +| controllers/comments_controller.rb:80:36:80:41 | call to params | | controllers/foo/bars_controller.rb:13:21:13:26 | call to params | | controllers/foo/bars_controller.rb:14:10:14:15 | call to params | | controllers/foo/bars_controller.rb:21:21:21:26 | call to params | | controllers/foo/bars_controller.rb:22:10:22:15 | call to params | +| controllers/posts_controller.rb:24:23:24:28 | call to params | | params_flow.rb:3:10:3:15 | call to params | | params_flow.rb:7:10:7:15 | call to params | | params_flow.rb:11:10:11:15 | call to params | @@ -116,10 +121,12 @@ paramsCalls | params_flow.rb:172:10:172:15 | call to params | | params_flow.rb:176:10:176:15 | call to params | paramsSources +| controllers/comments_controller.rb:80:36:80:41 | call to params | | controllers/foo/bars_controller.rb:13:21:13:26 | call to params | | controllers/foo/bars_controller.rb:14:10:14:15 | call to params | | controllers/foo/bars_controller.rb:21:21:21:26 | call to params | | controllers/foo/bars_controller.rb:22:10:22:15 | call to params | +| controllers/posts_controller.rb:24:23:24:28 | call to params | | params_flow.rb:3:10:3:15 | call to params | | params_flow.rb:7:10:7:15 | call to params | | params_flow.rb:11:10:11:15 | call to params | @@ -169,19 +176,22 @@ paramsSources | params_flow.rb:172:10:172:15 | call to params | | params_flow.rb:176:10:176:15 | call to params | httpInputAccesses -| controllers/comments_controller.rb:3:5:3:18 | call to params | ActionDispatch::Request#params | -| controllers/comments_controller.rb:4:5:4:22 | call to parameters | ActionDispatch::Request#parameters | -| controllers/comments_controller.rb:5:5:5:15 | call to GET | ActionDispatch::Request#GET | -| controllers/comments_controller.rb:6:5:6:16 | call to POST | ActionDispatch::Request#POST | -| controllers/comments_controller.rb:7:5:7:28 | call to query_parameters | ActionDispatch::Request#query_parameters | -| controllers/comments_controller.rb:8:5:8:30 | call to request_parameters | ActionDispatch::Request#request_parameters | -| controllers/comments_controller.rb:9:5:9:31 | call to filtered_parameters | ActionDispatch::Request#filtered_parameters | -| controllers/comments_controller.rb:51:12:51:30 | call to body_stream | ActionDispatch::Request#body_stream | +| controllers/application_controller.rb:11:53:11:64 | call to path | ActionDispatch::Request#path | +| controllers/comments_controller.rb:18:5:18:18 | call to params | ActionDispatch::Request#params | +| controllers/comments_controller.rb:19:5:19:22 | call to parameters | ActionDispatch::Request#parameters | +| controllers/comments_controller.rb:20:5:20:15 | call to GET | ActionDispatch::Request#GET | +| controllers/comments_controller.rb:21:5:21:16 | call to POST | ActionDispatch::Request#POST | +| controllers/comments_controller.rb:22:5:22:28 | call to query_parameters | ActionDispatch::Request#query_parameters | +| controllers/comments_controller.rb:23:5:23:30 | call to request_parameters | ActionDispatch::Request#request_parameters | +| controllers/comments_controller.rb:24:5:24:31 | call to filtered_parameters | ActionDispatch::Request#filtered_parameters | +| controllers/comments_controller.rb:69:12:69:30 | call to body_stream | ActionDispatch::Request#body_stream | +| controllers/comments_controller.rb:80:36:80:41 | call to params | ActionController::Metal#params | | controllers/foo/bars_controller.rb:10:27:10:33 | call to cookies | ActionController::Metal#cookies | | controllers/foo/bars_controller.rb:13:21:13:26 | call to params | ActionController::Metal#params | | controllers/foo/bars_controller.rb:14:10:14:15 | call to params | ActionController::Metal#params | | controllers/foo/bars_controller.rb:21:21:21:26 | call to params | ActionController::Metal#params | | controllers/foo/bars_controller.rb:22:10:22:15 | call to params | ActionController::Metal#params | +| controllers/posts_controller.rb:24:23:24:28 | call to params | ActionController::Metal#params | | input_access.rb:3:5:3:18 | call to params | ActionDispatch::Request#params | | input_access.rb:4:5:4:22 | call to parameters | ActionDispatch::Request#parameters | | input_access.rb:5:5:5:15 | call to GET | ActionDispatch::Request#GET | @@ -275,21 +285,22 @@ cookiesCalls cookiesSources | controllers/foo/bars_controller.rb:10:27:10:33 | call to cookies | redirectToCalls -| controllers/comments_controller.rb:40:21:40:49 | call to redirect_to | +| controllers/comments_controller.rb:58:21:58:49 | call to redirect_to | | controllers/foo/bars_controller.rb:17:5:17:30 | call to redirect_to | | controllers/foo/bars_controller.rb:27:5:27:39 | call to redirect_back_or_to | | controllers/foo/bars_controller.rb:31:5:31:56 | call to redirect_back | renderCalls -| controllers/comments_controller.rb:42:21:42:64 | call to render | +| controllers/comments_controller.rb:60:21:60:64 | call to render | +| controllers/comments_controller.rb:76:5:76:68 | call to render | | controllers/foo/bars_controller.rb:6:5:6:37 | call to render | | controllers/foo/bars_controller.rb:23:5:23:76 | call to render | | controllers/foo/bars_controller.rb:35:5:35:33 | call to render | | controllers/foo/bars_controller.rb:38:5:38:50 | call to render | | controllers/foo/bars_controller.rb:44:5:44:17 | call to render | httpResponses -| controllers/comments_controller.rb:11:5:11:17 | call to body= | controllers/comments_controller.rb:11:21:11:34 | ... = ... | -| controllers/comments_controller.rb:21:5:21:37 | call to send_file | controllers/comments_controller.rb:21:24:21:36 | "my-file.ext" | -| controllers/comments_controller.rb:47:5:47:20 | call to send_data | controllers/comments_controller.rb:47:15:47:20 | @photo | +| controllers/comments_controller.rb:26:5:26:17 | call to body= | controllers/comments_controller.rb:26:21:26:34 | ... = ... | +| controllers/comments_controller.rb:36:5:36:37 | call to send_file | controllers/comments_controller.rb:36:24:36:36 | "my-file.ext" | +| controllers/comments_controller.rb:65:5:65:20 | call to send_data | controllers/comments_controller.rb:65:15:65:20 | @photo | | controllers/foo/bars_controller.rb:15:16:15:97 | call to render_to_string | controllers/foo/bars_controller.rb:15:33:15:47 | "foo/bars/show" | | controllers/foo/bars_controller.rb:23:5:23:76 | call to render | controllers/foo/bars_controller.rb:23:12:23:26 | "foo/bars/show" | | controllers/foo/bars_controller.rb:35:5:35:33 | call to render | controllers/foo/bars_controller.rb:35:18:35:33 | call to [] | @@ -300,21 +311,21 @@ actionControllerHelperMethods getAssociatedControllerClasses controllerTemplateFiles headerWriteAccesses -| controllers/comments_controller.rb:15:5:15:35 | call to []= | content-type | controllers/comments_controller.rb:15:39:15:49 | ... = ... | -| controllers/comments_controller.rb:16:5:16:46 | call to set_header | content-length | controllers/comments_controller.rb:16:43:16:45 | 100 | -| controllers/comments_controller.rb:17:5:17:39 | call to []= | x-custom-header | controllers/comments_controller.rb:17:43:17:46 | ... = ... | -| controllers/comments_controller.rb:18:5:18:39 | call to []= | x-another-custom-header | controllers/comments_controller.rb:18:43:18:47 | ... = ... | -| controllers/comments_controller.rb:19:5:19:49 | call to add_header | x-yet-another | controllers/comments_controller.rb:19:42:19:49 | "indeed" | -| controllers/comments_controller.rb:25:5:25:21 | call to location= | location | controllers/comments_controller.rb:25:25:25:36 | ... = ... | -| controllers/comments_controller.rb:26:5:26:26 | call to cache_control= | cache-control | controllers/comments_controller.rb:26:30:26:36 | ... = ... | -| controllers/comments_controller.rb:27:5:27:27 | call to _cache_control= | cache-control | controllers/comments_controller.rb:27:31:27:37 | ... = ... | -| controllers/comments_controller.rb:28:5:28:17 | call to etag= | etag | controllers/comments_controller.rb:28:21:28:27 | ... = ... | -| controllers/comments_controller.rb:29:5:29:20 | call to charset= | content-type | controllers/comments_controller.rb:29:24:29:30 | ... = ... | -| controllers/comments_controller.rb:30:5:30:25 | call to content_type= | content-type | controllers/comments_controller.rb:30:29:30:35 | ... = ... | -| controllers/comments_controller.rb:32:5:32:17 | call to date= | date | controllers/comments_controller.rb:32:21:32:30 | ... = ... | -| controllers/comments_controller.rb:33:5:33:26 | call to last_modified= | last-modified | controllers/comments_controller.rb:33:30:33:43 | ... = ... | -| controllers/comments_controller.rb:34:5:34:22 | call to weak_etag= | etag | controllers/comments_controller.rb:34:26:34:32 | ... = ... | -| controllers/comments_controller.rb:35:5:35:24 | call to strong_etag= | etag | controllers/comments_controller.rb:35:28:35:34 | ... = ... | +| controllers/comments_controller.rb:30:5:30:35 | call to []= | content-type | controllers/comments_controller.rb:30:39:30:49 | ... = ... | +| controllers/comments_controller.rb:31:5:31:46 | call to set_header | content-length | controllers/comments_controller.rb:31:43:31:45 | 100 | +| controllers/comments_controller.rb:32:5:32:39 | call to []= | x-custom-header | controllers/comments_controller.rb:32:43:32:46 | ... = ... | +| controllers/comments_controller.rb:33:5:33:39 | call to []= | x-another-custom-header | controllers/comments_controller.rb:33:43:33:47 | ... = ... | +| controllers/comments_controller.rb:34:5:34:49 | call to add_header | x-yet-another | controllers/comments_controller.rb:34:42:34:49 | "indeed" | +| controllers/comments_controller.rb:40:5:40:21 | call to location= | location | controllers/comments_controller.rb:40:25:40:36 | ... = ... | +| controllers/comments_controller.rb:41:5:41:26 | call to cache_control= | cache-control | controllers/comments_controller.rb:41:30:41:36 | ... = ... | +| controllers/comments_controller.rb:42:5:42:27 | call to _cache_control= | cache-control | controllers/comments_controller.rb:42:31:42:37 | ... = ... | +| controllers/comments_controller.rb:43:5:43:17 | call to etag= | etag | controllers/comments_controller.rb:43:21:43:27 | ... = ... | +| controllers/comments_controller.rb:44:5:44:20 | call to charset= | content-type | controllers/comments_controller.rb:44:24:44:30 | ... = ... | +| controllers/comments_controller.rb:45:5:45:25 | call to content_type= | content-type | controllers/comments_controller.rb:45:29:45:35 | ... = ... | +| controllers/comments_controller.rb:47:5:47:17 | call to date= | date | controllers/comments_controller.rb:47:21:47:30 | ... = ... | +| controllers/comments_controller.rb:48:5:48:26 | call to last_modified= | last-modified | controllers/comments_controller.rb:48:30:48:43 | ... = ... | +| controllers/comments_controller.rb:49:5:49:22 | call to weak_etag= | etag | controllers/comments_controller.rb:49:26:49:32 | ... = ... | +| controllers/comments_controller.rb:50:5:50:24 | call to strong_etag= | etag | controllers/comments_controller.rb:50:28:50:34 | ... = ... | loggingCalls | logging.rb:3:9:3:31 | call to info | logging.rb:3:21:3:31 | "some info" | | logging.rb:4:9:4:31 | call to warn | logging.rb:4:21:4:31 | "a warning" | diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/Filters.expected b/ruby/ql/test/library-tests/frameworks/action_controller/Filters.expected new file mode 100644 index 00000000000..260a818fc41 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/action_controller/Filters.expected @@ -0,0 +1,53 @@ +| controllers/comments_controller.rb:17:3:51:5 | index | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/comments_controller.rb:99:3:100:5 | foo | +| controllers/comments_controller.rb:17:3:51:5 | index | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/application_controller.rb:6:3:8:5 | set_user | +| controllers/comments_controller.rb:17:3:51:5 | index | controllers/comments_controller.rb:17:3:51:5 | index | controllers/comments_controller.rb:87:3:89:5 | check_feature_flags | +| controllers/comments_controller.rb:17:3:51:5 | index | controllers/comments_controller.rb:87:3:89:5 | check_feature_flags | controllers/comments_controller.rb:95:3:97:5 | this_must_run_last | +| controllers/comments_controller.rb:17:3:51:5 | index | controllers/comments_controller.rb:91:3:93:5 | this_must_run_first | controllers/application_controller.rb:10:3:12:5 | log_request | +| controllers/comments_controller.rb:17:3:51:5 | index | controllers/comments_controller.rb:99:3:100:5 | foo | controllers/comments_controller.rb:102:3:103:5 | bar | +| controllers/comments_controller.rb:17:3:51:5 | index | controllers/comments_controller.rb:102:3:103:5 | bar | controllers/comments_controller.rb:17:3:51:5 | index | +| controllers/comments_controller.rb:53:3:54:5 | create | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/comments_controller.rb:74:3:77:5 | ensure_user_can_edit_comments | +| controllers/comments_controller.rb:53:3:54:5 | create | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/application_controller.rb:6:3:8:5 | set_user | +| controllers/comments_controller.rb:53:3:54:5 | create | controllers/comments_controller.rb:53:3:54:5 | create | controllers/comments_controller.rb:83:3:85:5 | log_comment_change | +| controllers/comments_controller.rb:53:3:54:5 | create | controllers/comments_controller.rb:74:3:77:5 | ensure_user_can_edit_comments | controllers/comments_controller.rb:99:3:100:5 | foo | +| controllers/comments_controller.rb:53:3:54:5 | create | controllers/comments_controller.rb:83:3:85:5 | log_comment_change | controllers/comments_controller.rb:87:3:89:5 | check_feature_flags | +| controllers/comments_controller.rb:53:3:54:5 | create | controllers/comments_controller.rb:87:3:89:5 | check_feature_flags | controllers/comments_controller.rb:95:3:97:5 | this_must_run_last | +| controllers/comments_controller.rb:53:3:54:5 | create | controllers/comments_controller.rb:91:3:93:5 | this_must_run_first | controllers/application_controller.rb:10:3:12:5 | log_request | +| controllers/comments_controller.rb:53:3:54:5 | create | controllers/comments_controller.rb:99:3:100:5 | foo | controllers/comments_controller.rb:102:3:103:5 | bar | +| controllers/comments_controller.rb:53:3:54:5 | create | controllers/comments_controller.rb:102:3:103:5 | bar | controllers/comments_controller.rb:53:3:54:5 | create | +| controllers/comments_controller.rb:56:3:62:5 | show | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/comments_controller.rb:79:3:81:5 | set_comment | +| controllers/comments_controller.rb:56:3:62:5 | show | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/application_controller.rb:6:3:8:5 | set_user | +| controllers/comments_controller.rb:56:3:62:5 | show | controllers/comments_controller.rb:56:3:62:5 | show | controllers/comments_controller.rb:87:3:89:5 | check_feature_flags | +| controllers/comments_controller.rb:56:3:62:5 | show | controllers/comments_controller.rb:79:3:81:5 | set_comment | controllers/comments_controller.rb:99:3:100:5 | foo | +| controllers/comments_controller.rb:56:3:62:5 | show | controllers/comments_controller.rb:87:3:89:5 | check_feature_flags | controllers/comments_controller.rb:95:3:97:5 | this_must_run_last | +| controllers/comments_controller.rb:56:3:62:5 | show | controllers/comments_controller.rb:91:3:93:5 | this_must_run_first | controllers/application_controller.rb:10:3:12:5 | log_request | +| controllers/comments_controller.rb:56:3:62:5 | show | controllers/comments_controller.rb:99:3:100:5 | foo | controllers/comments_controller.rb:102:3:103:5 | bar | +| controllers/comments_controller.rb:56:3:62:5 | show | controllers/comments_controller.rb:102:3:103:5 | bar | controllers/comments_controller.rb:56:3:62:5 | show | +| controllers/comments_controller.rb:64:3:66:5 | photo | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/comments_controller.rb:99:3:100:5 | foo | +| controllers/comments_controller.rb:64:3:66:5 | photo | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/application_controller.rb:6:3:8:5 | set_user | +| controllers/comments_controller.rb:64:3:66:5 | photo | controllers/comments_controller.rb:64:3:66:5 | photo | controllers/comments_controller.rb:83:3:85:5 | log_comment_change | +| controllers/comments_controller.rb:64:3:66:5 | photo | controllers/comments_controller.rb:83:3:85:5 | log_comment_change | controllers/comments_controller.rb:87:3:89:5 | check_feature_flags | +| controllers/comments_controller.rb:64:3:66:5 | photo | controllers/comments_controller.rb:87:3:89:5 | check_feature_flags | controllers/comments_controller.rb:95:3:97:5 | this_must_run_last | +| controllers/comments_controller.rb:64:3:66:5 | photo | controllers/comments_controller.rb:91:3:93:5 | this_must_run_first | controllers/application_controller.rb:10:3:12:5 | log_request | +| controllers/comments_controller.rb:64:3:66:5 | photo | controllers/comments_controller.rb:99:3:100:5 | foo | controllers/comments_controller.rb:102:3:103:5 | bar | +| controllers/comments_controller.rb:64:3:66:5 | photo | controllers/comments_controller.rb:102:3:103:5 | bar | controllers/comments_controller.rb:64:3:66:5 | photo | +| controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/comments_controller.rb:74:3:77:5 | ensure_user_can_edit_comments | +| controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/application_controller.rb:6:3:8:5 | set_user | +| controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/comments_controller.rb:83:3:85:5 | log_comment_change | +| controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/comments_controller.rb:74:3:77:5 | ensure_user_can_edit_comments | controllers/comments_controller.rb:79:3:81:5 | set_comment | +| controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/comments_controller.rb:79:3:81:5 | set_comment | controllers/comments_controller.rb:99:3:100:5 | foo | +| controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/comments_controller.rb:83:3:85:5 | log_comment_change | controllers/comments_controller.rb:87:3:89:5 | check_feature_flags | +| controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/comments_controller.rb:87:3:89:5 | check_feature_flags | controllers/comments_controller.rb:95:3:97:5 | this_must_run_last | +| controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/comments_controller.rb:91:3:93:5 | this_must_run_first | controllers/application_controller.rb:10:3:12:5 | log_request | +| controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/comments_controller.rb:99:3:100:5 | foo | controllers/comments_controller.rb:102:3:103:5 | bar | +| controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/comments_controller.rb:102:3:103:5 | bar | controllers/comments_controller.rb:68:3:70:5 | destroy | +| controllers/photos_controller.rb:3:3:6:5 | show | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/photos_controller.rb:3:3:6:5 | show | +| controllers/photos_controller.rb:3:3:6:5 | show | controllers/photos_controller.rb:3:3:6:5 | show | controllers/photos_controller.rb:8:3:9:5 | foo | +| controllers/posts_controller.rb:12:3:13:5 | index | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/posts_controller.rb:12:3:13:5 | index | +| controllers/posts_controller.rb:12:3:13:5 | index | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/application_controller.rb:6:3:8:5 | set_user | +| controllers/posts_controller.rb:15:3:16:5 | show | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/posts_controller.rb:15:3:16:5 | show | +| controllers/posts_controller.rb:15:3:16:5 | show | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/posts_controller.rb:23:3:25:5 | set_post | +| controllers/posts_controller.rb:15:3:16:5 | show | controllers/posts_controller.rb:23:3:25:5 | set_post | controllers/application_controller.rb:6:3:8:5 | set_user | +| controllers/posts_controller.rb:18:3:19:5 | upvote | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/posts_controller.rb:18:3:19:5 | upvote | +| controllers/posts_controller.rb:18:3:19:5 | upvote | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/posts_controller.rb:23:3:25:5 | set_post | +| controllers/posts_controller.rb:18:3:19:5 | upvote | controllers/posts_controller.rb:18:3:19:5 | upvote | controllers/posts_controller.rb:27:3:29:5 | log_upvote | +| controllers/posts_controller.rb:18:3:19:5 | upvote | controllers/posts_controller.rb:23:3:25:5 | set_post | controllers/application_controller.rb:6:3:8:5 | set_user | diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/Filters.ql b/ruby/ql/test/library-tests/frameworks/action_controller/Filters.ql new file mode 100644 index 00000000000..7a3a2c9ec3f --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/action_controller/Filters.ql @@ -0,0 +1,5 @@ +private import codeql.ruby.AST +private import codeql.ruby.frameworks.ActionController +private import codeql.ruby.DataFlow + +query predicate filterChain = ActionController::Filters::next/3; diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/controllers/application_controller.rb b/ruby/ql/test/library-tests/frameworks/action_controller/controllers/application_controller.rb new file mode 100644 index 00000000000..644a5331050 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/action_controller/controllers/application_controller.rb @@ -0,0 +1,13 @@ +class ApplicationController < ActionController::Base + before_action :log_request + + private + + def set_user + @user = User.find(session[:user_id]) + end + + def log_request + Rails.logger.info("Request: #{request.method} #{request.path}") + end +end diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/controllers/comments_controller.rb b/ruby/ql/test/library-tests/frameworks/action_controller/controllers/comments_controller.rb index daa96090b09..1129dc268ed 100644 --- a/ruby/ql/test/library-tests/frameworks/action_controller/controllers/comments_controller.rb +++ b/ruby/ql/test/library-tests/frameworks/action_controller/controllers/comments_controller.rb @@ -1,4 +1,19 @@ class CommentsController < ApplicationController + after_action :check_feature_flags + after_action :log_comment_change + prepend_after_action :this_must_run_last + before_action :set_user + before_action :ensure_user_can_edit_comments, only: WRITE_ACTIONS + before_action :set_comment, only: [:show, :edit, :update, :destroy] + before_action :foo, :bar + + # this overrides the earlier callback on L3 + after_action :log_comment_change, except: READ_ACTIONS + prepend_before_action :this_must_run_first + + WRITE_ACTIONS = %i[create update destroy] + READ_ACTIONS = %i[index show new] + def index request.params request.parameters @@ -35,6 +50,9 @@ class CommentsController < ApplicationController response.strong_etag = "value" end + def create + end + def show respond_to do |format| format.html { redirect_to(comment_view_url) } @@ -50,4 +68,37 @@ class CommentsController < ApplicationController def destroy body = request.body_stream end + + private + + def ensure_user_can_edit_comments + return if @user.can_edit_comments? + render status: 403, text: "You are not allowed to edit comments" + end + + def set_comment + @comment = @user.comments.find(params[:id]) + end + + def log_comment_change + AuditLog.create!(:comment_change, user: @user, comment: @comment) + end + + def check_feature_flags + raise CommentsNotEnabled unless FeatureFlag.enabled?(:comments) + end + + def this_must_run_first + # for whatever reason + end + + def this_must_run_last + # for whatever reason + end + + def foo + end + + def bar + end end diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/controllers/photos_controller.rb b/ruby/ql/test/library-tests/frameworks/action_controller/controllers/photos_controller.rb index 0de193b9029..f5f092a76e5 100644 --- a/ruby/ql/test/library-tests/frameworks/action_controller/controllers/photos_controller.rb +++ b/ruby/ql/test/library-tests/frameworks/action_controller/controllers/photos_controller.rb @@ -1,4 +1,10 @@ class PhotosController < ApplicationController + after_action :foo def show + @a = 1 + @b = 2 + end + + def foo end end \ No newline at end of file diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/controllers/posts_controller.rb b/ruby/ql/test/library-tests/frameworks/action_controller/controllers/posts_controller.rb index 9760219cdf2..ed1fa22c2fa 100644 --- a/ruby/ql/test/library-tests/frameworks/action_controller/controllers/posts_controller.rb +++ b/ruby/ql/test/library-tests/frameworks/action_controller/controllers/posts_controller.rb @@ -1,4 +1,14 @@ class PostsController < ApplicationController + before_action :set_user + append_before_action :set_post, only: [:show, :upvote] + after_action :log_upvote + + # these calls override the earlier ones + after_action :log_upvote, only: :upvote + before_action :set_user + skip_before_action :set_user + before_action :set_user + def index end @@ -7,4 +17,14 @@ class PostsController < ApplicationController def upvote end -end \ No newline at end of file + + private + + def set_post + @post = Post.find(params[:id]) + end + + def log_upvote + Rails.logger.info("Post upvoted: #{@post.id}") + end +end diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/routes.rb b/ruby/ql/test/library-tests/frameworks/action_controller/routes.rb new file mode 100644 index 00000000000..1ccc9e6238b --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/action_controller/routes.rb @@ -0,0 +1,11 @@ +Rails.application.routes.draw do + resources :users + resources :comments do + get "photo", on: :member + end + resources :photos + resources :posts do + post "upvote", on: :member + end + resources :tags +end