Add Net::HTTP request modelling

This commit is contained in:
Harry Maclean
2021-09-21 15:35:03 +01:00
parent 2bdea01c8a
commit 3000587849
4 changed files with 110 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
private import codeql.ruby.AST
private import codeql.ruby.Concepts
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.ApiGraphs
private import codeql.ruby.dataflow.internal.DataFlowPublic
/**
* A shortcut for uses of Net::HTTP
*/
private API::Node netHTTP() { result = API::getTopLevelMember("Net").getMember("HTTP") }
/**
* A call that returns the response body of a `Net::HTTP` request as a String.
* ```ruby
* req = Net::HTTP.new("example.com")
* response = req.get("/")
* body = response.body
* ```
*/
private class NetHTTPRequestResponseBody extends CallNode {
DataFlow::CallNode requestCall;
NetHTTPRequestResponseBody() {
exists(string methodName, API::Node requestCallNode |
requestCall = requestCallNode.getAnImmediateUse()
|
// Net::HTTP.get(...)
methodName = "get" and
requestCallNode = netHTTP().getReturn(methodName) and
this = requestCall
or
// Net::HTTP.post(...).body
methodName in ["post", "post_form"] and
requestCallNode = netHTTP().getReturn(methodName) and
this = requestCallNode.getAMethodCall(["body", "read_body", "entity"])
or
// Net::HTTP.new(..).get(..).body
methodName in [
"get", "get2", "request_get", "head", "head2", "request_head", "delete", "put", "patch",
"post", "post2", "request_post", "request"
] and
requestCallNode = netHTTP().getInstance().getReturn(methodName) and
this = requestCallNode.getAMethodCall(["body", "read_body", "entity"])
)
}
/**
* Gets the node representing the method call that initiates the request.
* This may be different from the node which returns the response body.
*/
DataFlow::Node getRequestCall() { result = requestCall }
/**
* Gets the node representing the URL of the request.
* Currently unused, but may be useful in future, e.g. to filter out certain requests.
*/
DataFlow::Node getURLArgument() { result = requestCall.getArgument(0) }
}
/**
* A `Net::HTTP` call which initiates an HTTP request.
*/
class NetHTTPRequest extends HTTP::Client::Request::Range {
private NetHTTPRequestResponseBody responseBody;
NetHTTPRequest() { this = responseBody.getRequestCall().asExpr().getExpr() }
override DataFlow::Node getResponseBody() { result = responseBody }
override string getFramework() { result = "Net::HTTP" }
}

View File

@@ -0,0 +1,27 @@
require "net/http"
uri = URI.parse("https://example.com")
Net::HTTP.get(uri)
resp = Net::HTTP.post(URI.parse(uri), "some_body")
resp.body
resp.read_body
resp.entity
req = Net::HTTP.new("https://example.com")
r1 = req.get("/")
r2 = req.post("/")
r3 = req.put("/")
r4 = req.patch("/")
r1.body
r2.read_body
r3.entity
r4.foo
def get(domain, path)
Net::HTTP.new(domain).get(path)
end
get("example.com", "/").body

View File

@@ -0,0 +1,8 @@
| HTTP.rb:4:1:4:18 | call to get | HTTP.rb:4:1:4:18 | call to get |
| HTTP.rb:6:8:6:50 | call to post | HTTP.rb:7:1:7:9 | call to body |
| HTTP.rb:6:8:6:50 | call to post | HTTP.rb:8:1:8:14 | call to read_body |
| HTTP.rb:6:8:6:50 | call to post | HTTP.rb:9:1:9:11 | call to entity |
| HTTP.rb:13:6:13:17 | call to get | HTTP.rb:18:1:18:7 | call to body |
| HTTP.rb:14:6:14:18 | call to post | HTTP.rb:19:1:19:12 | call to read_body |
| HTTP.rb:15:6:15:17 | call to put | HTTP.rb:20:1:20:9 | call to entity |
| HTTP.rb:24:3:24:33 | call to get | HTTP.rb:27:1:27:28 | call to body |

View File

@@ -0,0 +1,4 @@
import codeql.ruby.frameworks.stdlib.net.HTTP
import codeql.ruby.DataFlow
query DataFlow::Node netHTTPRequests(NetHTTPRequest e) { result = e.getResponseBody() }