Python: Address review comments

- changes `getReceiver` to `getObject`
- fixes `calls` to avoid unwanted cross-talk
- adds some more documentation to highlight the above issue
This commit is contained in:
Taus
2021-06-21 14:57:19 +00:00
committed by GitHub
parent 359bc5eff9
commit 768cab3642
7 changed files with 62 additions and 12 deletions

View File

@@ -42,7 +42,7 @@ API::Node sslContextInstance() {
class WrapSocketCall extends ConnectionCreation, DataFlow::MethodCallNode {
WrapSocketCall() { this = sslContextInstance().getMember("wrap_socket").getACall() }
override DataFlow::Node getContext() { result = this.getReceiver() }
override DataFlow::Node getContext() { result = this.getObject() }
}
class OptionsAugOr extends ProtocolRestriction, DataFlow::CfgNode {

View File

@@ -70,7 +70,7 @@ private module Re {
CompiledRegex() {
exists(DataFlow::MethodCallNode patternCall |
patternCall = API::moduleImport("re").getMember("compile").getACall() and
patternCall.flowsTo(this.getReceiver()) and
patternCall.flowsTo(this.getObject()) and
this.getMethodName() instanceof RegexExecutionMethods and
regexNode = patternCall.getArg(0)
)

View File

@@ -184,23 +184,38 @@ class CallCfgNode extends CfgNode, LocalSourceNode {
* A data-flow node corresponding to a method call, that is `foo.bar(...)`.
*
* Also covers the case where the method lookup is done separately from the call itself, as in
* `temp = foo.bar; temp(...)`.
* `temp = foo.bar; temp(...)`. Note that this is only tracked through local scope.
*/
class MethodCallNode extends CallCfgNode {
AttrRead method_lookup;
MethodCallNode() { method_lookup = this.getFunction().getALocalSource() }
/** Gets the name of the method being invoked (the `bar` in `foo.bar(...)`, if it can be determined. */
/**
* Gets the name of the method being invoked (the `bar` in `foo.bar(...)`) if it can be determined.
*
* Note that this method may have multiple results if a single call node represents calls to
* multiple different objects and methods. If you want to link up objects and method names
* accurately, use the `calls` method instead.
*/
string getMethodName() { result = method_lookup.getAttributeName() }
/** Gets the data-flow node corresponding to the receiver of this call. That is, the `foo` in `foo.bar(...)`. */
Node getReceiver() { result = method_lookup.getObject() }
/**
* Gets the data-flow node corresponding to the object receiving this call. That is, the `foo` in
* `foo.bar(...)`.
*
* Note that this method may have multiple results if a single call node represents calls to
* multiple different objects and methods. If you want to link up objects and method names
* accurately, use the `calls` method instead.
*/
Node getObject() { result = method_lookup.getObject() }
/** Holds if this data-flow node calls method `methodName` on receiver node `receiver`. */
predicate calls(Node receiver, string methodName) {
receiver = this.getReceiver() and
methodName = this.getMethodName()
/** Holds if this data-flow node calls method `methodName` on the object node `object`. */
predicate calls(Node object, string methodName) {
// As `getObject` and `getMethodName` may both have multiple results, we must look up the object
// and method name directly on `method_lookup`.
object = method_lookup.getObject() and
methodName = method_lookup.getAttributeName()
}
}

View File

@@ -267,8 +267,11 @@ private module CryptographyModel {
string algorithmName;
CryptographyGenericCipherOperation() {
this.getMethodName() in ["update", "update_into"] and
this.getReceiver() in [cipherEncryptor(algorithmName), cipherDecryptor(algorithmName)]
exists(DataFlow::Node object, string method |
object in [cipherEncryptor(algorithmName), cipherDecryptor(algorithmName)] and
method in ["update", "update_into"] and
this.calls(object, method)
)
}
override Cryptography::CryptographicAlgorithm getAlgorithm() {

View File

@@ -0,0 +1,8 @@
conjunctive_lookup
| test.py:6:1:6:6 | ControlFlowNode for meth() | meth() | obj1 | bar |
| test.py:6:1:6:6 | ControlFlowNode for meth() | meth() | obj1 | foo |
| test.py:6:1:6:6 | ControlFlowNode for meth() | meth() | obj2 | bar |
| test.py:6:1:6:6 | ControlFlowNode for meth() | meth() | obj2 | foo |
calls_lookup
| test.py:6:1:6:6 | ControlFlowNode for meth() | meth() | obj1 | foo |
| test.py:6:1:6:6 | ControlFlowNode for meth() | meth() | obj2 | bar |

View File

@@ -0,0 +1,6 @@
if cond:
meth = obj1.foo
else:
meth = obj2.bar
meth()

View File

@@ -0,0 +1,18 @@
import python
import semmle.python.dataflow.new.DataFlow
import experimental.dataflow.TestUtil.PrintNode
query predicate conjunctive_lookup(
DataFlow::MethodCallNode methCall, string call, string object, string methodName
) {
call = prettyNode(methCall) and
object = prettyNode(methCall.getObject()) and
methodName = methCall.getMethodName()
}
query predicate calls_lookup(
DataFlow::MethodCallNode methCall, string call, string object, string methodName
) {
call = prettyNode(methCall) and
exists(DataFlow::Node o | methCall.calls(o, methodName) and object = prettyNode(o))
}