Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7adc3c2

Browse files
committed
Upload ReDoS query, qhelp and tests
1 parent 3737764 commit 7adc3c2

4 files changed

Lines changed: 153 additions & 0 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
3+
<qhelp>
4+
<overview>
5+
<p>If aregular expression is built by a not escaped user-provided value, a user is likely to be able to cause a Denial of Service.</p>
6+
</overview>
7+
8+
<recommendation>
9+
<p>In case user input must compose a regular expression, it should be escaped with functions such as <code>re.escape</code>.
10+
<recommendation>
11+
12+
<references>
13+
<li>
14+
OWASP
15+
<a href="https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS">Regular Expression DoS</a>
16+
</li>
17+
<li>
18+
SonarSource
19+
<a href="https://rules.sonarsource.com/python/type/Vulnerability/RSPEC-2631">RSPEC-2631</a>
20+
</li>
21+
<li>
22+
CWE-
23+
<a href="http://cwe.mitre.org/data/definitions/400">400</a>
24+
</li>
25+
</references>
26+
27+
</qhelp>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
* @name Python Regex DoS
3+
* @description Python Regular Expression Denial of Service
4+
* @kind path-problem
5+
* @problem.severity error
6+
* @id python/regex-dos
7+
* @tags experimental
8+
* security
9+
* external/cwe/cwe-400
10+
*/
11+
12+
import python
13+
import semmle.python.dataflow.new.RemoteFlowSources
14+
import semmle.python.dataflow.new.DataFlow
15+
import semmle.python.dataflow.new.TaintTracking
16+
import semmle.python.dataflow.new.internal.TaintTrackingPublic
17+
import DataFlow::PathGraph
18+
19+
class ReMethods extends string {
20+
ReMethods() {
21+
this = "match" or
22+
this = "fullmatch" or
23+
this = "search" or
24+
this = "split" or
25+
this = "findall" or
26+
this = "finditer"
27+
}
28+
}
29+
30+
class DirectRegex extends DataFlow::Node {
31+
DirectRegex() {
32+
exists(string reMethod, CallNode reCall |
33+
reMethod instanceof ReMethods and
34+
reCall = Value::named("re." + reMethod).getACall() and
35+
this.asExpr() = reCall.getArg(0).getNode()
36+
)
37+
}
38+
}
39+
40+
class CompiledRegex extends DataFlow::Node {
41+
CompiledRegex() {
42+
exists(CallNode patternCall, SsaVariable patternVar, CallNode reMethodCall |
43+
patternCall = Value::named("re.compile").getACall() and
44+
patternVar.getDefinition().getImmediateDominator() = patternCall and
45+
patternVar.getAUse().getNode() = reMethodCall.getNode().getFunc().(Attribute).getObject() and
46+
reMethodCall.getNode().getFunc().(Attribute).getName() instanceof ReMethods and
47+
this.asExpr() = patternCall.getArg(0).getNode()
48+
)
49+
}
50+
}
51+
52+
class RegexDoSSink extends DataFlow::Node {
53+
RegexDoSSink() { this instanceof DirectRegex or this instanceof CompiledRegex }
54+
}
55+
56+
class EscapeSanitizer extends DataFlow::Node {
57+
EscapeSanitizer() {
58+
exists(Call c |
59+
(
60+
// avoid flow through any %escape% function
61+
c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%()
62+
c.getFunc().(Name).getId().matches("%escape%") // %escape%()
63+
) and
64+
this.asExpr() = c
65+
)
66+
}
67+
}
68+
69+
class RegexDoSFlowConfig extends TaintTracking::Configuration {
70+
RegexDoSFlowConfig() { this = "RegexDoSFlowConfig" }
71+
72+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
73+
74+
override predicate isSink(DataFlow::Node sink) { sink instanceof RegexDoSSink }
75+
76+
override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof EscapeSanitizer }
77+
}
78+
79+
from RegexDoSFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
80+
where config.hasFlowPath(source, sink)
81+
select sink.getNode(), source, sink, "$@ regex operation includes $@.", sink.getNode(), "This",
82+
source.getNode(), "a user-provided value"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from flask import request, Flask
2+
import re
3+
4+
app = Flask(__name__)
5+
6+
7+
@app.route("/direct")
8+
def direct():
9+
pattern = request.args['pattern']
10+
11+
re.search(pattern, "")
12+
13+
14+
@app.route("/compile")
15+
def compile():
16+
pattern = re.compile(request.args['pattern'])
17+
18+
pattern.search("")
19+
20+
21+
# if __name__ == "__main__":
22+
# app.run(debug=True)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from flask import request, Flask
2+
import re
3+
4+
app = Flask(__name__)
5+
6+
7+
@app.route("/direct")
8+
def direct():
9+
pattern = re.escape(request.args['pattern'])
10+
11+
re.search(pattern, "")
12+
13+
14+
@app.route("/compile")
15+
def compile():
16+
pattern = re.compile(re.escape(request.args['pattern']))
17+
18+
pattern.search("")
19+
20+
21+
# if __name__ == "__main__":
22+
# app.run(debug=True)

0 commit comments

Comments
 (0)