-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathHTTP.qll
More file actions
477 lines (409 loc) · 17 KB
/
HTTP.qll
File metadata and controls
477 lines (409 loc) · 17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
/**
* Provides classes for working with HTTP-related concepts such as requests and responses.
*/
overlay[local?]
module;
import go
/** Provides classes for modeling HTTP-related APIs. */
module Http {
/** Provides a class for modeling new HTTP response-writer APIs. */
module ResponseWriter {
/**
* A variable that is an HTTP response writer.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::ResponseWriter` instead.
*/
abstract class Range extends Variable {
/**
* Gets a data-flow node that is a use of this response writer.
*
* Note that `PostUpdateNode`s for nodes that this predicate gets do not need to be
* included, as they are handled by the concrete `ResponseWriter`'s `getANode`.
*/
abstract DataFlow::Node getANode();
}
}
/**
* A variable that is an HTTP response writer.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::ResponseWriter::Range` instead.
*/
class ResponseWriter extends Variable instanceof ResponseWriter::Range {
/** Gets the body that is written in this HTTP response. */
ResponseBody getBody() { result.getResponseWriter() = this }
/** Gets a header write that is written in this HTTP response. */
HeaderWrite getAHeaderWrite() { result.getResponseWriter() = this }
/** Gets a redirect that is sent in this HTTP response. */
Redirect getARedirect() { result.getResponseWriter() = this }
/** Gets a data-flow node that is a use of this response writer. */
DataFlow::Node getANode() {
result = super.getANode() or
result.(DataFlow::PostUpdateNode).getPreUpdateNode() = super.getANode()
}
}
/** Provides a class for modeling new HTTP header-write APIs. */
module HeaderWrite {
/**
* A data-flow node that represents a write to an HTTP header.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::HeaderWrite` instead.
*/
abstract class Range extends DataFlow::ExprNode {
/** Gets the (lower-case) name of a header set by this definition. */
string getHeaderName() { result = this.getName().getStringValue().toLowerCase() }
/** Gets the value of the header set by this definition. */
string getHeaderValue() {
result = this.getValue().getStringValue()
or
result = this.getValue().getIntValue().toString()
}
/** Holds if this header write defines the header `header`. */
predicate definesHeader(string header, string value) {
header = this.getHeaderName() and
value = this.getHeaderValue()
}
/**
* Gets the node representing the name of the header defined by this write.
*
* Note that a `HeaderWrite` targeting a constant header (e.g. a routine that always
* sets the `Content-Type` header) may not have such a node, so callers should use
* `getHeaderName` in preference to this method).
*/
abstract DataFlow::Node getName();
/** Gets the node representing the value of the header defined by this write. */
abstract DataFlow::Node getValue();
/** Gets the response writer associated with this header write, if any. */
abstract ResponseWriter getResponseWriter();
}
}
/**
* A data-flow node that represents a write to an HTTP header.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::HeaderWrite::Range` instead.
*/
class HeaderWrite extends DataFlow::ExprNode instanceof HeaderWrite::Range {
/** Gets the (lower-case) name of a header set by this definition. */
string getHeaderName() { result = super.getHeaderName() }
/** Gets the value of the header set by this definition. */
string getHeaderValue() { result = super.getHeaderValue() }
/** Holds if this header write defines the header `header`. */
predicate definesHeader(string header, string value) { super.definesHeader(header, value) }
/**
* Gets the node representing the name of the header defined by this write.
*
* Note that a `HeaderWrite` targeting a constant header (e.g. a routine that always
* sets the `Content-Type` header) may not have such a node, so callers should use
* `getHeaderName` in preference to this method).
*/
DataFlow::Node getName() { result = super.getName() }
/** Gets the node representing the value of the header defined by this write. */
DataFlow::Node getValue() { result = super.getValue() }
/** Gets the response writer associated with this header write, if any. */
ResponseWriter getResponseWriter() { result = super.getResponseWriter() }
}
/** A data-flow node whose value is written to an HTTP header. */
class Header extends DataFlow::Node {
HeaderWrite hw;
Header() {
this = hw.getName()
or
this = hw.getValue()
}
/** Gets the response writer associated with this header write, if any. */
ResponseWriter getResponseWriter() { result = hw.getResponseWriter() }
}
/** A data-flow node whose value is written to the value of an HTTP header. */
class HeaderValue extends Header {
HeaderValue() { this = hw.getValue() }
}
/** A data-flow node whose value is written to the name of an HTTP header. */
class HeaderName extends Header {
HeaderName() { this = hw.getName() }
}
/** Provides a class for modeling new HTTP request-body APIs. */
module RequestBody {
/**
* An expression representing a reader whose content is written to an HTTP request body.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::RequestBody` instead.
*/
abstract class Range extends DataFlow::Node { }
}
/**
* An expression representing a reader whose content is written to an HTTP request body.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::RequestBody::Range` instead.
*/
class RequestBody extends DataFlow::Node instanceof RequestBody::Range { }
/** Provides a class for modeling new HTTP response-body APIs. */
module ResponseBody {
/**
* An expression which is written to an HTTP response body.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::ResponseBody` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the response writer associated with this header write, if any. */
abstract ResponseWriter getResponseWriter();
/** Gets a content-type associated with this body. */
string getAContentType() {
exists(Http::HeaderWrite hw | hw = this.getResponseWriter().getAHeaderWrite() |
hw.getHeaderName() = "content-type" and
result = hw.getHeaderValue()
)
or
result = this.getAContentTypeNode().getStringValue()
}
/** Gets a dataflow node for a content-type associated with this body. */
DataFlow::Node getAContentTypeNode() {
exists(Http::HeaderWrite hw | hw = this.getResponseWriter().getAHeaderWrite() |
hw.getHeaderName() = "content-type" and
result = hw.getValue()
)
}
}
}
/**
* An expression which is written to an HTTP response body.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::ResponseBody::Range` instead.
*/
class ResponseBody extends DataFlow::Node instanceof ResponseBody::Range {
/** Gets the response writer associated with this header write, if any. */
ResponseWriter getResponseWriter() { result = super.getResponseWriter() }
/** Gets a content-type associated with this body. */
string getAContentType() { result = super.getAContentType() }
/** Gets a dataflow node for a content-type associated with this body. */
DataFlow::Node getAContentTypeNode() { result = super.getAContentTypeNode() }
}
/** Provides a class for modeling new HTTP template response-body APIs. */
module TemplateResponseBody {
/**
* An expression which is written to an HTTP response body via a template execution.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::ResponseBody` instead.
*/
abstract class Range extends ResponseBody::Range {
/** Gets the read of the variable inside the template where this value is read. */
abstract HtmlTemplate::TemplateRead getRead();
}
}
/**
* An expression which is written to an HTTP response body via a template execution.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::TemplateResponseBody::Range` instead.
*/
class TemplateResponseBody extends ResponseBody instanceof TemplateResponseBody::Range {
/** Gets the read of the variable inside the template where this value is read. */
HtmlTemplate::TemplateRead getRead() { result = super.getRead() }
}
/** Provides a class for modeling new HTTP client request APIs. */
module ClientRequest {
/**
* A call that performs a request to a URL.
*
* Example: An HTTP POST request is a client request that sends some
* `data` to a `url`, where both the headers and the body of the request
* contribute to the `data`.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::ClientRequest` instead.
*/
abstract class Range extends DataFlow::Node {
/**
* Gets the URL of the request.
*/
abstract DataFlow::Node getUrl();
}
}
/**
* A call that performs a request to a URL.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::ClientRequest::Range` instead.
*/
class ClientRequest extends DataFlow::Node instanceof ClientRequest::Range {
/**
* Gets the URL of the request.
*/
DataFlow::Node getUrl() { result = super.getUrl() }
}
/** Provides a class for modeling new HTTP redirect APIs. */
module Redirect {
/**
* An HTTP redirect.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::Redirect` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the data-flow node representing the URL being redirected to. */
abstract DataFlow::Node getUrl();
/** Gets the response writer that this redirect is sent on, if any. */
abstract ResponseWriter getResponseWriter();
}
/**
* An assignment of the HTTP Location header, which indicates the location for a
* redirect.
*/
private class LocationHeaderSet extends Range, HeaderWrite {
LocationHeaderSet() { this.getHeaderName() = "location" }
override DataFlow::Node getUrl() { result = this.getValue() }
override ResponseWriter getResponseWriter() { result = HeaderWrite.super.getResponseWriter() }
}
/**
* An HTTP request attribute that is generally not attacker-controllable for
* open redirect exploits; for example, a form field submitted in a POST request.
*/
abstract class UnexploitableSource extends DataFlow::Node { }
private predicate sinkKindInfo(string kind, int rw) {
kind = "url-redirection" and
rw = -2
or
kind = "url-redirection[receiver]" and
rw = -1
or
sinkModel(_, _, _, _, _, _, _, kind, _, _) and
exists(string rwStr |
rwStr.toInt() = rw and
kind = "url-redirection[" + rwStr + "]"
)
}
private class ExternalHttpRedirect extends Range, DataFlow::CallNode {
DataFlow::ArgumentNode url;
int rw;
ExternalHttpRedirect() {
this = url.getCall() and
exists(string kind |
sinkKindInfo(kind, rw) and
sinkNode(url, kind)
)
}
override DataFlow::Node getUrl() { result = url.getACorrespondingSyntacticArgument() }
override Http::ResponseWriter getResponseWriter() {
rw = -1 and result.getANode() = this.getReceiver()
or
rw >= 0 and result.getANode() = this.getArgument(rw)
}
}
}
/**
* An HTTP redirect.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::Redirect::Range` instead.
*/
class Redirect extends DataFlow::Node instanceof Redirect::Range {
/** Gets the data-flow node representing the URL being redirected to. */
DataFlow::Node getUrl() { result = super.getUrl() }
/** Gets the response writer that this redirect is sent on, if any. */
ResponseWriter getResponseWriter() { result = super.getResponseWriter() }
}
/** Provides a class for modeling new HTTP handler APIs. */
module RequestHandler {
/**
* An HTTP request handler.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::RequestHandler` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets a node that is used in a check that is tested before this handler is run. */
abstract predicate guardedBy(DataFlow::Node check);
}
}
/**
* An HTTP request handler.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::RequestHandler::Range` instead.
*/
class RequestHandler extends DataFlow::Node instanceof RequestHandler::Range {
/** Gets a node that is used in a check that is tested before this handler is run. */
predicate guardedBy(DataFlow::Node check) { super.guardedBy(check) }
}
/** Provides a class for modeling new HTTP response cookie write APIs. */
module CookieWrite {
/**
* A write of an HTTP Cookie to an HTTP response.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::CookieWrite` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the name of the cookie written. */
abstract DataFlow::Node getName();
/** Gets the value of the cookie written. */
abstract DataFlow::Node getValue();
/** Gets the `Secure` attribute of the cookie written. */
abstract DataFlow::Node getSecure();
/** Gets the `HttpOnly` attribute of the cookie written. */
abstract DataFlow::Node getHttpOnly();
}
}
/**
* A write of an HTTP Cookie to an HTTP response.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::CookieWrite::Range` instead.
*/
class CookieWrite extends DataFlow::Node instanceof CookieWrite::Range {
/** Gets the name of the cookie written. */
DataFlow::Node getName() { result = super.getName() }
/** Gets the value of the cookie written. */
DataFlow::Node getValue() { result = super.getValue() }
/** Gets the `Secure` attribute of the cookie written. */
DataFlow::Node getSecure() { result = super.getSecure() }
/** Gets the `HttpOnly` attribute of the cookie written. */
DataFlow::Node getHttpOnly() { result = super.getHttpOnly() }
}
/** Provides a class for modeling the new APIs for writes to options of an HTTP cookie. */
module CookieOptionWrite {
/**
* A write to an option of an HTTP cookie object.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `HTTP::CookieOptionWrite` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the node representing the cookie object for the options being set. */
abstract DataFlow::Node getCookieOutput();
/** Gets the name of the cookie represented, if any. */
abstract DataFlow::Node getName();
/** Gets the value of the cookie represented, if any. */
abstract DataFlow::Node getValue();
/** Gets the `Secure` attribute of the cookie represented, if any. */
abstract DataFlow::Node getSecure();
/** Gets the `HttpOnly` attribute of the cookie represented, if any. */
abstract DataFlow::Node getHttpOnly();
}
}
/**
* A write to an option of an HTTP cookie object.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `HTTP::CookieOptionWrite::Range` instead.
*/
class CookieOptionWrite extends DataFlow::Node instanceof CookieOptionWrite::Range {
/** Gets the node representing the cookie object for the options being set. */
DataFlow::Node getCookieOutput() { result = super.getCookieOutput() }
/** Gets the name of the cookie represented, if any. */
DataFlow::Node getName() { result = super.getName() }
/** Gets the value of the cookie represented, if any. */
DataFlow::Node getValue() { result = super.getValue() }
/** Gets the `Secure` attribute of the cookie represented, if any. */
DataFlow::Node getSecure() { result = super.getSecure() }
/** Gets the `HttpOnly` attribute of the cookie represented, if any. */
DataFlow::Node getHttpOnly() { result = super.getHttpOnly() }
}
}