mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #20427 from felickz/ruby-framework-grape
Ruby: Add support for Grape Framework
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Initial modeling for the Ruby Grape framework in `Grape.qll` has been added to detect API endpoints, parameters, and headers within Grape API classes.
|
||||
@@ -21,6 +21,7 @@ private import codeql.ruby.frameworks.Rails
|
||||
private import codeql.ruby.frameworks.Railties
|
||||
private import codeql.ruby.frameworks.Stdlib
|
||||
private import codeql.ruby.frameworks.Files
|
||||
private import codeql.ruby.frameworks.Grape
|
||||
private import codeql.ruby.frameworks.HttpClients
|
||||
private import codeql.ruby.frameworks.XmlParsing
|
||||
private import codeql.ruby.frameworks.ActionDispatch
|
||||
|
||||
300
ruby/ql/lib/codeql/ruby/frameworks/Grape.qll
Normal file
300
ruby/ql/lib/codeql/ruby/frameworks/Grape.qll
Normal file
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* Provides modeling for the `Grape` API framework.
|
||||
*/
|
||||
|
||||
private import codeql.ruby.AST
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.controlflow.CfgNodes
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.RemoteFlowSources
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.typetracking.TypeTracking
|
||||
private import codeql.ruby.frameworks.Rails
|
||||
private import codeql.ruby.frameworks.internal.Rails
|
||||
private import codeql.ruby.dataflow.internal.DataFlowDispatch
|
||||
private import codeql.ruby.dataflow.FlowSteps
|
||||
|
||||
/**
|
||||
* Provides modeling for Grape, a REST-like API framework for Ruby.
|
||||
* Grape allows you to build RESTful APIs in Ruby with minimal effort.
|
||||
*/
|
||||
module Grape {
|
||||
/**
|
||||
* A Grape API class which sits at the top of the class hierarchy.
|
||||
* In other words, it does not subclass any other Grape API class in source code.
|
||||
*/
|
||||
class RootApi extends GrapeApiClass {
|
||||
RootApi() { not this = any(GrapeApiClass parent).getAnImmediateDescendent() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that extends `Grape::API`.
|
||||
* For example,
|
||||
*
|
||||
* ```rb
|
||||
* class FooAPI < Grape::API
|
||||
* get '/users' do
|
||||
* name = params[:name]
|
||||
* User.where("name = #{name}")
|
||||
* end
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class GrapeApiClass extends DataFlow::ClassNode {
|
||||
GrapeApiClass() { this = grapeApiBaseClass().getADescendentModule() }
|
||||
|
||||
/**
|
||||
* 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()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `self` parameter belonging to a method defined within a
|
||||
* `helpers` block in this API class.
|
||||
*
|
||||
* These methods become available in endpoint contexts through Grape's DSL.
|
||||
*/
|
||||
DataFlow::SelfParameterNode getHelperSelf() {
|
||||
exists(DataFlow::CallNode helpersCall |
|
||||
helpersCall = this.getAModuleLevelCall("helpers") and
|
||||
result.getSelfVariable().getDeclaringScope().getOuterScope+() =
|
||||
helpersCall.getBlock().asExpr().getExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
exists(API::Node n | this = n.getAMethodCall("params").asExpr().getExpr() |
|
||||
// Params calls within endpoint blocks
|
||||
n = grapeApiInstance()
|
||||
or
|
||||
// Params calls within helper methods (defined in helpers blocks)
|
||||
n = any(GrapeApiClass c).getHelperSelf().track()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
// 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() {
|
||||
// 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() {
|
||||
// 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() {
|
||||
this = grapeApiInstance().getAMethodCall("headers").asExpr().getExpr() 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() {
|
||||
this = grapeApiInstance().getAMethodCall("cookies").asExpr().getExpr() 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() {
|
||||
// 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() { this = apiClass.getHelperSelf().getSelfVariable().getDeclaringScope() }
|
||||
|
||||
/**
|
||||
* 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
variableIsCaptured
|
||||
| app.rb:126:9:130:11 | self | CapturedVariable is not captured |
|
||||
consistencyOverview
|
||||
| CapturedVariable is not captured | 1 |
|
||||
201
ruby/ql/test/library-tests/frameworks/grape/Flow.expected
Normal file
201
ruby/ql/test/library-tests/frameworks/grape/Flow.expected
Normal file
@@ -0,0 +1,201 @@
|
||||
models
|
||||
edges
|
||||
| app.rb:103:13:103:18 | call to params | app.rb:103:13:103:70 | call to select | provenance | |
|
||||
| app.rb:103:13:103:18 | call to params | app.rb:103:13:103:70 | call to select : [collection] [element] | provenance | |
|
||||
| app.rb:103:13:103:70 | call to select | app.rb:189:21:189:31 | call to user_params | provenance | |
|
||||
| app.rb:103:13:103:70 | call to select | app.rb:205:21:205:31 | call to user_params | provenance | |
|
||||
| app.rb:103:13:103:70 | call to select : [collection] [element] | app.rb:189:21:189:31 | call to user_params : [collection] [element] | provenance | |
|
||||
| app.rb:103:13:103:70 | call to select : [collection] [element] | app.rb:205:21:205:31 | call to user_params : [collection] [element] | provenance | |
|
||||
| app.rb:107:13:107:32 | call to source | app.rb:183:18:183:43 | call to vulnerable_helper | provenance | |
|
||||
| app.rb:107:13:107:32 | call to source | app.rb:183:18:183:43 | call to vulnerable_helper | provenance | |
|
||||
| app.rb:111:13:111:33 | call to source | app.rb:190:25:190:37 | call to simple_helper | provenance | |
|
||||
| app.rb:111:13:111:33 | call to source | app.rb:190:25:190:37 | call to simple_helper | provenance | |
|
||||
| app.rb:118:17:118:43 | call to source | app.rb:212:23:212:39 | call to authenticate_user | provenance | |
|
||||
| app.rb:118:17:118:43 | call to source | app.rb:212:23:212:39 | call to authenticate_user | provenance | |
|
||||
| app.rb:122:17:122:47 | call to source | app.rb:216:23:216:48 | call to check_permissions | provenance | |
|
||||
| app.rb:122:17:122:47 | call to source | app.rb:216:23:216:48 | call to check_permissions | provenance | |
|
||||
| app.rb:128:17:128:42 | call to source | app.rb:220:29:220:80 | call to validate_email | provenance | |
|
||||
| app.rb:128:17:128:42 | call to source | app.rb:220:29:220:80 | call to validate_email | provenance | |
|
||||
| app.rb:134:17:134:42 | call to source | app.rb:225:28:225:39 | call to debug_helper | provenance | |
|
||||
| app.rb:134:17:134:42 | call to source | app.rb:225:28:225:39 | call to debug_helper | provenance | |
|
||||
| app.rb:140:17:140:37 | call to source | app.rb:230:25:230:37 | call to rescue_helper | provenance | |
|
||||
| app.rb:140:17:140:37 | call to source | app.rb:230:25:230:37 | call to rescue_helper | provenance | |
|
||||
| app.rb:150:17:150:35 | call to source | app.rb:235:27:235:37 | call to test_helper | provenance | |
|
||||
| app.rb:150:17:150:35 | call to source | app.rb:235:27:235:37 | call to test_helper | provenance | |
|
||||
| app.rb:166:9:166:15 | user_id | app.rb:173:14:173:20 | user_id | provenance | |
|
||||
| app.rb:166:19:166:24 | call to params | app.rb:166:19:166:34 | ...[...] | provenance | |
|
||||
| app.rb:166:19:166:34 | ...[...] | app.rb:166:9:166:15 | user_id | provenance | |
|
||||
| app.rb:167:9:167:16 | route_id | app.rb:174:14:174:21 | route_id | provenance | |
|
||||
| app.rb:167:20:167:40 | call to route_param | app.rb:167:9:167:16 | route_id | provenance | |
|
||||
| app.rb:168:9:168:12 | auth | app.rb:175:14:175:17 | auth | provenance | |
|
||||
| app.rb:168:16:168:22 | call to headers | app.rb:168:16:168:38 | ...[...] | provenance | |
|
||||
| app.rb:168:16:168:38 | ...[...] | app.rb:168:9:168:12 | auth | provenance | |
|
||||
| app.rb:169:9:169:15 | session | app.rb:176:14:176:20 | session | provenance | |
|
||||
| app.rb:169:19:169:25 | call to cookies | app.rb:169:19:169:38 | ...[...] | provenance | |
|
||||
| app.rb:169:19:169:38 | ...[...] | app.rb:169:9:169:15 | session | provenance | |
|
||||
| app.rb:183:9:183:14 | result | app.rb:184:14:184:19 | result | provenance | |
|
||||
| app.rb:183:9:183:14 | result | app.rb:184:14:184:19 | result | provenance | |
|
||||
| app.rb:183:18:183:43 | call to vulnerable_helper | app.rb:183:9:183:14 | result | provenance | |
|
||||
| app.rb:183:18:183:43 | call to vulnerable_helper | app.rb:183:9:183:14 | result | provenance | |
|
||||
| app.rb:189:9:189:17 | user_data | app.rb:191:14:191:22 | user_data | provenance | |
|
||||
| app.rb:189:9:189:17 | user_data : [collection] [element] | app.rb:191:14:191:22 | user_data | provenance | |
|
||||
| app.rb:189:21:189:31 | call to user_params | app.rb:189:9:189:17 | user_data | provenance | |
|
||||
| app.rb:189:21:189:31 | call to user_params : [collection] [element] | app.rb:189:9:189:17 | user_data : [collection] [element] | provenance | |
|
||||
| app.rb:190:9:190:21 | simple_result | app.rb:192:14:192:26 | simple_result | provenance | |
|
||||
| app.rb:190:9:190:21 | simple_result | app.rb:192:14:192:26 | simple_result | provenance | |
|
||||
| app.rb:190:25:190:37 | call to simple_helper | app.rb:190:9:190:21 | simple_result | provenance | |
|
||||
| app.rb:190:25:190:37 | call to simple_helper | app.rb:190:9:190:21 | simple_result | provenance | |
|
||||
| app.rb:199:13:199:19 | user_id | app.rb:200:18:200:24 | user_id | provenance | |
|
||||
| app.rb:199:23:199:28 | call to params | app.rb:199:23:199:33 | ...[...] | provenance | |
|
||||
| app.rb:199:23:199:33 | ...[...] | app.rb:199:13:199:19 | user_id | provenance | |
|
||||
| app.rb:205:9:205:17 | user_data | app.rb:206:14:206:22 | user_data | provenance | |
|
||||
| app.rb:205:9:205:17 | user_data : [collection] [element] | app.rb:206:14:206:22 | user_data | provenance | |
|
||||
| app.rb:205:21:205:31 | call to user_params | app.rb:205:9:205:17 | user_data | provenance | |
|
||||
| app.rb:205:21:205:31 | call to user_params : [collection] [element] | app.rb:205:9:205:17 | user_data : [collection] [element] | provenance | |
|
||||
| app.rb:212:9:212:19 | auth_result | app.rb:213:14:213:24 | auth_result | provenance | |
|
||||
| app.rb:212:9:212:19 | auth_result | app.rb:213:14:213:24 | auth_result | provenance | |
|
||||
| app.rb:212:23:212:39 | call to authenticate_user | app.rb:212:9:212:19 | auth_result | provenance | |
|
||||
| app.rb:212:23:212:39 | call to authenticate_user | app.rb:212:9:212:19 | auth_result | provenance | |
|
||||
| app.rb:216:9:216:19 | perm_result | app.rb:217:14:217:24 | perm_result | provenance | |
|
||||
| app.rb:216:9:216:19 | perm_result | app.rb:217:14:217:24 | perm_result | provenance | |
|
||||
| app.rb:216:23:216:48 | call to check_permissions | app.rb:216:9:216:19 | perm_result | provenance | |
|
||||
| app.rb:216:23:216:48 | call to check_permissions | app.rb:216:9:216:19 | perm_result | provenance | |
|
||||
| app.rb:220:9:220:25 | validation_result | app.rb:221:14:221:30 | validation_result | provenance | |
|
||||
| app.rb:220:9:220:25 | validation_result | app.rb:221:14:221:30 | validation_result | provenance | |
|
||||
| app.rb:220:29:220:80 | call to validate_email | app.rb:220:9:220:25 | validation_result | provenance | |
|
||||
| app.rb:220:29:220:80 | call to validate_email | app.rb:220:9:220:25 | validation_result | provenance | |
|
||||
| app.rb:225:13:225:24 | debug_result | app.rb:226:18:226:29 | debug_result | provenance | |
|
||||
| app.rb:225:13:225:24 | debug_result | app.rb:226:18:226:29 | debug_result | provenance | |
|
||||
| app.rb:225:28:225:39 | call to debug_helper | app.rb:225:13:225:24 | debug_result | provenance | |
|
||||
| app.rb:225:28:225:39 | call to debug_helper | app.rb:225:13:225:24 | debug_result | provenance | |
|
||||
| app.rb:230:9:230:21 | rescue_result | app.rb:231:14:231:26 | rescue_result | provenance | |
|
||||
| app.rb:230:9:230:21 | rescue_result | app.rb:231:14:231:26 | rescue_result | provenance | |
|
||||
| app.rb:230:25:230:37 | call to rescue_helper | app.rb:230:9:230:21 | rescue_result | provenance | |
|
||||
| app.rb:230:25:230:37 | call to rescue_helper | app.rb:230:9:230:21 | rescue_result | provenance | |
|
||||
| app.rb:235:13:235:23 | case_result | app.rb:236:18:236:28 | case_result | provenance | |
|
||||
| app.rb:235:13:235:23 | case_result | app.rb:236:18:236:28 | case_result | provenance | |
|
||||
| app.rb:235:27:235:37 | call to test_helper | app.rb:235:13:235:23 | case_result | provenance | |
|
||||
| app.rb:235:27:235:37 | call to test_helper | app.rb:235:13:235:23 | case_result | provenance | |
|
||||
nodes
|
||||
| app.rb:103:13:103:18 | call to params | semmle.label | call to params |
|
||||
| app.rb:103:13:103:70 | call to select | semmle.label | call to select |
|
||||
| app.rb:103:13:103:70 | call to select : [collection] [element] | semmle.label | call to select : [collection] [element] |
|
||||
| app.rb:107:13:107:32 | call to source | semmle.label | call to source |
|
||||
| app.rb:107:13:107:32 | call to source | semmle.label | call to source |
|
||||
| app.rb:111:13:111:33 | call to source | semmle.label | call to source |
|
||||
| app.rb:111:13:111:33 | call to source | semmle.label | call to source |
|
||||
| app.rb:118:17:118:43 | call to source | semmle.label | call to source |
|
||||
| app.rb:118:17:118:43 | call to source | semmle.label | call to source |
|
||||
| app.rb:122:17:122:47 | call to source | semmle.label | call to source |
|
||||
| app.rb:122:17:122:47 | call to source | semmle.label | call to source |
|
||||
| app.rb:128:17:128:42 | call to source | semmle.label | call to source |
|
||||
| app.rb:128:17:128:42 | call to source | semmle.label | call to source |
|
||||
| app.rb:134:17:134:42 | call to source | semmle.label | call to source |
|
||||
| app.rb:134:17:134:42 | call to source | semmle.label | call to source |
|
||||
| app.rb:140:17:140:37 | call to source | semmle.label | call to source |
|
||||
| app.rb:140:17:140:37 | call to source | semmle.label | call to source |
|
||||
| app.rb:150:17:150:35 | call to source | semmle.label | call to source |
|
||||
| app.rb:150:17:150:35 | call to source | semmle.label | call to source |
|
||||
| app.rb:166:9:166:15 | user_id | semmle.label | user_id |
|
||||
| app.rb:166:19:166:24 | call to params | semmle.label | call to params |
|
||||
| app.rb:166:19:166:34 | ...[...] | semmle.label | ...[...] |
|
||||
| app.rb:167:9:167:16 | route_id | semmle.label | route_id |
|
||||
| app.rb:167:20:167:40 | call to route_param | semmle.label | call to route_param |
|
||||
| app.rb:168:9:168:12 | auth | semmle.label | auth |
|
||||
| app.rb:168:16:168:22 | call to headers | semmle.label | call to headers |
|
||||
| app.rb:168:16:168:38 | ...[...] | semmle.label | ...[...] |
|
||||
| app.rb:169:9:169:15 | session | semmle.label | session |
|
||||
| app.rb:169:19:169:25 | call to cookies | semmle.label | call to cookies |
|
||||
| app.rb:169:19:169:38 | ...[...] | semmle.label | ...[...] |
|
||||
| app.rb:173:14:173:20 | user_id | semmle.label | user_id |
|
||||
| app.rb:174:14:174:21 | route_id | semmle.label | route_id |
|
||||
| app.rb:175:14:175:17 | auth | semmle.label | auth |
|
||||
| app.rb:176:14:176:20 | session | semmle.label | session |
|
||||
| app.rb:183:9:183:14 | result | semmle.label | result |
|
||||
| app.rb:183:9:183:14 | result | semmle.label | result |
|
||||
| app.rb:183:18:183:43 | call to vulnerable_helper | semmle.label | call to vulnerable_helper |
|
||||
| app.rb:183:18:183:43 | call to vulnerable_helper | semmle.label | call to vulnerable_helper |
|
||||
| app.rb:184:14:184:19 | result | semmle.label | result |
|
||||
| app.rb:184:14:184:19 | result | semmle.label | result |
|
||||
| app.rb:189:9:189:17 | user_data | semmle.label | user_data |
|
||||
| app.rb:189:9:189:17 | user_data : [collection] [element] | semmle.label | user_data : [collection] [element] |
|
||||
| app.rb:189:21:189:31 | call to user_params | semmle.label | call to user_params |
|
||||
| app.rb:189:21:189:31 | call to user_params : [collection] [element] | semmle.label | call to user_params : [collection] [element] |
|
||||
| app.rb:190:9:190:21 | simple_result | semmle.label | simple_result |
|
||||
| app.rb:190:9:190:21 | simple_result | semmle.label | simple_result |
|
||||
| app.rb:190:25:190:37 | call to simple_helper | semmle.label | call to simple_helper |
|
||||
| app.rb:190:25:190:37 | call to simple_helper | semmle.label | call to simple_helper |
|
||||
| app.rb:191:14:191:22 | user_data | semmle.label | user_data |
|
||||
| app.rb:192:14:192:26 | simple_result | semmle.label | simple_result |
|
||||
| app.rb:192:14:192:26 | simple_result | semmle.label | simple_result |
|
||||
| app.rb:199:13:199:19 | user_id | semmle.label | user_id |
|
||||
| app.rb:199:23:199:28 | call to params | semmle.label | call to params |
|
||||
| app.rb:199:23:199:33 | ...[...] | semmle.label | ...[...] |
|
||||
| app.rb:200:18:200:24 | user_id | semmle.label | user_id |
|
||||
| app.rb:205:9:205:17 | user_data | semmle.label | user_data |
|
||||
| app.rb:205:9:205:17 | user_data : [collection] [element] | semmle.label | user_data : [collection] [element] |
|
||||
| app.rb:205:21:205:31 | call to user_params | semmle.label | call to user_params |
|
||||
| app.rb:205:21:205:31 | call to user_params : [collection] [element] | semmle.label | call to user_params : [collection] [element] |
|
||||
| app.rb:206:14:206:22 | user_data | semmle.label | user_data |
|
||||
| app.rb:212:9:212:19 | auth_result | semmle.label | auth_result |
|
||||
| app.rb:212:9:212:19 | auth_result | semmle.label | auth_result |
|
||||
| app.rb:212:23:212:39 | call to authenticate_user | semmle.label | call to authenticate_user |
|
||||
| app.rb:212:23:212:39 | call to authenticate_user | semmle.label | call to authenticate_user |
|
||||
| app.rb:213:14:213:24 | auth_result | semmle.label | auth_result |
|
||||
| app.rb:213:14:213:24 | auth_result | semmle.label | auth_result |
|
||||
| app.rb:216:9:216:19 | perm_result | semmle.label | perm_result |
|
||||
| app.rb:216:9:216:19 | perm_result | semmle.label | perm_result |
|
||||
| app.rb:216:23:216:48 | call to check_permissions | semmle.label | call to check_permissions |
|
||||
| app.rb:216:23:216:48 | call to check_permissions | semmle.label | call to check_permissions |
|
||||
| app.rb:217:14:217:24 | perm_result | semmle.label | perm_result |
|
||||
| app.rb:217:14:217:24 | perm_result | semmle.label | perm_result |
|
||||
| app.rb:220:9:220:25 | validation_result | semmle.label | validation_result |
|
||||
| app.rb:220:9:220:25 | validation_result | semmle.label | validation_result |
|
||||
| app.rb:220:29:220:80 | call to validate_email | semmle.label | call to validate_email |
|
||||
| app.rb:220:29:220:80 | call to validate_email | semmle.label | call to validate_email |
|
||||
| app.rb:221:14:221:30 | validation_result | semmle.label | validation_result |
|
||||
| app.rb:221:14:221:30 | validation_result | semmle.label | validation_result |
|
||||
| app.rb:225:13:225:24 | debug_result | semmle.label | debug_result |
|
||||
| app.rb:225:13:225:24 | debug_result | semmle.label | debug_result |
|
||||
| app.rb:225:28:225:39 | call to debug_helper | semmle.label | call to debug_helper |
|
||||
| app.rb:225:28:225:39 | call to debug_helper | semmle.label | call to debug_helper |
|
||||
| app.rb:226:18:226:29 | debug_result | semmle.label | debug_result |
|
||||
| app.rb:226:18:226:29 | debug_result | semmle.label | debug_result |
|
||||
| app.rb:230:9:230:21 | rescue_result | semmle.label | rescue_result |
|
||||
| app.rb:230:9:230:21 | rescue_result | semmle.label | rescue_result |
|
||||
| app.rb:230:25:230:37 | call to rescue_helper | semmle.label | call to rescue_helper |
|
||||
| app.rb:230:25:230:37 | call to rescue_helper | semmle.label | call to rescue_helper |
|
||||
| app.rb:231:14:231:26 | rescue_result | semmle.label | rescue_result |
|
||||
| app.rb:231:14:231:26 | rescue_result | semmle.label | rescue_result |
|
||||
| app.rb:235:13:235:23 | case_result | semmle.label | case_result |
|
||||
| app.rb:235:13:235:23 | case_result | semmle.label | case_result |
|
||||
| app.rb:235:27:235:37 | call to test_helper | semmle.label | call to test_helper |
|
||||
| app.rb:235:27:235:37 | call to test_helper | semmle.label | call to test_helper |
|
||||
| app.rb:236:18:236:28 | case_result | semmle.label | case_result |
|
||||
| app.rb:236:18:236:28 | case_result | semmle.label | case_result |
|
||||
subpaths
|
||||
testFailures
|
||||
#select
|
||||
| app.rb:173:14:173:20 | user_id | app.rb:166:19:166:24 | call to params | app.rb:173:14:173:20 | user_id | $@ | app.rb:166:19:166:24 | call to params | call to params |
|
||||
| app.rb:174:14:174:21 | route_id | app.rb:167:20:167:40 | call to route_param | app.rb:174:14:174:21 | route_id | $@ | app.rb:167:20:167:40 | call to route_param | call to route_param |
|
||||
| app.rb:175:14:175:17 | auth | app.rb:168:16:168:22 | call to headers | app.rb:175:14:175:17 | auth | $@ | app.rb:168:16:168:22 | call to headers | call to headers |
|
||||
| app.rb:176:14:176:20 | session | app.rb:169:19:169:25 | call to cookies | app.rb:176:14:176:20 | session | $@ | app.rb:169:19:169:25 | call to cookies | call to cookies |
|
||||
| app.rb:184:14:184:19 | result | app.rb:107:13:107:32 | call to source | app.rb:184:14:184:19 | result | $@ | app.rb:107:13:107:32 | call to source | call to source |
|
||||
| app.rb:184:14:184:19 | result | app.rb:107:13:107:32 | call to source | app.rb:184:14:184:19 | result | $@ | app.rb:107:13:107:32 | call to source | call to source |
|
||||
| app.rb:191:14:191:22 | user_data | app.rb:103:13:103:18 | call to params | app.rb:191:14:191:22 | user_data | $@ | app.rb:103:13:103:18 | call to params | call to params |
|
||||
| app.rb:192:14:192:26 | simple_result | app.rb:111:13:111:33 | call to source | app.rb:192:14:192:26 | simple_result | $@ | app.rb:111:13:111:33 | call to source | call to source |
|
||||
| app.rb:192:14:192:26 | simple_result | app.rb:111:13:111:33 | call to source | app.rb:192:14:192:26 | simple_result | $@ | app.rb:111:13:111:33 | call to source | call to source |
|
||||
| app.rb:200:18:200:24 | user_id | app.rb:199:23:199:28 | call to params | app.rb:200:18:200:24 | user_id | $@ | app.rb:199:23:199:28 | call to params | call to params |
|
||||
| app.rb:206:14:206:22 | user_data | app.rb:103:13:103:18 | call to params | app.rb:206:14:206:22 | user_data | $@ | app.rb:103:13:103:18 | call to params | call to params |
|
||||
| app.rb:213:14:213:24 | auth_result | app.rb:118:17:118:43 | call to source | app.rb:213:14:213:24 | auth_result | $@ | app.rb:118:17:118:43 | call to source | call to source |
|
||||
| app.rb:213:14:213:24 | auth_result | app.rb:118:17:118:43 | call to source | app.rb:213:14:213:24 | auth_result | $@ | app.rb:118:17:118:43 | call to source | call to source |
|
||||
| app.rb:217:14:217:24 | perm_result | app.rb:122:17:122:47 | call to source | app.rb:217:14:217:24 | perm_result | $@ | app.rb:122:17:122:47 | call to source | call to source |
|
||||
| app.rb:217:14:217:24 | perm_result | app.rb:122:17:122:47 | call to source | app.rb:217:14:217:24 | perm_result | $@ | app.rb:122:17:122:47 | call to source | call to source |
|
||||
| app.rb:221:14:221:30 | validation_result | app.rb:128:17:128:42 | call to source | app.rb:221:14:221:30 | validation_result | $@ | app.rb:128:17:128:42 | call to source | call to source |
|
||||
| app.rb:221:14:221:30 | validation_result | app.rb:128:17:128:42 | call to source | app.rb:221:14:221:30 | validation_result | $@ | app.rb:128:17:128:42 | call to source | call to source |
|
||||
| app.rb:226:18:226:29 | debug_result | app.rb:134:17:134:42 | call to source | app.rb:226:18:226:29 | debug_result | $@ | app.rb:134:17:134:42 | call to source | call to source |
|
||||
| app.rb:226:18:226:29 | debug_result | app.rb:134:17:134:42 | call to source | app.rb:226:18:226:29 | debug_result | $@ | app.rb:134:17:134:42 | call to source | call to source |
|
||||
| app.rb:231:14:231:26 | rescue_result | app.rb:140:17:140:37 | call to source | app.rb:231:14:231:26 | rescue_result | $@ | app.rb:140:17:140:37 | call to source | call to source |
|
||||
| app.rb:231:14:231:26 | rescue_result | app.rb:140:17:140:37 | call to source | app.rb:231:14:231:26 | rescue_result | $@ | app.rb:140:17:140:37 | call to source | call to source |
|
||||
| app.rb:236:18:236:28 | case_result | app.rb:150:17:150:35 | call to source | app.rb:236:18:236:28 | case_result | $@ | app.rb:150:17:150:35 | call to source | call to source |
|
||||
| app.rb:236:18:236:28 | case_result | app.rb:150:17:150:35 | call to source | app.rb:236:18:236:28 | case_result | $@ | app.rb:150:17:150:35 | call to source | call to source |
|
||||
25
ruby/ql/test/library-tests/frameworks/grape/Flow.ql
Normal file
25
ruby/ql/test/library-tests/frameworks/grape/Flow.ql
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import utils.test.InlineFlowTest
|
||||
import PathGraph
|
||||
import codeql.ruby.frameworks.Grape
|
||||
import codeql.ruby.Concepts
|
||||
|
||||
module GrapeConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source instanceof Http::Server::RequestInputAccess::Range
|
||||
or
|
||||
DefaultFlowConfig::isSource(source)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { DefaultFlowConfig::isSink(sink) }
|
||||
}
|
||||
|
||||
import FlowTest<DefaultFlowConfig, GrapeConfig>
|
||||
|
||||
from PathNode source, PathNode sink
|
||||
where flowPath(source, sink)
|
||||
select sink, source, sink, "$@", source, source.toString()
|
||||
58
ruby/ql/test/library-tests/frameworks/grape/Grape.expected
Normal file
58
ruby/ql/test/library-tests/frameworks/grape/Grape.expected
Normal file
@@ -0,0 +1,58 @@
|
||||
grapeApiClasses
|
||||
| app.rb:1:1:90:3 | MyAPI |
|
||||
| app.rb:92:1:96:3 | AdminAPI |
|
||||
| app.rb:98:1:239:3 | UserAPI |
|
||||
grapeEndpoints
|
||||
| app.rb:1:1:90:3 | MyAPI | app.rb:7:3:11:5 | call to get | GET | /hello/:name |
|
||||
| app.rb:1:1:90:3 | MyAPI | app.rb:17:3:20:5 | call to post | POST | /messages |
|
||||
| app.rb:1:1:90:3 | MyAPI | app.rb:23:3:27:5 | call to put | PUT | /update/:id |
|
||||
| app.rb:1:1:90:3 | MyAPI | app.rb:30:3:32:5 | call to delete | DELETE | /items/:id |
|
||||
| app.rb:1:1:90:3 | MyAPI | app.rb:35:3:37:5 | call to patch | PATCH | /items/:id |
|
||||
| app.rb:1:1:90:3 | MyAPI | app.rb:40:3:42:5 | call to head | HEAD | /status |
|
||||
| app.rb:1:1:90:3 | MyAPI | app.rb:45:3:47:5 | call to options | OPTIONS | /info |
|
||||
| app.rb:1:1:90:3 | MyAPI | app.rb:50:3:54:5 | call to get | GET | /users/:user_id/posts/:post_id |
|
||||
| app.rb:1:1:90:3 | MyAPI | app.rb:78:3:82:5 | call to get | GET | /cookie_test |
|
||||
| app.rb:1:1:90:3 | MyAPI | app.rb:85:3:89:5 | call to get | GET | /header_test |
|
||||
| app.rb:92:1:96:3 | AdminAPI | app.rb:93:3:95:5 | call to get | GET | /admin |
|
||||
| app.rb:98:1:239:3 | UserAPI | app.rb:164:5:178:7 | call to get | GET | /comprehensive_test/:user_id |
|
||||
| app.rb:98:1:239:3 | UserAPI | app.rb:180:5:185:7 | call to get | GET | /helper_test/:user_id |
|
||||
| app.rb:98:1:239:3 | UserAPI | app.rb:187:5:193:7 | call to post | POST | /users |
|
||||
| app.rb:98:1:239:3 | UserAPI | app.rb:204:5:207:7 | call to post | POST | /users |
|
||||
| app.rb:98:1:239:3 | UserAPI | app.rb:210:5:238:7 | call to get | GET | /nested_test/:token |
|
||||
grapeParams
|
||||
| app.rb:8:12:8:17 | call to params |
|
||||
| app.rb:14:3:16:5 | call to params |
|
||||
| app.rb:18:11:18:16 | call to params |
|
||||
| app.rb:24:10:24:15 | call to params |
|
||||
| app.rb:31:5:31:10 | call to params |
|
||||
| app.rb:36:5:36:10 | call to params |
|
||||
| app.rb:60:12:60:17 | call to params |
|
||||
| app.rb:94:5:94:10 | call to params |
|
||||
| app.rb:103:13:103:18 | call to params |
|
||||
| app.rb:117:25:117:30 | call to params |
|
||||
| app.rb:166:19:166:24 | call to params |
|
||||
| app.rb:182:19:182:24 | call to params |
|
||||
| app.rb:199:23:199:28 | call to params |
|
||||
grapeHeaders
|
||||
| app.rb:9:18:9:24 | call to headers |
|
||||
| app.rb:46:5:46:11 | call to headers |
|
||||
| app.rb:66:3:69:5 | call to headers |
|
||||
| app.rb:86:12:86:18 | call to headers |
|
||||
| app.rb:87:14:87:20 | call to headers |
|
||||
| app.rb:156:5:158:7 | call to headers |
|
||||
| app.rb:168:16:168:22 | call to headers |
|
||||
grapeRequest
|
||||
| app.rb:25:12:25:18 | call to request |
|
||||
| app.rb:170:21:170:27 | call to request |
|
||||
grapeRouteParam
|
||||
| app.rb:51:15:51:35 | call to route_param |
|
||||
| app.rb:52:15:52:36 | call to route_param |
|
||||
| app.rb:57:3:63:5 | call to route_param |
|
||||
| app.rb:167:20:167:40 | call to route_param |
|
||||
| app.rb:196:5:202:7 | call to route_param |
|
||||
grapeCookies
|
||||
| app.rb:72:3:75:5 | call to cookies |
|
||||
| app.rb:79:15:79:21 | call to cookies |
|
||||
| app.rb:80:16:80:22 | call to cookies |
|
||||
| app.rb:160:5:162:7 | call to cookies |
|
||||
| app.rb:169:19:169:25 | call to cookies |
|
||||
24
ruby/ql/test/library-tests/frameworks/grape/Grape.ql
Normal file
24
ruby/ql/test/library-tests/frameworks/grape/Grape.ql
Normal file
@@ -0,0 +1,24 @@
|
||||
import ruby
|
||||
import codeql.ruby.frameworks.Grape
|
||||
import codeql.ruby.Concepts
|
||||
import codeql.ruby.AST
|
||||
|
||||
query predicate grapeApiClasses(Grape::GrapeApiClass api) { any() }
|
||||
|
||||
query predicate grapeEndpoints(
|
||||
Grape::GrapeApiClass api, Grape::GrapeEndpoint endpoint, string method, string path
|
||||
) {
|
||||
endpoint = api.getAnEndpoint() and
|
||||
method = endpoint.getHttpMethod() and
|
||||
path = endpoint.getPath()
|
||||
}
|
||||
|
||||
query predicate grapeParams(Grape::GrapeParamsSource params) { any() }
|
||||
|
||||
query predicate grapeHeaders(Grape::GrapeHeadersSource headers) { any() }
|
||||
|
||||
query predicate grapeRequest(Grape::GrapeRequestSource request) { any() }
|
||||
|
||||
query predicate grapeRouteParam(Grape::GrapeRouteParamSource routeParam) { any() }
|
||||
|
||||
query predicate grapeCookies(Grape::GrapeCookiesSource cookies) { any() }
|
||||
239
ruby/ql/test/library-tests/frameworks/grape/app.rb
Normal file
239
ruby/ql/test/library-tests/frameworks/grape/app.rb
Normal file
@@ -0,0 +1,239 @@
|
||||
class MyAPI < Grape::API
|
||||
version 'v1', using: :header, vendor: 'myapi'
|
||||
format :json
|
||||
prefix :api
|
||||
|
||||
desc 'Simple get endpoint'
|
||||
get '/hello/:name' do
|
||||
name = params[:name]
|
||||
user_agent = headers['User-Agent']
|
||||
"Hello #{name}!"
|
||||
end
|
||||
|
||||
desc 'Post endpoint with params'
|
||||
params do
|
||||
requires :message, type: String
|
||||
end
|
||||
post '/messages' do
|
||||
msg = params[:message]
|
||||
{ status: 'received', message: msg }
|
||||
end
|
||||
|
||||
desc 'Put endpoint accessing request'
|
||||
put '/update/:id' do
|
||||
id = params[:id]
|
||||
body = request.body.read
|
||||
{ id: id, body: body }
|
||||
end
|
||||
|
||||
desc 'Delete endpoint'
|
||||
delete '/items/:id' do
|
||||
params[:id]
|
||||
end
|
||||
|
||||
desc 'Patch endpoint'
|
||||
patch '/items/:id' do
|
||||
params[:id]
|
||||
end
|
||||
|
||||
desc 'Head endpoint'
|
||||
head '/status' do
|
||||
# Just return status
|
||||
end
|
||||
|
||||
desc 'Options endpoint'
|
||||
options '/info' do
|
||||
headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
|
||||
end
|
||||
|
||||
desc 'Route param endpoint'
|
||||
get '/users/:user_id/posts/:post_id' do
|
||||
user_id = route_param(:user_id)
|
||||
post_id = route_param('post_id')
|
||||
{ user_id: user_id, post_id: post_id }
|
||||
end
|
||||
|
||||
desc 'Route param block pattern'
|
||||
route_param :id do
|
||||
get do
|
||||
# params[:id] is user input from the path parameter
|
||||
id = params[:id]
|
||||
{ id: id }
|
||||
end
|
||||
end
|
||||
|
||||
# Headers block for defining expected headers
|
||||
headers do
|
||||
requires :Authorization, type: String
|
||||
optional 'X-Custom-Header', type: String
|
||||
end
|
||||
|
||||
# Cookies block for defining expected cookies
|
||||
cookies do
|
||||
requires :session_id, type: String
|
||||
optional :tracking_id, type: String
|
||||
end
|
||||
|
||||
desc 'Endpoint that uses cookies method'
|
||||
get '/cookie_test' do
|
||||
session = cookies[:session_id]
|
||||
tracking = cookies['tracking_id']
|
||||
{ session: session, tracking: tracking }
|
||||
end
|
||||
|
||||
desc 'Endpoint that uses headers method'
|
||||
get '/header_test' do
|
||||
auth = headers[:Authorization]
|
||||
custom = headers['X-Custom-Header']
|
||||
{ auth: auth, custom: custom }
|
||||
end
|
||||
end
|
||||
|
||||
class AdminAPI < Grape::API
|
||||
get '/admin' do
|
||||
params[:token]
|
||||
end
|
||||
end
|
||||
|
||||
class UserAPI < Grape::API
|
||||
VALID_PARAMS = %w(name email password password_confirmation)
|
||||
|
||||
helpers do
|
||||
def user_params
|
||||
params.select{|key,value| VALID_PARAMS.include?(key.to_s)} # Real helper implementation
|
||||
end
|
||||
|
||||
def vulnerable_helper(user_id)
|
||||
source "paramHelper" # Test parameter passing to helper
|
||||
end
|
||||
|
||||
def simple_helper
|
||||
source "simpleHelper" # Test simple helper return
|
||||
end
|
||||
|
||||
# Nested helper scenarios that require getParent+()
|
||||
module AuthHelpers
|
||||
def authenticate_user
|
||||
token = params[:token]
|
||||
source "nestedModuleHelper" # Test nested module helper
|
||||
end
|
||||
|
||||
def check_permissions(resource)
|
||||
source "nestedPermissionHelper" # Test nested module helper with params
|
||||
end
|
||||
end
|
||||
|
||||
class ValidationHelpers
|
||||
def self.validate_email(email)
|
||||
source "nestedClassHelper" # Test nested class helper
|
||||
end
|
||||
end
|
||||
|
||||
if Rails.env.development?
|
||||
def debug_helper
|
||||
source "conditionalHelper" # Test helper inside conditional block
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
def rescue_helper
|
||||
source "rescueHelper" # Test helper inside begin block
|
||||
end
|
||||
rescue
|
||||
# error handling
|
||||
end
|
||||
|
||||
# Helper inside a case statement
|
||||
case ENV['RACK_ENV']
|
||||
when 'test'
|
||||
def test_helper
|
||||
source "caseHelper" # Test helper inside case block
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Headers and cookies blocks for DSL testing
|
||||
headers do
|
||||
requires :Authorization, type: String
|
||||
end
|
||||
|
||||
cookies do
|
||||
requires :session_id, type: String
|
||||
end
|
||||
|
||||
get '/comprehensive_test/:user_id' do
|
||||
# Test all Grape input sources
|
||||
user_id = params[:user_id] # params taint source
|
||||
route_id = route_param(:user_id) # route_param taint source
|
||||
auth = headers[:Authorization] # headers taint source
|
||||
session = cookies[:session_id] # cookies taint source
|
||||
body_data = request.body.read # request taint source
|
||||
|
||||
# Test sinks for all sources
|
||||
sink user_id # $ hasTaintFlow
|
||||
sink route_id # $ hasTaintFlow
|
||||
sink auth # $ hasTaintFlow
|
||||
sink session # $ hasTaintFlow
|
||||
# Note: request.body.read may not be detected by this flow test config
|
||||
end
|
||||
|
||||
get '/helper_test/:user_id' do
|
||||
# Test helper method parameter passing dataflow
|
||||
user_id = params[:user_id]
|
||||
result = vulnerable_helper(user_id)
|
||||
sink result # $ hasValueFlow=paramHelper
|
||||
end
|
||||
|
||||
post '/users' do
|
||||
# Test helper method return dataflow
|
||||
user_data = user_params
|
||||
simple_result = simple_helper
|
||||
sink user_data # $ hasTaintFlow
|
||||
sink simple_result # $ hasValueFlow=simpleHelper
|
||||
end
|
||||
|
||||
# Test route_param block pattern
|
||||
route_param :id do
|
||||
get do
|
||||
# params[:id] should be user input from the path
|
||||
user_id = params[:id]
|
||||
sink user_id # $ hasTaintFlow
|
||||
end
|
||||
end
|
||||
|
||||
post '/users' do
|
||||
user_data = user_params
|
||||
sink user_data # $ hasTaintFlow
|
||||
end
|
||||
|
||||
# Test nested helper methods
|
||||
get '/nested_test/:token' do
|
||||
# Test nested module helper
|
||||
auth_result = authenticate_user
|
||||
sink auth_result # $ hasValueFlow=nestedModuleHelper
|
||||
|
||||
# Test nested module helper with parameters
|
||||
perm_result = check_permissions("admin")
|
||||
sink perm_result # $ hasValueFlow=nestedPermissionHelper
|
||||
|
||||
# Test nested class helper
|
||||
validation_result = ValidationHelpers.validate_email("test@example.com")
|
||||
sink validation_result # $ hasValueFlow=nestedClassHelper
|
||||
|
||||
# Test conditional helper (if it exists)
|
||||
if respond_to?(:debug_helper)
|
||||
debug_result = debug_helper
|
||||
sink debug_result # $ hasValueFlow=conditionalHelper
|
||||
end
|
||||
|
||||
# Test rescue helper
|
||||
rescue_result = rescue_helper
|
||||
sink rescue_result # $ hasValueFlow=rescueHelper
|
||||
|
||||
# Test case helper (if it exists)
|
||||
if respond_to?(:test_helper)
|
||||
case_result = test_helper
|
||||
sink case_result # $ hasValueFlow=caseHelper
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user