Skip to content

Commit 28fdeba

Browse files
committed
Structure development
1 parent 444a15a commit 28fdeba

9 files changed

Lines changed: 264 additions & 95 deletions

File tree

python/ql/src/Security/CWE-730/RegexInjection.qhelp renamed to python/ql/src/experimental/Security/CWE-730/RegexInjection.qhelp

File renamed without changes.

python/ql/src/Security/CWE-730/RegexInjection.ql renamed to python/ql/src/experimental/Security/CWE-730/RegexInjection.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313

1414
// determine precision above
1515
import python
16-
import semmle.python.security.dataflow.RegexInjection
16+
import experimental.semmle.python.security.injection.RegexInjection
1717
import DataFlow::PathGraph
1818

1919
from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
2020
where config.hasFlowPath(source, sink)
2121
select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.",
22-
sink.getNode(), "This", source.getNode(), "user-provided value"
22+
sink.getNode(), "This", source.getNode(), "user-provided value"
File renamed without changes.
File renamed without changes.

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,32 @@ 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+
17+
/** Provides classes for modeling Regular Expression-related APIs. */
18+
module RegexExecution {
19+
/**
20+
* A data-flow node that works with regular expressions.
21+
*
22+
* Extend this class to model new APIs. If you want to refine existing API models,
23+
* extend `RegexExecution` instead.
24+
*/
25+
abstract class Range extends DataFlow::Node {
26+
abstract DataFlow::Node getRegexNode();
27+
abstract Attribute getRegexMethod();
28+
}
29+
}
30+
31+
/**
32+
* A data-flow node that works with regular expressions.
33+
*
34+
* Extend this class to refine existing API models. If you want to model new APIs,
35+
* extend `RegexExecution::Range` instead.
36+
*/
37+
class RegexExecution extends DataFlow::Node {
38+
RegexExecution::Range range;
39+
40+
RegexExecution() { this = range }
41+
42+
DataFlow::Node getRegexNode() { result = range.getRegexNode() }
43+
Attribute getRegexMethod() { result = range.getRegexMethod() }
44+
}

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

Lines changed: 231 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,234 @@ 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
11+
12+
/** Provides models for the Python standard library. */
13+
private module Stdlib {
14+
// ---------------------------------------------------------------------------
15+
// re
16+
// ---------------------------------------------------------------------------
17+
private module Re {
18+
19+
/** Gets a reference to the `re` module. */
20+
private DataFlow::Node re(DataFlow::TypeTracker t) {
21+
t.start() and
22+
result = DataFlow::importNode("re")
23+
or
24+
exists(DataFlow::TypeTracker t2 | result = re(t2).track(t2, t))
25+
}
26+
27+
/** Gets a reference to the `re` module. */
28+
DataFlow::Node re() { result = re(DataFlow::TypeTracker::end()) }
29+
30+
/**
31+
* Gets a reference to the attribute `attr_name` of the `re` module.
32+
* WARNING: Only holds for a few predefined attributes.
33+
*/
34+
private DataFlow::Node re_attr(DataFlow::TypeTracker t, string attr_name) {
35+
attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn", "compile"] and
36+
(
37+
t.start() and
38+
result = DataFlow::importNode("re" + "." + attr_name)
39+
or
40+
t.startInAttr(attr_name) and
41+
result = re()
42+
)
43+
or
44+
// Due to bad performance when using normal setup with `re_attr(t2, attr_name).track(t2, t)`
45+
// we have inlined that code and forced a join
46+
exists(DataFlow::TypeTracker t2 |
47+
exists(DataFlow::StepSummary summary |
48+
re_attr_first_join(t2, attr_name, result, summary) and
49+
t = t2.append(summary)
50+
)
51+
)
52+
}
53+
54+
pragma[nomagic]
55+
private predicate re_attr_first_join(
56+
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
57+
) {
58+
DataFlow::StepSummary::step(re_attr(t2, attr_name), res, summary)
59+
}
60+
61+
/**
62+
* Gets a reference to the attribute `attr_name` of the `re` module.
63+
* WARNING: Only holds for a few predefined attributes.
64+
*/
65+
private DataFlow::Node re_attr(string attr_name) {
66+
result = re_attr(DataFlow::TypeTracker::end(), attr_name)
67+
}
68+
69+
/**
70+
* Gets a reference to any `attr_name` of the `re` module that immediately executes an expression.
71+
* WARNING: Only holds for a few predefined attributes.
72+
*/
73+
private DataFlow::Node re_exec_attr() {
74+
exists(string attr_name |
75+
attr_name in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] and
76+
result = re_attr(DataFlow::TypeTracker::end(), attr_name)
77+
)
78+
}
79+
80+
/**
81+
* A call to `re.match`
82+
* See https://docs.python.org/3/library/re.html#re.match
83+
*/
84+
private class ReMatchCall extends RegexExecution::Range, DataFlow::CfgNode {
85+
override CallNode node;
86+
87+
ReMatchCall() { node.getFunction() = re_attr("match").asCfgNode() }
88+
89+
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
90+
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
91+
}
92+
93+
/**
94+
* A call to `re.fullmatch`
95+
* See https://docs.python.org/3/library/re.html#re.fullmatch
96+
*/
97+
private class ReFullMatchCall extends RegexExecution::Range, DataFlow::CfgNode {
98+
override CallNode node;
99+
100+
ReFullMatchCall() { node.getFunction() = re_attr("fullmatch").asCfgNode() }
101+
102+
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
103+
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
104+
}
105+
106+
/**
107+
* A call to `re.search`
108+
* See https://docs.python.org/3/library/re.html#re.search
109+
*/
110+
private class ReSearchCall extends RegexExecution::Range, DataFlow::CfgNode {
111+
override CallNode node;
112+
113+
ReSearchCall() { node.getFunction() = re_attr("search").asCfgNode() }
114+
115+
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
116+
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
117+
}
118+
119+
/**
120+
* A call to `re.split`
121+
* See https://docs.python.org/3/library/re.html#re.split
122+
*/
123+
private class ReSplitCall extends RegexExecution::Range, DataFlow::CfgNode {
124+
override CallNode node;
125+
126+
ReSplitCall() { node.getFunction() = re_attr("split").asCfgNode() }
127+
128+
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
129+
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
130+
}
131+
132+
/**
133+
* A call to `re.findall`
134+
* See https://docs.python.org/3/library/re.html#re.findall
135+
*/
136+
private class ReFindAllCall extends RegexExecution::Range, DataFlow::CfgNode {
137+
override CallNode node;
138+
139+
ReFindAllCall() { node.getFunction() = re_attr("findall").asCfgNode() }
140+
141+
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
142+
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
143+
}
144+
145+
/**
146+
* A call to `re.finditer`
147+
* See https://docs.python.org/3/library/re.html#re.finditer
148+
*/
149+
private class ReFindIterCall extends RegexExecution::Range, DataFlow::CfgNode {
150+
override CallNode node;
151+
152+
ReFindIterCall() { node.getFunction() = re_attr("finditer").asCfgNode() }
153+
154+
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
155+
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
156+
}
157+
158+
/**
159+
* A call to `re.sub`
160+
* See https://docs.python.org/3/library/re.html#re.sub
161+
*/
162+
private class ReSubCall extends RegexExecution::Range, DataFlow::CfgNode {
163+
override CallNode node;
164+
165+
ReSubCall() { node.getFunction() = re_attr("sub").asCfgNode() }
166+
167+
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
168+
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
169+
}
170+
171+
/**
172+
* A call to `re.subn`
173+
* See https://docs.python.org/3/library/re.html#re.subn
174+
*/
175+
private class ReSubNCall extends RegexExecution::Range, DataFlow::CfgNode {
176+
override CallNode node;
177+
178+
ReSubNCall() { node.getFunction() = re_attr("subn").asCfgNode() }
179+
180+
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
181+
override Attribute getRegexMethod() { result = node.getNode().getFunc().(Attribute) }
182+
}
183+
184+
/**
185+
* A call to `re.compile`
186+
* See https://docs.python.org/3/library/re.html#re.match
187+
*/
188+
private class ReCompileCall extends RegexExecution::Range, DataFlow::CfgNode {
189+
override CallNode node;
190+
191+
ReCompileCall() { node.getFunction() = re_attr("compile").asCfgNode() }
192+
193+
override DataFlow::Node getRegexNode() { result.asCfgNode() = node.getArg(0) }
194+
override Attribute getRegexMethod() {
195+
exists (DataFlow::AttrRead reMethod |
196+
reMethod = re_exec_attr() and
197+
node.getFunction() = reMethod.getObject().getALocalSource().asCfgNode() and
198+
result = reMethod.asExpr().(Attribute)
199+
)
200+
}
201+
}
202+
203+
/**
204+
* A class for modeling expressions immediately executing a regular expression.
205+
* See `re_exec_attr()`
206+
*/
207+
private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range {
208+
DataFlow::Node regexNode;
209+
Attribute regexMethod;
210+
211+
DirectRegex() {
212+
// needs inheritance (?)
213+
this = re_exec_attr() and regexNode = this.getRegexNode() and regexMethod = this.getRegexMethod()
214+
}
215+
216+
override DataFlow::Node getRegexNode() { result = regexNode }
217+
override Attribute getRegexMethod() { result = regexMethod }
218+
}
219+
220+
/**
221+
* A class for finding `ReCompileCall` whose `Attribute` is an instance of `DirectRegex`.
222+
* See `ReCompileCall`, `DirectRegex`, `re_exec_attr()`
223+
*/
224+
private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range {
225+
DataFlow::Node regexNode;
226+
Attribute regexMethod;
227+
228+
CompiledRegex() {
229+
exists(DirectRegex reMethod, ReCompileCall compileCall |
230+
this = reMethod and
231+
reMethod.getRegexMethod() = compileCall.getRegexMethod() and
232+
regexNode = compileCall.getRegexNode() and
233+
regexMethod = reMethod.getRegexMethod()
234+
)
235+
}
236+
237+
override DataFlow::Node getRegexNode() { result = regexNode }
238+
override Attribute getRegexMethod() { result = regexMethod }
239+
}
240+
}
241+
}

python/ql/src/semmle/python/security/dataflow/RegexInjection.qll renamed to python/ql/src/experimental/semmle/python/security/injection/RegexInjection.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import python
7-
import semmle.python.Concepts
7+
import experimental.semmle.python.Concepts
88
import semmle.python.dataflow.new.DataFlow
99
import semmle.python.dataflow.new.TaintTracking
1010
import semmle.python.dataflow.new.RemoteFlowSources

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

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -625,49 +625,5 @@ module Cryptography {
625625
final override int minimumSecureKeySize() { result = 224 }
626626
}
627627
}
628-
/*
629-
*/
630-
631-
class ReMethods extends string {
632-
ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] }
633-
}
634-
635-
class DirectRegex extends DataFlow::Node {
636-
DirectRegex() {
637-
exists(ReMethods reMethod, DataFlow::CallCfgNode reCall |
638-
reCall = API::moduleImport("re").getMember(reMethod).getACall() and
639-
this = reCall.getArg(0)
640-
)
641628
}
642-
}
643-
644-
class CompiledRegex extends DataFlow::Node {
645-
CompiledRegex() {
646-
exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod |
647-
patternCall = API::moduleImport("re").getMember("compile").getACall() and
648-
patternCall = reMethod.getObject().getALocalSource() and
649-
reMethod.getAttributeName() instanceof ReMethods and
650-
this = patternCall.getArg(0)
651-
)
652-
}
653-
}
654-
655-
class RegexExecution extends DataFlow::Node {
656-
RegexExecution() { this instanceof DirectRegex or this instanceof CompiledRegex } // How should this be cross-imported with Stdlib?
657-
}
658-
/*
659-
*/
660-
661-
module RegexExecution {
662-
abstract class Range extends DataFlow::Node {
663-
abstract DataFlow::Node getRegexNode();
664-
}
665-
}
666-
667-
class RegexExecution extends DataFlow::Node {
668-
RegexExecution::Range range;
669-
670-
RegexExecution() { this = range }
671-
672-
DataFlow::Node getRegexNode() { result = range.getRegexNode() }
673-
}
629+
}

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

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -864,52 +864,6 @@ private module Stdlib {
864864
class Sqlite3 extends PEP249ModuleApiNode {
865865
Sqlite3() { this = API::moduleImport("sqlite3") }
866866
}
867-
868-
// ---------------------------------------------------------------------------
869-
// re
870-
// ---------------------------------------------------------------------------
871-
private module Re {
872-
/** List of re methods. */
873-
private class ReMethods extends string {
874-
ReMethods() { this in ["match", "fullmatch", "search", "split", "findall", "finditer"] }
875-
}
876-
877-
/** re.ReMethod(pattern, string) */
878-
private class DirectRegex extends DataFlow::CallCfgNode, RegexExecution::Range {
879-
DataFlow::Node regexNode;
880-
881-
DirectRegex() {
882-
this = API::moduleImport("re").getMember(any(ReMethods m)).getACall() and
883-
regexNode = this.getArg(0)
884-
}
885-
886-
override DataFlow::Node getRegexNode() { result = regexNode }
887-
}
888-
889-
/** re.compile(pattern).ReMethod */
890-
private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution::Range {
891-
DataFlow::Node regexNode;
892-
893-
CompiledRegex() {
894-
exists(DataFlow::CallCfgNode patternCall, DataFlow::AttrRead reMethod |
895-
this.getFunction() = reMethod and
896-
patternCall = API::moduleImport("re").getMember("compile").getACall() and
897-
patternCall = reMethod.getObject().getALocalSource() and
898-
reMethod.getAttributeName() instanceof ReMethods and
899-
regexNode = patternCall.getArg(0)
900-
)
901-
}
902-
903-
override DataFlow::Node getRegexNode() { result = regexNode }
904-
}
905-
906-
private class RegexEscape extends DataFlow::Node {
907-
RegexEscape() {
908-
this =
909-
API::moduleImport("re").getMember("escape").getACall().(DataFlow::CallCfgNode).getArg(0)
910-
}
911-
}
912-
}
913867
}
914868

915869
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)