Skip to content

Commit f8d2cbb

Browse files
committed
ruby: rack responses implement are HTTP responses
1 parent c87c266 commit f8d2cbb

File tree

3 files changed

+36
-17
lines changed

3 files changed

+36
-17
lines changed

ruby/ql/lib/codeql/ruby/frameworks/Rack.qll

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Provides modeling for the Rack library.
33
*/
44

5+
private import codeql.ruby.Concepts
56
private import codeql.ruby.controlflow.CfgNodes::ExprNodes
67
private import codeql.ruby.DataFlow
78
private import codeql.ruby.typetracking.TypeTracker
@@ -17,48 +18,63 @@ module Rack {
1718
*/
1819
class AppCandidate extends DataFlow::ClassNode {
1920
private DataFlow::MethodNode call;
21+
private PotentialResponseNode resp;
2022

2123
AppCandidate() {
2224
call = this.getInstanceMethod("call") and
2325
call.getNumberOfParameters() = 1 and
24-
call.getReturn() = trackRackResponse()
26+
call.getReturn() = trackRackResponse(resp)
2527
}
2628

2729
/**
2830
* Gets the environment of the request, which is the lone parameter to the `call` method.
2931
*/
3032
DataFlow::ParameterNode getEnv() { result = call.getParameter(0) }
33+
34+
/** Gets the response returned from the request. */
35+
PotentialResponseNode getResponse() { result = resp }
3136
}
3237

33-
private DataFlow::LocalSourceNode trackStatusCode(TypeTracker t, int i) {
38+
private DataFlow::LocalSourceNode trackInt(TypeTracker t, int i) {
3439
t.start() and
3540
result.getConstantValue().isInt(i)
3641
or
37-
exists(TypeTracker t2 | result = trackStatusCode(t2, i).track(t2, t))
42+
exists(TypeTracker t2 | result = trackInt(t2, i).track(t2, t))
3843
}
3944

40-
private DataFlow::Node trackStatusCode(int i) {
41-
trackStatusCode(TypeTracker::end(), i).flowsTo(result)
42-
}
45+
private DataFlow::Node trackInt(int i) { trackInt(TypeTracker::end(), i).flowsTo(result) }
4346

44-
class ResponseNode extends DataFlow::ArrayLiteralNode {
47+
private class PotentialResponseNode extends DataFlow::ArrayLiteralNode {
4548
// [status, headers, body]
46-
ResponseNode() { this.getNumberOfArguments() = 3 }
49+
PotentialResponseNode() { this.getNumberOfArguments() = 3 }
4750

4851
/**
4952
* Gets an HTTP status code that may be returned in this response.
5053
*/
51-
int getAStatusCode() { this.getElement(0) = trackStatusCode(result) }
54+
int getAStatusCode() { this.getElement(0) = trackInt(result) }
5255
}
5356

54-
private DataFlow::LocalSourceNode trackRackResponse(TypeTracker t) {
57+
private DataFlow::LocalSourceNode trackRackResponse(TypeTracker t, PotentialResponseNode n) {
5558
t.start() and
56-
result instanceof ResponseNode
59+
result = n
5760
or
58-
exists(TypeTracker t2 | result = trackRackResponse(t2).track(t2, t))
61+
exists(TypeTracker t2 | result = trackRackResponse(t2, n).track(t2, t))
62+
}
63+
64+
private DataFlow::Node trackRackResponse(PotentialResponseNode n) {
65+
trackRackResponse(TypeTracker::end(), n).flowsTo(result)
5966
}
6067

61-
private DataFlow::Node trackRackResponse() {
62-
trackRackResponse(TypeTracker::end()).flowsTo(result)
68+
/** A `DataFlow::Node` returned from a rack request. */
69+
class ResponseNode extends PotentialResponseNode, Http::Server::HttpResponse::Range {
70+
ResponseNode() { this = any(AppCandidate app).getResponse() }
71+
72+
override DataFlow::Node getBody() { result = this.getElement(2) }
73+
74+
// TODO
75+
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
76+
77+
// TODO
78+
override string getMimetypeDefault() { none() }
6379
}
6480
}

ruby/ql/test/library-tests/frameworks/rack/Rack.expected

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ rackApps
66
rackResponseStatusCodes
77
| rack.rb:7:5:7:63 | call to [] | 200 |
88
| rack.rb:7:5:7:63 | call to [] | 500 |
9-
| rack.rb:39:5:39:13 | call to [] | 1 |
9+
| rack.rb:18:5:18:27 | call to [] | <unknown> |
10+
| rack.rb:33:5:33:26 | call to [] | <unknown> |
1011
| rack.rb:56:7:56:22 | call to [] | 200 |
1112
| rack.rb:63:5:63:21 | call to [] | 400 |

ruby/ql/test/library-tests/frameworks/rack/Rack.ql

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ private import codeql.ruby.DataFlow
33

44
query predicate rackApps(Rack::AppCandidate c, DataFlow::ParameterNode env) { env = c.getEnv() }
55

6-
query predicate rackResponseStatusCodes(Rack::ResponseNode resp, int status) {
7-
status = resp.getAStatusCode()
6+
query predicate rackResponseStatusCodes(Rack::ResponseNode resp, string status) {
7+
if exists(resp.getAStatusCode())
8+
then status = resp.getAStatusCode().toString()
9+
else status = "<unknown>"
810
}

0 commit comments

Comments
 (0)