Skip to content

Conversation

@alexrford
Copy link
Contributor

@alexrford alexrford commented Jun 16, 2023

This is a follow-up to #13289.

Rack responses can be constructed using calling finish on a Rack::Response instance. This may be more convenient in some cases (e.g. default values are set when calling Rack::Response.new if not specified). This PR adds modelling to consider these calls to finish as creating a rack response.

This PR also extends recognition of rack apps to include:

  1. Cases where the call(env) method is a class method rather than an instance method
  2. "Anonymous" applications, e.g. those defined as lambdas or procs

In the case of anonymous applications we limit candidates to those which flow to arg0 of a run call, as considering all lambdas/procs can be prohibitively expensive.

@alexrford alexrford added the Ruby label Jun 16, 2023
@alexrford alexrford force-pushed the rb/rack-extend-app-and-resp branch from d7dbc40 to eb5b6a1 Compare June 20, 2023 16:16
@alexrford alexrford force-pushed the rb/rack-extend-app-and-resp branch from eb5b6a1 to f8140bc Compare June 22, 2023 12:45
@alexrford alexrford marked this pull request as ready for review June 23, 2023 15:12
@alexrford alexrford requested a review from a team as a code owner June 23, 2023 15:12
Comment on lines 1013 to 1021
/**
* Gets the singleton method named `name` available in this module, including methods inherited from ancestors.
*
* Overridden methods are not included.
*/
MethodNode getSingletonMethod(string name) {
result.asCallableAstNode() = super.getAnOwnSingletonMethod() and
result.getMethodName() = name
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment and method name suggests that this includes inherited methods, but the implementation only includes the own singleton methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦 thanks, yes the body doesn't match. We don't need this predicate if we drop the distinction between class apps and anonymous apps, so I'll just remove this predicate.

* A callable node that takes a single argument and, if it has a method name,
* is called "call".
*/
private class PotentialCallNode extends DataFlow::CallableNode {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a callable, the name should not imply that it is a call, which can lead to a lot of confusion. (Same applies to CallNode below)

Could we rename it to something like a "request handler" instead of just "call method" or "callable"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed this as PotentialRequestHandler and the version where we track the response type as RequestHandler.


private newtype TApp =
TClassApp(DataFlow::ClassNode cn, CallNode call) or
TAnonymousApp(CallNode call)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This newtype doesn't seem necessary to me.

The corresponding class can always be determined by checking if call is a method of some class.

But also, do we actually care about what the class is? Does Rack distinguish between handlers that are Procs versus handlers that are some of type of object? If not I'd just model the request handler as a DataFlow::CallableNode.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main purpose here was to maintain similar behaviour as before where we considered the class containing the call method as the app if such a class existed. In practice, I don't think it matters? As you mention, we can figure this out from the RequestHandler if it ends up being relevant.

On that note I've moved the RequestHandler itself to replace the RackApplication class - this is the DataFlow::CallableNode corresponding to the method/proc/lambda that takes the env and returns a rack compliant response.

@alexrford
Copy link
Contributor Author

@asgerf this is updated - as we briefly discussed, recognition of request handlers can likely be improved using your API graphs overhaul. The overall diff for this PR is simpler than before. I'm interested in if the publicly exposed API for Rack::App::RequestHandler looks reasonable, as the precise implementation is liable to change soon anyway.

@alexrford alexrford requested a review from asgerf June 26, 2023 14:49
@asgerf
Copy link
Contributor

asgerf commented Jul 3, 2023

I've started another evaluation with meta-queries as I'd like a closer look at what sinks are found by this heuristic in practice. LGTM if the results look reasonable.

Copy link
Contributor

@asgerf asgerf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - the evaluation was quiet

@alexrford alexrford merged commit b6912de into github:main Jul 5, 2023
@alexrford alexrford deleted the rb/rack-extend-app-and-resp branch July 5, 2023 10:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants