diff --git a/change-notes/1.21/analysis-javascript.md b/change-notes/1.21/analysis-javascript.md
index 276ef11d4e60..e1b4eaea6b0a 100644
--- a/change-notes/1.21/analysis-javascript.md
+++ b/change-notes/1.21/analysis-javascript.md
@@ -26,6 +26,7 @@
| 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. |
| Double escaping or unescaping | More results | This rule now considers the flow of regular expressions literals. |
| Expression has no effect | Fewer false-positive results | This rule now treats uses of `Object.defineProperty` more conservatively. |
+| Incomplete regular expression for hostnames | More results | This rule now tracks regular expressions for host names further. |
| Incomplete string escaping or encoding | More results | This rule now considers the flow of regular expressions literals. |
| Replacement of a substring with itself | More results | This rule now considers the flow of regular expressions literals. |
| 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. |
diff --git a/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.qhelp b/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.qhelp
index 71fefb488fa3..771c446c66f5 100644
--- a/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.qhelp
+++ b/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.qhelp
@@ -59,13 +59,14 @@
Address this vulnerability by escaping .
- appropriately: let regex = /(www|beta|)\.example\.com/.
+ appropriately: let regex = /((www|beta)\.)?example\.com/.
+ MDN: Regular Expressions
OWASP: SSRF
OWASP: XSS Unvalidated Redirects and Forwards Cheat Sheet.
diff --git a/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql b/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql
index 049b7f4c64a7..70589092064e 100644
--- a/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql
+++ b/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql
@@ -13,16 +13,35 @@
import javascript
/**
- * A taint tracking configuration for incomplete hostname regular expressions sources.
+ * Gets a node whose value may flow (inter-procedurally) to a position where it is interpreted
+ * as a regular expression.
*/
-class Configuration extends TaintTracking::Configuration {
- Configuration() { this = "IncompleteHostnameRegExpTracking" }
+DataFlow::Node regExpSource(DataFlow::Node re, DataFlow::TypeBackTracker t) {
+ t.start() and
+ re = result and
+ isInterpretedAsRegExp(result)
+ or
+ exists(DataFlow::TypeBackTracker t2, DataFlow::Node succ | succ = regExpSource(re, t2) |
+ t2 = t.smallstep(result, succ)
+ or
+ any(TaintTracking::AdditionalTaintStep dts).step(result, succ) and
+ t = t2
+ )
+}
- override predicate isSource(DataFlow::Node source) {
- isIncompleteHostNameRegExpPattern(source.getStringValue(), _)
- }
+DataFlow::Node regExpSource(DataFlow::Node re) {
+ result = regExpSource(re, DataFlow::TypeBackTracker::end())
+}
- override predicate isSink(DataFlow::Node sink) { isInterpretedAsRegExp(sink) }
+/** Holds if `re` is a regular expression with value `pattern`. */
+predicate regexp(DataFlow::Node re, string pattern, string kind, DataFlow::Node aux) {
+ re.asExpr().(RegExpLiteral).getValue() = pattern and
+ kind = "regular expression" and
+ aux = re
+ or
+ re = regExpSource(aux) and
+ pattern = re.getStringValue() and
+ kind = "string, which is used as a regular expression $@,"
}
/**
@@ -36,22 +55,14 @@ predicate isIncompleteHostNameRegExpPattern(string pattern, string hostPart) {
// an unescaped single `.`
"(?>
- result = "com|org|edu|gov|uk|net|io"
+ result = "(?:com|org|edu|gov|uk|net|io)(?![a-z0-9])"
}
}
diff --git a/javascript/ql/src/semmle/javascript/dataflow/Sources.qll b/javascript/ql/src/semmle/javascript/dataflow/Sources.qll
index b3c9e7353d5f..43c512885aad 100644
--- a/javascript/ql/src/semmle/javascript/dataflow/Sources.qll
+++ b/javascript/ql/src/semmle/javascript/dataflow/Sources.qll
@@ -179,10 +179,7 @@ class SourceNode extends DataFlow::Node {
*/
pragma[inline]
DataFlow::SourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) {
- exists(StepSummary summary |
- StepSummary::step(result, this, summary) and
- t = t2.prepend(summary)
- )
+ t2 = t.step(result, this)
}
}
diff --git a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll
index b7bd316d4a42..b5637212868a 100644
--- a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll
+++ b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll
@@ -41,13 +41,9 @@ class StepSummary extends TStepSummary {
or
this instanceof ReturnStep and result = "return"
or
- exists(string prop | this = StoreStep(prop) |
- result = "store " + prop
- )
+ exists(string prop | this = StoreStep(prop) | result = "store " + prop)
or
- exists(string prop | this = LoadStep(prop) |
- result = "load" + prop
- )
+ exists(string prop | this = LoadStep(prop) | result = "load " + prop)
}
}
@@ -56,40 +52,44 @@ module StepSummary {
* INTERNAL: Use `SourceNode.track()` or `SourceNode.backtrack()` instead.
*/
predicate step(DataFlow::SourceNode pred, DataFlow::SourceNode succ, StepSummary summary) {
- exists(DataFlow::Node predNode | pred.flowsTo(predNode) |
- // Flow through properties of objects
- propertyFlowStep(predNode, succ) and
- summary = LevelStep()
- or
- // Flow through global variables
- globalFlowStep(predNode, succ) and
- summary = LevelStep()
- or
- // Flow into function
- callStep(predNode, succ) and
- summary = CallStep()
- or
- // Flow out of function
- returnStep(predNode, succ) and
- summary = ReturnStep()
- or
- // Flow through an instance field between members of the same class
- DataFlow::localFieldStep(predNode, succ) and
- summary = LevelStep()
+ exists(DataFlow::Node mid | pred.flowsTo(mid) | smallstep(mid, succ, summary))
+ }
+
+ /**
+ * INTERNAL: Use `TypeBackTracker.smallstep()` instead.
+ */
+ predicate smallstep(DataFlow::Node pred, DataFlow::Node succ, StepSummary summary) {
+ // Flow through properties of objects
+ propertyFlowStep(pred, succ) and
+ summary = LevelStep()
+ or
+ // Flow through global variables
+ globalFlowStep(pred, succ) and
+ summary = LevelStep()
+ or
+ // Flow into function
+ callStep(pred, succ) and
+ summary = CallStep()
+ or
+ // Flow out of function
+ returnStep(pred, succ) and
+ summary = ReturnStep()
+ or
+ // Flow through an instance field between members of the same class
+ DataFlow::localFieldStep(pred, succ) and
+ summary = LevelStep()
+ or
+ exists(string prop |
+ basicStoreStep(pred, succ, prop) and
+ summary = StoreStep(prop)
or
- exists(string prop |
- basicStoreStep(predNode, succ, prop) and
- summary = StoreStep(prop)
- or
- loadStep(predNode, succ, prop) and
- summary = LoadStep(prop)
- )
+ loadStep(pred, succ, prop) and
+ summary = LoadStep(prop)
)
}
}
-private newtype TTypeTracker =
- MkTypeTracker(Boolean hasCall, OptionalPropertyName prop)
+private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop)
/**
* EXPERIMENTAL.
@@ -136,9 +136,7 @@ class TypeTracker extends TTypeTracker {
or
step = LoadStep(prop) and result = MkTypeTracker(hasCall, "")
or
- exists(string p |
- step = StoreStep(p) and prop = "" and result = MkTypeTracker(hasCall, p)
- )
+ exists(string p | step = StoreStep(p) and prop = "" and result = MkTypeTracker(hasCall, p))
}
/** Gets a textual representation of this summary. */
@@ -180,8 +178,7 @@ module TypeTracker {
TypeTracker end() { result.end() }
}
-private newtype TTypeBackTracker =
- MkTypeBackTracker(Boolean hasReturn, OptionalPropertyName prop)
+private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, OptionalPropertyName prop)
/**
* EXPERIMENTAL.
@@ -192,7 +189,7 @@ private newtype TTypeBackTracker =
* therefore expected to called with a certain type of value.
*
* Note that type back-tracking does not provide a source/sink relation, that is,
- * it may determine that a node will be used in an API call somwwhere, but it won't
+ * it may determine that a node will be used in an API call somewhere, but it won't
* determine exactly where that use was, or the path that led to the use.
*
* It is recommended that all uses of this type is written on the following form,
@@ -203,7 +200,7 @@ private newtype TTypeBackTracker =
* result = (< some API call >).getArgument(< n >).getALocalSource()
* or
* exists (DataFlow::TypeBackTracker t2 |
- * result = myCallback(t2).backtrack(t2, t)
+ * t2 = t.step(result, myCallback(t2))
* )
* }
*
@@ -225,9 +222,7 @@ class TypeBackTracker extends TTypeBackTracker {
or
step = ReturnStep() and result = MkTypeBackTracker(true, prop)
or
- exists(string p |
- step = LoadStep(p) and prop = "" and result = MkTypeBackTracker(hasReturn, p)
- )
+ exists(string p | step = LoadStep(p) and prop = "" and result = MkTypeBackTracker(hasReturn, p))
or
step = StoreStep(prop) and result = MkTypeBackTracker(hasReturn, "")
}
@@ -265,6 +260,37 @@ class TypeBackTracker extends TTypeBackTracker {
* This predicate is only defined if the type has not been tracked into a property.
*/
TypeBackTracker continue() { prop = "" and result = this }
+
+ /**
+ * Gets the summary that corresponds to having taken a backwards
+ * heap and/or inter-procedural step from `succ` to `pred`.
+ */
+ pragma[inline]
+ TypeBackTracker step(DataFlow::SourceNode pred, DataFlow::SourceNode succ) {
+ exists(StepSummary summary |
+ StepSummary::step(pred, succ, summary) and
+ this = result.prepend(summary)
+ )
+ }
+
+ /**
+ * Gets the summary that corresponds to having taken a backwards
+ * local, heap and/or inter-procedural step from `succ` to `pred`.
+ *
+ * Unlike `TypeBackTracker::step`, this predicate exposes all edges
+ * in the flowgraph, and not just the edges between
+ * `SourceNode`s. It may therefore be less performant.
+ */
+ pragma[inline]
+ TypeBackTracker smallstep(DataFlow::Node pred, DataFlow::Node succ) {
+ exists(StepSummary summary |
+ StepSummary::smallstep(pred, succ, summary) and
+ this = result.prepend(summary)
+ )
+ or
+ pred = succ.getAPredecessor() and
+ this = result
+ }
}
module TypeBackTracker {
diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp.expected b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp.expected
index cbb655c77bab..b34db52ed794 100644
--- a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp.expected
@@ -1,21 +1,24 @@
-| tst-IncompleteHostnameRegExp.js:3:2:3:28 | /http:\\ ... le.com/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:5:2:5:28 | /http:\\ ... le.net/ | This regular expression has an unescaped '.' before 'example.net', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:6:2:6:42 | /http:\\ ... b).com/ | This regular expression has an unescaped '.' before '(example-a\|example-b).com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:11:13:11:37 | "http:/ ... le.com" | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:12:10:12:34 | "http:/ ... le.com" | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:15:22:15:46 | "http:/ ... le.com" | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:17:13:17:31 | `test.example.com$` | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:17:14:17:30 | test.example.com$ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:19:17:19:34 | 'test.example.com' | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:22:27:22:44 | 'test.example.com' | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:28:22:28:39 | 'test.example.com' | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:37:2:37:54 | /^(http ... =$\|\\/)/ | This regular expression has an unescaped '.' before ')?example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:38:2:38:44 | /^(http ... p\\/f\\// | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:39:2:39:34 | /\\(http ... m\\/\\)/g | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:40:2:40:29 | /https? ... le.com/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:41:13:41:68 | '^http: ... e\\.com' | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:41:41:41:68 | '^https ... e\\.com' | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:42:13:42:61 | 'http[s ... \\/(.+)' | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:43:2:43:33 | /^https ... e.com$/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:44:9:44:100 | 'protos ... ernal)' | This regular expression has an unescaped '.' before 'example-b.com', so it might match more hosts than expected. |
-| tst-IncompleteHostnameRegExp.js:46:2:46:26 | /exampl ... le.com/ | This regular expression has an unescaped '.' before 'dev\|example.com', so it might match more hosts than expected. |
+| tst-IncompleteHostnameRegExp.js:3:2:3:28 | /http:\\ ... le.com/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:3:2:3:28 | /http:\\ ... le.com/ | here |
+| tst-IncompleteHostnameRegExp.js:5:2:5:28 | /http:\\ ... le.net/ | This regular expression has an unescaped '.' before 'example.net', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:5:2:5:28 | /http:\\ ... le.net/ | here |
+| tst-IncompleteHostnameRegExp.js:6:2:6:42 | /http:\\ ... b).com/ | This regular expression has an unescaped '.' before '(example-a\|example-b).com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:6:2:6:42 | /http:\\ ... b).com/ | here |
+| tst-IncompleteHostnameRegExp.js:11:13:11:37 | "http:/ ... le.com" | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:11:13:11:37 | "http:/ ... le.com" | here |
+| tst-IncompleteHostnameRegExp.js:12:10:12:35 | "^http: ... le.com" | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:12:10:12:35 | "^http: ... le.com" | here |
+| tst-IncompleteHostnameRegExp.js:17:13:17:31 | `test.example.com$` | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:17:13:17:31 | `test.example.com$` | here |
+| tst-IncompleteHostnameRegExp.js:17:14:17:30 | test.example.com$ | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:17:13:17:31 | `test.example.com$` | here |
+| tst-IncompleteHostnameRegExp.js:19:17:19:34 | 'test.example.com' | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:20:13:20:26 | `${hostname}$` | here |
+| tst-IncompleteHostnameRegExp.js:22:27:22:44 | 'test.example.com' | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:23:13:23:27 | domain.hostname | here |
+| tst-IncompleteHostnameRegExp.js:28:23:28:40 | 'test.example.com' | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:26:21:26:35 | domain.hostname | here |
+| tst-IncompleteHostnameRegExp.js:30:30:30:47 | 'test.example.com' | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:32:21:32:35 | domain.hostname | here |
+| tst-IncompleteHostnameRegExp.js:37:2:37:54 | /^(http ... =$\|\\/)/ | This regular expression has an unescaped '.' before ')?example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:37:2:37:54 | /^(http ... =$\|\\/)/ | here |
+| tst-IncompleteHostnameRegExp.js:38:2:38:44 | /^(http ... p\\/f\\// | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:38:2:38:44 | /^(http ... p\\/f\\// | here |
+| tst-IncompleteHostnameRegExp.js:39:2:39:34 | /\\(http ... m\\/\\)/g | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:39:2:39:34 | /\\(http ... m\\/\\)/g | here |
+| tst-IncompleteHostnameRegExp.js:40:2:40:29 | /https? ... le.com/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:40:2:40:29 | /https? ... le.com/ | here |
+| tst-IncompleteHostnameRegExp.js:41:13:41:68 | '^http: ... e\\.com' | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:41:13:41:68 | '^http: ... e\\.com' | here |
+| tst-IncompleteHostnameRegExp.js:41:41:41:68 | '^https ... e\\.com' | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:41:13:41:68 | '^http: ... e\\.com' | here |
+| tst-IncompleteHostnameRegExp.js:42:13:42:61 | 'http[s ... \\/(.+)' | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:42:13:42:61 | 'http[s ... \\/(.+)' | here |
+| tst-IncompleteHostnameRegExp.js:43:2:43:33 | /^https ... e.com$/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:43:2:43:33 | /^https ... e.com$/ | here |
+| tst-IncompleteHostnameRegExp.js:44:9:44:100 | 'protos ... ernal)' | This string, which is used as a regular expression $@, has an unescaped '.' before 'example-b.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:44:9:44:100 | 'protos ... ernal)' | here |
+| tst-IncompleteHostnameRegExp.js:46:2:46:26 | /exampl ... le.com/ | This regular expression has an unescaped '.' before 'dev\|example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:46:2:46:26 | /exampl ... le.com/ | here |
+| tst-IncompleteHostnameRegExp.js:48:13:48:68 | '^http: ... e\\.com' | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:48:13:48:68 | '^http: ... e\\.com' | here |
+| tst-IncompleteHostnameRegExp.js:48:41:48:68 | '^https ... e\\.com' | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:48:13:48:68 | '^http: ... e\\.com' | here |
+| tst-IncompleteHostnameRegExp.js:53:13:53:35 | 'test.' ... le.com' | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.js:53:13:53:35 | 'test.' ... le.com' | here |
diff --git a/javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteHostnameRegExp.js b/javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteHostnameRegExp.js
index 45e6476a22e1..708fbee16f29 100644
--- a/javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteHostnameRegExp.js
+++ b/javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteHostnameRegExp.js
@@ -9,10 +9,10 @@
/http:\/\/(?:.+)\\.test\\.example.com/; // NOT OK, but not yet supported with enough precision
/http:\/\/test.example.com\/(?:.*)/; // OK
new RegExp("http://test.example.com"); // NOT OK
- s.match("http://test.example.com"); // NOT OK
+ s.match("^http://test.example.com"); // NOT OK
function id(e) { return e; }
- new RegExp(id(id(id("http://test.example.com")))); // NOT OK
+ new RegExp(id(id(id("http://test.example.com")))); // NOT OK, but not supported by type tracking
new RegExp(`test.example.com$`); // NOT OK
@@ -22,16 +22,16 @@
let domain = { hostname: 'test.example.com' };
new RegExp(domain.hostname);
- function convert(domain) {
+ function convert1(domain) {
return new RegExp(domain.hostname);
}
- convert({ hostname: 'test.example.com' }); // NOT OK
+ convert1({ hostname: 'test.example.com' }); // NOT OK
- let domains = [ { hostname: 'test.example.com' } ]; // NOT OK, but not yet supported
- function convert(domain) {
+ let domains = [ { hostname: 'test.example.com' } ]; // NOT OK
+ function convert2(domain) {
return new RegExp(domain.hostname);
}
- domains.map(d => convert(d));
+ domains.map(d => convert2(d));
/(.+\.(?:example-a|example-b)\.com)/; // NOT OK, but not yet supported with enough precision
/^(https?:)?\/\/((service|www).)?example.com(?=$|\/)/; // NOT OK
@@ -44,4 +44,11 @@
RegExp('protos?://(localhost|.+.example.net|.+.example-a.com|.+.example-b.com|.+.example.internal)'); // NOT OK
/example.dev|example.com/; // OK, but still flagged
+
+ new RegExp('^http://localhost:8000|' + '^https?://.+\.example\.com'); // NOT OK
+
+ var primary = 'example.com';
+ new RegExp('test.' + primary); // NOT OK, but not detected
+
+ new RegExp('test.' + 'example.com'); // NOT OK
});