|
| 1 | +/** |
| 2 | + * @name Manually checking http verb instead of using built in rails routes and protections |
| 3 | + * @description Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. |
| 4 | + * @kind path-problem |
| 5 | + * @problem.severity error |
| 6 | + * @security-severity 5.0 |
| 7 | + * @precision low |
| 8 | + * @id rb/manually-checking-http-verb |
| 9 | + * @tags security |
| 10 | + */ |
| 11 | + |
| 12 | +import ruby |
| 13 | +import codeql.ruby.DataFlow |
| 14 | +import codeql.ruby.controlflow.CfgNodes |
| 15 | +import codeql.ruby.frameworks.ActionController |
| 16 | +import codeql.ruby.TaintTracking |
| 17 | +import DataFlow::PathGraph |
| 18 | + |
| 19 | +// any `request` calls in an action method |
| 20 | +class Request extends DataFlow::CallNode { |
| 21 | + Request() { |
| 22 | + this.getMethodName() = "request" and |
| 23 | + this.asExpr().getExpr().getEnclosingMethod() instanceof ActionControllerActionMethod |
| 24 | + } |
| 25 | +} |
| 26 | + |
| 27 | +// `request.env` |
| 28 | +class RequestEnvMethod extends DataFlow::CallNode { |
| 29 | + RequestEnvMethod() { |
| 30 | + this.getMethodName() = "env" and |
| 31 | + any(Request r).flowsTo(this.getReceiver()) |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +// `request.request_method` |
| 36 | +class RequestRequestMethod extends DataFlow::CallNode { |
| 37 | + RequestRequestMethod() { |
| 38 | + this.getMethodName() = "request_method" and |
| 39 | + any(Request r).flowsTo(this.getReceiver()) |
| 40 | + } |
| 41 | +} |
| 42 | + |
| 43 | +// `request.method` |
| 44 | +class RequestMethod extends DataFlow::CallNode { |
| 45 | + RequestMethod() { |
| 46 | + this.getMethodName() = "method" and |
| 47 | + any(Request r).flowsTo(this.getReceiver()) |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +// `request.raw_request_method` |
| 52 | +class RequestRawRequestMethod extends DataFlow::CallNode { |
| 53 | + RequestRawRequestMethod() { |
| 54 | + this.getMethodName() = "raw_request_method" and |
| 55 | + any(Request r).flowsTo(this.getReceiver()) |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +// `request.request_method_symbol` |
| 60 | +class RequestRequestMethodSymbol extends DataFlow::CallNode { |
| 61 | + RequestRequestMethodSymbol() { |
| 62 | + this.getMethodName() = "request_method_symbol" and |
| 63 | + any(Request r).flowsTo(this.getReceiver()) |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +// `request.get?` |
| 68 | +class RequestGet extends DataFlow::CallNode { |
| 69 | + RequestGet() { |
| 70 | + this.getMethodName() = "get?" and |
| 71 | + any(Request r).flowsTo(this.getReceiver()) |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +class HttpVerbConfig extends TaintTracking::Configuration { |
| 76 | + HttpVerbConfig() { this = "HttpVerbConfig" } |
| 77 | + |
| 78 | + override predicate isSource(DataFlow::Node source) { |
| 79 | + source instanceof RequestMethod or |
| 80 | + source instanceof RequestRequestMethod or |
| 81 | + source instanceof RequestEnvMethod or |
| 82 | + source instanceof RequestRawRequestMethod or |
| 83 | + source instanceof RequestRequestMethodSymbol or |
| 84 | + source instanceof RequestGet |
| 85 | + } |
| 86 | + |
| 87 | + override predicate isSink(DataFlow::Node sink) { |
| 88 | + exists(ExprNodes::ConditionalExprCfgNode c | c.getCondition() = sink.asExpr()) or |
| 89 | + exists(ExprNodes::CaseExprCfgNode c | c.getValue() = sink.asExpr()) |
| 90 | + } |
| 91 | +} |
| 92 | + |
| 93 | +from HttpVerbConfig config, DataFlow::PathNode source, DataFlow::PathNode sink |
| 94 | +where config.hasFlowPath(source, sink) |
| 95 | +select sink.getNode(), source, sink, |
| 96 | + "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." |
0 commit comments