mirror of
https://github.com/github/codeql.git
synced 2026-04-22 15:25:18 +02:00
Model some SQL fragment sinks in ActiveRecord model classes
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
* provide concrete subclasses.
|
||||
*/
|
||||
|
||||
import ruby
|
||||
private import codeql_ruby.DataFlow
|
||||
|
||||
/**
|
||||
* A data-flow node that executes SQL statements.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// TODO: calls to methods where the receiver extends ActiveRecord::Base, directly or not
|
||||
import ruby
|
||||
private import codeql_ruby.AST
|
||||
private import codeql_ruby.Concepts
|
||||
private import codeql_ruby.controlflow.CfgNodes
|
||||
private import codeql_ruby.DataFlow
|
||||
private import codeql_ruby.ast.internal.Module
|
||||
|
||||
private class ActiveRecordBaseAccess extends ConstantReadAccess {
|
||||
@@ -31,3 +32,104 @@ class ActiveRecordModelClass extends ClassDeclaration {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: methods are class methods rather than instance?
|
||||
// A parameter that may represent a credential value
|
||||
/*
|
||||
* private DataFlow::LocalSourceNode activeRecordModelAccess(TypeTracker t) {
|
||||
* t.start() and
|
||||
* exists(AssignExpr ae, Ssa::WriteDefinition def, ActiveRecordModelClass cls |
|
||||
* result.asExpr().getExpr() = def.getWriteAccess() and
|
||||
* result.asExpr().getExpr() = ae.getLeftOperand() and
|
||||
* resolveScopeExpr(ae.getRightOperand()) = cls.getModule()
|
||||
* )
|
||||
* or
|
||||
* exists(TypeTracker t2 | result = activeRecordModelAccess(t2).track(t2, t))
|
||||
* }
|
||||
*
|
||||
* private DataFlow::Node activeRecordModelAccess() {
|
||||
* activeRecordModelAccess(TypeTracker::end()).flowsTo(result)
|
||||
* }
|
||||
*
|
||||
* class ActiveRecordNode extends DataFlow::Node {
|
||||
* ActiveRecordNode() {
|
||||
* this = activeRecordModelAccess()
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
/*
|
||||
* class ActiveRecordModelReadAccess extends VariableReadAccess {
|
||||
* ActiveRecordModelReadAccess() {
|
||||
*
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
// A class method call whose receiver is an ActiveRecord model class
|
||||
class ActiveRecordModelClassMethodCall extends MethodCall {
|
||||
// The model class that receives this call
|
||||
private ActiveRecordModelClass recvCls;
|
||||
|
||||
ActiveRecordModelClassMethodCall() { recvCls.getModule() = resolveScopeExpr(this.getReceiver()) }
|
||||
|
||||
// TODO: handle some cases of non-constant receiver expressions
|
||||
ActiveRecordModelClass getResolvedReceiverScope() { result = recvCls }
|
||||
}
|
||||
|
||||
class SqlExecutingMethodCall extends ActiveRecordModelClassMethodCall {
|
||||
// The name of the method invoked
|
||||
private string methodName;
|
||||
// The zero-indexed position of the SQL fragment sink argument
|
||||
private int sqlFragmentArgumentIndex;
|
||||
|
||||
// TODO: determine when the argument may be a string, rather than a key-value pair
|
||||
// TODO: `find` with `lock:` option also takes an SQL fragment
|
||||
SqlExecutingMethodCall() {
|
||||
methodName = this.getMethodName() and
|
||||
(
|
||||
methodName = "calculate" and sqlFragmentArgumentIndex = 1
|
||||
or
|
||||
sqlFragmentArgumentIndex = 0 and
|
||||
(
|
||||
methodName = "delete_all"
|
||||
or
|
||||
methodName = "destroy_all"
|
||||
or
|
||||
methodName = "exists?"
|
||||
or
|
||||
methodName = "find_by"
|
||||
or
|
||||
methodName = "from"
|
||||
or
|
||||
methodName = "group"
|
||||
or
|
||||
methodName = "having"
|
||||
or
|
||||
methodName = "joins"
|
||||
or
|
||||
methodName = "lock"
|
||||
or
|
||||
methodName = "not"
|
||||
or
|
||||
methodName = "order"
|
||||
or
|
||||
methodName = "pluck"
|
||||
or
|
||||
methodName = "where"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Expr getSqlFragmentSinkArgument() { result = this.getArgument(sqlFragmentArgumentIndex) }
|
||||
}
|
||||
|
||||
class ActiveRecordSqlExecutionRange extends SqlExecution::Range {
|
||||
ExprCfgNode sql;
|
||||
|
||||
ActiveRecordSqlExecutionRange() {
|
||||
exists(SqlExecutingMethodCall mc | this.asExpr().getNode() = mc.getSqlFragmentSinkArgument())
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result.asExpr() = sql }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user