From 89596052cf725ce29a96250e02bce1ec1b9baf19 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Wed, 3 Apr 2019 13:15:32 +0200 Subject: [PATCH 01/10] JS: autoformat TypeTracking.qll --- .../javascript/dataflow/TypeTracking.qll | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll index b7bd316d4a42..336ca9ffca32 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) } } @@ -88,8 +84,7 @@ module StepSummary { } } -private newtype TTypeTracker = - MkTypeTracker(Boolean hasCall, OptionalPropertyName prop) +private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop) /** * EXPERIMENTAL. @@ -136,9 +131,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 +173,7 @@ module TypeTracker { TypeTracker end() { result.end() } } -private newtype TTypeBackTracker = - MkTypeBackTracker(Boolean hasReturn, OptionalPropertyName prop) +private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, OptionalPropertyName prop) /** * EXPERIMENTAL. @@ -225,9 +217,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, "") } From 9d1f511ca029dd38ec0630559bed37b37b7ca221 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Wed, 3 Apr 2019 13:16:54 +0200 Subject: [PATCH 02/10] JS: fixup missing space --- javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll index 336ca9ffca32..ea5fc91cb0c0 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll @@ -43,7 +43,7 @@ class StepSummary extends TStepSummary { or 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) } } From 1f565bd49cdfdbaac1371c90e2c1baa8cf1bbd72 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Wed, 3 Apr 2019 13:15:53 +0200 Subject: [PATCH 03/10] JS: Introduce TypeBackTracker::step and TypeBackTracker::smallstep --- .../semmle/javascript/dataflow/Sources.qll | 5 +- .../javascript/dataflow/TypeTracking.qll | 90 +++++++++++++------ 2 files changed, 64 insertions(+), 31 deletions(-) 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 ea5fc91cb0c0..c6b17e8294db 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll @@ -52,34 +52,39 @@ 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) ) } } @@ -255,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 { From 9eb039038e59bbbcc3c9adb5ac31183db4f72b03 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Tue, 9 Apr 2019 10:32:35 +0200 Subject: [PATCH 04/10] JS: update docstring example for TypeBackTracker --- javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll index c6b17e8294db..b5637212868a 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll @@ -189,7 +189,7 @@ private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, Optional * 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, @@ -200,7 +200,7 @@ private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, Optional * result = (< some API call >).getArgument(< n >).getALocalSource() * or * exists (DataFlow::TypeBackTracker t2 | - * result = myCallback(t2).backtrack(t2, t) + * t2 = t.step(result, myCallback(t2)) * ) * } * From 74144b027163a52c02d6eb6081cb07e7cc063e45 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Thu, 14 Mar 2019 08:02:29 +0100 Subject: [PATCH 05/10] JS: make RegExpPatterns::commonTLD more robust --- .../ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql | 4 ++-- .../Security/CWE-020/IncompleteUrlSubstringSanitization.ql | 4 ++-- javascript/ql/src/semmle/javascript/Regexp.qll | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql b/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql index 049b7f4c64a7..16dd4ae67fc3 100644 --- a/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql +++ b/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql @@ -36,7 +36,7 @@ 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])" } } From cf7d0a7ea598cb8f5f8617f5cd5014f974e890ff Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Wed, 3 Apr 2019 12:59:22 +0200 Subject: [PATCH 06/10] JS: fixup qhelp --- .../ql/src/Security/CWE-020/IncompleteHostnameRegExp.qhelp | 3 ++- .../src/Security/CWE-020/examples/IncompleteHostnameRegExp.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) 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/examples/IncompleteHostnameRegExp.js b/javascript/ql/src/Security/CWE-020/examples/IncompleteHostnameRegExp.js index cc73a8388f6f..d3dfc3a867d0 100644 --- a/javascript/ql/src/Security/CWE-020/examples/IncompleteHostnameRegExp.js +++ b/javascript/ql/src/Security/CWE-020/examples/IncompleteHostnameRegExp.js @@ -2,7 +2,7 @@ app.get('/some/path', function(req, res) { let url = req.param('url'), host = urlLib.parse(url).host; // BAD: the host of `url` may be controlled by an attacker - let regex = /(www|beta|).example.com/; + let regex = /((www|beta).)?example.com/; if (host.match(regex)) { res.redirect(url); } From d643904faf91abcc3355c1913d7ec2a8b81795eb Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Wed, 3 Apr 2019 13:00:07 +0200 Subject: [PATCH 07/10] JS: improve tests for fixup js/incomplete-hostname-regexp --- .../CWE-020/IncompleteHostnameRegExp.expected | 7 +++++-- .../CWE-020/tst-IncompleteHostnameRegExp.js | 19 +++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) 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..958063395a4c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp.expected +++ b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp.expected @@ -2,13 +2,13 @@ | 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:12:10:12:35 | "^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:28:23:28:40 | '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. | @@ -19,3 +19,6 @@ | 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:48:13:48:68 | '^http: ... e\\.com' | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | +| tst-IncompleteHostnameRegExp.js:48:41:48:68 | '^https ... e\\.com' | This 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' | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | 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..3f690fd81db1 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,7 +9,7 @@ /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 @@ -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 }); From 5a7101481c19ec8b1cad4ad8a88efa980dbeada2 Mon Sep 17 00:00:00 2001 From: Esben Sparre Andreasen Date: Wed, 3 Apr 2019 13:33:43 +0200 Subject: [PATCH 08/10] JS: make message for js/incomplete-hostname-regexp more informative --- .../CWE-020/IncompleteHostnameRegExp.ql | 21 ++++---- .../CWE-020/IncompleteHostnameRegExp.expected | 48 +++++++++---------- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql b/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql index 16dd4ae67fc3..7f6f3dbaa461 100644 --- a/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql +++ b/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql @@ -36,22 +36,25 @@ predicate isIncompleteHostNameRegExpPattern(string pattern, string hostPart) { // an unescaped single `.` "(? Date: Wed, 3 Apr 2019 13:35:43 +0200 Subject: [PATCH 09/10] JS: reformulate js/incomplete-hostname-regexp with type tracking --- .../CWE-020/IncompleteHostnameRegExp.ql | 48 +++++++++++-------- .../CWE-020/IncompleteHostnameRegExp.expected | 2 +- .../CWE-020/tst-IncompleteHostnameRegExp.js | 2 +- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql b/javascript/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql index 7f6f3dbaa461..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,11 @@ predicate isIncompleteHostNameRegExpPattern(string pattern, string hostPart) { // an unescaped single `.` "(? Date: Mon, 1 Apr 2019 08:41:28 +0200 Subject: [PATCH 10/10] JS: change notes for js/incomplete-hostname-regexp --- change-notes/1.21/analysis-javascript.md | 1 + 1 file changed, 1 insertion(+) 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. |