Skip to content

Commit 3d990c5

Browse files
committed
Get back to ApiGraphs
1 parent 30554a1 commit 3d990c5

3 files changed

Lines changed: 41 additions & 205 deletions

File tree

python/ql/src/experimental/semmle/python/Concepts.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ private import semmle.python.dataflow.new.DataFlow
1313
private import semmle.python.dataflow.new.RemoteFlowSources
1414
private import semmle.python.dataflow.new.TaintTracking
1515
private import experimental.semmle.python.Frameworks
16+
private import semmle.python.ApiGraphs
1617

1718
/** Provides classes for modeling Regular Expression-related APIs. */
1819
module RegexExecution {
@@ -24,6 +25,7 @@ module RegexExecution {
2425
*/
2526
abstract class Range extends DataFlow::Node {
2627
abstract DataFlow::Node getRegexNode();
28+
2729
abstract Attribute getRegexMethod();
2830
}
2931
}
@@ -40,5 +42,12 @@ class RegexExecution extends DataFlow::Node {
4042
RegexExecution() { this = range }
4143

4244
DataFlow::Node getRegexNode() { result = range.getRegexNode() }
45+
4346
Attribute getRegexMethod() { result = range.getRegexMethod() }
4447
}
48+
49+
class RegexEscape extends DataFlow::Node {
50+
RegexEscape() {
51+
this = API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0)
52+
}
53+
}

python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll

Lines changed: 31 additions & 203 deletions
Original file line numberDiff line numberDiff line change
@@ -8,221 +8,49 @@ private import semmle.python.dataflow.new.DataFlow
88
private import semmle.python.dataflow.new.TaintTracking
99
private import semmle.python.dataflow.new.RemoteFlowSources
1010
private import experimental.semmle.python.Concepts
11+
private import semmle.python.ApiGraphs
1112

1213
/** Provides models for the Python standard library. */
13-
private module Stdlib {
14-
// ---------------------------------------------------------------------------
15-
// re
16-
// ---------------------------------------------------------------------------
17-
private module Re {
18-
/** Gets a reference to the `re` module. */
19-
private DataFlow::Node re(DataFlow::TypeTracker t) {
20-
t.start() and
21-
result = DataFlow::importNode("re")
22-
or
23-
exists(DataFlow::TypeTracker t2 | result = re(t2).track(t2, t))
14+
private module Re {
15+
/** List of re methods. */
16+
private class ReMethods extends string {
17+
ReMethods() {
18+
this in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"]
2419
}
20+
}
2521

26-
/** Gets a reference to the `re` module. */
27-
DataFlow::Node re() { result = re(DataFlow::TypeTracker::end()) }
22+
private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range {
23+
DataFlow::Node regexNode;
24+
Attribute regexMethod;
2825

29-
/**
30-
* Gets a reference to the attribute `attr_name` of the `re` module.
31-
* WARNING: Only holds for a few predefined attributes.
32-
*/
33-
private DataFlow::Node re_attr(DataFlow::TypeTracker t, string attr_name) {
34-
attr_name in [
35-
"match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile",
36-
"escape"
37-
] and
38-
(
39-
t.start() and
40-
result = DataFlow::importNode("re" + "." + attr_name)
41-
or
42-
t.startInAttr(attr_name) and
43-
result = re()
44-
)
45-
or
46-
// Due to bad performance when using normal setup with `re_attr(t2, attr_name).track(t2, t)`
47-
// we have inlined that code and forced a join
48-
exists(DataFlow::TypeTracker t2 |
49-
exists(DataFlow::StepSummary summary |
50-
re_attr_first_join(t2, attr_name, result, summary) and
51-
t = t2.append(summary)
52-
)
53-
)
26+
DirectRegex() {
27+
this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and
28+
regexNode = this.getArg(0) and
29+
regexMethod = this.asExpr().(Attribute)
5430
}
5531

56-
pragma[nomagic]
57-
private predicate re_attr_first_join(
58-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
59-
) {
60-
DataFlow::StepSummary::step(re_attr(t2, attr_name), res, summary)
61-
}
32+
override DataFlow::Node getRegexNode() { result = regexNode }
6233

63-
/**
64-
* Gets a reference to the attribute `attr_name` of the `re` module.
65-
* WARNING: Only holds for a few predefined attributes.
66-
*/
67-
private DataFlow::Node re_attr(string attr_name) {
68-
result = re_attr(DataFlow::TypeTracker::end(), attr_name)
69-
}
34+
override Attribute getRegexMethod() { result = regexMethod }
35+
}
7036

71-
/**
72-
* Gets a reference to any `attr_name` of the `re` module that immediately executes an expression.
73-
* WARNING: Only holds for a few predefined attributes.
74-
*/
75-
private DataFlow::Node re_exec_attr() {
76-
exists(string attr_name |
77-
attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] and
78-
result = re_attr(DataFlow::TypeTracker::end(), attr_name)
37+
private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range {
38+
DataFlow::Node regexNode;
39+
Attribute regexMethod;
40+
41+
CompiledRegex() {
42+
exists(DataFlow::CallCfgNode patternCall, DirectRegex reMethod |
43+
this = reMethod and
44+
patternCall = API::moduleImport("re").getMember("compile").getACall() and
45+
patternCall = reMethod.(DataFlow::AttrRead).getObject().getALocalSource() and
46+
regexNode = patternCall.getArg(0) and
47+
// regexMethod is *not* worked out outside class instanciation because `CompiledRegex` focuses on re.compile(pattern).ReMethod
48+
regexMethod = reMethod.getRegexMethod()
7949
)
8050
}
8151

82-
/**
83-
* A call to `re.match`
84-
* See https://docs.python.org/3/library/re.html#re.match
85-
*/
86-
private class ReMatchCall extends RegexExecution::Range, DataFlow::CfgNode {
87-
override CallNode node;
88-
89-
ReMatchCall() { node.getFunction() = re_attr("match").asCfgNode() }
90-
91-
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
92-
93-
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
94-
}
95-
96-
/**
97-
* A call to `re.fullmatch`
98-
* See https://docs.python.org/3/library/re.html#re.fullmatch
99-
*/
100-
private class ReFullMatchCall extends RegexExecution::Range, DataFlow::CfgNode {
101-
override CallNode node;
102-
103-
ReFullMatchCall() { node.getFunction() = re_attr("fullmatch").asCfgNode() }
104-
105-
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
106-
107-
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
108-
}
109-
110-
/**
111-
* A call to `re.search`
112-
* See https://docs.python.org/3/library/re.html#re.search
113-
*/
114-
private class ReSearchCall extends RegexExecution::Range, DataFlow::CfgNode {
115-
override CallNode node;
116-
117-
ReSearchCall() { node.getFunction() = re_attr("search").asCfgNode() }
118-
119-
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
120-
121-
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
122-
}
123-
124-
/**
125-
* A call to `re.split`
126-
* See https://docs.python.org/3/library/re.html#re.split
127-
*/
128-
private class ReSplitCall extends RegexExecution::Range, DataFlow::CfgNode {
129-
override CallNode node;
130-
131-
ReSplitCall() { node.getFunction() = re_attr("split").asCfgNode() }
132-
133-
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
134-
135-
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
136-
}
137-
138-
/**
139-
* A call to `re.findall`
140-
* See https://docs.python.org/3/library/re.html#re.findall
141-
*/
142-
private class ReFindAllCall extends RegexExecution::Range, DataFlow::CfgNode {
143-
override CallNode node;
144-
145-
ReFindAllCall() { node.getFunction() = re_attr("findall").asCfgNode() }
146-
147-
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
148-
149-
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
150-
}
151-
152-
/**
153-
* A call to `re.finditer`
154-
* See https://docs.python.org/3/library/re.html#re.finditer
155-
*/
156-
private class ReFindIterCall extends RegexExecution::Range, DataFlow::CfgNode {
157-
override CallNode node;
158-
159-
ReFindIterCall() { node.getFunction() = re_attr("finditer").asCfgNode() }
160-
161-
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
162-
163-
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
164-
}
165-
166-
/**
167-
* A call to `re.sub`
168-
* See https://docs.python.org/3/library/re.html#re.sub
169-
*/
170-
private class ReSubCall extends RegexExecution::Range, DataFlow::CfgNode {
171-
override CallNode node;
172-
173-
ReSubCall() { node.getFunction() = re_attr("sub").asCfgNode() }
174-
175-
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
176-
177-
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
178-
}
179-
180-
/**
181-
* A call to `re.subn`
182-
* See https://docs.python.org/3/library/re.html#re.subn
183-
*/
184-
private class ReSubNCall extends RegexExecution::Range, DataFlow::CfgNode {
185-
override CallNode node;
52+
override DataFlow::Node getRegexNode() { result = regexNode }
18653

187-
ReSubNCall() { node.getFunction() = re_attr("subn").asCfgNode() }
188-
189-
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
190-
191-
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
192-
}
193-
194-
/**
195-
* A call to `re.escape`
196-
* See https://docs.python.org/3/library/re.html#re.escape
197-
*/
198-
private class ReEscapeCall extends RegexExecution::Range, DataFlow::CfgNode {
199-
override CallNode node;
200-
201-
ReEscapeCall() { node.getFunction() = re_attr("escape").asCfgNode() }
202-
203-
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
204-
205-
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
206-
}
207-
208-
/**
209-
* A call to `re.compile`
210-
* See https://docs.python.org/3/library/re.html#re.match
211-
*/
212-
private class ReCompileCall extends RegexExecution::Range, DataFlow::CfgNode {
213-
override CallNode node;
214-
215-
ReCompileCall() { node.getFunction() = re_attr("compile").asCfgNode() }
216-
217-
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
218-
219-
override Attribute getRegexMethod() {
220-
exists(DataFlow::AttrRead reMethod |
221-
reMethod = re_exec_attr() and
222-
node.getFunction() = reMethod.getObject().getALocalSource().asCfgNode() and
223-
result = reMethod.asExpr().(Attribute)
224-
)
225-
}
226-
}
54+
override Attribute getRegexMethod() { result = regexMethod }
22755
}
22856
}

python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import python
77
import experimental.semmle.python.Concepts
8-
import experimental.semmle.python.frameworks.Stdlib
98
import semmle.python.dataflow.new.DataFlow
109
import semmle.python.dataflow.new.TaintTracking
1110
import semmle.python.dataflow.new.RemoteFlowSources
@@ -21,5 +20,5 @@ class RegexInjectionFlowConfig extends TaintTracking::Configuration {
2120

2221
override predicate isSink(DataFlow::Node sink) { sink = any(RegexExecution re).getRegexNode() }
2322

24-
override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof ReEscapeCall }
23+
override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof RegexEscape }
2524
}

0 commit comments

Comments
 (0)