Skip to content

Commit a5a5deb

Browse files
author
Max Schaefer
committed
JavaScript: Move getStringValue(RegExpLiteral) into the library.
1 parent 0edb70f commit a5a5deb

2 files changed

Lines changed: 26 additions & 27 deletions

File tree

javascript/ql/src/Security/CWE-116/DoubleEscaping.ql

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,6 @@
1515

1616
import javascript
1717

18-
/**
19-
* Holds if `rl` is a simple constant, which is bound to the result of the predicate.
20-
*
21-
* For example, `/a/g` has string value `"a"` and `/abc/` has string value `"abc"`,
22-
* while `/ab?/` and `/a(?=b)/` do not have a string value.
23-
*
24-
* Flags are ignored, so `/a/i` is still considered to have string value `"a"`,
25-
* even though it also matches `"A"`.
26-
*
27-
* Note the somewhat subtle use of monotonic aggregate semantics, which makes the
28-
* `strictconcat` fail if one of the children of the root is not a constant (legacy
29-
* semantics would simply skip such children).
30-
*/
31-
language[monotonicAggregates]
32-
string getStringValue(RegExpLiteral rl) {
33-
exists(RegExpTerm root | root = rl.getRoot() |
34-
result = root.(RegExpConstant).getValue()
35-
or
36-
result = strictconcat(RegExpTerm ch, int i |
37-
ch = root.(RegExpSequence).getChild(i)
38-
|
39-
ch.(RegExpConstant).getValue() order by i
40-
)
41-
)
42-
}
43-
4418
/**
4519
* Gets a predecessor of `nd` that is not an SSA phi node.
4620
*/
@@ -163,7 +137,7 @@ class GlobalStringReplacement extends Replacement, DataFlow::MethodCallNode {
163137
}
164138

165139
override predicate replaces(string input, string output) {
166-
input = getStringValue(pattern) and
140+
input = pattern.getRoot().getConstantValue() and
167141
output = this.getArgument(1).getStringValue()
168142
or
169143
exists(DataFlow::FunctionNode replacer, DataFlow::PropRead pr, DataFlow::ObjectLiteralNode map |

javascript/ql/src/semmle/javascript/Regexp.qll

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,18 @@ class RegExpTerm extends Locatable, @regexpterm {
173173
parent.(StringLiteral).flow() instanceof RegExpPatternSource
174174
)
175175
}
176+
177+
/**
178+
* Gets the single string this regular-expression term matches.
179+
*
180+
* This predicate is only defined for (sequences/groups of) constant regular expressions.
181+
* In particular, terms involving zero-width assertions like `^` or `\b` are not considered
182+
* to have a constant value.
183+
*
184+
* Note that this predicate does not take flags of the enclosing regular-expression literal
185+
* into account.
186+
*/
187+
string getConstantValue() { none() }
176188
}
177189

178190
/**
@@ -223,6 +235,8 @@ class RegExpConstant extends RegExpTerm, @regexp_constant {
223235
predicate isCharacter() { any() }
224236

225237
override predicate isNullable() { none() }
238+
239+
override string getConstantValue() { result = getValue() }
226240
}
227241

228242
/**
@@ -289,6 +303,15 @@ class RegExpSequence extends RegExpTerm, @regexp_seq {
289303
override predicate isNullable() {
290304
forall(RegExpTerm child | child = getAChild() | child.isNullable())
291305
}
306+
307+
language[monotonicAggregates]
308+
override string getConstantValue() {
309+
// note: due to use of monotonic aggregates, this `strictconcat` will fail if
310+
// `getConstantValue` is undefined for any child
311+
result = strictconcat(RegExpTerm ch, int i | ch = getChild(i) |
312+
ch.getConstantValue() order by i
313+
)
314+
}
292315
}
293316

294317
/**
@@ -549,6 +572,8 @@ class RegExpGroup extends RegExpTerm, @regexp_group {
549572
string getName() { isNamedCapture(this, result) }
550573

551574
override predicate isNullable() { getAChild().isNullable() }
575+
576+
override string getConstantValue() { result = getAChild().getConstantValue() }
552577
}
553578

554579
/**

0 commit comments

Comments
 (0)