Skip to content

Commit c265c15

Browse files
authored
Merge pull request #15398 from RasmusWL/html-escape
Python: Add `html.escape` as HTML sanitizer
2 parents 8aa3542 + c70b32f commit c265c15

File tree

3 files changed

+42
-0
lines changed

3 files changed

+42
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added `html.escape` as a sanitizer for HTML.

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4830,6 +4830,35 @@ module StdlibPrivate {
48304830
override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getCommand() }
48314831
}
48324832
}
4833+
4834+
// ---------------------------------------------------------------------------
4835+
// html
4836+
// ---------------------------------------------------------------------------
4837+
/**
4838+
* A call to 'html.escape'.
4839+
* See https://docs.python.org/3/library/html.html#html.escape
4840+
*/
4841+
private class HtmlEscapeCall extends Escaping::Range, API::CallNode {
4842+
HtmlEscapeCall() {
4843+
this = API::moduleImport("html").getMember("escape").getACall() and
4844+
// if quote escaping is disabled, that might lead to XSS if the result is inserted
4845+
// in the attribute value of a tag, such as `<foo bar="escape_result">`. Since we
4846+
// don't know how values are being inserted, and we don't want to lose these
4847+
// results (FNs), we require quote escaping to be enabled. This might lead to some
4848+
// FPs, so we might need to revisit this in the future.
4849+
not this.getParameter(1, "quote")
4850+
.getAValueReachingSink()
4851+
.asExpr()
4852+
.(ImmutableLiteral)
4853+
.booleanValue() = false
4854+
}
4855+
4856+
override DataFlow::Node getAnInput() { result = this.getParameter(0, "s").asSink() }
4857+
4858+
override DataFlow::Node getOutput() { result = this }
4859+
4860+
override string getKind() { result = Escaping::getHtmlKind() }
4861+
}
48334862
}
48344863

48354864
// ---------------------------------------------------------------------------
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import html
2+
3+
s = "tainted"
4+
5+
html.escape(s) # $ escapeInput=s escapeKind=html escapeOutput=html.escape(..)
6+
html.escape(s, True) # $ escapeInput=s escapeKind=html escapeOutput=html.escape(..)
7+
# not considered html escapes, since they don't escape all relevant characters
8+
html.escape(s, False)
9+
html.escape(s, quote=False)

0 commit comments

Comments
 (0)