Skip to content

Commit 1e1674a

Browse files
authored
Merge pull request github#18089 from Napalys/napalys/regexp-unknown-flags
JS: RegExp unknown flags support and enhanced compatibility with RegExp objects
2 parents 6b7522f + 08ef0dc commit 1e1674a

36 files changed

+761
-24
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
category: majorAnalysis
3+
---
4+
* The `js/incomplete-sanitization` query now also checks regular expressions constructed using `new RegExp(..)`. Previously it only checked regular expression literals.
5+
* Regular expression-based sanitisers implemented with `new RegExp(..)` are now detected in more cases.
6+
* Regular expression related queries now account for unknown flags.

javascript/ql/lib/semmle/javascript/StandardLibrary.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ class StringReplaceCall extends DataFlow::MethodCallNode {
117117
*/
118118
predicate isGlobal() { this.getRegExp().isGlobal() or this.getMethodName() = "replaceAll" }
119119

120+
/**
121+
* Holds if this is a global replacement, that is, the first argument is a regular expression
122+
* with the `g` flag or unknown flags, or this is a call to `.replaceAll()`.
123+
*/
124+
predicate maybeGlobal() { this.getRegExp().maybeGlobal() or this.getMethodName() = "replaceAll" }
125+
120126
/**
121127
* Holds if this call to `replace` replaces `old` with `new`.
122128
*/

javascript/ql/lib/semmle/javascript/dataflow/Nodes.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,6 +1685,9 @@ class RegExpCreationNode extends DataFlow::SourceNode {
16851685
/** Holds if the constructed predicate has the `g` flag. */
16861686
predicate isGlobal() { RegExp::isGlobal(this.getFlags()) }
16871687

1688+
/** Holds if the constructed predicate has the `g` flag or unknown flags. */
1689+
predicate maybeGlobal() { RegExp::maybeGlobal(this.tryGetFlags()) }
1690+
16881691
/** Gets a data flow node referring to this regular expression. */
16891692
private DataFlow::SourceNode getAReference(DataFlow::TypeTracker t) {
16901693
t.start() and

javascript/ql/lib/semmle/javascript/security/IncompleteBlacklistSanitizer.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ private StringReplaceCall getAStringReplaceMethodCall(StringReplaceCall n) {
7474
module HtmlSanitization {
7575
private predicate fixedGlobalReplacement(StringReplaceCallSequence chain) {
7676
forall(StringReplaceCall member | member = chain.getAMember() |
77-
member.isGlobal() and member.getArgument(0) instanceof DataFlow::RegExpLiteralNode
77+
member.maybeGlobal() and member.getArgument(0) instanceof DataFlow::RegExpCreationNode
7878
)
7979
}
8080

javascript/ql/lib/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ module CleartextLogging {
3636
*/
3737
class MaskingReplacer extends Barrier, StringReplaceCall {
3838
MaskingReplacer() {
39-
this.isGlobal() and
39+
this.maybeGlobal() and
4040
exists(this.getRawReplacement().getStringValue()) and
41-
any(RegExpDot term).getLiteral() = this.getRegExp().asExpr()
41+
exists(DataFlow::RegExpCreationNode regexpObj |
42+
this.(StringReplaceCall).getRegExp() = regexpObj and
43+
regexpObj.getRoot() = any(RegExpDot term).getRootTerm()
44+
)
4245
}
4346
}
4447

javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class Configuration extends TaintTracking::Configuration {
4646
// Replacing with "_" is likely to be exploitable
4747
not replace.getRawReplacement().getStringValue() = "_" and
4848
(
49-
replace.isGlobal()
49+
replace.maybeGlobal()
5050
or
5151
// Non-global replace with a non-empty string can also prevent __proto__ by
5252
// inserting a chunk of text that doesn't fit anywhere in __proto__

javascript/ql/lib/semmle/javascript/security/dataflow/RegExpInjectionCustomizations.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ module RegExpInjection {
7676
*/
7777
class MetacharEscapeSanitizer extends Sanitizer, StringReplaceCall {
7878
MetacharEscapeSanitizer() {
79-
this.isGlobal() and
79+
this.maybeGlobal() and
8080
(
8181
RegExp::alwaysMatchesMetaCharacter(this.getRegExp().getRoot(), ["{", "[", "+"])
8282
or

javascript/ql/lib/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,10 @@ module TaintedPath {
221221
this instanceof StringReplaceCall and
222222
input = this.getReceiver() and
223223
output = this and
224-
not exists(RegExpLiteral literal, RegExpTerm term |
225-
this.(StringReplaceCall).getRegExp().asExpr() = literal and
226-
this.(StringReplaceCall).isGlobal() and
227-
literal.getRoot() = term
224+
not exists(DataFlow::RegExpCreationNode regexp, RegExpTerm term |
225+
this.(StringReplaceCall).getRegExp() = regexp and
226+
this.(StringReplaceCall).maybeGlobal() and
227+
regexp.getRoot() = term
228228
|
229229
term.getAMatchedString() = "/" or
230230
term.getAMatchedString() = "." or
@@ -305,9 +305,9 @@ module TaintedPath {
305305
input = this.getReceiver() and
306306
output = this and
307307
this.isGlobal() and
308-
exists(RegExpLiteral literal, RegExpTerm term |
309-
this.getRegExp().asExpr() = literal and
310-
literal.getRoot() = term and
308+
exists(DataFlow::RegExpCreationNode regexp, RegExpTerm term |
309+
this.getRegExp() = regexp and
310+
regexp.getRoot() = term and
311311
not term.getAMatchedString() = "/"
312312
|
313313
term.getAMatchedString() = "." or

javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ module UnsafeShellCommandConstruction {
245245
class ReplaceQuotesSanitizer extends Sanitizer, StringReplaceCall {
246246
ReplaceQuotesSanitizer() {
247247
this.getAReplacedString() = "'" and
248-
this.isGlobal() and
248+
this.maybeGlobal() and
249249
this.getRawReplacement().mayHaveStringValue(["'\\''", ""])
250250
}
251251
}

javascript/ql/lib/semmle/javascript/security/dataflow/Xss.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ module Shared {
3636
*/
3737
class MetacharEscapeSanitizer extends Sanitizer, StringReplaceCall {
3838
MetacharEscapeSanitizer() {
39-
this.isGlobal() and
39+
this.maybeGlobal() and
4040
(
4141
RegExp::alwaysMatchesMetaCharacter(this.getRegExp().getRoot(), ["<", "'", "\""])
4242
or

0 commit comments

Comments
 (0)