Skip to content

Commit b6ce37b

Browse files
committed
Add getURL to HTTP::Client::Request
This member predicate gets dataflow nodes which contribute to the URL of the request. Also consolidate the identical tests for each HTTP client.
1 parent 8fd8c9b commit b6ce37b

27 files changed

+131
-113
lines changed

ruby/ql/lib/codeql/ruby/Concepts.qll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,12 @@ module HTTP {
417417
/** Gets a node which returns the body of the response */
418418
DataFlow::Node getResponseBody() { result = super.getResponseBody() }
419419

420+
/**
421+
* Gets a node that contributes to the URL of the request.
422+
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
423+
*/
424+
DataFlow::Node getURL() { result = super.getURL() }
425+
420426
/** Gets a string that identifies the framework used for this request. */
421427
string getFramework() { result = super.getFramework() }
422428

@@ -442,6 +448,12 @@ module HTTP {
442448
/** Gets a node which returns the body of the response */
443449
abstract DataFlow::Node getResponseBody();
444450

451+
/**
452+
* Gets a node that contributes to the URL of the request.
453+
* Depending on the framework, a request may have multiple nodes which contribute to the URL.
454+
*/
455+
abstract DataFlow::Node getURL();
456+
445457
/** Gets a string that identifies the framework used for this request. */
446458
abstract string getFramework();
447459

ruby/ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ private import codeql.ruby.ApiGraphs
1818
* https://github.com/excon/excon/blob/master/README.md
1919
*/
2020
class ExconHttpRequest extends HTTP::Client::Request::Range {
21-
DataFlow::Node requestUse;
21+
DataFlow::CallNode requestUse;
2222
API::Node requestNode;
2323
API::Node connectionNode;
24+
DataFlow::Node connectionUse;
2425

2526
ExconHttpRequest() {
2627
requestUse = requestNode.getAnImmediateUse() and
28+
connectionUse = connectionNode.getAnImmediateUse() and
2729
connectionNode =
2830
[
2931
// one-off requests
@@ -44,6 +46,17 @@ class ExconHttpRequest extends HTTP::Client::Request::Range {
4446

4547
override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") }
4648

49+
override DataFlow::Node getURL() {
50+
// For one-off requests, the URL is in the first argument of the request method call.
51+
// For connection re-use, the URL is split between the first argument of the `new` call
52+
// the `path` keyword argument of the request method call.
53+
result = requestUse.getArgument(0) and not result.asExpr().getExpr() instanceof Pair
54+
or
55+
result = requestUse.getKeywordArgument("path")
56+
or
57+
result = connectionUse.(DataFlow::CallNode).getArgument(0)
58+
}
59+
4760
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {
4861
// Check for `ssl_verify_peer: false` in the options hash.
4962
exists(DataFlow::Node arg, int i |

ruby/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ private import codeql.ruby.ApiGraphs
1414
* ```
1515
*/
1616
class FaradayHttpRequest extends HTTP::Client::Request::Range {
17-
DataFlow::Node requestUse;
1817
API::Node requestNode;
1918
API::Node connectionNode;
19+
DataFlow::Node connectionUse;
20+
DataFlow::CallNode requestUse;
2021

2122
FaradayHttpRequest() {
2223
connectionNode =
@@ -29,11 +30,17 @@ class FaradayHttpRequest extends HTTP::Client::Request::Range {
2930
requestNode =
3031
connectionNode.getReturn(["get", "head", "delete", "post", "put", "patch", "trace"]) and
3132
requestUse = requestNode.getAnImmediateUse() and
33+
connectionUse = connectionNode.getAnImmediateUse() and
3234
this = requestUse.asExpr().getExpr()
3335
}
3436

3537
override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") }
3638

39+
override DataFlow::Node getURL() {
40+
result = requestUse.getArgument(0) or
41+
result = connectionUse.(DataFlow::CallNode).getArgument(0)
42+
}
43+
3744
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {
3845
// `Faraday::new` takes an options hash as its second argument, and we're
3946
// looking for

ruby/ql/lib/codeql/ruby/frameworks/http_clients/HttpClient.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ private import codeql.ruby.ApiGraphs
1212
class HttpClientRequest extends HTTP::Client::Request::Range {
1313
API::Node requestNode;
1414
API::Node connectionNode;
15-
DataFlow::Node requestUse;
15+
DataFlow::CallNode requestUse;
1616
string method;
1717

1818
HttpClientRequest() {
@@ -31,6 +31,8 @@ class HttpClientRequest extends HTTP::Client::Request::Range {
3131
this = requestUse.asExpr().getExpr()
3232
}
3333

34+
override DataFlow::Node getURL() { result = requestUse.getArgument(0) }
35+
3436
override DataFlow::Node getResponseBody() {
3537
// The `get_content` and `post_content` methods return the response body as
3638
// a string. The other methods return a `HTTPClient::Message` object which

ruby/ql/lib/codeql/ruby/frameworks/http_clients/Httparty.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ private import codeql.ruby.ApiGraphs
1818
*/
1919
class HttpartyRequest extends HTTP::Client::Request::Range {
2020
API::Node requestNode;
21-
DataFlow::Node requestUse;
21+
DataFlow::CallNode requestUse;
2222

2323
HttpartyRequest() {
2424
requestUse = requestNode.getAnImmediateUse() and
@@ -28,6 +28,8 @@ class HttpartyRequest extends HTTP::Client::Request::Range {
2828
this = requestUse.asExpr().getExpr()
2929
}
3030

31+
override DataFlow::Node getURL() { result = requestUse.getArgument(0) }
32+
3133
override DataFlow::Node getResponseBody() {
3234
// If HTTParty can recognise the response type, it will parse and return it
3335
// directly from the request call. Otherwise, it will return a `HTTParty::Response`

ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class NetHttpRequest extends HTTP::Client::Request::Range {
4646
* Gets the node representing the URL of the request.
4747
* Currently unused, but may be useful in future, e.g. to filter out certain requests.
4848
*/
49-
DataFlow::Node getURLArgument() { result = request.getArgument(0) }
49+
override DataFlow::Node getURL() { result = request.getArgument(0) }
5050

5151
override DataFlow::Node getResponseBody() { result = responseBody }
5252

ruby/ql/lib/codeql/ruby/frameworks/http_clients/OpenURI.qll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ private import codeql.ruby.frameworks.StandardLibrary
1414
*/
1515
class OpenUriRequest extends HTTP::Client::Request::Range {
1616
API::Node requestNode;
17-
DataFlow::Node requestUse;
17+
DataFlow::CallNode requestUse;
1818

1919
OpenUriRequest() {
2020
requestNode =
@@ -24,6 +24,8 @@ class OpenUriRequest extends HTTP::Client::Request::Range {
2424
this = requestUse.asExpr().getExpr()
2525
}
2626

27+
override DataFlow::Node getURL() { result = requestUse.getArgument(0) }
28+
2729
override DataFlow::Node getResponseBody() {
2830
result = requestNode.getAMethodCall(["read", "readlines"])
2931
}
@@ -48,14 +50,16 @@ class OpenUriRequest extends HTTP::Client::Request::Range {
4850
* ```
4951
*/
5052
class OpenUriKernelOpenRequest extends HTTP::Client::Request::Range {
51-
DataFlow::Node requestUse;
53+
DataFlow::CallNode requestUse;
5254

5355
OpenUriKernelOpenRequest() {
5456
requestUse instanceof KernelMethodCall and
5557
this.getMethodName() = "open" and
5658
this = requestUse.asExpr().getExpr()
5759
}
5860

61+
override DataFlow::Node getURL() { result = requestUse.getArgument(0) }
62+
5963
override DataFlow::CallNode getResponseBody() {
6064
result.asExpr().getExpr().(MethodCall).getMethodName() in ["read", "readlines"] and
6165
requestUse.(DataFlow::LocalSourceNode).flowsTo(result.getReceiver())

ruby/ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ private import codeql.ruby.ApiGraphs
99
* ```
1010
*/
1111
class RestClientHttpRequest extends HTTP::Client::Request::Range {
12-
DataFlow::Node requestUse;
12+
DataFlow::CallNode requestUse;
1313
API::Node requestNode;
1414
API::Node connectionNode;
1515

@@ -25,6 +25,8 @@ class RestClientHttpRequest extends HTTP::Client::Request::Range {
2525
this = requestUse.asExpr().getExpr()
2626
}
2727

28+
override DataFlow::Node getURL() { result = requestUse.getArgument(0) }
29+
2830
override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") }
2931

3032
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {

ruby/ql/lib/codeql/ruby/frameworks/http_clients/Typhoeus.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ private import codeql.ruby.ApiGraphs
99
* ```
1010
*/
1111
class TyphoeusHttpRequest extends HTTP::Client::Request::Range {
12-
DataFlow::Node requestUse;
12+
DataFlow::CallNode requestUse;
1313
API::Node requestNode;
1414

1515
TyphoeusHttpRequest() {
@@ -20,6 +20,8 @@ class TyphoeusHttpRequest extends HTTP::Client::Request::Range {
2020
this = requestUse.asExpr().getExpr()
2121
}
2222

23+
override DataFlow::Node getURL() { result = requestUse.getArgument(0) }
24+
2325
override DataFlow::Node getResponseBody() { result = requestNode.getAMethodCall("body") }
2426

2527
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {

ruby/ql/test/library-tests/frameworks/http_clients/Excon.expected

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)