Skip to content

Commit b36e9e0

Browse files
committed
JS: Filter out common string literal sinks
1 parent f563a01 commit b36e9e0

File tree

2 files changed

+43
-4
lines changed

2 files changed

+43
-4
lines changed

javascript/ql/src/semmle/javascript/frameworks/Templating.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ module Templating {
7575
getRawText().regexpMatch(getLikelyTemplateSyntax(getFile()).getRawInterpolationRegexp())
7676
}
7777

78+
/**
79+
* Holds if this performs HTML escaping on the result before inserting it in the template.
80+
*/
81+
predicate isEscapingInterpolation() {
82+
getRawText().regexpMatch(getLikelyTemplateSyntax(getFile()).getEscapingInterpolationRegexp())
83+
}
84+
7885
/** Holds if this occurs in a `script` tag. */
7986
predicate isInScriptTag() {
8087
// We want to exclude non-code scripts like JSON.

javascript/ql/src/semmle/javascript/security/dataflow/CodeInjectionCustomizations.qll

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,47 @@ module CodeInjection {
5656
*/
5757
class TemplateTagInScriptSink extends Sink {
5858
TemplateTagInScriptSink() {
59-
// Note: currently viewing all tags in code as sinks, but this can lead to
60-
// some FPs when values are escaped correctly.
6159
exists(Templating::TemplatePlaceholderTag tag |
62-
tag.isInCodeContext() and
63-
this = tag.asDataFlowNode()
60+
this = tag.asDataFlowNode() and
61+
tag.isEscapingInterpolation() // to avoid double reporting, raw interpolation is only flagged by the XSS query
62+
|
63+
// In an attribute, HTML entities are expanded prior to JS parsing, so the escaping performed by the
64+
// templating engine has no effect against code injection.
65+
tag.isInCodeAttribute()
66+
or
67+
// In a script tag, HTML entities are not expanded.
68+
// To reduce noise, we filter out a common pattern where a template tag occurs in a string literal,
69+
// since HTML escaping prevents breaking out of the string literal.
70+
//
71+
// var foo = "<%= foo %>";
72+
//
73+
// However, we still flag the special case where multiple such strings occur on the same line, as injection can sometimes
74+
// we obtained by injecting a backslash character at the end of the first one:
75+
//
76+
// init("<%= foo %">, "<%= bar %>");
77+
//
78+
// For example, setting foo to `\` and bar to `, alert(1));//`, code injection is obtained.
79+
tag.isInScriptTag() and
80+
not tag.getEnclosingExpr() = getLastStringWithPlaceholderOnLine(tag.getLocation().getFile(), tag.getLocation().getStartLine())
6481
)
6582
}
6683
}
6784

85+
/** Gets the last string literal containing a template placeholder on the given line. */
86+
pragma[nomagic]
87+
private StringLiteral getLastStringWithPlaceholderOnLine(File file, int line) {
88+
result = max(StringLiteral lit, Location loc |
89+
loc = lit.getLocation() and
90+
loc.getFile() = file and
91+
loc.getStartLine() = line and
92+
lit = any(Templating::TemplatePlaceholderTag tag | tag.isEscapingInterpolation()).getEnclosingExpr()
93+
|
94+
lit
95+
order by
96+
loc.getStartColumn()
97+
)
98+
}
99+
68100
/**
69101
* A server-side template tag occurring in the context of another template language.
70102
*/

0 commit comments

Comments
 (0)