add self parameters to API-graphs, and add support for self parameters in MaD

This commit is contained in:
Erik Krogh Kristensen
2022-04-27 13:48:42 +02:00
parent dc38aa8a96
commit 547047ef19
5 changed files with 44 additions and 3 deletions

View File

@@ -136,6 +136,9 @@ module API {
result = this.getASuccessor(Label::keywordParameter(name))
}
/** Gets the node representing the self parameter */
Node getSelfParameter() { result = this.getASuccessor(Label::selfParameter()) }
/**
* Gets the number of parameters of the function represented by this node.
*/
@@ -315,6 +318,12 @@ module API {
/** Gets the API node for a parameter of this invocation. */
Node getAParameter() { result = this.getParameter(_) }
/** Gets the object that this method-call is being called on, if this is a method-call */
Node getSelfParameter() {
result.getARhs() = this.(DataFlow::MethodCallNode).getObject() and
result = callee.getSelfParameter()
}
/** Gets the API node for the keyword parameter `name` of this invocation. */
Node getKeywordParameter(string name) {
result = callee.getKeywordParameter(name) and
@@ -595,6 +604,9 @@ module API {
lbl = Label::keywordParameter(name) and
ref.asExpr() = fn.getInnerScope().getArgByName(name)
)
or
lbl = Label::selfParameter() and
ref.asExpr() = any(PY::Parameter p | p = fn.getInnerScope().getAnArg() and p.isSelf())
)
or
// Built-ins, treated as members of the module `builtins`
@@ -661,6 +673,9 @@ module API {
exists(string name | lbl = Label::keywordParameter(name) |
arg = pred.getACall().getArgByName(name)
)
or
lbl = Label::selfParameter() and
arg = pred.getACall().(DataFlow::MethodCallNode).getObject()
)
}
@@ -777,6 +792,7 @@ module API {
or
exists(any(PY::Function f).getArgByName(name))
} or
MkLabelSelfParameter() or
MkLabelReturn() or
MkLabelSubclass() or
MkLabelAwait()
@@ -834,6 +850,11 @@ module API {
string getName() { result = name }
}
/** A label for the self parameter. */
class LabelSelfParameter extends ApiLabel, MkLabelSelfParameter {
override string toString() { result = "getSelfParameter()" }
}
/** A label that gets the return value of a function. */
class LabelReturn extends ApiLabel, MkLabelReturn {
override string toString() { result = "getReturn()" }
@@ -873,6 +894,9 @@ module API {
/** Gets the `parameter` edge label for the keyword parameter `name`. */
LabelKeywordParameter keywordParameter(string name) { result.getName() = name }
/** Gets the edge label for the self parameter. */
LabelSelfParameter selfParameter() { any() }
/** Gets the `return` edge label. */
LabelReturn return() { any() }

View File

@@ -63,6 +63,10 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
or
token.getName() = "Method" and
result = node.getMember(token.getAnArgument()).getReturn()
or
token.getName() = ["Argument", "Parameter"] and
token.getAnArgument() = "self" and
result = node.getSelfParameter()
// Some features don't have MaD tokens yet, they would need to be added to API-graphs first.
// - decorators ("DecoratedClass", "DecoratedMember", "DecoratedParameter")
// - Array/Map elements ("ArrayElement", "Element", "MapKey", "MapValue")
@@ -78,7 +82,7 @@ API::Node getExtraSuccessorFromInvoke(API::CallNode node, AccessPathToken token)
or
token.getName() = "Argument" and
token.getAnArgument() = "self" and
result.getARhs() = node.(DataFlow::MethodCallNode).getObject() // TODO: Get proper support for this in API-graphs?
result = node.getSelfParameter()
or
token.getName() = "Argument" and
exists(string arg | arg + ":" = token.getAnArgument() | result = node.getKeywordParameter(arg))

View File

@@ -23,6 +23,7 @@ isSink
| test.py:33:22:33:24 | ControlFlowNode for one | test-source |
| test.py:33:27:33:29 | ControlFlowNode for two | test-source |
| test.py:33:32:33:36 | ControlFlowNode for three | test-source |
| test.py:57:7:57:12 | ControlFlowNode for ArgPos | test-source |
isSource
| test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source |
| test.py:9:8:9:14 | ControlFlowNode for alias() | test-source |
@@ -43,6 +44,7 @@ isSource
| test.py:46:7:46:16 | ControlFlowNode for SubClass() | test-source |
| test.py:51:8:51:18 | ControlFlowNode for Sub2Class() | test-source |
| test.py:53:7:53:16 | ControlFlowNode for Attribute() | test-source |
| test.py:60:13:60:16 | ControlFlowNode for self | test-source |
syntaxErrors
| Member[foo |
| Member[foo] .Member[bar] |

View File

@@ -50,4 +50,12 @@ class Sub2Class (CommonTokens.Class):
sub2 = Sub2Class()
val = inst.foo()
val = inst.foo()
from testlib import ArgPos
val = ArgPos.selfThing(arg, named=2)
class SubClass (ArgPos.MyClass):
def foo(self, arg, named=2):
pass

View File

@@ -49,11 +49,12 @@ class Sinks extends ModelInput::SinkModelCsv {
// callsite filter.
"testlib;;Member[CallFilter].Member[arityOne].WithArity[1].Argument[0..];test-source", //
"testlib;;Member[CallFilter].Member[twoOrMore].WithArity[2..].Argument[0..];test-source", //
// testing non-positional arguments
"testlib;;Member[ArgPos].Member[selfThing].Argument[self];test-source", //
]
}
}
// TODO: Uniform tokens for fields
// TODO: Non-positional arguments (including Named parameters)
// TODO: Any argument
// TODO: Test taint steps.
@@ -75,6 +76,8 @@ class Sources extends ModelInput::SourceModelCsv {
"testlib;;Member[CommonTokens].Member[Super].Subclass.Instance;test-source", //
// method
"testlib;;Member[CommonTokens].Member[Class].Instance.Method[foo];test-source", //
// testing non-positional arguments
"testlib;;Member[ArgPos].Member[MyClass].Subclass.Member[foo].Parameter[self];test-source", //
]
}
}