зеркало из https://github.com/github/codeql.git
Ruby: minor overhaul of ActiveRecord model
Old version had scalability issues when adding taking more interprocedural flow and inheritance into account.
This commit is contained in:
Родитель
bb3b973b32
Коммит
8bc4193ce0
|
@ -30,10 +30,8 @@ private predicate isBuiltInMethodForActiveRecordModelInstance(string methodName)
|
|||
methodName = objectInstanceMethodName()
|
||||
}
|
||||
|
||||
private API::Node activeRecordClassApiNode() {
|
||||
private API::Node activeRecordBaseClass() {
|
||||
result =
|
||||
// class Foo < ActiveRecord::Base
|
||||
// class Bar < Foo
|
||||
[
|
||||
API::getTopLevelMember("ActiveRecord").getMember("Base"),
|
||||
// In Rails applications `ApplicationRecord` typically extends `ActiveRecord::Base`, but we
|
||||
|
@ -42,6 +40,46 @@ private API::Node activeRecordClassApiNode() {
|
|||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an object with methods from the ActiveRecord query interface.
|
||||
*/
|
||||
private API::Node activeRecordQueryBuilder() {
|
||||
result = activeRecordBaseClass()
|
||||
or
|
||||
result = activeRecordBaseClass().getInstance()
|
||||
or
|
||||
// Assume any method call might return an ActiveRecord::Relation
|
||||
// These are dynamically generated
|
||||
result = activeRecordQueryBuilderMethodAccess(_).getReturn()
|
||||
}
|
||||
|
||||
/** Gets a call targeting the ActiveRecord query interface. */
|
||||
private API::MethodAccessNode activeRecordQueryBuilderMethodAccess(string name) {
|
||||
result = activeRecordQueryBuilder().getMethod(name) and
|
||||
// Due to the heuristic tracking of query builder objects, add a restriction for methods with a known call target
|
||||
not isUnlikelyExternalCall(result)
|
||||
}
|
||||
|
||||
/** Gets a call targeting the ActiveRecord query interface. */
|
||||
private DataFlow::CallNode activeRecordQueryBuilderCall(string name) {
|
||||
result = activeRecordQueryBuilderMethodAccess(name).asCall()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` is unlikely to call into an external library, since it has a possible
|
||||
* call target in its enclosing module.
|
||||
*/
|
||||
private predicate isUnlikelyExternalCall(API::MethodAccessNode node) {
|
||||
exists(DataFlow::ModuleNode mod, DataFlow::CallNode call | call = node.asCall() |
|
||||
call.getATarget() = [mod.getAnOwnSingletonMethod(), mod.getAnOwnInstanceMethod()] and
|
||||
call.getEnclosingMethod() = [mod.getAnOwnSingletonMethod(), mod.getAnOwnInstanceMethod()]
|
||||
)
|
||||
}
|
||||
|
||||
private API::Node activeRecordConnectionInstance() {
|
||||
result = activeRecordBaseClass().getReturn("connection")
|
||||
}
|
||||
|
||||
/**
|
||||
* A `ClassDeclaration` for a class that inherits from `ActiveRecord::Base`. For example,
|
||||
*
|
||||
|
@ -55,20 +93,19 @@ private API::Node activeRecordClassApiNode() {
|
|||
* ```
|
||||
*/
|
||||
class ActiveRecordModelClass extends ClassDeclaration {
|
||||
private DataFlow::ClassNode cls;
|
||||
|
||||
ActiveRecordModelClass() {
|
||||
this.getSuperclassExpr() =
|
||||
activeRecordClassApiNode().getASubclass().getAValueReachableFromSource().asExpr().getExpr()
|
||||
cls = activeRecordBaseClass().getADescendentModule() and this = cls.getADeclaration()
|
||||
}
|
||||
|
||||
// Gets the class declaration for this class and all of its super classes
|
||||
private ModuleBase getAllClassDeclarations() {
|
||||
result = this.getModule().getSuperClass*().getADeclaration()
|
||||
}
|
||||
private ModuleBase getAllClassDeclarations() { result = cls.getAnAncestor().getADeclaration() }
|
||||
|
||||
/**
|
||||
* Gets methods defined in this class that may access a field from the database.
|
||||
*/
|
||||
Method getAPotentialFieldAccessMethod() {
|
||||
deprecated Method getAPotentialFieldAccessMethod() {
|
||||
// It's a method on this class or one of its super classes
|
||||
result = this.getAllClassDeclarations().getAMethod() and
|
||||
// There is a value that can be returned by this method which may include field data
|
||||
|
@ -90,58 +127,84 @@ class ActiveRecordModelClass extends ClassDeclaration {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the class as a `DataFlow::ClasNode`. */
|
||||
DataFlow::ClassNode getClassNode() { result = cls }
|
||||
}
|
||||
|
||||
/** A class method call whose receiver is an `ActiveRecordModelClass`. */
|
||||
class ActiveRecordModelClassMethodCall extends MethodCall {
|
||||
private ActiveRecordModelClass recvCls;
|
||||
/**
|
||||
* Gets a potential reference to an ActiveRecord class object.
|
||||
*/
|
||||
deprecated private API::Node getAnActiveRecordModelClassRef() {
|
||||
result = any(ActiveRecordModelClass cls).getClassNode().trackModule()
|
||||
or
|
||||
// For methods with an unknown call target, assume this might be a database field, thus returning another ActiveRecord object.
|
||||
// In this case we do not know which class it belongs to, which is why this predicate can't associate the reference with a specific class.
|
||||
result = getAnUnknownActiveRecordModelClassCall().getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call performed on an ActiveRecord class object, without a known call target in the codebase.
|
||||
*/
|
||||
deprecated private API::MethodAccessNode getAnUnknownActiveRecordModelClassCall() {
|
||||
result = getAnActiveRecordModelClassRef().getMethod(_) and
|
||||
result.asCall().asExpr().getExpr() instanceof UnknownMethodCall
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `ActiveRecordModelClass.getClassNode().trackModule().getMethod()` instead.
|
||||
*
|
||||
* A class method call whose receiver is an `ActiveRecordModelClass`.
|
||||
*/
|
||||
deprecated class ActiveRecordModelClassMethodCall extends MethodCall {
|
||||
ActiveRecordModelClassMethodCall() {
|
||||
// e.g. Foo.where(...)
|
||||
recvCls.getModule() = this.getReceiver().(ConstantReadAccess).getModule()
|
||||
or
|
||||
// e.g. Foo.joins(:bars).where(...)
|
||||
recvCls = this.getReceiver().(ActiveRecordModelClassMethodCall).getReceiverClass()
|
||||
or
|
||||
// e.g. self.where(...) within an ActiveRecordModelClass
|
||||
this.getReceiver() instanceof SelfVariableAccess and
|
||||
this.getEnclosingModule() = recvCls
|
||||
this = getAnUnknownActiveRecordModelClassCall().asCall().asExpr().getExpr()
|
||||
}
|
||||
|
||||
/** The `ActiveRecordModelClass` of the receiver of this method. */
|
||||
ActiveRecordModelClass getReceiverClass() { result = recvCls }
|
||||
/** Gets the `ActiveRecordModelClass` of the receiver of this method, if it can be determined. */
|
||||
ActiveRecordModelClass getReceiverClass() {
|
||||
this = result.getClassNode().trackModule().getMethod(_).asCall().asExpr().getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
private Expr sqlFragmentArgument(MethodCall call) {
|
||||
exists(string methodName |
|
||||
methodName = call.getMethodName() and
|
||||
(
|
||||
methodName =
|
||||
[
|
||||
"delete_all", "delete_by", "destroy_all", "destroy_by", "exists?", "find_by", "find_by!",
|
||||
"find_or_create_by", "find_or_create_by!", "find_or_initialize_by", "find_by_sql", "from",
|
||||
"group", "having", "joins", "lock", "not", "order", "reorder", "pluck", "where",
|
||||
"rewhere", "select", "reselect", "update_all"
|
||||
] and
|
||||
result = call.getArgument(0)
|
||||
or
|
||||
methodName = "calculate" and result = call.getArgument(1)
|
||||
or
|
||||
methodName in ["average", "count", "maximum", "minimum", "sum", "count_by_sql"] and
|
||||
result = call.getArgument(0)
|
||||
or
|
||||
// This format was supported until Rails 2.3.8
|
||||
methodName = ["all", "find", "first", "last"] and
|
||||
result = call.getKeywordArgument("conditions")
|
||||
or
|
||||
methodName = "reload" and
|
||||
result = call.getKeywordArgument("lock")
|
||||
or
|
||||
// Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to
|
||||
// SQLi if user supplied input is passed in as an argument.
|
||||
methodName = "annotate" and
|
||||
result = call.getArgument(_)
|
||||
)
|
||||
private predicate sqlFragmentArgumentInner(DataFlow::CallNode call, DataFlow::Node sink) {
|
||||
call =
|
||||
activeRecordQueryBuilderCall([
|
||||
"delete_all", "delete_by", "destroy_all", "destroy_by", "exists?", "find_by", "find_by!",
|
||||
"find_or_create_by", "find_or_create_by!", "find_or_initialize_by", "find_by_sql", "from",
|
||||
"group", "having", "joins", "lock", "not", "order", "reorder", "pluck", "where", "rewhere",
|
||||
"select", "reselect", "update_all"
|
||||
]) and
|
||||
sink = call.getArgument(0)
|
||||
or
|
||||
call = activeRecordQueryBuilderCall("calculate") and
|
||||
sink = call.getArgument(1)
|
||||
or
|
||||
call =
|
||||
activeRecordQueryBuilderCall(["average", "count", "maximum", "minimum", "sum", "count_by_sql"]) and
|
||||
sink = call.getArgument(0)
|
||||
or
|
||||
// This format was supported until Rails 2.3.8
|
||||
call = activeRecordQueryBuilderCall(["all", "find", "first", "last"]) and
|
||||
sink = call.getKeywordArgument("conditions")
|
||||
or
|
||||
call = activeRecordQueryBuilderCall("reload") and
|
||||
sink = call.getKeywordArgument("lock")
|
||||
or
|
||||
// Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to
|
||||
// SQLi if user supplied input is passed in as an argument.
|
||||
call = activeRecordQueryBuilderCall("annotate") and
|
||||
sink = call.getArgument(_)
|
||||
or
|
||||
call = activeRecordConnectionInstance().getAMethodCall("execute") and
|
||||
sink = call.getArgument(0)
|
||||
}
|
||||
|
||||
private predicate sqlFragmentArgument(DataFlow::CallNode call, DataFlow::Node sink) {
|
||||
exists(DataFlow::Node arg |
|
||||
sqlFragmentArgumentInner(call, arg) and
|
||||
sink = [arg, arg.(DataFlow::ArrayLiteralNode).getElement(0)] and
|
||||
unsafeSqlExpr(sink.asExpr().getExpr())
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -162,6 +225,8 @@ private predicate unsafeSqlExpr(Expr sqlFragmentExpr) {
|
|||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use the `SqlExecution` concept or `ActiveRecordSqlExecutionRange`.
|
||||
*
|
||||
* A method call that may result in executing unintended user-controlled SQL
|
||||
* queries if the `getSqlFragmentSinkArgument()` expression is tainted by
|
||||
* unsanitized user-controlled input. For example, supposing that `User` is an
|
||||
|
@ -175,55 +240,32 @@ private predicate unsafeSqlExpr(Expr sqlFragmentExpr) {
|
|||
* as `"') OR 1=1 --"` could result in the application looking up all users
|
||||
* rather than just one with a matching name.
|
||||
*/
|
||||
class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMethodCall {
|
||||
// The SQL fragment argument itself
|
||||
private Expr sqlFragmentExpr;
|
||||
deprecated class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMethodCall {
|
||||
private DataFlow::CallNode call;
|
||||
|
||||
PotentiallyUnsafeSqlExecutingMethodCall() {
|
||||
exists(Expr arg |
|
||||
arg = sqlFragmentArgument(this) and
|
||||
unsafeSqlExpr(sqlFragmentExpr) and
|
||||
(
|
||||
sqlFragmentExpr = arg
|
||||
or
|
||||
sqlFragmentExpr = arg.(ArrayLiteral).getElement(0)
|
||||
) and
|
||||
// Check that method has not been overridden
|
||||
not exists(SingletonMethod m |
|
||||
m.getName() = this.getMethodName() and
|
||||
m.getOuterScope() = this.getReceiverClass()
|
||||
)
|
||||
)
|
||||
call.asExpr().getExpr() = this and sqlFragmentArgument(call, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL fragment argument of this method call.
|
||||
*/
|
||||
Expr getSqlFragmentSinkArgument() { result = sqlFragmentExpr }
|
||||
Expr getSqlFragmentSinkArgument() {
|
||||
exists(DataFlow::Node sink |
|
||||
sqlFragmentArgument(call, sink) and result = sink.asExpr().getExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `SqlExecution::Range` for an argument to a
|
||||
* `PotentiallyUnsafeSqlExecutingMethodCall` that may be vulnerable to being
|
||||
* controlled by user input.
|
||||
* A SQL execution arising from a call to the ActiveRecord library.
|
||||
*/
|
||||
class ActiveRecordSqlExecutionRange extends SqlExecution::Range {
|
||||
ActiveRecordSqlExecutionRange() {
|
||||
exists(PotentiallyUnsafeSqlExecutingMethodCall mc |
|
||||
this.asExpr().getNode() = mc.getSqlFragmentSinkArgument()
|
||||
)
|
||||
or
|
||||
this = activeRecordConnectionInstance().getAMethodCall("execute").getArgument(0) and
|
||||
unsafeSqlExpr(this.asExpr().getExpr())
|
||||
}
|
||||
ActiveRecordSqlExecutionRange() { sqlFragmentArgument(_, this) }
|
||||
|
||||
override DataFlow::Node getSql() { result = this }
|
||||
}
|
||||
|
||||
private API::Node activeRecordConnectionInstance() {
|
||||
result = activeRecordClassApiNode().getReturn("connection")
|
||||
}
|
||||
|
||||
// TODO: model `ActiveRecord` sanitizers
|
||||
// https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html
|
||||
/**
|
||||
|
@ -241,15 +283,8 @@ abstract class ActiveRecordModelInstantiation extends OrmInstantiation::Range,
|
|||
override predicate methodCallMayAccessField(string methodName) {
|
||||
// The method is not a built-in, and...
|
||||
not isBuiltInMethodForActiveRecordModelInstance(methodName) and
|
||||
(
|
||||
// ...There is no matching method definition in the class, or...
|
||||
not exists(this.getClass().getMethod(methodName))
|
||||
or
|
||||
// ...the called method can access a field.
|
||||
exists(Method m | m = this.getClass().getAPotentialFieldAccessMethod() |
|
||||
m.getName() = methodName
|
||||
)
|
||||
)
|
||||
// ...There is no matching method definition in the class
|
||||
not exists(this.getClass().getMethod(methodName))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,21 +352,10 @@ private class ActiveRecordModelFinderCall extends ActiveRecordModelInstantiation
|
|||
}
|
||||
|
||||
// A `self` reference that may resolve to an active record model object
|
||||
private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation,
|
||||
DataFlow::SelfParameterNode
|
||||
{
|
||||
private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation {
|
||||
private ActiveRecordModelClass cls;
|
||||
|
||||
ActiveRecordModelClassSelfReference() {
|
||||
exists(MethodBase m |
|
||||
m = this.getCallable() and
|
||||
m.getEnclosingModule() = cls and
|
||||
m = cls.getAMethod()
|
||||
) and
|
||||
// In a singleton method, `self` refers to the class itself rather than an
|
||||
// instance of that class
|
||||
not this.getSelfVariable().getDeclaringScope() instanceof SingletonMethod
|
||||
}
|
||||
ActiveRecordModelClassSelfReference() { this = cls.getClassNode().getAnOwnInstanceSelf() }
|
||||
|
||||
final override ActiveRecordModelClass getClass() { result = cls }
|
||||
}
|
||||
|
@ -342,7 +366,7 @@ private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInsta
|
|||
class ActiveRecordInstance extends DataFlow::Node {
|
||||
private ActiveRecordModelInstantiation instantiation;
|
||||
|
||||
ActiveRecordInstance() { this = instantiation or instantiation.flowsTo(this) }
|
||||
ActiveRecordInstance() { this = instantiation.track().getAValueReachableFromSource() }
|
||||
|
||||
/** Gets the `ActiveRecordModelClass` that this is an instance of. */
|
||||
ActiveRecordModelClass getClass() { result = instantiation.getClass() }
|
||||
|
@ -380,12 +404,12 @@ private module Persistence {
|
|||
/** A call to e.g. `User.create(name: "foo")` */
|
||||
private class CreateLikeCall extends DataFlow::CallNode, PersistentWriteAccess::Range {
|
||||
CreateLikeCall() {
|
||||
exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and
|
||||
this.getMethodName() =
|
||||
[
|
||||
"create", "create!", "create_or_find_by", "create_or_find_by!", "find_or_create_by",
|
||||
"find_or_create_by!", "insert", "insert!"
|
||||
]
|
||||
this =
|
||||
activeRecordBaseClass()
|
||||
.getAMethodCall([
|
||||
"create", "create!", "create_or_find_by", "create_or_find_by!", "find_or_create_by",
|
||||
"find_or_create_by!", "insert", "insert!"
|
||||
])
|
||||
}
|
||||
|
||||
override DataFlow::Node getValue() {
|
||||
|
@ -402,8 +426,7 @@ private module Persistence {
|
|||
/** A call to e.g. `User.update(1, name: "foo")` */
|
||||
private class UpdateLikeClassMethodCall extends DataFlow::CallNode, PersistentWriteAccess::Range {
|
||||
UpdateLikeClassMethodCall() {
|
||||
exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and
|
||||
this.getMethodName() = ["update", "update!", "upsert"]
|
||||
this = activeRecordBaseClass().getAMethodCall(["update", "update!", "upsert"])
|
||||
}
|
||||
|
||||
override DataFlow::Node getValue() {
|
||||
|
@ -448,10 +471,7 @@ private module Persistence {
|
|||
* ```
|
||||
*/
|
||||
private class TouchAllCall extends DataFlow::CallNode, PersistentWriteAccess::Range {
|
||||
TouchAllCall() {
|
||||
exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and
|
||||
this.getMethodName() = "touch_all"
|
||||
}
|
||||
TouchAllCall() { this = activeRecordQueryBuilderCall("touch_all") }
|
||||
|
||||
override DataFlow::Node getValue() { result = this.getKeywordArgument("time") }
|
||||
}
|
||||
|
@ -461,8 +481,7 @@ private module Persistence {
|
|||
private ExprNodes::ArrayLiteralCfgNode arr;
|
||||
|
||||
InsertAllLikeCall() {
|
||||
exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and
|
||||
this.getMethodName() = ["insert_all", "insert_all!", "upsert_all"] and
|
||||
this = activeRecordBaseClass().getAMethodCall(["insert_all", "insert_all!", "upsert_all"]) and
|
||||
arr = this.getArgument(0).asExpr()
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ activeRecordInstances
|
|||
| ActiveRecord.rb:9:5:9:68 | call to find |
|
||||
| ActiveRecord.rb:13:5:13:40 | call to find_by |
|
||||
| ActiveRecord.rb:13:5:13:46 | call to users |
|
||||
| ActiveRecord.rb:35:5:35:51 | call to authenticate |
|
||||
| ActiveRecord.rb:36:5:36:30 | call to find_by_name |
|
||||
| ActiveRecord.rb:55:5:57:7 | if ... |
|
||||
| ActiveRecord.rb:55:43:56:40 | then ... |
|
||||
|
@ -107,12 +108,14 @@ activeRecordSqlExecutionRanges
|
|||
| ActiveRecord.rb:19:16:19:24 | condition |
|
||||
| ActiveRecord.rb:28:30:28:44 | ...[...] |
|
||||
| ActiveRecord.rb:29:20:29:42 | "id = '#{...}'" |
|
||||
| ActiveRecord.rb:30:21:30:45 | call to [] |
|
||||
| ActiveRecord.rb:30:22:30:44 | "id = '#{...}'" |
|
||||
| ActiveRecord.rb:31:16:31:21 | <<-SQL |
|
||||
| ActiveRecord.rb:34:20:34:47 | "user.id = '#{...}'" |
|
||||
| ActiveRecord.rb:46:20:46:32 | ... + ... |
|
||||
| ActiveRecord.rb:52:16:52:28 | "name #{...}" |
|
||||
| ActiveRecord.rb:56:20:56:39 | "username = #{...}" |
|
||||
| ActiveRecord.rb:68:21:68:44 | ...[...] |
|
||||
| ActiveRecord.rb:106:27:106:76 | "this is an unsafe annotation:..." |
|
||||
activeRecordModelClassMethodCalls
|
||||
| ActiveRecord.rb:2:3:2:17 | call to has_many |
|
||||
|
@ -127,7 +130,6 @@ activeRecordModelClassMethodCalls
|
|||
| ActiveRecord.rb:31:5:31:35 | call to where |
|
||||
| ActiveRecord.rb:34:5:34:14 | call to where |
|
||||
| ActiveRecord.rb:34:5:34:48 | call to not |
|
||||
| ActiveRecord.rb:35:5:35:51 | call to authenticate |
|
||||
| ActiveRecord.rb:36:5:36:30 | call to find_by_name |
|
||||
| ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method |
|
||||
| ActiveRecord.rb:46:5:46:33 | call to delete_by |
|
||||
|
@ -135,7 +137,6 @@ activeRecordModelClassMethodCalls
|
|||
| ActiveRecord.rb:56:7:56:40 | call to find_by |
|
||||
| ActiveRecord.rb:60:5:60:33 | call to find_by |
|
||||
| ActiveRecord.rb:62:5:62:34 | call to find |
|
||||
| ActiveRecord.rb:68:5:68:45 | call to delete_by |
|
||||
| ActiveRecord.rb:72:5:72:24 | call to create |
|
||||
| ActiveRecord.rb:76:5:76:66 | call to create |
|
||||
| ActiveRecord.rb:80:5:80:68 | call to create |
|
||||
|
@ -152,6 +153,96 @@ activeRecordModelClassMethodCalls
|
|||
| associations.rb:12:3:12:32 | call to has_and_belongs_to_many |
|
||||
| associations.rb:16:3:16:18 | call to belongs_to |
|
||||
| associations.rb:19:11:19:20 | call to new |
|
||||
| associations.rb:21:9:21:21 | call to posts |
|
||||
| associations.rb:21:9:21:28 | call to create |
|
||||
| associations.rb:23:12:23:25 | call to comments |
|
||||
| associations.rb:23:12:23:32 | call to create |
|
||||
| associations.rb:25:11:25:22 | call to author |
|
||||
| associations.rb:27:9:27:21 | call to posts |
|
||||
| associations.rb:27:9:27:28 | call to create |
|
||||
| associations.rb:29:1:29:13 | call to posts |
|
||||
| associations.rb:29:1:29:22 | ... << ... |
|
||||
| associations.rb:31:1:31:12 | call to author= |
|
||||
| associations.rb:35:1:35:14 | call to comments |
|
||||
| associations.rb:35:1:35:21 | call to create |
|
||||
| associations.rb:35:1:35:28 | call to create |
|
||||
| associations.rb:37:1:37:13 | call to posts |
|
||||
| associations.rb:37:1:37:20 | call to reload |
|
||||
| associations.rb:37:1:37:27 | call to create |
|
||||
| associations.rb:39:1:39:15 | call to build_tag |
|
||||
| associations.rb:40:1:40:15 | call to build_tag |
|
||||
| associations.rb:42:1:42:13 | call to posts |
|
||||
| associations.rb:42:1:42:25 | call to push |
|
||||
| associations.rb:43:1:43:13 | call to posts |
|
||||
| associations.rb:43:1:43:27 | call to concat |
|
||||
| associations.rb:44:1:44:13 | call to posts |
|
||||
| associations.rb:44:1:44:19 | call to build |
|
||||
| associations.rb:45:1:45:13 | call to posts |
|
||||
| associations.rb:45:1:45:20 | call to create |
|
||||
| associations.rb:46:1:46:13 | call to posts |
|
||||
| associations.rb:46:1:46:21 | call to create! |
|
||||
| associations.rb:47:1:47:13 | call to posts |
|
||||
| associations.rb:47:1:47:20 | call to delete |
|
||||
| associations.rb:48:1:48:13 | call to posts |
|
||||
| associations.rb:48:1:48:24 | call to delete_all |
|
||||
| associations.rb:49:1:49:13 | call to posts |
|
||||
| associations.rb:49:1:49:21 | call to destroy |
|
||||
| associations.rb:50:1:50:13 | call to posts |
|
||||
| associations.rb:50:1:50:25 | call to destroy_all |
|
||||
| associations.rb:51:1:51:13 | call to posts |
|
||||
| associations.rb:51:1:51:22 | call to distinct |
|
||||
| associations.rb:51:1:51:36 | call to find |
|
||||
| associations.rb:52:1:52:13 | call to posts |
|
||||
| associations.rb:52:1:52:19 | call to reset |
|
||||
| associations.rb:52:1:52:33 | call to find |
|
||||
| associations.rb:53:1:53:13 | call to posts |
|
||||
| associations.rb:53:1:53:20 | call to reload |
|
||||
| associations.rb:53:1:53:34 | call to find |
|
||||
activeRecordModelClassMethodCallsReplacement
|
||||
| ActiveRecord.rb:1:1:3:3 | UserGroup | ActiveRecord.rb:2:3:2:17 | call to has_many |
|
||||
| ActiveRecord.rb:1:1:3:3 | UserGroup | ActiveRecord.rb:13:5:13:40 | call to find_by |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:6:3:6:24 | call to belongs_to |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:9:5:9:68 | call to find |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:19:5:19:25 | call to destroy_by |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:28:5:28:45 | call to calculate |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:29:5:29:43 | call to delete_by |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:30:5:30:46 | call to destroy_by |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:31:5:31:35 | call to where |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:34:5:34:14 | call to where |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:35:5:35:51 | call to authenticate |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:36:5:36:30 | call to find_by_name |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:46:5:46:33 | call to delete_by |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:52:5:52:29 | call to order |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:56:7:56:40 | call to find_by |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:60:5:60:33 | call to find_by |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:62:5:62:34 | call to find |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:68:5:68:45 | call to delete_by |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:72:5:72:24 | call to create |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:76:5:76:66 | call to create |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:80:5:80:68 | call to create |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:84:5:84:16 | call to create |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:88:5:88:27 | call to update |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:92:5:92:69 | call to update |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:96:5:96:71 | call to update |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:102:13:102:54 | call to annotate |
|
||||
| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:106:13:106:77 | call to annotate |
|
||||
| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:19:5:19:25 | call to destroy_by |
|
||||
| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:68:5:68:45 | call to delete_by |
|
||||
| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:72:5:72:24 | call to create |
|
||||
| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:76:5:76:66 | call to create |
|
||||
| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:80:5:80:68 | call to create |
|
||||
| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:84:5:84:16 | call to create |
|
||||
| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:88:5:88:27 | call to update |
|
||||
| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:92:5:92:69 | call to update |
|
||||
| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:96:5:96:71 | call to update |
|
||||
| associations.rb:1:1:3:3 | Author | associations.rb:2:3:2:17 | call to has_many |
|
||||
| associations.rb:1:1:3:3 | Author | associations.rb:19:11:19:20 | call to new |
|
||||
| associations.rb:5:1:9:3 | Post | associations.rb:6:3:6:20 | call to belongs_to |
|
||||
| associations.rb:5:1:9:3 | Post | associations.rb:7:3:7:20 | call to has_many |
|
||||
| associations.rb:5:1:9:3 | Post | associations.rb:8:3:8:31 | call to has_and_belongs_to_many |
|
||||
| associations.rb:11:1:13:3 | Tag | associations.rb:12:3:12:32 | call to has_and_belongs_to_many |
|
||||
| associations.rb:15:1:17:3 | Comment | associations.rb:16:3:16:18 | call to belongs_to |
|
||||
potentiallyUnsafeSqlExecutingMethodCall
|
||||
| ActiveRecord.rb:9:5:9:68 | call to find |
|
||||
| ActiveRecord.rb:19:5:19:25 | call to destroy_by |
|
||||
|
|
|
@ -9,9 +9,19 @@ query predicate activeRecordInstances(ActiveRecordInstance i) { any() }
|
|||
|
||||
query predicate activeRecordSqlExecutionRanges(ActiveRecordSqlExecutionRange range) { any() }
|
||||
|
||||
query predicate activeRecordModelClassMethodCalls(ActiveRecordModelClassMethodCall call) { any() }
|
||||
deprecated query predicate activeRecordModelClassMethodCalls(ActiveRecordModelClassMethodCall call) {
|
||||
any()
|
||||
}
|
||||
|
||||
query predicate potentiallyUnsafeSqlExecutingMethodCall(PotentiallyUnsafeSqlExecutingMethodCall call) {
|
||||
query predicate activeRecordModelClassMethodCallsReplacement(
|
||||
ActiveRecordModelClass cls, DataFlow::CallNode call
|
||||
) {
|
||||
call = cls.getClassNode().trackModule().getAMethodCall(_)
|
||||
}
|
||||
|
||||
deprecated query predicate potentiallyUnsafeSqlExecutingMethodCall(
|
||||
PotentiallyUnsafeSqlExecutingMethodCall call
|
||||
) {
|
||||
any()
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче