Skip to content

Commit 3000587

Browse files
committed
Add Net::HTTP request modelling
1 parent 2bdea01 commit 3000587

File tree

4 files changed

+110
-0
lines changed

4 files changed

+110
-0
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
private import codeql.ruby.AST
2+
private import codeql.ruby.Concepts
3+
private import codeql.ruby.dataflow.RemoteFlowSources
4+
private import codeql.ruby.ApiGraphs
5+
private import codeql.ruby.dataflow.internal.DataFlowPublic
6+
7+
/**
8+
* A shortcut for uses of Net::HTTP
9+
*/
10+
private API::Node netHTTP() { result = API::getTopLevelMember("Net").getMember("HTTP") }
11+
12+
/**
13+
* A call that returns the response body of a `Net::HTTP` request as a String.
14+
* ```ruby
15+
* req = Net::HTTP.new("example.com")
16+
* response = req.get("/")
17+
* body = response.body
18+
* ```
19+
*/
20+
private class NetHTTPRequestResponseBody extends CallNode {
21+
DataFlow::CallNode requestCall;
22+
23+
NetHTTPRequestResponseBody() {
24+
exists(string methodName, API::Node requestCallNode |
25+
requestCall = requestCallNode.getAnImmediateUse()
26+
|
27+
// Net::HTTP.get(...)
28+
methodName = "get" and
29+
requestCallNode = netHTTP().getReturn(methodName) and
30+
this = requestCall
31+
or
32+
// Net::HTTP.post(...).body
33+
methodName in ["post", "post_form"] and
34+
requestCallNode = netHTTP().getReturn(methodName) and
35+
this = requestCallNode.getAMethodCall(["body", "read_body", "entity"])
36+
or
37+
// Net::HTTP.new(..).get(..).body
38+
methodName in [
39+
"get", "get2", "request_get", "head", "head2", "request_head", "delete", "put", "patch",
40+
"post", "post2", "request_post", "request"
41+
] and
42+
requestCallNode = netHTTP().getInstance().getReturn(methodName) and
43+
this = requestCallNode.getAMethodCall(["body", "read_body", "entity"])
44+
)
45+
}
46+
47+
/**
48+
* Gets the node representing the method call that initiates the request.
49+
* This may be different from the node which returns the response body.
50+
*/
51+
DataFlow::Node getRequestCall() { result = requestCall }
52+
53+
/**
54+
* Gets the node representing the URL of the request.
55+
* Currently unused, but may be useful in future, e.g. to filter out certain requests.
56+
*/
57+
DataFlow::Node getURLArgument() { result = requestCall.getArgument(0) }
58+
}
59+
60+
/**
61+
* A `Net::HTTP` call which initiates an HTTP request.
62+
*/
63+
class NetHTTPRequest extends HTTP::Client::Request::Range {
64+
private NetHTTPRequestResponseBody responseBody;
65+
66+
NetHTTPRequest() { this = responseBody.getRequestCall().asExpr().getExpr() }
67+
68+
override DataFlow::Node getResponseBody() { result = responseBody }
69+
70+
override string getFramework() { result = "Net::HTTP" }
71+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
require "net/http"
2+
3+
uri = URI.parse("https://example.com")
4+
Net::HTTP.get(uri)
5+
6+
resp = Net::HTTP.post(URI.parse(uri), "some_body")
7+
resp.body
8+
resp.read_body
9+
resp.entity
10+
11+
req = Net::HTTP.new("https://example.com")
12+
13+
r1 = req.get("/")
14+
r2 = req.post("/")
15+
r3 = req.put("/")
16+
r4 = req.patch("/")
17+
18+
r1.body
19+
r2.read_body
20+
r3.entity
21+
r4.foo
22+
23+
def get(domain, path)
24+
Net::HTTP.new(domain).get(path)
25+
end
26+
27+
get("example.com", "/").body
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
| HTTP.rb:4:1:4:18 | call to get | HTTP.rb:4:1:4:18 | call to get |
2+
| HTTP.rb:6:8:6:50 | call to post | HTTP.rb:7:1:7:9 | call to body |
3+
| HTTP.rb:6:8:6:50 | call to post | HTTP.rb:8:1:8:14 | call to read_body |
4+
| HTTP.rb:6:8:6:50 | call to post | HTTP.rb:9:1:9:11 | call to entity |
5+
| HTTP.rb:13:6:13:17 | call to get | HTTP.rb:18:1:18:7 | call to body |
6+
| HTTP.rb:14:6:14:18 | call to post | HTTP.rb:19:1:19:12 | call to read_body |
7+
| HTTP.rb:15:6:15:17 | call to put | HTTP.rb:20:1:20:9 | call to entity |
8+
| HTTP.rb:24:3:24:33 | call to get | HTTP.rb:27:1:27:28 | 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.stdlib.net.HTTP
2+
import codeql.ruby.DataFlow
3+
4+
query DataFlow::Node netHTTPRequests(NetHTTPRequest e) { result = e.getResponseBody() }

0 commit comments

Comments
 (0)