Skip to content

Commit

Permalink
Merge pull request #17566 from yoff/python/dict-can-take-multiple-args
Browse files Browse the repository at this point in the history
Python: All dict constructor args are relevant
  • Loading branch information
yoff authored Oct 4, 2024
2 parents db5e452 + 201c4aa commit 306b087
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -368,14 +368,13 @@ abstract class DataFlowFunction extends DataFlowCallable, TFunction {
int positionalOffset() { result = 0 }

override ParameterNode getParameter(ParameterPosition ppos) {
// Do not handle lower bound positions (such as `[1..]`) here
// they are handled by parameter matching and would create
// inconsistencies here as multiple parameters could match such a position.
exists(int index | ppos.isPositional(index) |
result.getParameter() = func.getArg(index + this.positionalOffset())
)
or
exists(int index1, int index2 | ppos.isPositionalLowerBound(index1) and index2 >= index1 |
result.getParameter() = func.getArg(index2 + this.positionalOffset())
)
or
exists(string name | ppos.isKeyword(name) | result.getParameter() = func.getArgByName(name))
or
// `*args`
Expand Down
11 changes: 10 additions & 1 deletion python/ql/lib/semmle/python/frameworks/Stdlib.qll
Original file line number Diff line number Diff line change
Expand Up @@ -4235,7 +4235,11 @@ module StdlibPrivate {
// ---------------------------------------------------------------------------
// Flow summaries for functions contructing containers
// ---------------------------------------------------------------------------
/** A flow summary for `dict`. */
/**
* A flow summary for `dict`.
*
* see https://docs.python.org/3/library/stdtypes.html#dict
*/
class DictSummary extends SummarizedCallable {
DictSummary() { this = "builtins.dict" }

Expand All @@ -4246,18 +4250,23 @@ module StdlibPrivate {
}

override predicate propagatesFlow(string input, string output, boolean preservesValue) {
// The positional argument contains a mapping.
// TODO: Add the list-of-pairs version
// TODO: these values can be overwritten by keyword arguments
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
input = "Argument[0].DictionaryElement[" + key + "]" and
output = "ReturnValue.DictionaryElement[" + key + "]" and
preservesValue = true
)
or
// The keyword arguments are added to the dictionary.
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
input = "Argument[" + key + ":]" and
output = "ReturnValue.DictionaryElement[" + key + "]" and
preservesValue = true
)
or
// Imprecise content in any argument ends up on the container itself.
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ def test_dict_from_dict():
SINK(d2["k"]) #$ flow="SOURCE, l:-2 -> d2['k']"
SINK_F(d2["k1"])

@expects(4)
def test_dict_from_multiple_args():
d = dict([("k", SOURCE), ("k1", NONSOURCE)], k2 = SOURCE, k3 = NONSOURCE)
SINK(d["k"]) #$ MISSING: flow="SOURCE, l:-1 -> d['k']"
SINK_F(d["k1"])
SINK(d["k2"]) #$ flow="SOURCE, l:-3 -> d['k2']"
SINK_F(d["k3"])

## Container methods

### List
Expand Down

0 comments on commit 306b087

Please sign in to comment.