mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Refactor Grape framework to be encapsulated properly in Module
This commit is contained in:
@@ -29,293 +29,299 @@ module Grape {
|
|||||||
not exists(GrapeApiClass parent | this != parent and this = parent.getADescendent())
|
not exists(GrapeApiClass parent | this != parent and this = parent.getADescendent())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that extends `Grape::API`.
|
* A class that extends `Grape::API`.
|
||||||
* For example,
|
* For example,
|
||||||
*
|
*
|
||||||
* ```rb
|
* ```rb
|
||||||
* class FooAPI < Grape::API
|
* class FooAPI < Grape::API
|
||||||
* get '/users' do
|
* get '/users' do
|
||||||
* name = params[:name]
|
* name = params[:name]
|
||||||
* User.where("name = #{name}")
|
* User.where("name = #{name}")
|
||||||
* end
|
* end
|
||||||
* end
|
* end
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
class GrapeApiClass extends DataFlow::ClassNode {
|
class GrapeApiClass extends DataFlow::ClassNode {
|
||||||
GrapeApiClass() {
|
GrapeApiClass() {
|
||||||
this = grapeApiBaseClass().getADescendentModule() and
|
this = grapeApiBaseClass().getADescendentModule() and
|
||||||
not exists(DataFlow::ModuleNode m | m = grapeApiBaseClass().asModule() | this = m)
|
not exists(DataFlow::ModuleNode m | m = grapeApiBaseClass().asModule() | this = m)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a `GrapeEndpoint` defined in this class.
|
||||||
|
*/
|
||||||
|
GrapeEndpoint getAnEndpoint() { result.getApiClass() = this }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a `self` that possibly refers to an instance of this class.
|
||||||
|
*/
|
||||||
|
DataFlow::LocalSourceNode getSelf() {
|
||||||
|
result = this.getAnInstanceSelf()
|
||||||
|
or
|
||||||
|
// Include the module-level `self` to recover some cases where a block at the module level
|
||||||
|
// is invoked with an instance as the `self`.
|
||||||
|
result = this.getModuleLevelSelf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataFlow::ConstRef grapeApiBaseClass() {
|
||||||
|
result = DataFlow::getConstant("Grape").getConstant("API")
|
||||||
|
}
|
||||||
|
|
||||||
|
private API::Node grapeApiInstance() { result = any(GrapeApiClass cls).getSelf().track() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Grape API endpoint (get, post, put, delete, etc.) call within a `Grape::API` class.
|
||||||
|
*/
|
||||||
|
class GrapeEndpoint extends DataFlow::CallNode {
|
||||||
|
private GrapeApiClass apiClass;
|
||||||
|
|
||||||
|
GrapeEndpoint() {
|
||||||
|
this =
|
||||||
|
apiClass.getAModuleLevelCall(["get", "post", "put", "delete", "patch", "head", "options"])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the HTTP method for this endpoint (e.g., "GET", "POST", etc.)
|
||||||
|
*/
|
||||||
|
string getHttpMethod() { result = this.getMethodName().toUpperCase() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the API class containing this endpoint.
|
||||||
|
*/
|
||||||
|
GrapeApiClass getApiClass() { result = apiClass }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the block containing the endpoint logic.
|
||||||
|
*/
|
||||||
|
DataFlow::BlockNode getBody() { result = this.getBlock() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the path pattern for this endpoint, if specified.
|
||||||
|
*/
|
||||||
|
string getPath() { result = this.getArgument(0).getConstantValue().getString() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a `GrapeEndpoint` defined in this class.
|
* A `RemoteFlowSource::Range` to represent accessing the
|
||||||
|
* Grape parameters available via the `params` method within an endpoint.
|
||||||
*/
|
*/
|
||||||
GrapeEndpoint getAnEndpoint() { result.getApiClass() = this }
|
class GrapeParamsSource extends Http::Server::RequestInputAccess::Range {
|
||||||
|
GrapeParamsSource() { this.asExpr().getExpr() instanceof GrapeParamsCall }
|
||||||
|
|
||||||
/**
|
override string getSourceType() { result = "Grape::API#params" }
|
||||||
* Gets a `self` that possibly refers to an instance of this class.
|
|
||||||
*/
|
|
||||||
DataFlow::LocalSourceNode getSelf() {
|
|
||||||
result = this.getAnInstanceSelf()
|
|
||||||
or
|
|
||||||
// Include the module-level `self` to recover some cases where a block at the module level
|
|
||||||
// is invoked with an instance as the `self`.
|
|
||||||
result = this.getModuleLevelSelf()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private DataFlow::ConstRef grapeApiBaseClass() {
|
override Http::Server::RequestInputKind getKind() {
|
||||||
result = DataFlow::getConstant("Grape").getConstant("API")
|
result = Http::Server::parameterInputKind()
|
||||||
}
|
}
|
||||||
|
|
||||||
private API::Node grapeApiInstance() { result = any(GrapeApiClass cls).getSelf().track() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Grape API endpoint (get, post, put, delete, etc.) call within a `Grape::API` class.
|
|
||||||
*/
|
|
||||||
class GrapeEndpoint extends DataFlow::CallNode {
|
|
||||||
private GrapeApiClass apiClass;
|
|
||||||
|
|
||||||
GrapeEndpoint() {
|
|
||||||
this =
|
|
||||||
apiClass.getAModuleLevelCall(["get", "post", "put", "delete", "patch", "head", "options"])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the HTTP method for this endpoint (e.g., "GET", "POST", etc.)
|
* A call to `params` from within a Grape API endpoint or helper method.
|
||||||
*/
|
*/
|
||||||
string getHttpMethod() { result = this.getMethodName().toUpperCase() }
|
private class GrapeParamsCall extends ParamsCallImpl {
|
||||||
|
GrapeParamsCall() {
|
||||||
/**
|
// Params calls within endpoint blocks
|
||||||
* Gets the API class containing this endpoint.
|
exists(GrapeApiClass api |
|
||||||
*/
|
this.getMethodName() = "params" and
|
||||||
GrapeApiClass getApiClass() { result = apiClass }
|
this.getParent+() = api.getADeclaration()
|
||||||
|
)
|
||||||
/**
|
or
|
||||||
* Gets the block containing the endpoint logic.
|
// Params calls within helper methods (defined in helpers blocks)
|
||||||
*/
|
exists(GrapeApiClass api, DataFlow::CallNode helpersCall |
|
||||||
DataFlow::BlockNode getBody() { result = this.getBlock() }
|
helpersCall = api.getAModuleLevelCall("helpers") and
|
||||||
|
this.getMethodName() = "params" and
|
||||||
/**
|
this.getParent+() = helpersCall.getBlock().asExpr().getExpr()
|
||||||
* Gets the path pattern for this endpoint, if specified.
|
)
|
||||||
*/
|
}
|
||||||
string getPath() { result = this.getArgument(0).getConstantValue().getString() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A `RemoteFlowSource::Range` to represent accessing the
|
|
||||||
* Grape parameters available via the `params` method within an endpoint.
|
|
||||||
*/
|
|
||||||
class GrapeParamsSource extends Http::Server::RequestInputAccess::Range {
|
|
||||||
GrapeParamsSource() { this.asExpr().getExpr() instanceof GrapeParamsCall }
|
|
||||||
|
|
||||||
override string getSourceType() { result = "Grape::API#params" }
|
|
||||||
|
|
||||||
override Http::Server::RequestInputKind getKind() { result = Http::Server::parameterInputKind() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `params` from within a Grape API endpoint or helper method.
|
|
||||||
*/
|
|
||||||
private class GrapeParamsCall extends ParamsCallImpl {
|
|
||||||
GrapeParamsCall() {
|
|
||||||
// Params calls within endpoint blocks
|
|
||||||
exists(GrapeApiClass api |
|
|
||||||
this.getMethodName() = "params" and
|
|
||||||
this.getParent+() = api.getADeclaration()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Params calls within helper methods (defined in helpers blocks)
|
|
||||||
exists(GrapeApiClass api, DataFlow::CallNode helpersCall |
|
|
||||||
helpersCall = api.getAModuleLevelCall("helpers") and
|
|
||||||
this.getMethodName() = "params" and
|
|
||||||
this.getParent+() = helpersCall.getBlock().asExpr().getExpr()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `headers` from within a Grape API endpoint or headers block.
|
|
||||||
* Headers can also be a source of user input.
|
|
||||||
*/
|
|
||||||
class GrapeHeadersSource extends Http::Server::RequestInputAccess::Range {
|
|
||||||
GrapeHeadersSource() {
|
|
||||||
this.asExpr().getExpr() instanceof GrapeHeadersCall
|
|
||||||
or
|
|
||||||
this.asExpr().getExpr() instanceof GrapeHeadersBlockCall
|
|
||||||
}
|
|
||||||
|
|
||||||
override string getSourceType() { result = "Grape::API#headers" }
|
|
||||||
|
|
||||||
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `headers` from within a Grape API endpoint.
|
|
||||||
*/
|
|
||||||
private class GrapeHeadersCall extends MethodCall {
|
|
||||||
GrapeHeadersCall() {
|
|
||||||
exists(GrapeEndpoint endpoint |
|
|
||||||
this.getParent+() = endpoint.getBody().asCallableAstNode() and
|
|
||||||
this.getMethodName() = "headers"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Also handle cases where headers is called on an instance of a Grape API class
|
|
||||||
this = grapeApiInstance().getAMethodCall("headers").asExpr().getExpr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `request` from within a Grape API endpoint.
|
|
||||||
* The request object can contain user input.
|
|
||||||
*/
|
|
||||||
class GrapeRequestSource extends Http::Server::RequestInputAccess::Range {
|
|
||||||
GrapeRequestSource() { this.asExpr().getExpr() instanceof GrapeRequestCall }
|
|
||||||
|
|
||||||
override string getSourceType() { result = "Grape::API#request" }
|
|
||||||
|
|
||||||
override Http::Server::RequestInputKind getKind() { result = Http::Server::parameterInputKind() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `route_param` from within a Grape API endpoint.
|
|
||||||
* Route parameters are extracted from the URL path and can be a source of user input.
|
|
||||||
*/
|
|
||||||
class GrapeRouteParamSource extends Http::Server::RequestInputAccess::Range {
|
|
||||||
GrapeRouteParamSource() { this.asExpr().getExpr() instanceof GrapeRouteParamCall }
|
|
||||||
|
|
||||||
override string getSourceType() { result = "Grape::API#route_param" }
|
|
||||||
|
|
||||||
override Http::Server::RequestInputKind getKind() { result = Http::Server::parameterInputKind() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `request` from within a Grape API endpoint.
|
|
||||||
*/
|
|
||||||
private class GrapeRequestCall extends MethodCall {
|
|
||||||
GrapeRequestCall() {
|
|
||||||
exists(GrapeEndpoint endpoint |
|
|
||||||
this.getParent+() = endpoint.getBody().asCallableAstNode() and
|
|
||||||
this.getMethodName() = "request"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Also handle cases where request is called on an instance of a Grape API class
|
|
||||||
this = grapeApiInstance().getAMethodCall("request").asExpr().getExpr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `route_param` from within a Grape API endpoint.
|
|
||||||
*/
|
|
||||||
private class GrapeRouteParamCall extends MethodCall {
|
|
||||||
GrapeRouteParamCall() {
|
|
||||||
exists(GrapeEndpoint endpoint |
|
|
||||||
this.getParent+() = endpoint.getBody().asExpr().getExpr() and
|
|
||||||
this.getMethodName() = "route_param"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Also handle cases where route_param is called on an instance of a Grape API class
|
|
||||||
this = grapeApiInstance().getAMethodCall("route_param").asExpr().getExpr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `headers` block within a Grape API class.
|
|
||||||
* This is different from the headers() method call - this is the DSL block for defining header requirements.
|
|
||||||
*/
|
|
||||||
private class GrapeHeadersBlockCall extends MethodCall {
|
|
||||||
GrapeHeadersBlockCall() {
|
|
||||||
exists(GrapeApiClass api |
|
|
||||||
this.getParent+() = api.getADeclaration() and
|
|
||||||
this.getMethodName() = "headers" and
|
|
||||||
exists(this.getBlock())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `cookies` block within a Grape API class.
|
|
||||||
* This DSL block defines cookie requirements and those cookies are user-controlled.
|
|
||||||
*/
|
|
||||||
private class GrapeCookiesBlockCall extends MethodCall {
|
|
||||||
GrapeCookiesBlockCall() {
|
|
||||||
exists(GrapeApiClass api |
|
|
||||||
this.getParent+() = api.getADeclaration() and
|
|
||||||
this.getMethodName() = "cookies" and
|
|
||||||
exists(this.getBlock())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `cookies` method from within a Grape API endpoint or cookies block.
|
|
||||||
* Similar to headers, cookies can be accessed as a method and are user-controlled input.
|
|
||||||
*/
|
|
||||||
class GrapeCookiesSource extends Http::Server::RequestInputAccess::Range {
|
|
||||||
GrapeCookiesSource() {
|
|
||||||
this.asExpr().getExpr() instanceof GrapeCookiesCall
|
|
||||||
or
|
|
||||||
this.asExpr().getExpr() instanceof GrapeCookiesBlockCall
|
|
||||||
}
|
|
||||||
|
|
||||||
override string getSourceType() { result = "Grape::API#cookies" }
|
|
||||||
|
|
||||||
override Http::Server::RequestInputKind getKind() { result = Http::Server::cookieInputKind() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `cookies` method from within a Grape API endpoint.
|
|
||||||
*/
|
|
||||||
private class GrapeCookiesCall extends MethodCall {
|
|
||||||
GrapeCookiesCall() {
|
|
||||||
exists(GrapeEndpoint endpoint |
|
|
||||||
this.getParent+() = endpoint.getBody().asCallableAstNode() and
|
|
||||||
this.getMethodName() = "cookies"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Also handle cases where cookies is called on an instance of a Grape API class
|
|
||||||
this = grapeApiInstance().getAMethodCall("cookies").asExpr().getExpr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A method defined within a `helpers` block in a Grape API class.
|
|
||||||
* These methods become available in endpoint contexts through Grape's DSL.
|
|
||||||
*/
|
|
||||||
private class GrapeHelperMethod extends Method {
|
|
||||||
private GrapeApiClass apiClass;
|
|
||||||
|
|
||||||
GrapeHelperMethod() {
|
|
||||||
exists(DataFlow::CallNode helpersCall |
|
|
||||||
helpersCall = apiClass.getAModuleLevelCall("helpers") and
|
|
||||||
this.getParent+() = helpersCall.getBlock().asExpr().getExpr()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the API class that contains this helper method.
|
* A call to `headers` from within a Grape API endpoint or headers block.
|
||||||
|
* Headers can also be a source of user input.
|
||||||
*/
|
*/
|
||||||
GrapeApiClass getApiClass() { result = apiClass }
|
class GrapeHeadersSource extends Http::Server::RequestInputAccess::Range {
|
||||||
}
|
GrapeHeadersSource() {
|
||||||
|
this.asExpr().getExpr() instanceof GrapeHeadersCall
|
||||||
|
or
|
||||||
|
this.asExpr().getExpr() instanceof GrapeHeadersBlockCall
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
override string getSourceType() { result = "Grape::API#headers" }
|
||||||
* Additional call-target to resolve helper method calls defined in `helpers` blocks.
|
|
||||||
*
|
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
|
||||||
* This class is responsible for resolving calls to helper methods defined in
|
}
|
||||||
* `helpers` blocks, allowing the dataflow framework to accurately track
|
|
||||||
* the flow of information between these methods and their call sites.
|
/**
|
||||||
*/
|
* A call to `headers` from within a Grape API endpoint.
|
||||||
private class GrapeHelperMethodTarget extends AdditionalCallTarget {
|
*/
|
||||||
override DataFlowCallable viableTarget(CfgNodes::ExprNodes::CallCfgNode call) {
|
private class GrapeHeadersCall extends MethodCall {
|
||||||
// Find calls to helper methods from within Grape endpoints or other helper methods
|
GrapeHeadersCall() {
|
||||||
exists(GrapeHelperMethod helperMethod, MethodCall mc |
|
exists(GrapeEndpoint endpoint |
|
||||||
result.asCfgScope() = helperMethod and
|
this.getParent+() = endpoint.getBody().asCallableAstNode() and
|
||||||
mc = call.getAstNode() and
|
this.getMethodName() = "headers"
|
||||||
mc.getMethodName() = helperMethod.getName() and
|
)
|
||||||
mc.getParent+() = helperMethod.getApiClass().getADeclaration()
|
or
|
||||||
)
|
// Also handle cases where headers is called on an instance of a Grape API class
|
||||||
|
this = grapeApiInstance().getAMethodCall("headers").asExpr().getExpr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to `request` from within a Grape API endpoint.
|
||||||
|
* The request object can contain user input.
|
||||||
|
*/
|
||||||
|
class GrapeRequestSource extends Http::Server::RequestInputAccess::Range {
|
||||||
|
GrapeRequestSource() { this.asExpr().getExpr() instanceof GrapeRequestCall }
|
||||||
|
|
||||||
|
override string getSourceType() { result = "Grape::API#request" }
|
||||||
|
|
||||||
|
override Http::Server::RequestInputKind getKind() {
|
||||||
|
result = Http::Server::parameterInputKind()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to `route_param` from within a Grape API endpoint.
|
||||||
|
* Route parameters are extracted from the URL path and can be a source of user input.
|
||||||
|
*/
|
||||||
|
class GrapeRouteParamSource extends Http::Server::RequestInputAccess::Range {
|
||||||
|
GrapeRouteParamSource() { this.asExpr().getExpr() instanceof GrapeRouteParamCall }
|
||||||
|
|
||||||
|
override string getSourceType() { result = "Grape::API#route_param" }
|
||||||
|
|
||||||
|
override Http::Server::RequestInputKind getKind() {
|
||||||
|
result = Http::Server::parameterInputKind()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to `request` from within a Grape API endpoint.
|
||||||
|
*/
|
||||||
|
private class GrapeRequestCall extends MethodCall {
|
||||||
|
GrapeRequestCall() {
|
||||||
|
exists(GrapeEndpoint endpoint |
|
||||||
|
this.getParent+() = endpoint.getBody().asCallableAstNode() and
|
||||||
|
this.getMethodName() = "request"
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// Also handle cases where request is called on an instance of a Grape API class
|
||||||
|
this = grapeApiInstance().getAMethodCall("request").asExpr().getExpr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to `route_param` from within a Grape API endpoint.
|
||||||
|
*/
|
||||||
|
private class GrapeRouteParamCall extends MethodCall {
|
||||||
|
GrapeRouteParamCall() {
|
||||||
|
exists(GrapeEndpoint endpoint |
|
||||||
|
this.getParent+() = endpoint.getBody().asExpr().getExpr() and
|
||||||
|
this.getMethodName() = "route_param"
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// Also handle cases where route_param is called on an instance of a Grape API class
|
||||||
|
this = grapeApiInstance().getAMethodCall("route_param").asExpr().getExpr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to `headers` block within a Grape API class.
|
||||||
|
* This is different from the headers() method call - this is the DSL block for defining header requirements.
|
||||||
|
*/
|
||||||
|
private class GrapeHeadersBlockCall extends MethodCall {
|
||||||
|
GrapeHeadersBlockCall() {
|
||||||
|
exists(GrapeApiClass api |
|
||||||
|
this.getParent+() = api.getADeclaration() and
|
||||||
|
this.getMethodName() = "headers" and
|
||||||
|
exists(this.getBlock())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to `cookies` block within a Grape API class.
|
||||||
|
* This DSL block defines cookie requirements and those cookies are user-controlled.
|
||||||
|
*/
|
||||||
|
private class GrapeCookiesBlockCall extends MethodCall {
|
||||||
|
GrapeCookiesBlockCall() {
|
||||||
|
exists(GrapeApiClass api |
|
||||||
|
this.getParent+() = api.getADeclaration() and
|
||||||
|
this.getMethodName() = "cookies" and
|
||||||
|
exists(this.getBlock())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to `cookies` method from within a Grape API endpoint or cookies block.
|
||||||
|
* Similar to headers, cookies can be accessed as a method and are user-controlled input.
|
||||||
|
*/
|
||||||
|
class GrapeCookiesSource extends Http::Server::RequestInputAccess::Range {
|
||||||
|
GrapeCookiesSource() {
|
||||||
|
this.asExpr().getExpr() instanceof GrapeCookiesCall
|
||||||
|
or
|
||||||
|
this.asExpr().getExpr() instanceof GrapeCookiesBlockCall
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getSourceType() { result = "Grape::API#cookies" }
|
||||||
|
|
||||||
|
override Http::Server::RequestInputKind getKind() { result = Http::Server::cookieInputKind() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to `cookies` method from within a Grape API endpoint.
|
||||||
|
*/
|
||||||
|
private class GrapeCookiesCall extends MethodCall {
|
||||||
|
GrapeCookiesCall() {
|
||||||
|
exists(GrapeEndpoint endpoint |
|
||||||
|
this.getParent+() = endpoint.getBody().asCallableAstNode() and
|
||||||
|
this.getMethodName() = "cookies"
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// Also handle cases where cookies is called on an instance of a Grape API class
|
||||||
|
this = grapeApiInstance().getAMethodCall("cookies").asExpr().getExpr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method defined within a `helpers` block in a Grape API class.
|
||||||
|
* These methods become available in endpoint contexts through Grape's DSL.
|
||||||
|
*/
|
||||||
|
private class GrapeHelperMethod extends Method {
|
||||||
|
private GrapeApiClass apiClass;
|
||||||
|
|
||||||
|
GrapeHelperMethod() {
|
||||||
|
exists(DataFlow::CallNode helpersCall |
|
||||||
|
helpersCall = apiClass.getAModuleLevelCall("helpers") and
|
||||||
|
this.getParent+() = helpersCall.getBlock().asExpr().getExpr()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the API class that contains this helper method.
|
||||||
|
*/
|
||||||
|
GrapeApiClass getApiClass() { result = apiClass }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional call-target to resolve helper method calls defined in `helpers` blocks.
|
||||||
|
*
|
||||||
|
* This class is responsible for resolving calls to helper methods defined in
|
||||||
|
* `helpers` blocks, allowing the dataflow framework to accurately track
|
||||||
|
* the flow of information between these methods and their call sites.
|
||||||
|
*/
|
||||||
|
private class GrapeHelperMethodTarget extends AdditionalCallTarget {
|
||||||
|
override DataFlowCallable viableTarget(CfgNodes::ExprNodes::CallCfgNode call) {
|
||||||
|
// Find calls to helper methods from within Grape endpoints or other helper methods
|
||||||
|
exists(GrapeHelperMethod helperMethod, MethodCall mc |
|
||||||
|
result.asCfgScope() = helperMethod and
|
||||||
|
mc = call.getAstNode() and
|
||||||
|
mc.getMethodName() = helperMethod.getName() and
|
||||||
|
mc.getParent+() = helperMethod.getApiClass().getADeclaration()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,20 +3,20 @@ import codeql.ruby.frameworks.Grape
|
|||||||
import codeql.ruby.Concepts
|
import codeql.ruby.Concepts
|
||||||
import codeql.ruby.AST
|
import codeql.ruby.AST
|
||||||
|
|
||||||
query predicate grapeApiClasses(GrapeApiClass api) { any() }
|
query predicate grapeApiClasses(Grape::GrapeApiClass api) { any() }
|
||||||
|
|
||||||
query predicate grapeEndpoints(GrapeApiClass api, GrapeEndpoint endpoint, string method, string path) {
|
query predicate grapeEndpoints(Grape::GrapeApiClass api, Grape::GrapeEndpoint endpoint, string method, string path) {
|
||||||
endpoint = api.getAnEndpoint() and
|
endpoint = api.getAnEndpoint() and
|
||||||
method = endpoint.getHttpMethod() and
|
method = endpoint.getHttpMethod() and
|
||||||
path = endpoint.getPath()
|
path = endpoint.getPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
query predicate grapeParams(GrapeParamsSource params) { any() }
|
query predicate grapeParams(Grape::GrapeParamsSource params) { any() }
|
||||||
|
|
||||||
query predicate grapeHeaders(GrapeHeadersSource headers) { any() }
|
query predicate grapeHeaders(Grape::GrapeHeadersSource headers) { any() }
|
||||||
|
|
||||||
query predicate grapeRequest(GrapeRequestSource request) { any() }
|
query predicate grapeRequest(Grape::GrapeRequestSource request) { any() }
|
||||||
|
|
||||||
query predicate grapeRouteParam(GrapeRouteParamSource routeParam) { any() }
|
query predicate grapeRouteParam(Grape::GrapeRouteParamSource routeParam) { any() }
|
||||||
|
|
||||||
query predicate grapeCookies(GrapeCookiesSource cookies) { any() }
|
query predicate grapeCookies(Grape::GrapeCookiesSource cookies) { any() }
|
||||||
|
|||||||
Reference in New Issue
Block a user