Skip to content

Commit 0ea228e

Browse files
authored
Merge pull request github#315 from github/hmac-outgoing-http
Model more HTTP clients
2 parents 1d58f8c + a9c00a0 commit 0ea228e

34 files changed

+324
-32
lines changed

ql/lib/codeql/ruby/Frameworks.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ private import codeql.ruby.frameworks.ActiveRecord
77
private import codeql.ruby.frameworks.ActionView
88
private import codeql.ruby.frameworks.StandardLibrary
99
private import codeql.ruby.frameworks.Files
10-
private import codeql.ruby.frameworks.HTTPClients
10+
private import codeql.ruby.frameworks.HttpClients

ql/lib/codeql/ruby/frameworks/HTTPClients.qll

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Helper file that imports all HTTP clients.
3+
*/
4+
5+
private import codeql.ruby.frameworks.http_clients.NetHttp
6+
private import codeql.ruby.frameworks.http_clients.Excon
7+
private import codeql.ruby.frameworks.http_clients.Faraday
8+
private import codeql.ruby.frameworks.http_clients.RestClient
9+
private import codeql.ruby.frameworks.http_clients.Httparty
10+
private import codeql.ruby.frameworks.http_clients.HttpClient
11+
private import codeql.ruby.frameworks.http_clients.OpenURI
12+
private import codeql.ruby.frameworks.http_clients.Typhoeus

ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ private import codeql.ruby.ApiGraphs
1717
* TODO: pipelining, streaming responses
1818
* https://github.com/excon/excon/blob/master/README.md
1919
*/
20-
class ExconHTTPRequest extends HTTP::Client::Request::Range {
20+
class ExconHttpRequest extends HTTP::Client::Request::Range {
2121
DataFlow::Node request;
2222
DataFlow::CallNode responseBody;
2323

24-
ExconHTTPRequest() {
24+
ExconHttpRequest() {
2525
exists(API::Node requestNode | request = requestNode.getAnImmediateUse() |
2626
requestNode =
2727
[

ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ private import codeql.ruby.ApiGraphs
1313
* connection.get("/").body
1414
* ```
1515
*/
16-
class FaradayHTTPRequest extends HTTP::Client::Request::Range {
16+
class FaradayHttpRequest extends HTTP::Client::Request::Range {
1717
DataFlow::Node request;
1818
DataFlow::CallNode responseBody;
1919

20-
FaradayHTTPRequest() {
20+
FaradayHttpRequest() {
2121
exists(API::Node requestNode |
2222
requestNode =
2323
[
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
private import ruby
2+
private import codeql.ruby.Concepts
3+
private import codeql.ruby.ApiGraphs
4+
5+
/**
6+
* A call that makes an HTTP request using `HTTPClient`.
7+
* ```ruby
8+
* HTTPClient.get("http://example.com").body
9+
* HTTPClient.get_content("http://example.com")
10+
* ```
11+
*/
12+
class HttpClientRequest extends HTTP::Client::Request::Range {
13+
DataFlow::Node request;
14+
DataFlow::CallNode responseBody;
15+
16+
HttpClientRequest() {
17+
exists(API::Node requestNode, string method |
18+
request = requestNode.getAnImmediateUse() and
19+
method in [
20+
"get", "head", "delete", "options", "post", "put", "trace", "get_content", "post_content"
21+
]
22+
|
23+
requestNode = API::getTopLevelMember("HTTPClient").getReturn(method) and
24+
(
25+
// The `get_content` and `post_content` methods return the response body as a string.
26+
// The other methods return a `HTTPClient::Message` object which has various methods
27+
// that return the response body.
28+
method in ["get_content", "post_content"] and responseBody = request
29+
or
30+
not method in ["get_content", "put_content"] and
31+
responseBody = requestNode.getAMethodCall(["body", "http_body", "content", "dump"])
32+
) and
33+
this = request.asExpr().getExpr()
34+
)
35+
}
36+
37+
override DataFlow::Node getResponseBody() { result = responseBody }
38+
39+
override string getFramework() { result = "HTTPClient" }
40+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
private import ruby
2+
private import codeql.ruby.Concepts
3+
private import codeql.ruby.ApiGraphs
4+
5+
/**
6+
* A call that makes an HTTP request using `HTTParty`.
7+
* ```ruby
8+
* # one-off request - returns the response body
9+
* HTTParty.get("http://example.com")
10+
*
11+
* # TODO: module inclusion
12+
* class MyClass
13+
* include HTTParty
14+
* end
15+
*
16+
* MyClass.new("http://example.com")
17+
* ```
18+
*/
19+
class HttpartyRequest extends HTTP::Client::Request::Range {
20+
DataFlow::Node request;
21+
DataFlow::CallNode responseBody;
22+
23+
HttpartyRequest() {
24+
exists(API::Node requestNode | request = requestNode.getAnImmediateUse() |
25+
requestNode =
26+
API::getTopLevelMember("HTTParty")
27+
.getReturn(["get", "head", "delete", "options", "post", "put", "patch"]) and
28+
(
29+
// If HTTParty can recognise the response type, it will parse and return it
30+
// directly from the request call. Otherwise, it will return a `HTTParty::Response`
31+
// object that has a `#body` method.
32+
// So if there's a call to `#body` on the response, treat that as the response body.
33+
exists(DataFlow::Node r | r = requestNode.getAMethodCall("body") | responseBody = r)
34+
or
35+
// Otherwise, treat the response as the response body.
36+
not exists(DataFlow::Node r | r = requestNode.getAMethodCall("body")) and
37+
responseBody = request
38+
) and
39+
this = request.asExpr().getExpr()
40+
)
41+
}
42+
43+
override DataFlow::Node getResponseBody() { result = responseBody }
44+
45+
override string getFramework() { result = "HTTParty" }
46+
}

ql/lib/codeql/ruby/frameworks/http_clients/NetHTTP.qll renamed to ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ private import codeql.ruby.dataflow.internal.DataFlowPublic
1313
* response = req.get("/")
1414
* ```
1515
*/
16-
class NetHTTPRequest extends HTTP::Client::Request::Range {
16+
class NetHttpRequest extends HTTP::Client::Request::Range {
1717
private DataFlow::CallNode request;
1818
private DataFlow::Node responseBody;
1919

20-
NetHTTPRequest() {
20+
NetHttpRequest() {
2121
exists(API::Node requestNode, string method |
2222
request = requestNode.getAnImmediateUse() and
2323
this = request.asExpr().getExpr()
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
private import ruby
2+
private import codeql.ruby.Concepts
3+
private import codeql.ruby.ApiGraphs
4+
private import codeql.ruby.frameworks.StandardLibrary
5+
6+
/**
7+
* A call that makes an HTTP request using `OpenURI`.
8+
* ```ruby
9+
* Kernel.open("http://example.com").read
10+
* URI.open("http://example.com").readlines
11+
* URI.parse("http://example.com").open.read
12+
* ```
13+
*/
14+
class OpenURIRequest extends HTTP::Client::Request::Range {
15+
DataFlow::Node request;
16+
DataFlow::CallNode responseBody;
17+
18+
OpenURIRequest() {
19+
exists(API::Node requestNode | request = requestNode.getAnImmediateUse() |
20+
requestNode =
21+
[API::getTopLevelMember("URI"), API::getTopLevelMember("URI").getReturn("parse")]
22+
.getReturn("open") and
23+
responseBody = requestNode.getAMethodCall(["read", "readlines"]) and
24+
this = request.asExpr().getExpr()
25+
)
26+
or
27+
// Kernel.open("http://example.com").read
28+
// open("http://example.com").read
29+
this instanceof KernelMethodCall and
30+
this.getMethodName() = "open" and
31+
request.asExpr().getExpr() = this and
32+
responseBody.asExpr().getExpr().(MethodCall).getMethodName() in ["read", "readlines"] and
33+
request.(DataFlow::LocalSourceNode).flowsTo(responseBody.getReceiver())
34+
}
35+
36+
override DataFlow::Node getResponseBody() { result = responseBody }
37+
38+
override string getFramework() { result = "OpenURI" }
39+
}

ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ private import codeql.ruby.ApiGraphs
88
* RestClient.get("http://example.com").body
99
* ```
1010
*/
11-
class RestClientHTTPRequest extends HTTP::Client::Request::Range {
11+
class RestClientHttpRequest extends HTTP::Client::Request::Range {
1212
DataFlow::Node request;
1313
DataFlow::CallNode responseBody;
1414

15-
RestClientHTTPRequest() {
15+
RestClientHttpRequest() {
1616
exists(API::Node requestNode |
1717
requestNode =
1818
API::getTopLevelMember("RestClient")

0 commit comments

Comments
 (0)