Skip to content

Commit 0778d90

Browse files
authored
python: fix implementation of lambdaCreation
- still identifying summarized callables by name. I think ther shoudl perhaps be a `getAUse` next to `getACall`. - also fix tests, adding a standard taint configuration
1 parent 92c4c87 commit 0778d90

7 files changed

Lines changed: 107 additions & 41 deletions

File tree

‎python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -936,14 +936,16 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
936936
creation.asExpr() = c.(DataFlowLambda).getDefinition()
937937
or
938938
// normal function
939-
// TODO: reconsider this code
940-
kind = kind and
941-
exists(Call call, Name f, FunctionDef def |
942-
f = call.getAnArg() and
943-
def.getDefinedFunction().getName() = f.getId() and
944-
// c.getCallableValue() = def.getDefinedFunction().getDefinition() and
945-
c.getName() = f.getId() and
946-
creation.asExpr() = call
939+
exists(FunctionDef def |
940+
def.defines(creation.asVar().getSourceVariable()) and
941+
def.getDefinedFunction() = c.(DataFlowCallableValue).getCallableValue().getScope()
942+
)
943+
or
944+
// summarized function
945+
exists(Call call, Name arg |
946+
arg = call.getAnArg() and
947+
c.(LibraryCallableValue).getName() = arg.getId() and
948+
creation.asExpr() = arg
947949
)
948950
}
949951

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import python
2+
import experimental.dataflow.TestUtil.FlowTest
3+
import experimental.dataflow.testTaintConfig
4+
private import semmle.python.dataflow.new.internal.PrintNode
5+
6+
class DataFlowTest extends FlowTest {
7+
DataFlowTest() { this = "DataFlowTest" }
8+
9+
override string flowTag() { result = "flow" }
10+
11+
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
12+
exists(TestConfiguration cfg | cfg.hasFlow(source, sink))
13+
}
14+
}
15+
16+
query predicate missingAnnotationOnSINK(Location location, string error, string element) {
17+
error = "ERROR, you should add `# $ MISSING: flow` annotation" and
18+
exists(DataFlow::Node sink |
19+
exists(DataFlow::CallCfgNode call |
20+
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
21+
call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and
22+
(sink = call.getArg(_) or sink = call.getArgByName(_))
23+
) and
24+
location = sink.getLocation() and
25+
element = prettyExpr(sink.asExpr()) and
26+
not any(TestConfiguration config).hasFlow(_, sink) and
27+
not exists(FalseNegativeExpectation missingResult |
28+
missingResult.getTag() = "flow" and
29+
missingResult.getLocation().getFile() = location.getFile() and
30+
missingResult.getLocation().getStartLine() = location.getStartLine()
31+
)
32+
)
33+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import python
22
private import TestSummaries
3-
import experimental.dataflow.TestUtil.NormalDataflowTest
3+
import experimental.dataflow.TestUtil.NormalTaintTrackingTest

‎python/ql/test/experimental/dataflow/summaries/summaries.expected‎

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
edges
2-
| summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:32:20:32:25 | ControlFlowNode for SOURCE |
3-
| summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:36:48:36:53 | ControlFlowNode for SOURCE |
4-
| summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:44:25:44:32 | ControlFlowNode for List |
5-
| summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:44:26:44:31 | ControlFlowNode for SOURCE |
6-
| summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:51:34:51:39 | ControlFlowNode for SOURCE |
7-
| summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:57:51:57:56 | ControlFlowNode for SOURCE |
8-
| summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:60:41:60:46 | ControlFlowNode for SOURCE |
9-
| summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:64:33:64:38 | ControlFlowNode for SOURCE |
10-
| summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:65:6:65:26 | ControlFlowNode for Subscript |
112
| summaries.py:32:11:32:26 | ControlFlowNode for identity() | summaries.py:33:6:33:12 | ControlFlowNode for tainted |
123
| summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | summaries.py:32:11:32:26 | ControlFlowNode for identity() |
134
| summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() | summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda |
145
| summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() |
156
| summaries.py:44:16:44:33 | ControlFlowNode for reversed() [List element] | summaries.py:45:6:45:17 | ControlFlowNode for tainted_list [List element] |
167
| summaries.py:44:25:44:32 | ControlFlowNode for List | summaries.py:45:6:45:20 | ControlFlowNode for Subscript |
178
| summaries.py:44:25:44:32 | ControlFlowNode for List [List element] | summaries.py:44:16:44:33 | ControlFlowNode for reversed() [List element] |
9+
| summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | summaries.py:44:25:44:32 | ControlFlowNode for List |
1810
| summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | summaries.py:44:25:44:32 | ControlFlowNode for List [List element] |
1911
| summaries.py:45:6:45:17 | ControlFlowNode for tainted_list [List element] | summaries.py:45:6:45:20 | ControlFlowNode for Subscript |
2012
| summaries.py:51:18:51:41 | ControlFlowNode for map() [List element] | summaries.py:52:6:52:19 | ControlFlowNode for tainted_mapped [List element] |
@@ -31,9 +23,9 @@ edges
3123
| summaries.py:61:6:61:27 | ControlFlowNode for tainted_mapped_summary [List element] | summaries.py:61:6:61:30 | ControlFlowNode for Subscript |
3224
| summaries.py:64:22:64:39 | ControlFlowNode for json_loads() [List element] | summaries.py:65:6:65:23 | ControlFlowNode for tainted_resultlist [List element] |
3325
| summaries.py:64:33:64:38 | ControlFlowNode for SOURCE | summaries.py:64:22:64:39 | ControlFlowNode for json_loads() [List element] |
26+
| summaries.py:64:33:64:38 | ControlFlowNode for SOURCE | summaries.py:65:6:65:26 | ControlFlowNode for Subscript |
3427
| summaries.py:65:6:65:23 | ControlFlowNode for tainted_resultlist [List element] | summaries.py:65:6:65:26 | ControlFlowNode for Subscript |
3528
nodes
36-
| summaries.py:10:10:10:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str |
3729
| summaries.py:32:11:32:26 | ControlFlowNode for identity() | semmle.label | ControlFlowNode for identity() |
3830
| summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
3931
| summaries.py:33:6:33:12 | ControlFlowNode for tainted | semmle.label | ControlFlowNode for tainted |
@@ -68,10 +60,10 @@ nodes
6860
subpaths
6961
invalidSpecComponent
7062
#select
71-
| summaries.py:33:6:33:12 | ControlFlowNode for tainted | summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:33:6:33:12 | ControlFlowNode for tainted | $@ | summaries.py:10:10:10:17 | ControlFlowNode for Str | ControlFlowNode for Str |
72-
| summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | $@ | summaries.py:10:10:10:17 | ControlFlowNode for Str | ControlFlowNode for Str |
73-
| summaries.py:45:6:45:20 | ControlFlowNode for Subscript | summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:45:6:45:20 | ControlFlowNode for Subscript | $@ | summaries.py:10:10:10:17 | ControlFlowNode for Str | ControlFlowNode for Str |
74-
| summaries.py:52:6:52:22 | ControlFlowNode for Subscript | summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:52:6:52:22 | ControlFlowNode for Subscript | $@ | summaries.py:10:10:10:17 | ControlFlowNode for Str | ControlFlowNode for Str |
75-
| summaries.py:58:6:58:31 | ControlFlowNode for Subscript | summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:58:6:58:31 | ControlFlowNode for Subscript | $@ | summaries.py:10:10:10:17 | ControlFlowNode for Str | ControlFlowNode for Str |
76-
| summaries.py:61:6:61:30 | ControlFlowNode for Subscript | summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:61:6:61:30 | ControlFlowNode for Subscript | $@ | summaries.py:10:10:10:17 | ControlFlowNode for Str | ControlFlowNode for Str |
77-
| summaries.py:65:6:65:26 | ControlFlowNode for Subscript | summaries.py:10:10:10:17 | ControlFlowNode for Str | summaries.py:65:6:65:26 | ControlFlowNode for Subscript | $@ | summaries.py:10:10:10:17 | ControlFlowNode for Str | ControlFlowNode for Str |
63+
| summaries.py:33:6:33:12 | ControlFlowNode for tainted | summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | summaries.py:33:6:33:12 | ControlFlowNode for tainted | $@ | summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
64+
| summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | $@ | summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
65+
| summaries.py:45:6:45:20 | ControlFlowNode for Subscript | summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | summaries.py:45:6:45:20 | ControlFlowNode for Subscript | $@ | summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
66+
| summaries.py:52:6:52:22 | ControlFlowNode for Subscript | summaries.py:51:34:51:39 | ControlFlowNode for SOURCE | summaries.py:52:6:52:22 | ControlFlowNode for Subscript | $@ | summaries.py:51:34:51:39 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
67+
| summaries.py:58:6:58:31 | ControlFlowNode for Subscript | summaries.py:57:51:57:56 | ControlFlowNode for SOURCE | summaries.py:58:6:58:31 | ControlFlowNode for Subscript | $@ | summaries.py:57:51:57:56 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
68+
| summaries.py:61:6:61:30 | ControlFlowNode for Subscript | summaries.py:60:41:60:46 | ControlFlowNode for SOURCE | summaries.py:61:6:61:30 | ControlFlowNode for Subscript | $@ | summaries.py:60:41:60:46 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
69+
| summaries.py:65:6:65:26 | ControlFlowNode for Subscript | summaries.py:64:33:64:38 | ControlFlowNode for SOURCE | summaries.py:65:6:65:26 | ControlFlowNode for Subscript | $@ | summaries.py:64:33:64:38 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |

‎python/ql/test/experimental/dataflow/summaries/summaries.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def SINK_F(x):
3838

3939
# A lambda that breaks the flow
4040
untainted_lambda = apply_lambda(lambda x: 1, SOURCE)
41-
SINK_F(untainted_lambda) # $ SPURIOUS: flow="SOURCE, l:-1 -> untainted_lambda"
41+
SINK_F(untainted_lambda)
4242

4343
# Collection summaries
4444
tainted_list = reversed([SOURCE])

‎python/ql/test/experimental/dataflow/summaries/summaries.ql‎

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,14 @@ import DataFlow::PathGraph
88
import semmle.python.dataflow.new.TaintTracking
99
import semmle.python.dataflow.new.internal.FlowSummaryImpl
1010
import semmle.python.ApiGraphs
11+
import experimental.dataflow.testTaintConfig
1112
private import TestSummaries
1213

1314
query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) {
1415
(sc.propagatesFlowExt(s, _, _) or sc.propagatesFlowExt(_, s, _)) and
1516
Private::External::invalidSpecComponent(s, c)
1617
}
1718

18-
class Conf extends TaintTracking::Configuration {
19-
Conf() { this = "FlowSummaries" }
20-
21-
override predicate isSource(DataFlow::Node src) { src.asExpr().(StrConst).getS() = "source" }
22-
23-
override predicate isSink(DataFlow::Node sink) {
24-
exists(Call mc |
25-
mc.getFunc().(Name).getId() = "SINK" and
26-
mc.getAnArg() = sink.asExpr()
27-
)
28-
}
29-
}
30-
31-
from DataFlow::PathNode source, DataFlow::PathNode sink, Conf conf
19+
from DataFlow::PathNode source, DataFlow::PathNode sink, TestConfiguration conf
3220
where conf.hasFlowPath(source, sink)
3321
select sink, source, sink, "$@", source, source.toString()
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Configuration to test selected data flow
3+
* Sources in the source code are denoted by the special name `SOURCE`,
4+
* and sinks are denoted by arguments to the special function `SINK`.
5+
* For example, given the test code
6+
* ```python
7+
* def test():
8+
* s = SOURCE
9+
* SINK(s)
10+
* ```
11+
* `SOURCE` will be a source and the second occurance of `s` will be a sink.
12+
*
13+
* In order to test literals, alternative sources are defined for each type:
14+
*
15+
* for | use
16+
* ----------
17+
* string | `"source"`
18+
* integer | `42`
19+
* float | `42.0`
20+
* complex | `42j` (not supported yet)
21+
*/
22+
23+
private import python
24+
import semmle.python.dataflow.new.DataFlow
25+
import semmle.python.dataflow.new.TaintTracking
26+
27+
class TestConfiguration extends TaintTracking::Configuration {
28+
TestConfiguration() { this = "TestConfiguration" }
29+
30+
override predicate isSource(DataFlow::Node node) {
31+
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE"
32+
or
33+
node.(DataFlow::CfgNode).getNode().getNode().(StrConst).getS() = "source"
34+
or
35+
node.(DataFlow::CfgNode).getNode().getNode().(IntegerLiteral).getN() = "42"
36+
or
37+
node.(DataFlow::CfgNode).getNode().getNode().(FloatLiteral).getN() = "42.0"
38+
// No support for complex numbers
39+
}
40+
41+
override predicate isSink(DataFlow::Node node) {
42+
exists(CallNode call |
43+
call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and
44+
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
45+
)
46+
}
47+
48+
override predicate isSanitizerIn(DataFlow::Node node) { this.isSource(node) }
49+
50+
override int explorationLimit() { result = 5 }
51+
}

0 commit comments

Comments
 (0)