diff --git a/ql/lib/codeql/ruby/frameworks/HTTPClients.qll b/ql/lib/codeql/ruby/frameworks/HTTPClients.qll index 92883f85aa1..6e2d1183355 100644 --- a/ql/lib/codeql/ruby/frameworks/HTTPClients.qll +++ b/ql/lib/codeql/ruby/frameworks/HTTPClients.qll @@ -4,3 +4,4 @@ private import codeql.ruby.frameworks.http_clients.NetHTTP private import codeql.ruby.frameworks.http_clients.Excon +private import codeql.ruby.frameworks.http_clients.Faraday diff --git a/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll b/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll new file mode 100644 index 00000000000..21f3ba52b78 --- /dev/null +++ b/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll @@ -0,0 +1,38 @@ +private import ruby +private import codeql.ruby.Concepts +private import codeql.ruby.ApiGraphs + +/** + * A call that makes an HTTP request using `Faraday`. + * ```ruby + * # one-off request + * Faraday.get("http://example.com").body + * + * # connection re-use + * connection = Faraday.new("http://example.com") + * connection.get("/").body + * ``` + */ +class FaradayHTTPRequest extends HTTP::Client::Request::Range { + DataFlow::Node request; + DataFlow::CallNode responseBody; + + FaradayHTTPRequest() { + exists(API::Node requestNode | + requestNode = + [ + // one-off requests + API::getTopLevelMember("Faraday"), + // connection re-use + API::getTopLevelMember("Faraday").getInstance() + ].getReturn(["get", "head", "delete", "post", "put", "patch", "trace"]) and + responseBody = requestNode.getAMethodCall("body") and + request = requestNode.getAnImmediateUse() and + this = request.asExpr().getExpr() + ) + } + + override DataFlow::Node getResponseBody() { result = responseBody } + + override string getFramework() { result = "Faraday" } +} diff --git a/ql/test/library-tests/frameworks/http_clients/Faraday.expected b/ql/test/library-tests/frameworks/http_clients/Faraday.expected new file mode 100644 index 00000000000..2013b49dd4a --- /dev/null +++ b/ql/test/library-tests/frameworks/http_clients/Faraday.expected @@ -0,0 +1,9 @@ +| Faraday.rb:3:9:3:42 | call to get | Faraday.rb:4:1:4:10 | call to body | +| Faraday.rb:6:9:6:62 | call to post | Faraday.rb:7:1:7:10 | call to body | +| Faraday.rb:9:9:9:61 | call to put | Faraday.rb:10:1:10:10 | call to body | +| Faraday.rb:12:9:12:63 | call to patch | Faraday.rb:13:1:13:10 | call to body | +| Faraday.rb:15:9:15:45 | call to delete | Faraday.rb:16:1:16:10 | call to body | +| Faraday.rb:18:9:18:43 | call to head | Faraday.rb:19:1:19:10 | call to body | +| Faraday.rb:24:9:24:44 | call to trace | Faraday.rb:25:1:25:10 | call to body | +| Faraday.rb:28:9:28:27 | call to get | Faraday.rb:29:1:29:10 | call to body | +| Faraday.rb:31:10:31:46 | call to post | Faraday.rb:32:1:32:11 | call to body | diff --git a/ql/test/library-tests/frameworks/http_clients/Faraday.ql b/ql/test/library-tests/frameworks/http_clients/Faraday.ql new file mode 100644 index 00000000000..705c226429e --- /dev/null +++ b/ql/test/library-tests/frameworks/http_clients/Faraday.ql @@ -0,0 +1,4 @@ +import codeql.ruby.frameworks.http_clients.Faraday +import codeql.ruby.DataFlow + +query DataFlow::Node faradayHTTPRequests(FaradayHTTPRequest e) { result = e.getResponseBody() } diff --git a/ql/test/library-tests/frameworks/http_clients/Faraday.rb b/ql/test/library-tests/frameworks/http_clients/Faraday.rb new file mode 100644 index 00000000000..ee8cccb44b0 --- /dev/null +++ b/ql/test/library-tests/frameworks/http_clients/Faraday.rb @@ -0,0 +1,32 @@ +require "faraday" + +resp1 = Faraday.get("http://example.com/") +resp1.body + +resp2 = Faraday.post("http://example.com/", body: "some_data") +resp2.body + +resp3 = Faraday.put("http://example.com/", body: "some_data") +resp3.body + +resp4 = Faraday.patch("http://example.com/", body: "some_data") +resp4.body + +resp5 = Faraday.delete("http://example.com/") +resp5.body + +resp6 = Faraday.head("http://example.com/") +resp6.body + +resp7 = Faraday.options("http://example.com/") +resp7.body + +resp8 = Faraday.trace("http://example.com/") +resp8.body + +connection = Faraday.new("http://example.com") +resp9 = connection.get("/") +resp9.body + +resp10 = connection.post("/foo", some: "data") +resp10.body \ No newline at end of file