Ruby: replace OrmWriteAccess with PersistentWriteAccess concept

This commit is contained in:
Alex Ford
2022-03-09 18:59:16 +00:00
parent 98dbe3aaf3
commit ee433637f8
10 changed files with 46 additions and 57 deletions

View File

@@ -626,33 +626,28 @@ module OrmInstantiation {
}
/**
* A data-flow node that may represent a write to the database in an ORM system.
* A data flow node that writes persistent data.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `OrmWriteAccess::Range` instead.
* extend `PersistentWriteAccess::Range` instead.
*/
class OrmWriteAccess extends DataFlow::Node instanceof OrmWriteAccess::Range {
/**
* Gets the name of a field that is assigned to `value` by this write.
*/
string getFieldNameAssignedTo(DataFlow::Node value) {
result = super.getFieldNameAssignedTo(value)
}
class PersistentWriteAccess extends DataFlow::Node instanceof PersistentWriteAccess::Range {
DataFlow::Node getValue() { result = super.getValue() }
}
/** Provides a class for modeling new ORM write access APIs. */
module OrmWriteAccess {
/** Provides a class for modeling new persistent write access APIs. */
module PersistentWriteAccess {
/**
* A data-flow node that may represent a write to the database in an ORM system.
* A data flow node that writes persistent data.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `OrmWriteAccess` instead.
* extend `PersistentWriteAccess` instead.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets the name of a field that is assigned to `value` by this write.
* Gets the data flow node corresponding to the written value.
*/
abstract string getFieldNameAssignedTo(DataFlow::Node value);
abstract DataFlow::Node getValue();
}
}

View File

@@ -323,7 +323,7 @@ private module Persistence {
* A call to a method that may modify or create a model object and write it to
* the database. Examples include `create`, `insert`, and `update`.
*/
abstract private class ModifyAndSaveCall extends DataFlow::CallNode, OrmWriteAccess::Range {
abstract private class ModifyAndSaveCall extends DataFlow::CallNode, PersistentWriteAccess::Range {
/**
* Holds if the given key-value pair is set on an object by this call.
*/
@@ -334,14 +334,11 @@ private module Persistence {
*/
abstract ActiveRecordModelClass getClass();
final override string getFieldNameAssignedTo(DataFlow::Node value) {
final override DataFlow::Node getValue() {
exists(ExprCfgNode keyExpr, ExprCfgNode valueExpr |
this.setsKeyValuePair(keyExpr, valueExpr)
|
keyExpr.getConstantValue().isStringOrSymbol(result) and
// avoid vacuous matches where the key is not a string or not a symbol
not result = "" and
value.asExpr() = valueExpr
result.asExpr() = valueExpr
)
}
}
@@ -480,24 +477,21 @@ private module Persistence {
/**
* An assignment like `user.name = "foo"`. Though this does not write to the
* database without a subsequent call to persist the object, it is considered
* as an `OrmWriteAccess` to avoid missing cases where the path to a
* as an `PersistentWriteAccess` to avoid missing cases where the path to a
* subsequent write is not clear.
*/
private class AssignAttribute extends DataFlow::Node, OrmWriteAccess::Range {
private DataFlow::CallNode setter;
private class AssignAttribute extends DataFlow::Node, PersistentWriteAccess::Range {
private ExprNodes::AssignExprCfgNode assignNode;
AssignAttribute() {
assignNode = this.asExpr() and
setter.getArgument(0) = this and
setter instanceof ActiveRecordInstanceMethodCall and
setter.asExpr().getExpr() instanceof SetterMethodCall
exists(DataFlow::CallNode setter |
assignNode = this.asExpr() and
setter.getArgument(0) = this and
setter instanceof ActiveRecordInstanceMethodCall and
setter.asExpr().getExpr() instanceof SetterMethodCall
)
}
override string getFieldNameAssignedTo(DataFlow::Node value) {
result + "=" = setter.getMethodName() and
// match RHS
assignNode.getRhs() = value.asExpr()
}
override DataFlow::Node getValue() { assignNode.getRhs() = result.asExpr() }
}
}