diff --git a/ruby/ql/lib/change-notes/2025-07-21-nethttprequest-improvements.md b/ruby/ql/lib/change-notes/2025-07-21-nethttprequest-improvements.md new file mode 100644 index 000000000000..7de3ed050e2f --- /dev/null +++ b/ruby/ql/lib/change-notes/2025-07-21-nethttprequest-improvements.md @@ -0,0 +1,7 @@ +--- +category: fix +--- +* Made the following changes to `NetHttpRequest` + * Adds `connectionNode`, like other Ruby HTTP clients + * Makes `requestNode` and `connectionNode` public so subclasses can use them + * Adds detection of `Net::HTTP.start`, a common way to make HTTP requests in Ruby diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll index 3a0b484e5465..0b4156ff813d 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll @@ -12,15 +12,22 @@ private import codeql.ruby.DataFlow /** * A `Net::HTTP` call which initiates an HTTP request. * ```ruby + * # one-off request * Net::HTTP.get("http://example.com/") * Net::HTTP.post("http://example.com/", "some_data") * req = Net::HTTP.new("example.com") * response = req.get("/") + * + * # connection re-use + * Net::HTTP.start("http://example.com") do |http| + * http.get("/") + * end * ``` */ class NetHttpRequest extends Http::Client::Request::Range instanceof DataFlow::CallNode { private DataFlow::CallNode request; - private API::Node requestNode; + API::Node requestNode; + API::Node connectionNode; private boolean returnsResponseBody; NetHttpRequest() { @@ -30,20 +37,27 @@ class NetHttpRequest extends Http::Client::Request::Range instanceof DataFlow::C | // Net::HTTP.get(...) method in ["get", "get_response"] and - requestNode = API::getTopLevelMember("Net").getMember("HTTP").getReturn(method) and + connectionNode = API::getTopLevelMember("Net").getMember("HTTP") and + requestNode = connectionNode.getReturn(method) and returnsResponseBody = true or // Net::HTTP.post(...).body method in ["post", "post_form"] and - requestNode = API::getTopLevelMember("Net").getMember("HTTP").getReturn(method) and + connectionNode = API::getTopLevelMember("Net").getMember("HTTP") and + requestNode = connectionNode.getReturn(method) and returnsResponseBody = false or // Net::HTTP.new(..).get(..).body + // Net::HTTP.start(..) do |http| http.get(..) end method in [ "get", "get2", "request_get", "head", "head2", "request_head", "delete", "put", "patch", "post", "post2", "request_post", "request" ] and - requestNode = API::getTopLevelMember("Net").getMember("HTTP").getInstance().getReturn(method) and + connectionNode = [ + API::getTopLevelMember("Net").getMember("HTTP").getInstance(), + API::getTopLevelMember("Net").getMember("HTTP").getMethod("start").getBlock().getParameter(0) + ] and + requestNode = connectionNode.getReturn(method) and returnsResponseBody = false ) } diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.expected b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.expected index f6275da34ace..e3a36c04819d 100644 --- a/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.expected +++ b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.expected @@ -46,6 +46,8 @@ httpRequests | NetHttp.rb:16:6:16:19 | call to patch | | NetHttp.rb:24:3:24:33 | call to get | | NetHttp.rb:29:1:29:32 | call to post | +| NetHttp.rb:33:1:33:22 | call to request | +| NetHttp.rb:36:3:36:15 | call to get | | OpenURI.rb:3:9:3:41 | call to open | | OpenURI.rb:6:9:6:34 | call to open | | OpenURI.rb:9:9:9:38 | call to open | @@ -123,6 +125,8 @@ getFramework | NetHttp.rb:16:6:16:19 | call to patch | Net::HTTP | | NetHttp.rb:24:3:24:33 | call to get | Net::HTTP | | NetHttp.rb:29:1:29:32 | call to post | Net::HTTP | +| NetHttp.rb:33:1:33:22 | call to request | Net::HTTP | +| NetHttp.rb:36:3:36:15 | call to get | Net::HTTP | | OpenURI.rb:3:9:3:41 | call to open | OpenURI | | OpenURI.rb:6:9:6:34 | call to open | OpenURI | | OpenURI.rb:9:9:9:38 | call to open | OpenURI | @@ -292,6 +296,9 @@ getAUrlPart | NetHttp.rb:24:3:24:33 | call to get | NetHttp.rb:24:17:24:22 | domain | | NetHttp.rb:24:3:24:33 | call to get | NetHttp.rb:24:29:24:32 | path | | NetHttp.rb:29:1:29:32 | call to post | NetHttp.rb:29:16:29:18 | uri | +| NetHttp.rb:33:1:33:22 | call to request | NetHttp.rb:31:22:31:42 | "https://example.com" | +| NetHttp.rb:33:1:33:22 | call to request | NetHttp.rb:33:14:33:21 | root_get | +| NetHttp.rb:36:3:36:15 | call to get | NetHttp.rb:36:12:36:14 | "/" | | OpenURI.rb:3:9:3:41 | call to open | OpenURI.rb:3:21:3:40 | "http://example.com" | | OpenURI.rb:6:9:6:34 | call to open | OpenURI.rb:6:14:6:33 | "http://example.com" | | OpenURI.rb:9:9:9:38 | call to open | OpenURI.rb:9:18:9:37 | "http://example.com" | diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.rb b/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.rb index 608b46ece9aa..63f1522dfad4 100644 --- a/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.rb +++ b/ruby/ql/test/library-tests/frameworks/http_clients/NetHttp.rb @@ -27,3 +27,11 @@ def get(domain, path) get("example.com", "/").body Net::HTTP.post(uri, "some_body") # note: response body not accessed + +http = Net::HTTP.new("https://example.com") +root_get = Net::HTTP::Get.new("/") +http.request(root_get) + +Net::HTTP.start("https://example.com") do |http| + http.get("/") +end pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy