Skip to content

Commit c040d48

Browse files
committed
Swift: Copy MissingRegexAnchor query from JS.
1 parent dc9f171 commit c040d48

File tree

8 files changed

+449
-0
lines changed

8 files changed

+449
-0
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
9+
Sanitizing untrusted input with regular expressions is a
10+
common technique. However, it is error-prone to match untrusted input
11+
against regular expressions without anchors such as <code>^</code> or
12+
<code>$</code>. Malicious input can bypass such security checks by
13+
embedding one of the allowed patterns in an unexpected location.
14+
15+
</p>
16+
17+
<p>
18+
19+
Even if the matching is not done in a security-critical
20+
context, it may still cause undesirable behavior when the regular
21+
expression accidentally matches.
22+
23+
</p>
24+
</overview>
25+
26+
<recommendation>
27+
<p>
28+
29+
Use anchors to ensure that regular expressions match at
30+
the expected locations.
31+
32+
</p>
33+
</recommendation>
34+
35+
<example>
36+
37+
<p>
38+
39+
The following example code checks that a URL redirection
40+
will reach the <code>example.com</code> domain, or one of its
41+
subdomains, and not some malicious site.
42+
43+
</p>
44+
45+
<sample src="examples/MissingRegExpAnchor_BAD.js"/>
46+
47+
<p>
48+
49+
The check with the regular expression match is, however, easy to bypass. For example
50+
by embedding <code>http://example.com/</code> in the query
51+
string component: <code>http://evil-example.net/?x=http://example.com/</code>.
52+
53+
Address these shortcomings by using anchors in the regular expression instead:
54+
55+
</p>
56+
57+
<sample src="examples/MissingRegExpAnchor_GOOD.js"/>
58+
59+
<p>
60+
61+
A related mistake is to write a regular expression with
62+
multiple alternatives, but to only include an anchor for one of the
63+
alternatives. As an example, the regular expression
64+
<code>/^www\.example\.com|beta\.example\.com/</code> will match the host
65+
<code>evil.beta.example.com</code> because the regular expression is parsed
66+
as <code>/(^www\.example\.com)|(beta\.example\.com)/</code>
67+
68+
</p>
69+
</example>
70+
71+
<references>
72+
<li>MDN: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions">Regular Expressions</a></li>
73+
<li>OWASP: <a href="https://www.owasp.org/index.php/Server_Side_Request_Forgery">SSRF</a></li>
74+
<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html">XSS Unvalidated Redirects and Forwards Cheat Sheet</a>.</li>
75+
</references>
76+
</qhelp>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @name Missing regular expression anchor
3+
* @description Regular expressions without anchors can be vulnerable to bypassing.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @security-severity 7.8
7+
* @precision medium
8+
* @id js/regex/missing-regexp-anchor
9+
* @tags correctness
10+
* security
11+
* external/cwe/cwe-020
12+
*/
13+
14+
private import javascript
15+
private import semmle.javascript.security.regexp.HostnameRegexp as HostnameRegexp
16+
private import codeql.regex.MissingRegExpAnchor as MissingRegExpAnchor
17+
private import semmle.javascript.security.regexp.RegExpTreeView::RegExpTreeView as TreeImpl
18+
19+
private module Impl implements
20+
MissingRegExpAnchor::MissingRegExpAnchorSig<TreeImpl, HostnameRegexp::Impl>
21+
{
22+
predicate isUsedAsReplace(RegExpPatternSource pattern) {
23+
// is used for capture or replace
24+
exists(DataFlow::MethodCallNode mcn, string name | name = mcn.getMethodName() |
25+
name = "exec" and
26+
mcn = pattern.getARegExpObject().getAMethodCall() and
27+
exists(mcn.getAPropertyRead())
28+
or
29+
exists(DataFlow::Node arg |
30+
arg = mcn.getArgument(0) and
31+
(
32+
pattern.getARegExpObject().flowsTo(arg) or
33+
pattern.getAParse() = arg
34+
)
35+
|
36+
name = "replace"
37+
or
38+
name = "match" and exists(mcn.getAPropertyRead())
39+
)
40+
)
41+
}
42+
43+
string getEndAnchorText() { result = "$" }
44+
}
45+
46+
import MissingRegExpAnchor::Make<TreeImpl, HostnameRegexp::Impl, Impl>
47+
48+
from DataFlow::Node nd, string msg
49+
where
50+
isUnanchoredHostnameRegExp(nd, msg)
51+
or
52+
isSemiAnchoredHostnameRegExp(nd, msg)
53+
or
54+
hasMisleadingAnchorPrecedence(nd, msg)
55+
// isLineAnchoredHostnameRegExp is not used here, as it is not relevant to JS.
56+
select nd, msg
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
app.get("/some/path", function(req, res) {
2+
let url = req.param("url");
3+
// BAD: the host of `url` may be controlled by an attacker
4+
if (url.match(/https?:\/\/www\.example\.com\//)) {
5+
res.redirect(url);
6+
}
7+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
app.get("/some/path", function(req, res) {
2+
let url = req.param("url");
3+
// GOOD: the host of `url` can not be controlled by an attacker
4+
if (url.match(/^https?:\/\/www\.example\.com\//)) {
5+
res.redirect(url);
6+
}
7+
});
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
| tst-SemiAnchoredRegExp.js:3:2:3:7 | /^a\|b/ | Misleading operator precedence. The subexpression '^a' is anchored at the beginning, but the other parts of this regular expression are not |
2+
| tst-SemiAnchoredRegExp.js:6:2:6:9 | /^a\|b\|c/ | Misleading operator precedence. The subexpression '^a' is anchored at the beginning, but the other parts of this regular expression are not |
3+
| tst-SemiAnchoredRegExp.js:12:2:12:9 | /^a\|(b)/ | Misleading operator precedence. The subexpression '^a' is anchored at the beginning, but the other parts of this regular expression are not |
4+
| tst-SemiAnchoredRegExp.js:14:2:14:11 | /^(a)\|(b)/ | Misleading operator precedence. The subexpression '^(a)' is anchored at the beginning, but the other parts of this regular expression are not |
5+
| tst-SemiAnchoredRegExp.js:17:2:17:7 | /a\|b$/ | Misleading operator precedence. The subexpression 'b$' is anchored at the end, but the other parts of this regular expression are not |
6+
| tst-SemiAnchoredRegExp.js:20:2:20:9 | /a\|b\|c$/ | Misleading operator precedence. The subexpression 'c$' is anchored at the end, but the other parts of this regular expression are not |
7+
| tst-SemiAnchoredRegExp.js:26:2:26:9 | /(a)\|b$/ | Misleading operator precedence. The subexpression 'b$' is anchored at the end, but the other parts of this regular expression are not |
8+
| tst-SemiAnchoredRegExp.js:28:2:28:11 | /(a)\|(b)$/ | Misleading operator precedence. The subexpression '(b)$' is anchored at the end, but the other parts of this regular expression are not |
9+
| tst-SemiAnchoredRegExp.js:30:2:30:23 | /^good. ... er.com/ | Misleading operator precedence. The subexpression '^good.com' is anchored at the beginning, but the other parts of this regular expression are not |
10+
| tst-SemiAnchoredRegExp.js:31:2:31:25 | /^good\\ ... r\\.com/ | Misleading operator precedence. The subexpression '^good\\.com' is anchored at the beginning, but the other parts of this regular expression are not |
11+
| tst-SemiAnchoredRegExp.js:32:2:32:27 | /^good\\ ... \\\\.com/ | Misleading operator precedence. The subexpression '^good\\\\.com' is anchored at the beginning, but the other parts of this regular expression are not |
12+
| tst-SemiAnchoredRegExp.js:33:2:33:29 | /^good\\ ... \\\\.com/ | Misleading operator precedence. The subexpression '^good\\\\\\.com' is anchored at the beginning, but the other parts of this regular expression are not |
13+
| tst-SemiAnchoredRegExp.js:34:2:34:31 | /^good\\ ... \\\\.com/ | Misleading operator precedence. The subexpression '^good\\\\\\\\.com' is anchored at the beginning, but the other parts of this regular expression are not |
14+
| tst-SemiAnchoredRegExp.js:36:2:36:16 | /^foo\|bar\|baz$/ | Misleading operator precedence. The subexpression '^foo' is anchored at the beginning, but the other parts of this regular expression are not |
15+
| tst-SemiAnchoredRegExp.js:36:2:36:16 | /^foo\|bar\|baz$/ | Misleading operator precedence. The subexpression 'baz$' is anchored at the end, but the other parts of this regular expression are not |
16+
| tst-SemiAnchoredRegExp.js:42:13:42:18 | "^a\|b" | Misleading operator precedence. The subexpression '^a' is anchored at the beginning, but the other parts of this regular expression are not |
17+
| tst-SemiAnchoredRegExp.js:45:13:45:20 | "^a\|b\|c" | Misleading operator precedence. The subexpression '^a' is anchored at the beginning, but the other parts of this regular expression are not |
18+
| tst-SemiAnchoredRegExp.js:51:13:51:20 | "^a\|(b)" | Misleading operator precedence. The subexpression '^a' is anchored at the beginning, but the other parts of this regular expression are not |
19+
| tst-SemiAnchoredRegExp.js:53:13:53:22 | "^(a)\|(b)" | Misleading operator precedence. The subexpression '^(a)' is anchored at the beginning, but the other parts of this regular expression are not |
20+
| tst-SemiAnchoredRegExp.js:56:13:56:18 | "a\|b$" | Misleading operator precedence. The subexpression 'b$' is anchored at the end, but the other parts of this regular expression are not |
21+
| tst-SemiAnchoredRegExp.js:59:13:59:20 | "a\|b\|c$" | Misleading operator precedence. The subexpression 'c$' is anchored at the end, but the other parts of this regular expression are not |
22+
| tst-SemiAnchoredRegExp.js:65:13:65:20 | "(a)\|b$" | Misleading operator precedence. The subexpression 'b$' is anchored at the end, but the other parts of this regular expression are not |
23+
| tst-SemiAnchoredRegExp.js:67:13:67:22 | "(a)\|(b)$" | Misleading operator precedence. The subexpression '(b)$' is anchored at the end, but the other parts of this regular expression are not |
24+
| tst-SemiAnchoredRegExp.js:69:13:69:34 | '^good. ... er.com' | Misleading operator precedence. The subexpression '^good.com' is anchored at the beginning, but the other parts of this regular expression are not |
25+
| tst-SemiAnchoredRegExp.js:70:13:70:36 | '^good\\ ... r\\.com' | Misleading operator precedence. The subexpression '^good.com' is anchored at the beginning, but the other parts of this regular expression are not |
26+
| tst-SemiAnchoredRegExp.js:71:13:71:38 | '^good\\ ... \\\\.com' | Misleading operator precedence. The subexpression '^good\\.com' is anchored at the beginning, but the other parts of this regular expression are not |
27+
| tst-SemiAnchoredRegExp.js:72:13:72:40 | '^good\\ ... \\\\.com' | Misleading operator precedence. The subexpression '^good\\.com' is anchored at the beginning, but the other parts of this regular expression are not |
28+
| tst-SemiAnchoredRegExp.js:73:13:73:42 | '^good\\ ... \\\\.com' | Misleading operator precedence. The subexpression '^good\\\\.com' is anchored at the beginning, but the other parts of this regular expression are not |
29+
| tst-SemiAnchoredRegExp.js:82:2:82:27 | /(\\.xxx ... .zzz)$/ | Misleading operator precedence. The subexpression '(\\.zzz)$' is anchored at the end, but the other parts of this regular expression are not |
30+
| tst-SemiAnchoredRegExp.js:84:2:84:23 | /\\.xxx\| ... zzz$/ig | Misleading operator precedence. The subexpression '\\.zzz$' is anchored at the end, but the other parts of this regular expression are not |
31+
| tst-SemiAnchoredRegExp.js:85:2:85:19 | /\\.xxx\|\\.yyy\|zzz$/ | Misleading operator precedence. The subexpression 'zzz$' is anchored at the end, but the other parts of this regular expression are not |
32+
| tst-SemiAnchoredRegExp.js:87:2:87:28 | /^(xxx ... yyy)/i | Misleading operator precedence. The subexpression '^(xxx yyy zzz)' is anchored at the beginning, but the other parts of this regular expression are not |
33+
| tst-SemiAnchoredRegExp.js:88:2:88:53 | /^(xxx ... x\|1st/i | Misleading operator precedence. The subexpression '^(xxx yyy zzz)' is anchored at the beginning, but the other parts of this regular expression are not |
34+
| tst-SemiAnchoredRegExp.js:89:2:89:24 | /^(xxx: ... (zzz:)/ | Misleading operator precedence. The subexpression '^(xxx:)' is anchored at the beginning, but the other parts of this regular expression are not |
35+
| tst-SemiAnchoredRegExp.js:90:2:90:23 | /^(xxx? ... zzz\\/)/ | Misleading operator precedence. The subexpression '^(xxx?:)' is anchored at the beginning, but the other parts of this regular expression are not |
36+
| tst-SemiAnchoredRegExp.js:91:2:91:16 | /^@media\|@page/ | Misleading operator precedence. The subexpression '^@media' is anchored at the beginning, but the other parts of this regular expression are not |
37+
| tst-SemiAnchoredRegExp.js:92:2:92:32 | /^\\s*(x ... :yyy\\// | Misleading operator precedence. The subexpression '^\\s*(xxx?\|yyy\|zzz):' is anchored at the beginning, but the other parts of this regular expression are not |
38+
| tst-SemiAnchoredRegExp.js:93:2:93:21 | /^click\|mouse\|touch/ | Misleading operator precedence. The subexpression '^click' is anchored at the beginning, but the other parts of this regular expression are not |
39+
| tst-SemiAnchoredRegExp.js:94:2:94:43 | /^http: ... r\\.com/ | Misleading operator precedence. The subexpression '^http:\\/\\/good\\.com' is anchored at the beginning, but the other parts of this regular expression are not |
40+
| tst-SemiAnchoredRegExp.js:95:2:95:47 | /^https ... r\\.com/ | Misleading operator precedence. The subexpression '^https?:\\/\\/good\\.com' is anchored at the beginning, but the other parts of this regular expression are not |
41+
| tst-SemiAnchoredRegExp.js:96:2:96:55 | /^mouse ... ragend/ | Misleading operator precedence. The subexpression '^mouse' is anchored at the beginning, but the other parts of this regular expression are not |
42+
| tst-SemiAnchoredRegExp.js:97:2:97:14 | /^xxx:\|yyy:/i | Misleading operator precedence. The subexpression '^xxx:' is anchored at the beginning, but the other parts of this regular expression are not |
43+
| tst-SemiAnchoredRegExp.js:98:2:98:18 | /_xxx\|_yyy\|_zzz$/ | Misleading operator precedence. The subexpression '_zzz$' is anchored at the end, but the other parts of this regular expression are not |
44+
| tst-UnanchoredUrlRegExp.js:3:47:3:65 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
45+
| tst-UnanchoredUrlRegExp.js:4:58:4:76 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
46+
| tst-UnanchoredUrlRegExp.js:5:47:5:66 | "^https?://good.com" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
47+
| tst-UnanchoredUrlRegExp.js:6:47:6:68 | /^https ... od.com/ | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
48+
| tst-UnanchoredUrlRegExp.js:7:47:7:91 | "(^http ... 2.com)" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
49+
| tst-UnanchoredUrlRegExp.js:8:47:8:90 | "(https ... e.com)" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
50+
| tst-UnanchoredUrlRegExp.js:10:2:10:22 | /https? ... od.com/ | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
51+
| tst-UnanchoredUrlRegExp.js:11:13:11:31 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
52+
| tst-UnanchoredUrlRegExp.js:13:48:13:66 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
53+
| tst-UnanchoredUrlRegExp.js:15:13:15:31 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
54+
| tst-UnanchoredUrlRegExp.js:19:47:19:65 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
55+
| tst-UnanchoredUrlRegExp.js:20:47:20:70 | "https? ... m:8080" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
56+
| tst-UnanchoredUrlRegExp.js:23:3:23:21 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
57+
| tst-UnanchoredUrlRegExp.js:24:3:24:23 | /https? ... od.com/ | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
58+
| tst-UnanchoredUrlRegExp.js:25:14:25:32 | "https?://good.com" | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
59+
| tst-UnanchoredUrlRegExp.js:26:3:26:22 | "^https?://good.com" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. |
60+
| tst-UnanchoredUrlRegExp.js:35:2:35:32 | /https? ... 0-9]+)/ | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
61+
| tst-UnanchoredUrlRegExp.js:77:11:77:32 | /vimeo\\ ... 0-9]+)/ | When this is used as a regular expression on a URL, it may match anywhere, and arbitrary hosts may come before or after it. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE-020/MissingRegExpAnchor.ql
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
(function coreRegExp() {
2+
/^a|/;
3+
/^a|b/; // NOT OK
4+
/a|^b/;
5+
/^a|^b/;
6+
/^a|b|c/; // NOT OK
7+
/a|^b|c/;
8+
/a|b|^c/;
9+
/^a|^b|c/;
10+
11+
/(^a)|b/;
12+
/^a|(b)/; // NOT OK
13+
/^a|(^b)/;
14+
/^(a)|(b)/; // NOT OK
15+
16+
17+
/a|b$/; // NOT OK
18+
/a$|b/;
19+
/a$|b$/;
20+
/a|b|c$/; // NOT OK
21+
/a|b$|c/;
22+
/a$|b|c/;
23+
/a|b$|c$/;
24+
25+
/a|(b$)/;
26+
/(a)|b$/; // NOT OK
27+
/(a$)|b$/;
28+
/(a)|(b)$/; // NOT OK
29+
30+
/^good.com|better.com/; // NOT OK
31+
/^good\.com|better\.com/; // NOT OK
32+
/^good\\.com|better\\.com/; // NOT OK
33+
/^good\\\.com|better\\\.com/; // NOT OK
34+
/^good\\\\.com|better\\\\.com/; // NOT OK
35+
36+
/^foo|bar|baz$/; // NOT OK
37+
/^foo|%/; // OK
38+
});
39+
40+
(function coreString() {
41+
new RegExp("^a|");
42+
new RegExp("^a|b"); // NOT OK
43+
new RegExp("a|^b");
44+
new RegExp("^a|^b");
45+
new RegExp("^a|b|c"); // NOT OK
46+
new RegExp("a|^b|c");
47+
new RegExp("a|b|^c");
48+
new RegExp("^a|^b|c");
49+
50+
new RegExp("(^a)|b");
51+
new RegExp("^a|(b)"); // NOT OK
52+
new RegExp("^a|(^b)");
53+
new RegExp("^(a)|(b)"); // NOT OK
54+
55+
56+
new RegExp("a|b$"); // NOT OK
57+
new RegExp("a$|b");
58+
new RegExp("a$|b$");
59+
new RegExp("a|b|c$"); // NOT OK
60+
new RegExp("a|b$|c");
61+
new RegExp("a$|b|c");
62+
new RegExp("a|b$|c$");
63+
64+
new RegExp("a|(b$)");
65+
new RegExp("(a)|b$"); // NOT OK
66+
new RegExp("(a$)|b$");
67+
new RegExp("(a)|(b)$"); // NOT OK
68+
69+
new RegExp('^good.com|better.com'); // NOT OK
70+
new RegExp('^good\.com|better\.com'); // NOT OK
71+
new RegExp('^good\\.com|better\\.com'); // NOT OK
72+
new RegExp('^good\\\.com|better\\\.com'); // NOT OK
73+
new RegExp('^good\\\\.com|better\\\\.com'); // NOT OK
74+
});
75+
76+
(function realWorld() {
77+
// real-world examples that have been anonymized a bit
78+
79+
/*
80+
* NOT OK: flagged
81+
*/
82+
/(\.xxx)|(\.yyy)|(\.zzz)$/;
83+
/(^left|right|center)\sbottom$/; // not flagged at the moment due to interior anchors
84+
/\.xxx|\.yyy|\.zzz$/ig;
85+
/\.xxx|\.yyy|zzz$/;
86+
/^([A-Z]|xxx[XY]$)/; // not flagged at the moment due to interior anchors
87+
/^(xxx yyy zzz)|(xxx yyy)/i;
88+
/^(xxx yyy zzz)|(xxx yyy)|(1st( xxx)? yyy)|xxx|1st/i;
89+
/^(xxx:)|(yyy:)|(zzz:)/;
90+
/^(xxx?:)|(yyy:zzz\/)/;
91+
/^@media|@page/;
92+
/^\s*(xxx?|yyy|zzz):|xxx:yyy\//;
93+
/^click|mouse|touch/;
94+
/^http:\/\/good\.com|http:\/\/better\.com/;
95+
/^https?:\/\/good\.com|https?:\/\/better\.com/;
96+
/^mouse|touch|click|contextmenu|drop|dragover|dragend/;
97+
/^xxx:|yyy:/i;
98+
/_xxx|_yyy|_zzz$/;
99+
/em|%$/; // not flagged at the moment due to the anchor not being for letters
100+
101+
/*
102+
* MAYBE OK due to apparent complexity: not flagged
103+
*/
104+
/(?:^[#?]?|&)([^=&]+)(?:=([^&]*))?/g;
105+
/(^\s*|;\s*)\*.*;/m;
106+
/(^\s*|\[)(?:xxx|yyy_(?:xxx|yyy)|xxx|yyy(?:xxx|yyy)?|xxx|yyy)\b/m;
107+
/\s\S| \t|\t |\s$/;
108+
/\{[^}{]*\{|\}[^}{]*\}|\{[^}]*$/g;
109+
/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/;
110+
/^(\/\/)|([a-z]+:(\/\/)?)/;
111+
/^[=?!#%@$]|!(?=[:}])/;
112+
/^[\[\]!:]|[<>]/;
113+
/^for\b|\b(?:xxx|yyy)\b/i;
114+
/^if\b|\b(?:xxx|yyy|zzz)\b/i;
115+
116+
/*
117+
* OK: not flagged
118+
*/
119+
/$^|only-match/g;
120+
/(#.+)|#$/;
121+
/(NaN| {2}|^$)/;
122+
/[^\n]*(?:\n|[^\n]$)/g;
123+
/^$|\/(?:xxx|yyy)zzz/i;
124+
/^(\/|(xxx|yyy|zzz)$)/;
125+
/^9$|27/;
126+
/^\+|\s*/g;
127+
/xxx_yyy=\w+|^$/;
128+
/^(?:mouse|contextmenu)|click/;
129+
});
130+
131+
function replaceTest(x) {
132+
return x.replace(/^a|b/, ''); // OK - possibly replacing too much, but not obviously a problem
133+
}

0 commit comments

Comments
 (0)