diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll index 7cb48d4784d..2ec44234c08 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll @@ -1093,10 +1093,21 @@ module Conversions { ) } + predicate formatReadStep(Node nodeFrom, ContentSet c, Node nodeTo) { + // % formatting + exists(BinaryExprNode fmt | fmt = nodeTo.asCfgNode() | + fmt.getOp() instanceof Mod and + fmt.getRight() = nodeFrom.asCfgNode() + ) and + c instanceof TupleElementContent + } + predicate readStep(Node nodeFrom, ContentSet c, Node nodeTo) { decoderReadStep(nodeFrom, c, nodeTo) or encoderReadStep(nodeFrom, c, nodeTo) + or + formatReadStep(nodeFrom, c, nodeTo) } } diff --git a/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py b/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py index 58d1c5160c4..42ac758bfff 100644 --- a/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py +++ b/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_string.py @@ -115,7 +115,7 @@ def percent_fmt(): ensure_tainted( tainted_fmt % (1, 2), # $ tainted "%s foo bar" % ts, # $ tainted - "%s %s %s" % (1, 2, ts), # $ MISSING: tainted + "%s %s %s" % (1, 2, ts), # $ tainted )