Skip to content

Commit ee51298

Browse files
committed
Model the Excon HTTP client
1 parent 83705c5 commit ee51298

File tree

5 files changed

+93
-0
lines changed

5 files changed

+93
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
*/
44

55
private import codeql.ruby.frameworks.http_clients.NetHTTP
6+
private import codeql.ruby.frameworks.http_clients.Excon
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 `Excon`.
7+
* ```ruby
8+
* # one-off request
9+
* Excon.get("http://example.com").body
10+
*
11+
* # connection re-use
12+
* connection = Excon.new("http://example.com")
13+
* connection.get(path: "/").body
14+
* connection.request(method: :get, path: "/")
15+
* ```
16+
*
17+
* TODO: pipelining, streaming responses
18+
* https://github.com/excon/excon/blob/master/README.md
19+
*/
20+
class ExconHTTPRequest extends HTTP::Client::Request::Range {
21+
DataFlow::Node request;
22+
DataFlow::CallNode responseBody;
23+
24+
ExconHTTPRequest() {
25+
exists(API::Node requestNode | request = requestNode.getAnImmediateUse() |
26+
requestNode =
27+
[
28+
// one-off requests
29+
API::getTopLevelMember("Excon"),
30+
// connection re-use
31+
API::getTopLevelMember("Excon").getInstance()
32+
]
33+
.getReturn([
34+
// Excon#request exists but Excon.request doesn't.
35+
// This shouldn't be a problem - in real code the latter would raise NoMethodError anyway.
36+
"get", "head", "delete", "options", "post", "put", "patch", "trace", "request"
37+
]) and
38+
responseBody = requestNode.getAMethodCall("body") and
39+
this = request.asExpr().getExpr()
40+
)
41+
}
42+
43+
override DataFlow::Node getResponseBody() { result = responseBody }
44+
45+
override string getFramework() { result = "Excon" }
46+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
| Excon.rb:3:9:3:40 | call to get | Excon.rb:4:1:4:10 | call to body |
2+
| Excon.rb:6:9:6:60 | call to post | Excon.rb:7:1:7:10 | call to body |
3+
| Excon.rb:9:9:9:59 | call to put | Excon.rb:10:1:10:10 | call to body |
4+
| Excon.rb:12:9:12:61 | call to patch | Excon.rb:13:1:13:10 | call to body |
5+
| Excon.rb:15:9:15:43 | call to delete | Excon.rb:16:1:16:10 | call to body |
6+
| Excon.rb:18:9:18:41 | call to head | Excon.rb:19:1:19:10 | call to body |
7+
| Excon.rb:21:9:21:44 | call to options | Excon.rb:22:1:22:10 | call to body |
8+
| Excon.rb:24:9:24:42 | call to trace | Excon.rb:25:1:25:10 | call to body |
9+
| Excon.rb:28:9:28:33 | call to get | Excon.rb:29:1:29:10 | call to body |
10+
| Excon.rb:31:10:31:38 | call to post | Excon.rb:32:1:32:11 | call to body |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import codeql.ruby.frameworks.http_clients.Excon
2+
import codeql.ruby.DataFlow
3+
4+
query DataFlow::Node exconHTTPRequests(ExconHTTPRequest e) { result = e.getResponseBody() }
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
require "excon"
2+
3+
resp1 = Excon.get("http://example.com/")
4+
resp1.body
5+
6+
resp2 = Excon.post("http://example.com/", body: "some_data")
7+
resp2.body
8+
9+
resp3 = Excon.put("http://example.com/", body: "some_data")
10+
resp3.body
11+
12+
resp4 = Excon.patch("http://example.com/", body: "some_data")
13+
resp4.body
14+
15+
resp5 = Excon.delete("http://example.com/")
16+
resp5.body
17+
18+
resp6 = Excon.head("http://example.com/")
19+
resp6.body
20+
21+
resp7 = Excon.options("http://example.com/")
22+
resp7.body
23+
24+
resp8 = Excon.trace("http://example.com/")
25+
resp8.body
26+
27+
connection = Excon.new("http://example.com")
28+
resp9 = connection.get(path: "/")
29+
resp9.body
30+
31+
resp10 = connection.post(path: "/foo")
32+
resp10.body

0 commit comments

Comments
 (0)