diff --git a/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/App.qll b/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/App.qll index 0a344f46dc8..eb8b283fa16 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/App.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/App.qll @@ -7,15 +7,20 @@ private import codeql.ruby.DataFlow private import codeql.ruby.typetracking.TypeTracker private import Response::Private as RP -private DataFlow::LocalSourceNode trackRackResponse(TypeTracker t, RP::PotentialResponseNode n) { - t.start() and - result = n - or - exists(TypeTracker t2 | result = trackRackResponse(t2, n).track(t2, t)) +/** A method node for a method named `call`. */ +private class CallMethodNode extends DataFlow::MethodNode { + CallMethodNode() { this.getMethodName() = "call" } } -private DataFlow::Node trackRackResponse(RP::PotentialResponseNode n) { - trackRackResponse(TypeTracker::end(), n).flowsTo(result) +private DataFlow::LocalSourceNode trackRackResponse(TypeBackTracker t, CallMethodNode call) { + t.start() and + result = call.getAReturningNode() + or + exists(TypeBackTracker t2 | result = trackRackResponse(t2, call).backtrack(t2, t)) +} + +private RP::PotentialResponseNode trackRackResponse(CallMethodNode call) { + result = trackRackResponse(TypeBackTracker::end(), call) } /** @@ -28,13 +33,13 @@ module App { * (traditionally called `env`) and returns a rack-compatible response. */ class AppCandidate extends DataFlow::ClassNode { - private DataFlow::MethodNode call; + private CallMethodNode call; private RP::PotentialResponseNode resp; AppCandidate() { call = this.getInstanceMethod("call") and call.getNumberOfParameters() = 1 and - call.getAReturningNode() = trackRackResponse(resp) + resp = trackRackResponse(call) } /** @@ -42,7 +47,7 @@ module App { */ DataFlow::ParameterNode getEnv() { result = call.getParameter(0) } - /** Gets the response returned from the request. */ + /** Gets the response returned from a request to this application. */ RP::PotentialResponseNode getResponse() { result = resp } } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/Response.qll b/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/Response.qll index 26162a8d306..d5ca488fcda 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/Response.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/Response.qll @@ -24,10 +24,7 @@ module Private { /** A `DataFlow::Node` that may be a rack response. This is detected heuristically, if something "looks like" a rack response syntactically then we consider it to be a potential response node. */ class PotentialResponseNode extends DataFlow::ArrayLiteralNode { // [status, headers, body] - PotentialResponseNode() { - this.getNumberOfArguments() = 3 and - this.asExpr().getExpr().getEnclosingModule+().getAMethod().getName() = "call" - } + PotentialResponseNode() { this.getNumberOfArguments() = 3 } /** * Gets an HTTP status code that may be returned in this response.