diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll index 28c1c652df46..5e987eadc275 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll @@ -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` diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib.qll index 2d2c6c0d34f5..e336b1e99f23 100644 --- a/python/ql/lib/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/lib/semmle/python/frameworks/Stdlib.qll @@ -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" } @@ -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 diff --git a/python/ql/test/library-tests/dataflow/coverage/test_builtins.py b/python/ql/test/library-tests/dataflow/coverage/test_builtins.py index 245923370765..29afa9bfdb25 100644 --- a/python/ql/test/library-tests/dataflow/coverage/test_builtins.py +++ b/python/ql/test/library-tests/dataflow/coverage/test_builtins.py @@ -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