diff --git a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll index 184e8a255a7f..45a68e39357d 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll @@ -1225,19 +1225,25 @@ module TaintTracking { * An equality test on `e.origin` or `e.source` where `e` is a `postMessage` event object, * considered as a sanitizer for `e`. */ - private class PostMessageEventSanitizer extends AdditionalSanitizerGuardNode, DataFlow::ValueNode { + private class PostMessageEventSanitizer extends AdditionalSanitizerGuardNode { VarAccess event; - override EqualityTest astNode; + boolean polarity; PostMessageEventSanitizer() { - exists(string prop | prop = "origin" or prop = "source" | - astNode.getAnOperand().(PropAccess).accesses(event, prop) and - event.mayReferToParameter(any(PostMessageEventHandler h).getEventParameter()) + event.mayReferToParameter(any(PostMessageEventHandler h).getEventParameter()) and + exists(DataFlow::PropRead read | read.accesses(event.flow(), ["origin", "source"]) | + exists(EqualityTest test | polarity = test.getPolarity() and this.getAstNode() = test | + test.getAnOperand().flow() = read + ) + or + exists(InclusionTest test | polarity = test.getPolarity() and this = test | + test.getContainedNode() = read + ) ) } override predicate sanitizes(boolean outcome, Expr e) { - outcome = astNode.getPolarity() and + outcome = polarity and e = event } diff --git a/javascript/ql/src/Security/CWE-020/MissingOriginCheck.qhelp b/javascript/ql/src/Security/CWE-020/MissingOriginCheck.qhelp new file mode 100644 index 000000000000..cb048b0c7713 --- /dev/null +++ b/javascript/ql/src/Security/CWE-020/MissingOriginCheck.qhelp @@ -0,0 +1,43 @@ + + + + + +

+The "message" event is used to send messages between windows. +An untrusted window can send a message to a trusted window, and it is up to the receiver to verify the legitimacy of the message. One way of performing that verification is to check the origin of the message ensure that it originates from a trusted window. +

+
+ + +

+Always verify the origin of incoming messages. +

+
+ + +

+The example below uses a received message to execute some code. However, the +origin of the message is not checked, so it might be possible for an attacker +to execute arbitrary code. +

+ + +

+The example is fixed below, where the origin is checked to be trusted. +It is therefore not possible for a malicious user to perform an attack using an untrusted origin. +

+ + +
+ + + +
  • Window.postMessage().
  • +
  • Web message manipulation.
  • +
  • The pitfalls of postMessage.
  • + +
    +
    diff --git a/javascript/ql/src/Security/CWE-020/MissingOriginCheck.ql b/javascript/ql/src/Security/CWE-020/MissingOriginCheck.ql new file mode 100644 index 000000000000..543d3996abbe --- /dev/null +++ b/javascript/ql/src/Security/CWE-020/MissingOriginCheck.ql @@ -0,0 +1,83 @@ +/** + * @name Missing origin verification in `postMessage` handler + * @description Missing origin verification in a `postMessage` handler allows any windows to send arbitrary data to the handler. + * @kind problem + * @problem.severity warning + * @security-severity 5 + * @precision medium + * @id js/missing-origin-check + * @tags correctness + * security + * external/cwe/cwe-020 + */ + +import javascript + +/** A function that handles "message" events. */ +class PostMessageHandler extends DataFlow::FunctionNode { + override PostMessageEventHandler astNode; + + /** Gets the parameter that contains the event. */ + DataFlow::ParameterNode getEventParameter() { + result = DataFlow::parameterNode(astNode.getEventParameter()) + } +} + +/** Gets a reference to the event from a postmessage `handler` */ +DataFlow::SourceNode event(DataFlow::TypeTracker t, PostMessageHandler handler) { + t.start() and + result = handler.getEventParameter() + or + exists(DataFlow::TypeTracker t2 | result = event(t2, handler).track(t2, t)) +} + +/** Gets a reference to the .origin from a postmessage event. */ +DataFlow::SourceNode origin(DataFlow::TypeTracker t, PostMessageHandler handler) { + t.start() and + result = event(DataFlow::TypeTracker::end(), handler).getAPropertyRead("origin") + or + result = + origin(t.continue(), handler) + .getAMethodCall([ + "toString", "toLowerCase", "toUpperCase", "toLocaleLowerCase", "toLocaleUpperCase" + ]) + or + exists(DataFlow::TypeTracker t2 | result = origin(t2, handler).track(t2, t)) +} + +/** Gets a reference to the .source from a postmessage event. */ +DataFlow::SourceNode source(DataFlow::TypeTracker t, PostMessageHandler handler) { + t.start() and + result = event(DataFlow::TypeTracker::end(), handler).getAPropertyRead("source") + or + exists(DataFlow::TypeTracker t2 | result = source(t2, handler).track(t2, t)) +} + +/** Gets a reference to the origin or the source of a postmessage event. */ +DataFlow::SourceNode sourceOrOrigin(PostMessageHandler handler) { + result = source(DataFlow::TypeTracker::end(), handler) or + result = origin(DataFlow::TypeTracker::end(), handler) +} + +/** Holds if there exists a check of the .origin or .source of the postmessage `handler`. */ +predicate hasOriginCheck(PostMessageHandler handler) { + // event.origin === "constant" + exists(EqualityTest test | sourceOrOrigin(handler).flowsToExpr(test.getAnOperand())) + or + // set.includes(event.source) + exists(InclusionTest test | sourceOrOrigin(handler).flowsTo(test.getContainedNode())) + or + // "safeOrigin".startsWith(event.origin) + exists(StringOps::StartsWith starts | + origin(DataFlow::TypeTracker::end(), handler).flowsTo(starts.getSubstring()) + ) + or + // "safeOrigin".endsWith(event.origin) + exists(StringOps::EndsWith ends | + origin(DataFlow::TypeTracker::end(), handler).flowsTo(ends.getSubstring()) + ) +} + +from PostMessageHandler handler +where not hasOriginCheck(handler) +select handler.getEventParameter(), "Postmessage handler has no origin check." diff --git a/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageNoOriginCheck.js b/javascript/ql/src/Security/CWE-020/examples/MissingOriginCheckBad.js similarity index 100% rename from javascript/ql/src/experimental/Security/CWE-020/examples/postMessageNoOriginCheck.js rename to javascript/ql/src/Security/CWE-020/examples/MissingOriginCheckBad.js diff --git a/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageWithOriginCheck.js b/javascript/ql/src/Security/CWE-020/examples/MissingOriginCheckGood.js similarity index 79% rename from javascript/ql/src/experimental/Security/CWE-020/examples/postMessageWithOriginCheck.js rename to javascript/ql/src/Security/CWE-020/examples/MissingOriginCheckGood.js index e528f9802359..8dec29f2e184 100644 --- a/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageWithOriginCheck.js +++ b/javascript/ql/src/Security/CWE-020/examples/MissingOriginCheckGood.js @@ -1,7 +1,7 @@ function postMessageHandler(event) { console.log(event.origin) // GOOD: the origin property is checked - if (event.origin === 'www.example.com') { + if (event.origin === 'https://www.example.com') { // do something } } diff --git a/javascript/ql/src/change-notes/2022-04-12-postmessage-origin-verification.md b/javascript/ql/src/change-notes/2022-04-12-postmessage-origin-verification.md new file mode 100644 index 000000000000..f59652a8640a --- /dev/null +++ b/javascript/ql/src/change-notes/2022-04-12-postmessage-origin-verification.md @@ -0,0 +1,5 @@ +--- +category: newQuery +--- +* The `js/missing-origin-check` query has been added. It highlights "message" event handlers that do not check the origin of the event. + The query previously existed as the experimental `js/missing-postmessageorigin-verification` query. \ No newline at end of file diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp deleted file mode 100644 index b8bcf242b55b..000000000000 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.qhelp +++ /dev/null @@ -1,40 +0,0 @@ - - - - - -

    If you use cross-origin communication between Window objects and do expect to receive messages from other sites, always verify the sender's identity using the origin and possibly source properties of the recevied `MessageEvent`.

    - -

    Unexpected behaviours, like `DOM-based XSS` could occur, if the event handler for incoming data does not check the origin of the data received and handles the data in an unsafe way.

    -
    - - -

    -Always verify the sender's identity of incoming messages. -

    - -
    - - -

    In the first example, the `MessageEvent.data` is passed to the `eval` function withouth checking the origin. This means that any window can send arbitrary messages that will be executed in the window receiving the message

    - - -

    In the second example, the `MessageEvent.origin` is verified with an unsecure check. For example, using `event.origin.indexOf('www.example.com') > -1` can be bypassed because the string `www.example.com` could appear anywhere in `event.origin` (i.e. `www.example.com.mydomain.com`)

    - - -

    In the third example, the `MessageEvent.origin` is properly checked against a trusted origin.

    - - -
    - - - -
  • CWE-020: Improper Input Validation
  • -
  • Window.postMessage()
  • -
  • Web-message manipulation
  • -
  • The pitfalls of postMessage
  • - -
    -
    diff --git a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql b/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql deleted file mode 100644 index 43d46d9133b3..000000000000 --- a/javascript/ql/src/experimental/Security/CWE-020/PostMessageNoOriginCheck.ql +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @name Missing `MessageEvent.origin` verification in `postMessage` handlers - * @description Missing the `MessageEvent.origin` verification in `postMessage` handlers, allows any windows to send arbitrary data to the `MessageEvent` listener. - * This could lead to unexpected behavior, especially when `MessageEvent.data` is used in an unsafe way. - * @kind problem - * @problem.severity warning - * @precision high - * @id js/missing-postmessageorigin-verification - * @tags correctness - * security - * external/cwe/cwe-020 - */ - -import javascript -import semmle.javascript.security.dataflow.DOM - -/** - * A method call for the insecure functions used to verify the `MessageEvent.origin`. - */ -class InsufficientOriginChecks extends DataFlow::Node { - InsufficientOriginChecks() { - exists(DataFlow::Node node | - this.(StringOps::StartsWith).getSubstring() = node or - this.(StringOps::Includes).getSubstring() = node or - this.(StringOps::EndsWith).getSubstring() = node - ) - } -} - -/** - * A function handler for the `MessageEvent`. - */ -class PostMessageHandler extends DataFlow::FunctionNode { - PostMessageHandler() { this.getFunction() instanceof PostMessageEventHandler } -} - -/** - * The `MessageEvent` parameter received by the handler - */ -class PostMessageEvent extends DataFlow::SourceNode { - PostMessageEvent() { exists(PostMessageHandler handler | this = handler.getParameter(0)) } - - /** - * Holds if an access on `MessageEvent.origin` is in an `EqualityTest` and there is no call of an insufficient verification method on `MessageEvent.origin` - */ - predicate hasOriginChecked() { - exists(EqualityTest test | - this.getAPropertyRead(["origin", "source"]).flowsToExpr(test.getAnOperand()) - ) - } - - /** - * Holds if there is an insufficient method call (i.e indexOf) used to verify `MessageEvent.origin` - */ - predicate hasOriginInsufficientlyChecked() { - this.getAPropertyRead("origin").getAMethodCall*() instanceof InsufficientOriginChecks - } -} - -from PostMessageEvent event -where not event.hasOriginChecked() or event.hasOriginInsufficientlyChecked() -select event, "Missing or unsafe origin verification." diff --git a/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageInsufficientCheck.js b/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageInsufficientCheck.js deleted file mode 100644 index b92887f74e8e..000000000000 --- a/javascript/ql/src/experimental/Security/CWE-020/examples/postMessageInsufficientCheck.js +++ /dev/null @@ -1,14 +0,0 @@ -function postMessageHandler(event) { - let origin = event.origin.toLowerCase(); - - let host = window.location.host; - - // BAD - if (origin.indexOf(host) === -1) - return; - - - eval(event.data); -} - -window.addEventListener('message', postMessageHandler, false); \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp.expected b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp/IncompleteHostnameRegExp.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp.expected rename to javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp/IncompleteHostnameRegExp.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp.qlref b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp/IncompleteHostnameRegExp.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp.qlref rename to javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp/IncompleteHostnameRegExp.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteHostnameRegExp.js b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp/tst-IncompleteHostnameRegExp.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteHostnameRegExp.js rename to javascript/ql/test/query-tests/Security/CWE-020/IncompleteHostnameRegExp/tst-IncompleteHostnameRegExp.js diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck.expected b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck/IncompleteUrlSchemeCheck.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck.expected rename to javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck/IncompleteUrlSchemeCheck.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck.js b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck/IncompleteUrlSchemeCheck.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck.js rename to javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck/IncompleteUrlSchemeCheck.js diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck.qlref b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck/IncompleteUrlSchemeCheck.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck.qlref rename to javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck/IncompleteUrlSchemeCheck.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheckGood.js b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck/IncompleteUrlSchemeCheckGood.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheckGood.js rename to javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSchemeCheck/IncompleteUrlSchemeCheckGood.js diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization.expected b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization/IncompleteUrlSubstringSanitization.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization.expected rename to javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization/IncompleteUrlSubstringSanitization.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization.qlref b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization/IncompleteUrlSubstringSanitization.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization.qlref rename to javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization/IncompleteUrlSubstringSanitization.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteUrlSubstringSanitization.js b/javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization/tst-IncompleteUrlSubstringSanitization.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/tst-IncompleteUrlSubstringSanitization.js rename to javascript/ql/test/query-tests/Security/CWE-020/IncompleteUrlSubstringSanitization/tst-IncompleteUrlSubstringSanitization.js diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck.expected b/javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck/IncorrectSuffixCheck.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck.expected rename to javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck/IncorrectSuffixCheck.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck.qlref b/javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck/IncorrectSuffixCheck.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck.qlref rename to javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck/IncorrectSuffixCheck.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-020/examples/IncorrectSuffixCheck.js b/javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck/examples/IncorrectSuffixCheck.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/examples/IncorrectSuffixCheck.js rename to javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck/examples/IncorrectSuffixCheck.js diff --git a/javascript/ql/test/query-tests/Security/CWE-020/examples/IncorrectSuffixCheckGood.js b/javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck/examples/IncorrectSuffixCheckGood.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/examples/IncorrectSuffixCheckGood.js rename to javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck/examples/IncorrectSuffixCheckGood.js diff --git a/javascript/ql/test/query-tests/Security/CWE-020/tst.js b/javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck/tst.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/tst.js rename to javascript/ql/test/query-tests/Security/CWE-020/IncorrectSuffixCheck/tst.js diff --git a/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/MissingOriginCheck.expected b/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/MissingOriginCheck.expected new file mode 100644 index 000000000000..58fb6ce79978 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/MissingOriginCheck.expected @@ -0,0 +1,3 @@ +| tst.js:11:20:11:24 | event | Postmessage handler has no origin check. | +| tst.js:24:27:24:27 | e | Postmessage handler has no origin check. | +| tst.js:40:27:40:27 | e | Postmessage handler has no origin check. | diff --git a/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/MissingOriginCheck.qlref b/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/MissingOriginCheck.qlref new file mode 100644 index 000000000000..02296c134e1a --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/MissingOriginCheck.qlref @@ -0,0 +1 @@ +Security/CWE-020/MissingOriginCheck.ql \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/tst.js b/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/tst.js new file mode 100644 index 000000000000..6e5c0ce6a14a --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-020/MissingOriginCheck/tst.js @@ -0,0 +1,70 @@ +window.onmessage = event => { // OK - good origin check + let origin = event.origin.toLowerCase(); + + if (origin !== window.location.origin) { + return; + } + + eval(event.data); +} + +window.onmessage = event => { // NOT OK - no origin check + let origin = event.origin.toLowerCase(); + + console.log(origin); + eval(event.data); +} + +window.onmessage = event => { // OK - there is an origin check + if (event.origin === "https://www.example.com") { + // do something + } +} + +self.onmessage = function(e) { // NOT OK + Commands[e.data.cmd].apply(null, e.data.args); +}; + +window.onmessage = event => { // OK - there is an origin check + if (mySet.includes(event.origin)) { + // do something + } +} + +window.onmessage = event => { // OK - there is an origin check + if (mySet.includes(event.source)) { + // do something + } +} + +self.onmessage = function(e) { // NOT OK + Commands[e.data.cmd].apply(null, e.data.args); +}; + +window.addEventListener('message', function(e) { // OK - has a good origin check + if (is_sysend_post_message(e) && is_valid_origin(e.origin)) { + var payload = JSON.parse(e.data); + if (payload && payload.name === uniq_prefix) { + var data = unserialize(payload.data); + sysend.broadcast(payload.key, data); + } + } +}); + +function is_valid_origin(origin) { + if (!domains) { + warn("no domains configured"); + return true; + } + var valid = domains.includes(origin); + if (!valid) { + warn("invalid origin: " + origin); + } + return valid; +} + +window.onmessage = event => { // OK - the check is OK + if ("https://www.example.com".startsWith(event.origin)) { + // do something + } +} diff --git a/javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor.expected b/javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor/MissingRegExpAnchor.expected similarity index 88% rename from javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor.expected rename to javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor/MissingRegExpAnchor.expected index abc97d625d92..0554d8263838 100644 --- a/javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor.expected +++ b/javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor/MissingRegExpAnchor.expected @@ -1,13 +1,3 @@ -| tst-IncompleteHostnameRegExp.js:2:2:2:24 | /^http: ... le.com/ | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. | -| tst-IncompleteHostnameRegExp.js:3:2:3:29 | /^http: ... le.com/ | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. | -| tst-IncompleteHostnameRegExp.js:5:2:5:29 | /^http: ... le.net/ | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. | -| tst-IncompleteHostnameRegExp.js:6:2:6:43 | /^http: ... b).com/ | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. | -| tst-IncompleteHostnameRegExp.js:11:13:11:38 | "^http: ... le.com" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. | -| tst-IncompleteHostnameRegExp.js:12:14:12:39 | "^http: ... le.com" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. | -| tst-IncompleteHostnameRegExp.js:15:22:15:47 | "^http: ... le.com" | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. | -| tst-IncompleteHostnameRegExp.js:19:17:19:35 | '^test.example.com' | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. | -| tst-IncompleteHostnameRegExp.js:40:2:40:30 | /^https ... le.com/ | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. | -| tst-IncompleteHostnameRegExp.js:55:13:55:39 | '^http: ... le.com' | This hostname pattern may match any domain name, as it is missing a '$' or '/' at the end. | | 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 | diff --git a/javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor.qlref b/javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor/MissingRegExpAnchor.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor.qlref rename to javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor/MissingRegExpAnchor.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-020/tst-SemiAnchoredRegExp.js b/javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor/tst-SemiAnchoredRegExp.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/tst-SemiAnchoredRegExp.js rename to javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor/tst-SemiAnchoredRegExp.js diff --git a/javascript/ql/test/query-tests/Security/CWE-020/tst-UnanchoredUrlRegExp.js b/javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor/tst-UnanchoredUrlRegExp.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/tst-UnanchoredUrlRegExp.js rename to javascript/ql/test/query-tests/Security/CWE-020/MissingRegExpAnchor/tst-UnanchoredUrlRegExp.js diff --git a/javascript/ql/test/query-tests/Security/CWE-020/UntrustedDataToExternalAPI.expected b/javascript/ql/test/query-tests/Security/CWE-020/UntrustedDataToExternalAPI/UntrustedDataToExternalAPI.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/UntrustedDataToExternalAPI.expected rename to javascript/ql/test/query-tests/Security/CWE-020/UntrustedDataToExternalAPI/UntrustedDataToExternalAPI.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-020/UntrustedDataToExternalAPI.qlref b/javascript/ql/test/query-tests/Security/CWE-020/UntrustedDataToExternalAPI/UntrustedDataToExternalAPI.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/UntrustedDataToExternalAPI.qlref rename to javascript/ql/test/query-tests/Security/CWE-020/UntrustedDataToExternalAPI/UntrustedDataToExternalAPI.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-020/tst-UntrustedDataToExternalAPI.js b/javascript/ql/test/query-tests/Security/CWE-020/UntrustedDataToExternalAPI/tst-UntrustedDataToExternalAPI.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/tst-UntrustedDataToExternalAPI.js rename to javascript/ql/test/query-tests/Security/CWE-020/UntrustedDataToExternalAPI/tst-UntrustedDataToExternalAPI.js diff --git a/javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape.expected b/javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/UselessCharacterEscape.expected similarity index 94% rename from javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape.expected rename to javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/UselessCharacterEscape.expected index 39084b5867e8..6cd6e27b0edc 100644 --- a/javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape.expected +++ b/javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/UselessCharacterEscape.expected @@ -1,6 +1,3 @@ -| tst-IncompleteHostnameRegExp.js:42:13:42:65 | '^http[ ... \\/(.+)' | The escape sequence '\\/' is equivalent to just '/'. | -| tst-SemiAnchoredRegExp.js:72:13:72:40 | '^good\\ ... \\\\.com' | The escape sequence '\\.' is equivalent to just '.'. | -| tst-SemiAnchoredRegExp.js:109:2:109:45 | /^((\\+\| ... ?\\d\\d)/ | The escape sequence '\\:' is equivalent to just ':'. | | tst-escapes.js:19:8:19:11 | "\\ " | The escape sequence '\\ ' is equivalent to just ' '. | | tst-escapes.js:20:1:20:54 | /\\a\\b\\c ... x\\y\\z"/ | The escape sequence '\\a' is equivalent to just 'a'. | | tst-escapes.js:20:1:20:54 | /\\a\\b\\c ... x\\y\\z"/ | The escape sequence '\\e' is equivalent to just 'e'. | diff --git a/javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape.ql b/javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/UselessCharacterEscape.ql similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape.ql rename to javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/UselessCharacterEscape.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-020/UselessRegExpCharacterEscape.expected b/javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/UselessRegExpCharacterEscape.expected similarity index 92% rename from javascript/ql/test/query-tests/Security/CWE-020/UselessRegExpCharacterEscape.expected rename to javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/UselessRegExpCharacterEscape.expected index d37ee6a0e28c..f7badbcbd86a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-020/UselessRegExpCharacterEscape.expected +++ b/javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/UselessRegExpCharacterEscape.expected @@ -1,6 +1,3 @@ -| tst-IncompleteHostnameRegExp.js:55:26:55:27 | '\\.' is equivalent to just '.', so the sequence may still represent a meta-character | The escape sequence '\\.' is equivalent to just '.', so the sequence may still represent a meta-character when it is used in a $@. | tst-IncompleteHostnameRegExp.js:55:13:55:39 | '^http: ... le.com' | regular expression | -| tst-SemiAnchoredRegExp.js:70:19:70:20 | '\\.' is equivalent to just '.', so the sequence may still represent a meta-character | The escape sequence '\\.' is equivalent to just '.', so the sequence may still represent a meta-character when it is used in a $@. | tst-SemiAnchoredRegExp.js:70:13:70:36 | '^good\\ ... r\\.com' | regular expression | -| tst-SemiAnchoredRegExp.js:70:31:70:32 | '\\.' is equivalent to just '.', so the sequence may still represent a meta-character | The escape sequence '\\.' is equivalent to just '.', so the sequence may still represent a meta-character when it is used in a $@. | tst-SemiAnchoredRegExp.js:70:13:70:36 | '^good\\ ... r\\.com' | regular expression | | tst-escapes.js:13:11:13:12 | '\\b' is a backspace, and not a word-boundary assertion | The escape sequence '\\b' is a backspace, and not a word-boundary assertion when it is used in a $@. | tst-escapes.js:13:8:13:61 | "\\a\\b\\c ... \\x\\y\\z" | regular expression | | tst-escapes.js:13:13:13:14 | '\\c' is equivalent to just 'c', so the sequence is not a character class | The escape sequence '\\c' is equivalent to just 'c', so the sequence is not a character class when it is used in a $@. | tst-escapes.js:13:8:13:61 | "\\a\\b\\c ... \\x\\y\\z" | regular expression | | tst-escapes.js:13:15:13:16 | '\\d' is equivalent to just 'd', so the sequence is not a character class | The escape sequence '\\d' is equivalent to just 'd', so the sequence is not a character class when it is used in a $@. | tst-escapes.js:13:8:13:61 | "\\a\\b\\c ... \\x\\y\\z" | regular expression | diff --git a/javascript/ql/test/query-tests/Security/CWE-020/UselessRegExpCharacterEscape.qlref b/javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/UselessRegExpCharacterEscape.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/UselessRegExpCharacterEscape.qlref rename to javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/UselessRegExpCharacterEscape.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-020/tst-escapes.js b/javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/tst-escapes.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-020/tst-escapes.js rename to javascript/ql/test/query-tests/Security/CWE-020/UselessCharacterEscape/tst-escapes.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/addEventListener.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/addEventListener.js index fbb1dcfe01d0..97d21371d082 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/addEventListener.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/addEventListener.js @@ -14,4 +14,17 @@ function test() { } window.addEventListener("message", foo.bind(null, {data: 'items'})); + + window.onmessage = e => { + if (e.origin !== "https://foobar.com") { + return; + } + document.write(e.data); // OK - there is an origin check + } + + window.onmessage = e => { + if (mySet.includes(e.origin)) { + document.write(e.data); // OK - there is an origin check + } + } }