|
1 | 1 | /** |
2 | | - * Provides a taint-tracking configuration for reasoning about unvalidated URL |
3 | | - * redirection problems on the client side. |
| 2 | + * Provides a taint-tracking configuration for reasoning about |
| 3 | + * unvalidated URL redirection problems on the client side. |
| 4 | + * |
| 5 | + * Note, for performance reasons: only import this file if |
| 6 | + * `ClientSideUrlRedirect::Configuration` is needed, otherwise |
| 7 | + * `ClientSideUrlRedirectCustomizations` should be imported instead. |
4 | 8 | */ |
5 | 9 |
|
6 | 10 | import javascript |
7 | 11 | import semmle.javascript.security.dataflow.RemoteFlowSources |
8 | 12 | import UrlConcatenation |
9 | 13 |
|
10 | 14 | module ClientSideUrlRedirect { |
11 | | - /** |
12 | | - * A data flow source for unvalidated URL redirect vulnerabilities. |
13 | | - */ |
14 | | - abstract class Source extends DataFlow::Node { } |
15 | | - |
16 | | - /** |
17 | | - * A data flow sink for unvalidated URL redirect vulnerabilities. |
18 | | - */ |
19 | | - abstract class Sink extends DataFlow::Node { } |
20 | | - |
21 | | - /** |
22 | | - * A sanitizer for unvalidated URL redirect vulnerabilities. |
23 | | - */ |
24 | | - abstract class Sanitizer extends DataFlow::Node { } |
25 | | - |
26 | | - /** |
27 | | - * A flow label for values that represent the URL of the current document, and |
28 | | - * hence are only partially user-controlled. |
29 | | - */ |
30 | | - class DocumentUrl extends DataFlow::FlowLabel { |
31 | | - DocumentUrl() { this = "document.url" } |
32 | | - } |
| 15 | + import ClientSideUrlRedirectCustomizations::ClientSideUrlRedirect |
33 | 16 |
|
34 | 17 | /** |
35 | 18 | * A taint-tracking configuration for reasoning about unvalidated URL redirections. |
@@ -68,111 +51,4 @@ module ClientSideUrlRedirect { |
68 | 51 | succ.(DataFlow::PropRead).accesses(pred, "href") |
69 | 52 | } |
70 | 53 | } |
71 | | - |
72 | | - /** A source of remote user input, considered as a flow source for unvalidated URL redirects. */ |
73 | | - class RemoteFlowSourceAsSource extends Source { |
74 | | - RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource } |
75 | | - } |
76 | | - |
77 | | - /** |
78 | | - * Holds if `queryAccess` is an expression that may access the query string |
79 | | - * of a URL that flows into `nd` (that is, the part after the `?`). |
80 | | - */ |
81 | | - private predicate queryAccess(DataFlow::Node nd, DataFlow::Node queryAccess) { |
82 | | - exists(string propertyName | |
83 | | - queryAccess.asExpr().(PropAccess).accesses(nd.asExpr(), propertyName) |
84 | | - | |
85 | | - propertyName = "search" or propertyName = "hash" |
86 | | - ) |
87 | | - or |
88 | | - exists(MethodCallExpr mce, string methodName | |
89 | | - mce = queryAccess.asExpr() and mce.calls(nd.asExpr(), methodName) |
90 | | - | |
91 | | - methodName = "split" and |
92 | | - // exclude `location.href.split('?')[0]`, which can never refer to the query string |
93 | | - not exists(PropAccess pacc | mce = pacc.getBase() | pacc.getPropertyName() = "0") |
94 | | - or |
95 | | - (methodName = "substring" or methodName = "substr") and |
96 | | - // exclude `location.href.substring(0, ...)` and similar, which can |
97 | | - // never refer to the query string |
98 | | - not mce.getArgument(0).(NumberLiteral).getIntValue() = 0 |
99 | | - ) |
100 | | - or |
101 | | - exists(MethodCallExpr mce | |
102 | | - queryAccess.asExpr() = mce and |
103 | | - mce = any(RegExpLiteral re).flow().(DataFlow::SourceNode).getAMethodCall("exec").asExpr() and |
104 | | - nd.asExpr() = mce.getArgument(0) |
105 | | - ) |
106 | | - } |
107 | | - |
108 | | - /** |
109 | | - * A sink which is used to set the window location. |
110 | | - */ |
111 | | - class LocationSink extends Sink, DataFlow::ValueNode { |
112 | | - LocationSink() { |
113 | | - // A call to a `window.navigate` or `window.open` |
114 | | - exists(string name | |
115 | | - name = "navigate" or |
116 | | - name = "open" or |
117 | | - name = "openDialog" or |
118 | | - name = "showModalDialog" |
119 | | - | |
120 | | - this = DataFlow::globalVarRef(name).getACall().getArgument(0) |
121 | | - ) |
122 | | - or |
123 | | - // A call to `location.replace` or `location.assign` |
124 | | - exists(DataFlow::MethodCallNode locationCall, string name | |
125 | | - locationCall = DOM::locationRef().getAMethodCall(name) and |
126 | | - this = locationCall.getArgument(0) |
127 | | - | |
128 | | - name = "replace" or name = "assign" |
129 | | - ) |
130 | | - or |
131 | | - // An assignment to `location` |
132 | | - exists(Assignment assgn | isLocation(assgn.getTarget()) and astNode = assgn.getRhs()) |
133 | | - or |
134 | | - // An assignment to `location.href`, `location.protocol` or `location.hostname` |
135 | | - exists(DataFlow::PropWrite pw, string propName | |
136 | | - pw = DOM::locationRef().getAPropertyWrite(propName) and |
137 | | - this = pw.getRhs() |
138 | | - | |
139 | | - propName = "href" or propName = "protocol" or propName = "hostname" |
140 | | - ) |
141 | | - or |
142 | | - // A redirection using the AngularJS `$location` service |
143 | | - exists(AngularJS::ServiceReference service | |
144 | | - service.getName() = "$location" and |
145 | | - this.asExpr() = service.getAMethodCall("url").getArgument(0) |
146 | | - ) |
147 | | - } |
148 | | - } |
149 | | - |
150 | | - /** |
151 | | - * An expression that may be interpreted as the URL of a script. |
152 | | - */ |
153 | | - abstract class ScriptUrlSink extends Sink { } |
154 | | - |
155 | | - /** |
156 | | - * An argument expression to `new Worker(...)`, viewed as |
157 | | - * a `ScriptUrlSink`. |
158 | | - */ |
159 | | - class WebWorkerScriptUrlSink extends ScriptUrlSink, DataFlow::ValueNode { |
160 | | - WebWorkerScriptUrlSink() { |
161 | | - this = DataFlow::globalVarRef("Worker").getAnInstantiation().getArgument(0) |
162 | | - } |
163 | | - } |
164 | | - |
165 | | - /** |
166 | | - * A script or iframe `src` attribute, viewed as a `ScriptUrlSink`. |
167 | | - */ |
168 | | - class SrcAttributeUrlSink extends ScriptUrlSink, DataFlow::ValueNode { |
169 | | - SrcAttributeUrlSink() { |
170 | | - exists(DOM::AttributeDefinition attr, string eltName | |
171 | | - attr.getElement().getName() = eltName and |
172 | | - (eltName = "script" or eltName = "iframe") and |
173 | | - attr.getName() = "src" and |
174 | | - this = attr.getValueNode() |
175 | | - ) |
176 | | - } |
177 | | - } |
178 | 54 | } |
0 commit comments