|
12 | 12 | */ |
13 | 13 |
|
14 | 14 | import javascript |
| 15 | +import semmle.javascript.security.dataflow.DOM |
15 | 16 |
|
16 | 17 | /** |
17 | | - * A call to a `window` event listener |
| 18 | + * A method call for the insecure functions used to verify the `MessageEvent.origin`. |
18 | 19 | */ |
19 | | -class WindowListeners extends DataFlow::CallNode { |
20 | | - WindowListeners() { |
21 | | - exists(DataFlow::SourceNode source | |
22 | | - source = DataFlow::globalVarRef("window") and |
23 | | - this = source.getAMethodCall("addEventListener") |
| 20 | +class InsufficientOriginChecks extends DataFlow::MethodCallNode { |
| 21 | + InsufficientOriginChecks() { |
| 22 | + exists(string name | name = getMethodName() | |
| 23 | + name = "indexOf" or |
| 24 | + name = "includes" or |
| 25 | + name = "endsWith" or |
| 26 | + name = "startsWith" or |
| 27 | + name = "lastIndexOf" |
24 | 28 | ) |
25 | 29 | } |
26 | 30 | } |
27 | 31 |
|
28 | 32 | /** |
29 | | - * A call to a `window` event listener for the `MessageEvent` |
30 | | - */ |
31 | | -class PostMessageListeners extends DataFlow::CallNode { |
32 | | - PostMessageListeners() { |
33 | | - exists(WindowListeners listener | |
34 | | - listener.getArgument(0).mayHaveStringValue("message") and |
35 | | - this = listener |
36 | | - ) |
37 | | - } |
38 | | -} |
39 | | - |
40 | | -/** |
41 | | - * A function handler for the `MessageEvent`. It is the second argument of a `MessageEvent` listener |
| 33 | + * A function handler for the `MessageEvent`. |
42 | 34 | */ |
43 | 35 | class PostMessageHandler extends DataFlow::FunctionNode { |
44 | | - PostMessageHandler() { |
45 | | - exists(PostMessageListeners listener | this = listener.getArgument(1).getAFunctionValue()) |
46 | | - } |
| 36 | + PostMessageHandler() { exists(PostMessageEventHandler handler | this.getFunction() = handler) } |
47 | 37 | } |
48 | 38 |
|
49 | 39 | /** |
50 | | - * The `MessageEvent` received by the handler |
| 40 | + * The `MessageEvent` parameter received by the handler |
51 | 41 | */ |
52 | 42 | class PostMessageEvent extends DataFlow::SourceNode { |
53 | 43 | PostMessageEvent() { exists(PostMessageHandler handler | this = handler.getParameter(0)) } |
54 | 44 |
|
| 45 | + VarAccess event; |
| 46 | + EqualityTest astNode; |
| 47 | + |
| 48 | + /** |
| 49 | + * Holds if an access on `MessageEvent.origin` is in an `EqualityTest` and there is no call of an insufficient verification method on `MessageEvent.origin` |
| 50 | + */ |
| 51 | + predicate hasOriginChecked() { |
| 52 | + exists(string prop | prop = "origin" or prop = "source" | |
| 53 | + astNode.getAnOperand().(PropAccess).accesses(event, prop) and |
| 54 | + event.mayReferToParameter*(this.asExpr()) and |
| 55 | + not this.hasOriginInsufficientlyChecked() |
| 56 | + ) |
| 57 | + } |
| 58 | + |
55 | 59 | /** |
56 | | - * Holds if a call to a method on `MessageEvent.origin` or a read of `MessageEvent.origin` is in an `if` statement |
| 60 | + * Holds if there is an insufficient method call (i.e indexOf) used to verify `MessageEvent.origin` |
57 | 61 | */ |
58 | | - predicate isOriginChecked() { |
59 | | - exists(IfStmt ifStmt | |
60 | | - ifStmt = this.getAPropertyRead("origin").getAMethodCall*().asExpr().getEnclosingStmt() or |
61 | | - ifStmt = this.getAPropertyRead("origin").asExpr().getEnclosingStmt() |
| 62 | + predicate hasOriginInsufficientlyChecked() { |
| 63 | + exists(InsufficientOriginChecks insufficientChecks | |
| 64 | + this.getAPropertyRead("origin").getAMethodCall*() = insufficientChecks |
62 | 65 | ) |
63 | 66 | } |
64 | 67 | } |
65 | 68 |
|
66 | | -from PostMessageEvent event, PostMessageHandler handler |
67 | | -where |
68 | | - event = handler.getParameter(0) and |
69 | | - not event.isOriginChecked() |
70 | | -select "The `MessageEvent.origin` property is not checked.", event, handler |
| 69 | +from PostMessageEvent event |
| 70 | +where not event.hasOriginChecked() |
| 71 | +select event, "Missing or unsafe origin verification" |
0 commit comments