Skip to content

Commit 306b087

Browse files
authored
Merge pull request #17566 from yoff/python/dict-can-take-multiple-args
Python: All dict constructor args are relevant
2 parents db5e452 + 201c4aa commit 306b087

File tree

3 files changed

+21
-5
lines changed

3 files changed

+21
-5
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,14 +368,13 @@ abstract class DataFlowFunction extends DataFlowCallable, TFunction {
368368
int positionalOffset() { result = 0 }
369369

370370
override ParameterNode getParameter(ParameterPosition ppos) {
371+
// Do not handle lower bound positions (such as `[1..]`) here
372+
// they are handled by parameter matching and would create
373+
// inconsistencies here as multiple parameters could match such a position.
371374
exists(int index | ppos.isPositional(index) |
372375
result.getParameter() = func.getArg(index + this.positionalOffset())
373376
)
374377
or
375-
exists(int index1, int index2 | ppos.isPositionalLowerBound(index1) and index2 >= index1 |
376-
result.getParameter() = func.getArg(index2 + this.positionalOffset())
377-
)
378-
or
379378
exists(string name | ppos.isKeyword(name) | result.getParameter() = func.getArgByName(name))
380379
or
381380
// `*args`

python/ql/lib/semmle/python/frameworks/Stdlib.qll

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4235,7 +4235,11 @@ module StdlibPrivate {
42354235
// ---------------------------------------------------------------------------
42364236
// Flow summaries for functions contructing containers
42374237
// ---------------------------------------------------------------------------
4238-
/** A flow summary for `dict`. */
4238+
/**
4239+
* A flow summary for `dict`.
4240+
*
4241+
* see https://docs.python.org/3/library/stdtypes.html#dict
4242+
*/
42394243
class DictSummary extends SummarizedCallable {
42404244
DictSummary() { this = "builtins.dict" }
42414245

@@ -4246,18 +4250,23 @@ module StdlibPrivate {
42464250
}
42474251

42484252
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
4253+
// The positional argument contains a mapping.
4254+
// TODO: Add the list-of-pairs version
4255+
// TODO: these values can be overwritten by keyword arguments
42494256
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
42504257
input = "Argument[0].DictionaryElement[" + key + "]" and
42514258
output = "ReturnValue.DictionaryElement[" + key + "]" and
42524259
preservesValue = true
42534260
)
42544261
or
4262+
// The keyword arguments are added to the dictionary.
42554263
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
42564264
input = "Argument[" + key + ":]" and
42574265
output = "ReturnValue.DictionaryElement[" + key + "]" and
42584266
preservesValue = true
42594267
)
42604268
or
4269+
// Imprecise content in any argument ends up on the container itself.
42614270
input = "Argument[0]" and
42624271
output = "ReturnValue" and
42634272
preservesValue = false

python/ql/test/library-tests/dataflow/coverage/test_builtins.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,14 @@ def test_dict_from_dict():
142142
SINK(d2["k"]) #$ flow="SOURCE, l:-2 -> d2['k']"
143143
SINK_F(d2["k1"])
144144

145+
@expects(4)
146+
def test_dict_from_multiple_args():
147+
d = dict([("k", SOURCE), ("k1", NONSOURCE)], k2 = SOURCE, k3 = NONSOURCE)
148+
SINK(d["k"]) #$ MISSING: flow="SOURCE, l:-1 -> d['k']"
149+
SINK_F(d["k1"])
150+
SINK(d["k2"]) #$ flow="SOURCE, l:-3 -> d['k2']"
151+
SINK_F(d["k3"])
152+
145153
## Container methods
146154

147155
### List

0 commit comments

Comments
 (0)