This commit is contained in:
Alex Ford
2021-10-05 11:50:16 +01:00
parent 6065e29aba
commit 1929a95e89
3 changed files with 37 additions and 31 deletions

View File

@@ -548,7 +548,9 @@ module XmlParserCall {
class OrmInstantiation extends DataFlow::Node instanceof OrmInstantiation::Range {
/** Holds if a call to `methodName` on this instance may return a field of this ORM object. */
bindingset[methodName]
predicate methodCallMayAccessField(string methodName) { super.methodCallMayAccessField(methodName) }
predicate methodCallMayAccessField(string methodName) {
super.methodCallMayAccessField(methodName)
}
}
/** Provides a class for modeling new ORM object instantiation APIs. */

View File

@@ -65,19 +65,23 @@ class ActiveRecordModelClass extends ClassDeclaration {
/**
* Gets methods defined in this class that may access a field from the database.
*/
*/
Method methodMayAccessField() {
result = this.getAMethod() and
// There is a value that can be returned by this method which may include field data
exists(DataFlow::Node returned, ActiveRecordInstanceMethodCall cNode, MethodCall c |
exprNodeReturnedFrom(returned, result) and cNode.flowsTo(returned) and c = cNode.asExpr().getExpr() |
exprNodeReturnedFrom(returned, result) and
cNode.flowsTo(returned) and
c = cNode.asExpr().getExpr()
|
// The referenced method is not built-in, and...
not isBuiltInMethodForActiveRecordModelInstance(c.getMethodName()) and (
not isBuiltInMethodForActiveRecordModelInstance(c.getMethodName()) and
(
// TODO: this would be more accurate if we also checked methods defined in
// super classes and mixins
// ...There is no matching method definition in the class, or...
not exists(cNode.getInstance().getClass().getMethod(c.getMethodName())) or
not exists(cNode.getInstance().getClass().getMethod(c.getMethodName()))
or
// ...the called method can access a field
c.getATarget() = cNode.getInstance().getClass().methodMayAccessField()
)
@@ -221,18 +225,20 @@ private string constantQualifiedName(ConstantWriteAccess w) {
/**
* A node that may evaluate to one or more `ActiveRecordModelClass` instances.
*/
abstract class ActiveRecordModelInstantiation extends OrmInstantiation::Range, DataFlow::LocalSourceNode {
abstract class ActiveRecordModelInstantiation extends OrmInstantiation::Range,
DataFlow::LocalSourceNode {
abstract ActiveRecordModelClass getClass();
bindingset[methodName]
override predicate methodCallMayAccessField(string methodName) {
// The method is not a built-in, and...
not isBuiltInMethodForActiveRecordModelInstance(methodName) and (
not isBuiltInMethodForActiveRecordModelInstance(methodName) and
(
// ...There is no matching method definition in the class, or...
not exists(this.getClass().getMethod(methodName)) or
not exists(this.getClass().getMethod(methodName))
or
// ...the called method can access a field.
exists(Method m |
m = this.getClass().methodMayAccessField() |
exists(Method m | m = this.getClass().methodMayAccessField() |
// We rely on matching by name here as the call graph might not have
m.getName() = methodName
)
@@ -317,6 +323,8 @@ private class ActiveRecordInstance extends DataFlow::Node {
// A call whose receiver may be an active record model object
private class ActiveRecordInstanceMethodCall extends DataFlow::CallNode {
private ActiveRecordInstance instance;
ActiveRecordInstanceMethodCall() { this.getReceiver() = instance }
ActiveRecordInstance getInstance() { result = instance }
}
}

View File

@@ -67,41 +67,37 @@ private predicate isPrivateKernelMethod(string method) {
string basicObjectInstanceMethodName() {
result in [
"equal?", "instance_eval", "instance_exec", "method_missing", "singleton_method_added",
"singleton_method_removed", "singleton_method_undefined"
]
"equal?", "instance_eval", "instance_exec", "method_missing", "singleton_method_added",
"singleton_method_removed", "singleton_method_undefined"
]
}
/**
* Instance methods on `BasicObject`, which are available to all classes.
*/
class BasicObjectInstanceMethodCall extends UnknownMethodCall {
BasicObjectInstanceMethodCall() {
this.getMethodName() = basicObjectInstanceMethodName()
}
BasicObjectInstanceMethodCall() { this.getMethodName() = basicObjectInstanceMethodName() }
}
string objectInstanceMethodName() {
result in [
"!~", "<=>", "===", "=~", "callable_methods", "define_singleton_method", "display",
"do_until", "do_while", "dup", "enum_for", "eql?", "extend", "f", "freeze", "h", "hash",
"inspect", "instance_of?", "instance_variable_defined?", "instance_variable_get",
"instance_variable_set", "instance_variables", "is_a?", "itself", "kind_of?",
"matching_methods", "method", "method_missing", "methods", "nil?", "object_id",
"private_methods", "protected_methods", "public_method", "public_methods", "public_send",
"remove_instance_variable", "respond_to?", "respond_to_missing?", "send",
"shortest_abbreviation", "singleton_class", "singleton_method", "singleton_methods",
"taint", "tainted?", "to_enum", "to_s", "trust", "untaint", "untrust", "untrusted?"
]
"!~", "<=>", "===", "=~", "callable_methods", "define_singleton_method", "display",
"do_until", "do_while", "dup", "enum_for", "eql?", "extend", "f", "freeze", "h", "hash",
"inspect", "instance_of?", "instance_variable_defined?", "instance_variable_get",
"instance_variable_set", "instance_variables", "is_a?", "itself", "kind_of?",
"matching_methods", "method", "method_missing", "methods", "nil?", "object_id",
"private_methods", "protected_methods", "public_method", "public_methods", "public_send",
"remove_instance_variable", "respond_to?", "respond_to_missing?", "send",
"shortest_abbreviation", "singleton_class", "singleton_method", "singleton_methods", "taint",
"tainted?", "to_enum", "to_s", "trust", "untaint", "untrust", "untrusted?"
]
}
/**
* Instance methods on `Object`, which are available to all classes except `BasicObject`.
*/
class ObjectInstanceMethodCall extends UnknownMethodCall {
ObjectInstanceMethodCall() {
this.getMethodName() = objectInstanceMethodName()
}
ObjectInstanceMethodCall() { this.getMethodName() = objectInstanceMethodName() }
}
/**