Skip to content

Commit b93c04b

Browse files
committed
python: Add reverse flow in some patterns
Particularly in value and literal patterns. This is getting a little bit into the guards aspect of matching. We could similarly add reverse flow in terms of sub-patterns storing to a sequence pattern, a flow step from alternatives to an-or-pattern, etc.. It does not seem too likely that sources are embedded in patterns to begin with, but for secrets perhaps? It is illustrated by the literal test. The value test still fails. I believe we miss flow in general from the static attribute.
1 parent cb52ab6 commit b93c04b

File tree

2 files changed

+41
-9
lines changed

2 files changed

+41
-9
lines changed

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

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1595,10 +1595,10 @@ import IterableUnpacking
15951595
*
15961596
* - as pattern: subject flows to alias as well as to the interior pattern
15971597
* - or pattern: subject flows to each alternative
1598-
* - literal pattern: no flow
1598+
* - literal pattern: flow from the literal to the pattern, to add information
15991599
* - capture pattern: subject flows to the variable
16001600
* - wildcard pattern: no flow
1601-
* - value pattern: no flow
1601+
* - value pattern: flow from the value to the pattern, to add information
16021602
* - sequence pattern: each element reads from subject at the associated index
16031603
* - star pattern: subject flows to the variable, possibly via a conversion
16041604
* - mapping pattern: each value reads from subject at the associated key
@@ -1636,14 +1636,16 @@ module MatchUnpacking {
16361636
*/
16371637
predicate matchAsFlowStep(Node nodeFrom, Node nodeTo) {
16381638
exists(MatchAsPattern subject, Name alias | alias = subject.getAlias() |
1639+
// We make the subject flow to the interior pattern via the alis.
1640+
// That way, information can propagate from the interior pattern to the alias.
1641+
//
1642+
// the subject flows to the interior pattern
16391643
nodeFrom.asCfgNode().getNode() = subject and
1640-
(
1641-
// the subject flows to the alias
1642-
nodeTo.asVar().getDefinition().(PatternAliasDefinition).getDefiningNode().getNode() = alias
1643-
or
1644-
// the subject flows to the interior pattern
1645-
nodeTo.asCfgNode().getNode() = subject.getPattern()
1646-
)
1644+
nodeTo.asCfgNode().getNode() = subject.getPattern()
1645+
or
1646+
// the interior pattern flows to the alias
1647+
nodeFrom.asCfgNode().getNode() = subject.getPattern() and
1648+
nodeTo.asVar().getDefinition().(PatternAliasDefinition).getDefiningNode().getNode() = alias
16471649
)
16481650
}
16491651

@@ -1658,6 +1660,17 @@ module MatchUnpacking {
16581660
)
16591661
}
16601662

1663+
/**
1664+
* literal pattern: flow from the literal to the pattern, to add information
1665+
* syntax (toplevel): `case literal:`
1666+
*/
1667+
predicate matchLiteralFlowStep(Node nodeFrom, Node nodeTo) {
1668+
exists(MatchLiteralPattern pattern, Expr literal | literal = pattern.getLiteral() |
1669+
nodeFrom.asExpr() = literal and
1670+
nodeTo.asCfgNode().getNode() = pattern
1671+
)
1672+
}
1673+
16611674
/**
16621675
* capture pattern: subject flows to the variable
16631676
* syntax (toplevel): `case var:`
@@ -1669,6 +1682,17 @@ module MatchUnpacking {
16691682
)
16701683
}
16711684

1685+
/**
1686+
* value pattern: flow from the value to the pattern, to add information
1687+
* syntax (toplevel): `case Dotted.value:`
1688+
*/
1689+
predicate matchValueFlowStep(Node nodeFrom, Node nodeTo) {
1690+
exists(MatchValuePattern pattern, Expr value | value = pattern.getValue() |
1691+
nodeFrom.asExpr() = value and
1692+
nodeTo.asCfgNode().getNode() = pattern
1693+
)
1694+
}
1695+
16721696
/**
16731697
* sequence pattern: each element reads from subject at the associated index
16741698
* syntax (toplevel): `case [a, b]:`
@@ -1814,8 +1838,12 @@ module MatchUnpacking {
18141838
or
18151839
matchOrFlowStep(nodeFrom, nodeTo)
18161840
or
1841+
matchLiteralFlowStep(nodeFrom, nodeTo)
1842+
or
18171843
matchCaptureFlowStep(nodeFrom, nodeTo)
18181844
or
1845+
matchValueFlowStep(nodeFrom, nodeTo)
1846+
or
18191847
matchMappingFlowStep(nodeFrom, nodeTo)
18201848
}
18211849

python/ql/test/experimental/dataflow/match/test.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ def test_or_pattern():
4646
SINK(x) #$ flow="SOURCE, l:-3 -> x"
4747

4848
# No flow for literal pattern
49+
def test_literal_pattern():
50+
match SOURCE:
51+
case 42 as x:
52+
SINK(x) #$ flow="SOURCE, l:-2 -> x" flow="42, l:-1 -> x"
4953

5054
def test_capture_pattern():
5155
match SOURCE:

0 commit comments

Comments
 (0)