Skip to content

Commit 54b4e59

Browse files
authored
Merge pull request #1182 from esben-semmle/js/sourcenode-regexp-literals
Approved by xiemaisi
2 parents a4de82d + 6908c54 commit 54b4e59

File tree

16 files changed

+53
-9
lines changed

16 files changed

+53
-9
lines changed

change-notes/1.21/analysis-javascript.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,15 @@
1717

1818
| **Query** | **Expected impact** | **Change** |
1919
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
20-
| Expression has no effect | Fewer false-positive results | This rule now treats uses of `Object.defineProperty` more conservatively. |
21-
| Useless assignment to property | Fewer false-positive results | This rule now ignores reads of additional getters. |
2220
| Arbitrary file write during zip extraction ("Zip Slip") | More results | This rule now considers more libraries, including tar as well as zip. |
23-
| Client-side URL redirect | Fewer false-positive results | This rule now treats URLs as safe in more cases where the hostname cannot be tampered with. |
21+
| Client-side URL redirect | More results and fewer false-positive results | This rule now recognizes additional uses of the document URL. This rule now treats URLs as safe in more cases where the hostname cannot be tampered with. |
22+
| Double escaping or unescaping | More results | This rule now considers the flow of regular expressions literals. |
23+
| Expression has no effect | Fewer false-positive results | This rule now treats uses of `Object.defineProperty` more conservatively. |
24+
| Incomplete string escaping or encoding | More results | This rule now considers the flow of regular expressions literals. |
25+
| Replacement of a substring with itself | More results | This rule now considers the flow of regular expressions literals. |
2426
| Server-side URL redirect | Fewer false-positive results | This rule now treats URLs as safe in more cases where the hostname cannot be tampered with. |
27+
| Useless assignment to property | Fewer false-positive results | This rule now ignore reads of additional getters. |
2528

2629
## Changes to QL libraries
30+
31+
* `RegExpLiteral` is now a `DataFlow::SourceNode`.

javascript/ql/src/RegExp/IdentityReplacement.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import javascript
1818
*/
1919
predicate matchesString(Expr e, string s) {
2020
exists(RegExpLiteral rl |
21-
rl = e and
21+
rl.flow().(DataFlow::SourceNode).flowsToExpr(e) and
2222
not rl.isIgnoreCase() and
2323
regExpMatchesString(rl.getRoot(), s)
2424
)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class Replacement extends DataFlow::Node {
7070
Replacement() {
7171
exists(DataFlow::MethodCallNode mcn | this = mcn |
7272
mcn.getMethodName() = "replace" and
73-
mcn.getArgument(0).asExpr() = pattern and
73+
pattern.flow().(DataFlow::SourceNode).flowsTo(mcn.getArgument(0))and
7474
mcn.getNumArgument() = 2 and
7575
pattern.isGlobal()
7676
)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ predicate isSimple(RegExpTerm t) {
5959
*/
6060
predicate isBackslashEscape(MethodCallExpr mce, RegExpLiteral re) {
6161
mce.getMethodName() = "replace" and
62-
re = mce.getArgument(0) and
62+
re.flow().(DataFlow::SourceNode).flowsToExpr(mce.getArgument(0)) and
6363
re.isGlobal() and
6464
exists(string new | new = mce.getArgument(1).getStringValue() |
6565
// `new` is `\$&`, `\$1` or similar
@@ -104,7 +104,7 @@ predicate allBackslashesEscaped(DataFlow::Node nd) {
104104
from MethodCallExpr repl, Expr old, string msg
105105
where
106106
repl.getMethodName() = "replace" and
107-
old = repl.getArgument(0) and
107+
(old = repl.getArgument(0) or old.flow().(DataFlow::SourceNode).flowsToExpr(repl.getArgument(0))) and
108108
(
109109
not old.(RegExpLiteral).isGlobal() and
110110
msg = "This replaces only the first occurrence of " + old + "." and

javascript/ql/src/semmle/javascript/dataflow/Sources.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ module SourceNode {
211211
* - object expressions
212212
* - array expressions
213213
* - JSX literals
214+
* - regular expression literals
214215
*
215216
* This class is for internal use only and should not normally be used directly.
216217
*/
@@ -224,7 +225,8 @@ module SourceNode {
224225
astNode instanceof ArrayExpr or
225226
astNode instanceof JSXNode or
226227
astNode instanceof GlobalVarAccess or
227-
astNode instanceof ExternalModuleReference
228+
astNode instanceof ExternalModuleReference or
229+
astNode instanceof RegExpLiteral
228230
)
229231
or
230232
exists(SsaExplicitDefinition ssa, VarDef def |

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ module ClientSideUrlRedirect {
100100
or
101101
exists(MethodCallExpr mce |
102102
queryAccess.asExpr() = mce and
103-
mce.calls(any(RegExpLiteral re), "exec") and
103+
mce = any(RegExpLiteral re).flow().(DataFlow::SourceNode).getAMethodCall("exec").asExpr() and
104104
nd.asExpr() = mce.getArgument(0)
105105
)
106106
}

javascript/ql/test/library-tests/DataFlow/sources.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
| sources.js:3:2:3:1 | this |
1212
| sources.js:3:2:5:1 | functio ... x+19;\\n} |
1313
| sources.js:3:11:3:11 | x |
14+
| sources.js:7:1:7:3 | /x/ |
1415
| tst.js:1:1:1:0 | this |
1516
| tst.js:1:1:1:24 | import ... m 'fs'; |
1617
| tst.js:1:10:1:11 | fs |

javascript/ql/test/library-tests/DataFlow/sources.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ new (x => x);
33
(function(x) {
44
return x+19;
55
})(23);
6+
7+
/x/;

javascript/ql/test/query-tests/RegExp/IdentityReplacement/IdentityReplacement.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
| IdentityReplacement.js:1:27:1:30 | /"/g | This replaces '"' with itself. |
2+
| IdentityReplacement.js:4:14:4:21 | indirect | This replaces '"' with itself. |
23
| tst.js:1:13:1:16 | "\\\\" | This replaces '\\' with itself. |
34
| tst.js:2:13:2:18 | /(\\\\)/ | This replaces '\\' with itself. |
45
| tst.js:3:13:3:17 | /["]/ | This replaces '"' with itself. |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
var escaped = raw.replace(/"/g, '\"');
2+
(function() {
3+
var indirect = /"/g;
4+
raw.replace(indirect, '\"');
5+
});

0 commit comments

Comments
 (0)