Move to experimental folder

This commit is contained in:
jorgectf
2021-03-18 20:19:08 +01:00
parent afc4f51e9c
commit 21f8135fa6
4 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<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>
</overview>
<recommendation>
<p>In case user input must compose a regular expression, it should be escaped with functions such as <code>re.escape</code>.
<recommendation>
<references>
<li>
OWASP
<a href="https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS">Regular Expression DoS</a>
</li>
<li>
SonarSource
<a href="https://rules.sonarsource.com/python/type/Vulnerability/RSPEC-2631">RSPEC-2631</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,86 @@
/**
* @name Regular expression injection
* @description User input should not be used in regular expressions without first being escaped,
* otherwise a malicious user may be able to inject an expression that could require
* exponential time on certain inputs.
* @kind path-problem
* @problem.severity error
* @id python/regex-injection
* @tags security
* external/cwe/cwe-730
* external/cwe/cwe-400
*/
// determine precision above
import python
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.dataflow.new.internal.TaintTrackingPublic
import DataFlow::PathGraph
// Should this be moved to a different structure? (For other queries to be able to use it)
class ReMethods extends string {
ReMethods() {
this = "match" or
this = "fullmatch" or
this = "search" or
this = "split" or
this = "findall" or
this = "finditer"
}
}
class DirectRegex extends DataFlow::Node {
DirectRegex() {
exists(string reMethod, CallNode reCall |
reMethod instanceof ReMethods and
reCall = Value::named("re." + reMethod).getACall() and
this.asExpr() = reCall.getArg(0).getNode()
)
}
}
class CompiledRegex extends DataFlow::Node {
CompiledRegex() {
exists(CallNode patternCall, SsaVariable patternVar, CallNode reMethodCall |
patternCall = Value::named("re.compile").getACall() and
patternVar.getDefinition().getImmediateDominator() = patternCall and
patternVar.getAUse().getNode() = reMethodCall.getNode().getFunc().(Attribute).getObject() and
reMethodCall.getNode().getFunc().(Attribute).getName() instanceof ReMethods and
this.asExpr() = patternCall.getArg(0).getNode()
)
}
}
class RegexInjectionSink extends DataFlow::Node {
RegexInjectionSink() { this instanceof DirectRegex or this instanceof CompiledRegex }
}
class EscapeSanitizer extends DataFlow::Node {
EscapeSanitizer() {
exists(Call c |
(
// avoid flow through any %escape% function
c.getFunc().(Attribute).getName().matches("%escape%") or // something.%escape%()
c.getFunc().(Name).getId().matches("%escape%") // %escape%()
) and
this.asExpr() = c
)
}
}
class RegexInjectionFlowConfig extends TaintTracking::Configuration {
RegexInjectionFlowConfig() { this = "RegexInjectionFlowConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof RegexInjectionSink }
override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof EscapeSanitizer }
}
from RegexInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "$@ regular expression is constructed from a $@.",
sink.getNode(), "This", source.getNode(), "user-provided value"

View File

@@ -0,0 +1,22 @@
from flask import request, Flask
import re
app = Flask(__name__)
@app.route("/direct")
def direct():
pattern = request.args['pattern']
re.search(pattern, "")
@app.route("/compile")
def compile():
pattern = re.compile(request.args['pattern'])
pattern.search("")
# if __name__ == "__main__":
# app.run(debug=True)

View File

@@ -0,0 +1,22 @@
from flask import request, Flask
import re
app = Flask(__name__)
@app.route("/direct")
def direct():
pattern = re.escape(request.args['pattern'])
re.search(pattern, "")
@app.route("/compile")
def compile():
pattern = re.compile(re.escape(request.args['pattern']))
pattern.search("")
# if __name__ == "__main__":
# app.run(debug=True)