Swift: Copy MissingRegexAnchor query from JS.

This commit is contained in:
Geoffrey White
2023-08-15 10:59:33 +01:00
parent dc9f171ee6
commit c040d4847b
8 changed files with 449 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Sanitizing untrusted input with regular expressions is a
common technique. However, it is error-prone to match untrusted input
against regular expressions without anchors such as <code>^</code> or
<code>$</code>. Malicious input can bypass such security checks by
embedding one of the allowed patterns in an unexpected location.
</p>
<p>
Even if the matching is not done in a security-critical
context, it may still cause undesirable behavior when the regular
expression accidentally matches.
</p>
</overview>
<recommendation>
<p>
Use anchors to ensure that regular expressions match at
the expected locations.
</p>
</recommendation>
<example>
<p>
The following example code checks that a URL redirection
will reach the <code>example.com</code> domain, or one of its
subdomains, and not some malicious site.
</p>
<sample src="examples/MissingRegExpAnchor_BAD.js"/>
<p>
The check with the regular expression match is, however, easy to bypass. For example
by embedding <code>http://example.com/</code> in the query
string component: <code>http://evil-example.net/?x=http://example.com/</code>.
Address these shortcomings by using anchors in the regular expression instead:
</p>
<sample src="examples/MissingRegExpAnchor_GOOD.js"/>
<p>
A related mistake is to write a regular expression with
multiple alternatives, but to only include an anchor for one of the
alternatives. As an example, the regular expression
<code>/^www\.example\.com|beta\.example\.com/</code> will match the host
<code>evil.beta.example.com</code> because the regular expression is parsed
as <code>/(^www\.example\.com)|(beta\.example\.com)/</code>
</p>
</example>
<references>
<li>MDN: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions">Regular Expressions</a></li>
<li>OWASP: <a href="https://www.owasp.org/index.php/Server_Side_Request_Forgery">SSRF</a></li>
<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>
</references>
</qhelp>

View File

@@ -0,0 +1,56 @@
/**
* @name Missing regular expression anchor
* @description Regular expressions without anchors can be vulnerable to bypassing.
* @kind problem
* @problem.severity warning
* @security-severity 7.8
* @precision medium
* @id js/regex/missing-regexp-anchor
* @tags correctness
* security
* external/cwe/cwe-020
*/
private import javascript
private import semmle.javascript.security.regexp.HostnameRegexp as HostnameRegexp
private import codeql.regex.MissingRegExpAnchor as MissingRegExpAnchor
private import semmle.javascript.security.regexp.RegExpTreeView::RegExpTreeView as TreeImpl
private module Impl implements
MissingRegExpAnchor::MissingRegExpAnchorSig<TreeImpl, HostnameRegexp::Impl>
{
predicate isUsedAsReplace(RegExpPatternSource pattern) {
// is used for capture or replace
exists(DataFlow::MethodCallNode mcn, string name | name = mcn.getMethodName() |
name = "exec" and
mcn = pattern.getARegExpObject().getAMethodCall() and
exists(mcn.getAPropertyRead())
or
exists(DataFlow::Node arg |
arg = mcn.getArgument(0) and
(
pattern.getARegExpObject().flowsTo(arg) or
pattern.getAParse() = arg
)
|
name = "replace"
or
name = "match" and exists(mcn.getAPropertyRead())
)
)
}
string getEndAnchorText() { result = "$" }
}
import MissingRegExpAnchor::Make<TreeImpl, HostnameRegexp::Impl, Impl>
from DataFlow::Node nd, string msg
where
isUnanchoredHostnameRegExp(nd, msg)
or
isSemiAnchoredHostnameRegExp(nd, msg)
or
hasMisleadingAnchorPrecedence(nd, msg)
// isLineAnchoredHostnameRegExp is not used here, as it is not relevant to JS.
select nd, msg

View File

@@ -0,0 +1,7 @@
app.get("/some/path", function(req, res) {
let url = req.param("url");
// BAD: the host of `url` may be controlled by an attacker
if (url.match(/https?:\/\/www\.example\.com\//)) {
res.redirect(url);
}
});

View File

@@ -0,0 +1,7 @@
app.get("/some/path", function(req, res) {
let url = req.param("url");
// GOOD: the host of `url` can not be controlled by an attacker
if (url.match(/^https?:\/\/www\.example\.com\//)) {
res.redirect(url);
}
});

View File

@@ -0,0 +1,61 @@
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |

View File

@@ -0,0 +1 @@
Security/CWE-020/MissingRegExpAnchor.ql

View File

@@ -0,0 +1,133 @@
(function coreRegExp() {
/^a|/;
/^a|b/; // NOT OK
/a|^b/;
/^a|^b/;
/^a|b|c/; // NOT OK
/a|^b|c/;
/a|b|^c/;
/^a|^b|c/;
/(^a)|b/;
/^a|(b)/; // NOT OK
/^a|(^b)/;
/^(a)|(b)/; // NOT OK
/a|b$/; // NOT OK
/a$|b/;
/a$|b$/;
/a|b|c$/; // NOT OK
/a|b$|c/;
/a$|b|c/;
/a|b$|c$/;
/a|(b$)/;
/(a)|b$/; // NOT OK
/(a$)|b$/;
/(a)|(b)$/; // NOT OK
/^good.com|better.com/; // NOT OK
/^good\.com|better\.com/; // NOT OK
/^good\\.com|better\\.com/; // NOT OK
/^good\\\.com|better\\\.com/; // NOT OK
/^good\\\\.com|better\\\\.com/; // NOT OK
/^foo|bar|baz$/; // NOT OK
/^foo|%/; // OK
});
(function coreString() {
new RegExp("^a|");
new RegExp("^a|b"); // NOT OK
new RegExp("a|^b");
new RegExp("^a|^b");
new RegExp("^a|b|c"); // NOT OK
new RegExp("a|^b|c");
new RegExp("a|b|^c");
new RegExp("^a|^b|c");
new RegExp("(^a)|b");
new RegExp("^a|(b)"); // NOT OK
new RegExp("^a|(^b)");
new RegExp("^(a)|(b)"); // NOT OK
new RegExp("a|b$"); // NOT OK
new RegExp("a$|b");
new RegExp("a$|b$");
new RegExp("a|b|c$"); // NOT OK
new RegExp("a|b$|c");
new RegExp("a$|b|c");
new RegExp("a|b$|c$");
new RegExp("a|(b$)");
new RegExp("(a)|b$"); // NOT OK
new RegExp("(a$)|b$");
new RegExp("(a)|(b)$"); // NOT OK
new RegExp('^good.com|better.com'); // NOT OK
new RegExp('^good\.com|better\.com'); // NOT OK
new RegExp('^good\\.com|better\\.com'); // NOT OK
new RegExp('^good\\\.com|better\\\.com'); // NOT OK
new RegExp('^good\\\\.com|better\\\\.com'); // NOT OK
});
(function realWorld() {
// real-world examples that have been anonymized a bit
/*
* NOT OK: flagged
*/
/(\.xxx)|(\.yyy)|(\.zzz)$/;
/(^left|right|center)\sbottom$/; // not flagged at the moment due to interior anchors
/\.xxx|\.yyy|\.zzz$/ig;
/\.xxx|\.yyy|zzz$/;
/^([A-Z]|xxx[XY]$)/; // not flagged at the moment due to interior anchors
/^(xxx yyy zzz)|(xxx yyy)/i;
/^(xxx yyy zzz)|(xxx yyy)|(1st( xxx)? yyy)|xxx|1st/i;
/^(xxx:)|(yyy:)|(zzz:)/;
/^(xxx?:)|(yyy:zzz\/)/;
/^@media|@page/;
/^\s*(xxx?|yyy|zzz):|xxx:yyy\//;
/^click|mouse|touch/;
/^http:\/\/good\.com|http:\/\/better\.com/;
/^https?:\/\/good\.com|https?:\/\/better\.com/;
/^mouse|touch|click|contextmenu|drop|dragover|dragend/;
/^xxx:|yyy:/i;
/_xxx|_yyy|_zzz$/;
/em|%$/; // not flagged at the moment due to the anchor not being for letters
/*
* MAYBE OK due to apparent complexity: not flagged
*/
/(?:^[#?]?|&)([^=&]+)(?:=([^&]*))?/g;
/(^\s*|;\s*)\*.*;/m;
/(^\s*|\[)(?:xxx|yyy_(?:xxx|yyy)|xxx|yyy(?:xxx|yyy)?|xxx|yyy)\b/m;
/\s\S| \t|\t |\s$/;
/\{[^}{]*\{|\}[^}{]*\}|\{[^}]*$/g;
/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/;
/^(\/\/)|([a-z]+:(\/\/)?)/;
/^[=?!#%@$]|!(?=[:}])/;
/^[\[\]!:]|[<>]/;
/^for\b|\b(?:xxx|yyy)\b/i;
/^if\b|\b(?:xxx|yyy|zzz)\b/i;
/*
* OK: not flagged
*/
/$^|only-match/g;
/(#.+)|#$/;
/(NaN| {2}|^$)/;
/[^\n]*(?:\n|[^\n]$)/g;
/^$|\/(?:xxx|yyy)zzz/i;
/^(\/|(xxx|yyy|zzz)$)/;
/^9$|27/;
/^\+|\s*/g;
/xxx_yyy=\w+|^$/;
/^(?:mouse|contextmenu)|click/;
});
function replaceTest(x) {
return x.replace(/^a|b/, ''); // OK - possibly replacing too much, but not obviously a problem
}

View File

@@ -0,0 +1,108 @@
(function(x){
if ("http://evil.com/?http://good.com".match("https?://good.com")) {} // NOT OK
if ("http://evil.com/?http://good.com".match(new RegExp("https?://good.com"))) {} // NOT OK
if ("http://evil.com/?http://good.com".match("^https?://good.com")) {} // NOT OK - missing post-anchor
if ("http://evil.com/?http://good.com".match(/^https?:\/\/good.com/)) {} // NOT OK - missing post-anchor
if ("http://evil.com/?http://good.com".match("(^https?://good1.com)|(^https?://good2.com)")) {} // NOT OK - missing post-anchor
if ("http://evil.com/?http://good.com".match("(https?://good.com)|(^https?://goodie.com)")) {} // NOT OK - missing post-anchor
/https?:\/\/good.com/.exec("http://evil.com/?http://good.com"); // NOT OK
new RegExp("https?://good.com").exec("http://evil.com/?http://good.com"); // NOT OK
if ("http://evil.com/?http://good.com".search("https?://good.com") > -1) {} // NOT OK
new RegExp("https?://good.com").test("http://evil.com/?http://good.com"); // NOT OK
if ("something".match("other")) {} // OK
if ("something".match("x.commissary")) {} // OK
if ("http://evil.com/?http://good.com".match("https?://good.com")) {} // NOT OK
if ("http://evil.com/?http://good.com".match("https?://good.com:8080")) {} // NOT OK
let trustedUrls = [
"https?://good.com", // NOT OK, referenced below
/https?:\/\/good.com/, // NOT OK, referenced below
new RegExp("https?://good.com"), // NOT OK, referenced below
"^https?://good.com" // NOT OK - missing post-anchor
];
function isTrustedUrl(url) {
for (let trustedUrl of trustedUrls) {
if (url.match(trustedUrl)) return true;
}
return false;
}
/https?:\/\/good.com\/([0-9]+)/.exec(url); // NOT OK
"https://verygood.com/?id=" + /https?:\/\/good.com\/([0-9]+)/.exec(url)[0]; // OK
"http" + (secure? "s": "") + "://" + "verygood.com/?id=" + /https?:\/\/good.com\/([0-9]+)/.exec(url)[0]; // OK
"http" + (secure? "s": "") + "://" + ("verygood.com/?id=" + /https?:\/\/good.com\/([0-9]+)/.exec(url)[0]); // OK
// g or .replace?
file = file.replace(
/https:\/\/cdn\.ampproject\.org\/v0\/amp-story-0\.1\.js/g,
hostName + '/dist/v0/amp-story-1.0.max.js'
);
// missing context of use
const urlPatterns = [
{
regex: /youtube.com\/embed\/([a-z0-9\?&=\-_]+)/i, // OK
type: 'iframe', w: 560, h: 314,
url: '//www.youtube.com/embed/$1',
allowFullscreen: true
}];
// ditto
F.helpers.media = {
defaults : {
youtube : {
matcher : /(youtube\.com|youtu\.be)\/(watch\?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*)).*/i,
params : {
autoplay : 1,
autohide : 1,
fs : 1,
rel : 0,
hd : 1,
wmode : 'opaque',
enablejsapi : 1
},
type : 'iframe',
url : '//www.youtube.com/embed/$3'
}}}
// ditto
var urlPatterns = [
{regex: /youtu\.be\/([\w\-.]+)/, type: 'iframe', w: 425, h: 350, url: '//www.youtube.com/embed/$1'},
{regex: /youtube\.com(.+)v=([^&]+)/, type: 'iframe', w: 425, h: 350, url: '//www.youtube.com/embed/$2'},
{regex: /vimeo\.com\/([0-9]+)/, type: 'iframe', w: 425, h: 350, url: '//player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc'},
];
// check optional successsor to TLD
new RegExp("(Pingdom.com_bot_version_)(\\d+)\\.(\\d+)")
// replace and spaces
error.replace(/See https:\/\/github\.com\/Squirrel\/Squirrel\.Mac\/issues\/182 for more information/, 'See [this link](https://github.com/Microsoft/vscode/issues/7426#issuecomment-425093469) for more information');
// not a url
var sharedScript = /<script\s.*src="(app:\/\/.+\.gaiamobile\.org)?\/?(shared\/.+)".*>/;
// replace
const repo = repoURL.replace(/http(s)?:\/\/(\d+\.)?github.com\//gi, '')
// replace and space
cmp.replace(/<option value="http:\/\/codemirror.net\/">HEAD<\/option>/,
"<option value=\"http://codemirror.net/\">HEAD</option>\n <option value=\"http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=" + number + ";f=\">" + number + "</option>");
// replace and space
const helpMsg = /For help see https:\/\/nodejs.org\/en\/docs\/inspector\s*/;
msg = msg.replace(helpMsg, '');
// not a url
pkg.source.match(/<a:skin.*?\s+xmlns:a="http:\/\/ajax.org\/2005\/aml"/m)
// replace
path.replace(/engine.io/, "$&-client");
/\.com|\.org/; // OK, has no domain name
/example\.com|whatever/; // OK, the other disjunction doesn't match a hostname
});