|
1 | 1 | /**
|
2 |
| - * @name Missing `MessageEvent.origin` verification in `postMessage` handlers |
3 |
| - * @description Missing the `MessageEvent.origin` verification in `postMessage` handlers, allows any windows to send arbitrary data to the `MessageEvent` listener. |
4 |
| - * This could lead to unexpected behavior, especially when `MessageEvent.data` is used in an unsafe way. |
| 2 | + * @name Missing origin verification in `postMessage` handler |
| 3 | + * @description Missing origin verification in a `postMessage` handler allows any windows to send arbitrary data to the handler. |
5 | 4 | * @kind problem
|
6 | 5 | * @problem.severity warning
|
7 |
| - * @precision high |
8 |
| - * @id js/missing-postmessageorigin-verification |
| 6 | + * @security-severity 5 |
| 7 | + * @precision medium |
| 8 | + * @id js/missing-origin-verification |
9 | 9 | * @tags correctness
|
10 | 10 | * security
|
11 | 11 | * external/cwe/cwe-020
|
12 | 12 | */
|
13 | 13 |
|
14 | 14 | import javascript
|
15 |
| -import semmle.javascript.security.dataflow.DOM |
16 | 15 |
|
17 |
| -/** |
18 |
| - * A method call for the insecure functions used to verify the `MessageEvent.origin`. |
19 |
| - */ |
20 |
| -class InsufficientOriginChecks extends DataFlow::Node { |
21 |
| - InsufficientOriginChecks() { |
22 |
| - exists(DataFlow::Node node | |
23 |
| - this.(StringOps::StartsWith).getSubstring() = node or |
24 |
| - this.(StringOps::Includes).getSubstring() = node or |
25 |
| - this.(StringOps::EndsWith).getSubstring() = node |
26 |
| - ) |
| 16 | +/** A function that handles "message" events. */ |
| 17 | +class PostMessageHandler extends DataFlow::FunctionNode { |
| 18 | + override PostMessageEventHandler astNode; |
| 19 | + |
| 20 | + /** Gets the parameter that contains the event. */ |
| 21 | + DataFlow::ParameterNode getEventParameter() { |
| 22 | + result = DataFlow::parameterNode(astNode.getEventParameter()) |
27 | 23 | }
|
28 | 24 | }
|
29 | 25 |
|
30 |
| -/** |
31 |
| - * A function handler for the `MessageEvent`. |
32 |
| - */ |
33 |
| -class PostMessageHandler extends DataFlow::FunctionNode { |
34 |
| - PostMessageHandler() { this.getFunction() instanceof PostMessageEventHandler } |
| 26 | +/** Gets a reference to the event from a postmessage `handler` */ |
| 27 | +DataFlow::SourceNode event(DataFlow::TypeTracker t, PostMessageHandler handler) { |
| 28 | + t.start() and |
| 29 | + result = handler.getEventParameter() |
| 30 | + or |
| 31 | + exists(DataFlow::TypeTracker t2 | result = event(t2, handler).track(t2, t)) |
35 | 32 | }
|
36 | 33 |
|
37 |
| -/** |
38 |
| - * The `MessageEvent` parameter received by the handler |
39 |
| - */ |
40 |
| -class PostMessageEvent extends DataFlow::SourceNode { |
41 |
| - PostMessageEvent() { exists(PostMessageHandler handler | this = handler.getParameter(0)) } |
42 |
| - |
43 |
| - /** |
44 |
| - * Holds if an access on `MessageEvent.origin` is in an `EqualityTest` and there is no call of an insufficient verification method on `MessageEvent.origin` |
45 |
| - */ |
46 |
| - predicate hasOriginChecked() { |
47 |
| - exists(EqualityTest test | |
48 |
| - this.getAPropertyRead(["origin", "source"]).flowsToExpr(test.getAnOperand()) |
49 |
| - ) |
50 |
| - } |
| 34 | +/** Gets a reference to the .origin from a postmessage event. */ |
| 35 | +DataFlow::SourceNode origin(DataFlow::TypeTracker t, PostMessageHandler handler) { |
| 36 | + t.start() and |
| 37 | + result = event(DataFlow::TypeTracker::end(), handler).getAPropertyRead("origin") |
| 38 | + or |
| 39 | + result = |
| 40 | + origin(t.continue(), handler) |
| 41 | + .getAMethodCall([ |
| 42 | + "toString", "toLowerCase", "toUpperCase", "toLocaleLowerCase", "toLocaleUpperCase" |
| 43 | + ]) |
| 44 | + or |
| 45 | + exists(DataFlow::TypeTracker t2 | result = origin(t2, handler).track(t2, t)) |
| 46 | +} |
51 | 47 |
|
52 |
| - /** |
53 |
| - * Holds if there is an insufficient method call (i.e indexOf) used to verify `MessageEvent.origin` |
54 |
| - */ |
55 |
| - predicate hasOriginInsufficientlyChecked() { |
56 |
| - this.getAPropertyRead("origin").getAMethodCall*() instanceof InsufficientOriginChecks |
57 |
| - } |
| 48 | +/** Gets a reference to the .source from a postmessage event. */ |
| 49 | +DataFlow::SourceNode source(DataFlow::TypeTracker t, PostMessageHandler handler) { |
| 50 | + t.start() and |
| 51 | + result = event(DataFlow::TypeTracker::end(), handler).getAPropertyRead("source") |
| 52 | + or |
| 53 | + exists(DataFlow::TypeTracker t2 | result = source(t2, handler).track(t2, t)) |
| 54 | +} |
| 55 | + |
| 56 | +/** Gets a reference to the origin or the source of a postmessage event. */ |
| 57 | +DataFlow::SourceNode sourceOrOrigin(PostMessageHandler handler) { |
| 58 | + result = source(DataFlow::TypeTracker::end(), handler) or |
| 59 | + result = origin(DataFlow::TypeTracker::end(), handler) |
| 60 | +} |
| 61 | + |
| 62 | +/** Holds if there exists a check of the .origin or .source of the postmessage `handler`. */ |
| 63 | +predicate hasOriginCheck(PostMessageHandler handler) { |
| 64 | + // event.origin === "constant" |
| 65 | + exists(EqualityTest test | sourceOrOrigin(handler).flowsToExpr(test.getAnOperand())) |
| 66 | + or |
| 67 | + // set.includes(event.source) |
| 68 | + exists(InclusionTest test | sourceOrOrigin(handler).flowsTo(test.getContainedNode())) |
58 | 69 | }
|
59 | 70 |
|
60 |
| -from PostMessageEvent event |
61 |
| -where not event.hasOriginChecked() or event.hasOriginInsufficientlyChecked() |
62 |
| -select event, "Missing or unsafe origin verification." |
| 71 | +from PostMessageHandler handler |
| 72 | +where not hasOriginCheck(handler) |
| 73 | +select handler.getEventParameter(), "Postmessage handler has no origin check." |
0 commit comments