Skip to content

Commit 4d6d6a4

Browse files
authored
Merge pull request #236 from github/more-concepts
Port some concepts to Concepts.qll
2 parents e29e61f + 403dee2 commit 4d6d6a4

File tree

2 files changed

+305
-0
lines changed

2 files changed

+305
-0
lines changed

ql/src/codeql_ruby/Concepts.qll

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
* provide concrete subclasses.
55
*/
66

7+
private import codeql_ruby.AST
8+
private import codeql_ruby.CFG
79
private import codeql_ruby.DataFlow
810
private import codeql_ruby.Frameworks
11+
private import codeql_ruby.dataflow.RemoteFlowSources
912

1013
/**
1114
* A data-flow node that executes SQL statements.
@@ -35,3 +38,300 @@ module SqlExecution {
3538
abstract DataFlow::Node getSql();
3639
}
3740
}
41+
42+
/**
43+
* A data-flow node that escapes meta-characters, which could be used to prevent
44+
* injection attacks.
45+
*
46+
* Extend this class to refine existing API models. If you want to model new APIs,
47+
* extend `Escaping::Range` instead.
48+
*/
49+
class Escaping extends DataFlow::Node {
50+
Escaping::Range range;
51+
52+
Escaping() {
53+
this = range and
54+
// escapes that don't have _both_ input/output defined are not valid
55+
exists(range.getAnInput()) and
56+
exists(range.getOutput())
57+
}
58+
59+
/** Gets an input that will be escaped. */
60+
DataFlow::Node getAnInput() { result = range.getAnInput() }
61+
62+
/** Gets the output that contains the escaped data. */
63+
DataFlow::Node getOutput() { result = range.getOutput() }
64+
65+
/**
66+
* Gets the context that this function escapes for, such as `html`, or `url`.
67+
*/
68+
string getKind() { result = range.getKind() }
69+
}
70+
71+
/** Provides a class for modeling new escaping APIs. */
72+
module Escaping {
73+
/**
74+
* A data-flow node that escapes meta-characters, which could be used to prevent
75+
* injection attacks.
76+
*
77+
* Extend this class to model new APIs. If you want to refine existing API models,
78+
* extend `Escaping` instead.
79+
*/
80+
abstract class Range extends DataFlow::Node {
81+
/** Gets an input that will be escaped. */
82+
abstract DataFlow::Node getAnInput();
83+
84+
/** Gets the output that contains the escaped data. */
85+
abstract DataFlow::Node getOutput();
86+
87+
/**
88+
* Gets the context that this function escapes for.
89+
*
90+
* While kinds are represented as strings, this should not be relied upon. Use the
91+
* predicates in the `Escaping` module, such as `getHtmlKind`.
92+
*/
93+
abstract string getKind();
94+
}
95+
96+
/** Gets the escape-kind for escaping a string so it can safely be included in HTML. */
97+
string getHtmlKind() { result = "html" }
98+
}
99+
100+
/**
101+
* An escape of a string so it can be safely included in
102+
* the body of an HTML element, for example, replacing `{}` in
103+
* `<p>{}</p>`.
104+
*/
105+
class HtmlEscaping extends Escaping {
106+
HtmlEscaping() { range.getKind() = Escaping::getHtmlKind() }
107+
}
108+
109+
/** Provides classes for modeling HTTP-related APIs. */
110+
module HTTP {
111+
/** Provides classes for modeling HTTP servers. */
112+
module Server {
113+
/**
114+
* A data-flow node that sets up a route on a server.
115+
*
116+
* Extend this class to refine existing API models. If you want to model new APIs,
117+
* extend `RouteSetup::Range` instead.
118+
*/
119+
class RouteSetup extends DataFlow::Node {
120+
RouteSetup::Range range;
121+
122+
RouteSetup() { this = range }
123+
124+
/** Gets the URL pattern for this route, if it can be statically determined. */
125+
string getUrlPattern() { result = range.getUrlPattern() }
126+
127+
/**
128+
* Gets a function that will handle incoming requests for this route, if any.
129+
*
130+
* NOTE: This will be modified in the near future to have a `RequestHandler` result, instead of a `Method`.
131+
*/
132+
Method getARequestHandler() { result = range.getARequestHandler() }
133+
134+
/**
135+
* Gets a parameter that will receive parts of the url when handling incoming
136+
* requests for this route, if any. These automatically become a `RemoteFlowSource`.
137+
*/
138+
Parameter getARoutedParameter() { result = range.getARoutedParameter() }
139+
140+
/** Gets a string that identifies the framework used for this route setup. */
141+
string getFramework() { result = range.getFramework() }
142+
}
143+
144+
/** Provides a class for modeling new HTTP routing APIs. */
145+
module RouteSetup {
146+
/**
147+
* A data-flow node that sets up a route on a server.
148+
*
149+
* Extend this class to model new APIs. If you want to refine existing API models,
150+
* extend `RouteSetup` instead.
151+
*/
152+
abstract class Range extends DataFlow::Node {
153+
/** Gets the argument used to set the URL pattern. */
154+
abstract DataFlow::Node getUrlPatternArg();
155+
156+
/** Gets the URL pattern for this route, if it can be statically determined. */
157+
string getUrlPattern() {
158+
exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
159+
this.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(strNode) and
160+
result = strNode.getExpr().getValueText()
161+
)
162+
}
163+
164+
/**
165+
* Gets a function that will handle incoming requests for this route, if any.
166+
*
167+
* NOTE: This will be modified in the near future to have a `RequestHandler` result, instead of a `Method`.
168+
*/
169+
abstract Method getARequestHandler();
170+
171+
/**
172+
* Gets a parameter that will receive parts of the url when handling incoming
173+
* requests for this route, if any. These automatically become a `RemoteFlowSource`.
174+
*/
175+
abstract Parameter getARoutedParameter();
176+
177+
/** Gets a string that identifies the framework used for this route setup. */
178+
abstract string getFramework();
179+
}
180+
}
181+
182+
/**
183+
* A function that will handle incoming HTTP requests.
184+
*
185+
* Extend this class to refine existing API models. If you want to model new APIs,
186+
* extend `RequestHandler::Range` instead.
187+
*/
188+
class RequestHandler extends Method {
189+
RequestHandler::Range range;
190+
191+
RequestHandler() { this = range }
192+
193+
/**
194+
* Gets a parameter that could receive parts of the url when handling incoming
195+
* requests, if any. These automatically become a `RemoteFlowSource`.
196+
*/
197+
Parameter getARoutedParameter() { result = range.getARoutedParameter() }
198+
199+
/** Gets a string that identifies the framework used for this route setup. */
200+
string getFramework() { result = range.getFramework() }
201+
}
202+
203+
/** Provides a class for modeling new HTTP request handlers. */
204+
module RequestHandler {
205+
/**
206+
* A function that will handle incoming HTTP requests.
207+
*
208+
* Extend this class to model new APIs. If you want to refine existing API models,
209+
* extend `RequestHandler` instead.
210+
*
211+
* Only extend this class if you can't provide a `RouteSetup`, since we handle that case automatically.
212+
*/
213+
abstract class Range extends Method {
214+
/**
215+
* Gets a parameter that could receive parts of the url when handling incoming
216+
* requests, if any. These automatically become a `RemoteFlowSource`.
217+
*/
218+
abstract Parameter getARoutedParameter();
219+
220+
/** Gets a string that identifies the framework used for this request handler. */
221+
abstract string getFramework();
222+
}
223+
}
224+
225+
private class RequestHandlerFromRouteSetup extends RequestHandler::Range {
226+
RouteSetup rs;
227+
228+
RequestHandlerFromRouteSetup() { this = rs.getARequestHandler() }
229+
230+
override Parameter getARoutedParameter() {
231+
result = rs.getARoutedParameter() and
232+
result = this.getAParameter()
233+
}
234+
235+
override string getFramework() { result = rs.getFramework() }
236+
}
237+
238+
/** A parameter that will receive parts of the url when handling an incoming request. */
239+
private class RoutedParameter extends RemoteFlowSource::Range, DataFlow::ParameterNode {
240+
RequestHandler handler;
241+
242+
RoutedParameter() { this.getParameter() = handler.getARoutedParameter() }
243+
244+
override string getSourceType() { result = handler.getFramework() + " RoutedParameter" }
245+
}
246+
247+
/**
248+
* A data-flow node that creates a HTTP response on a server.
249+
*
250+
* Note: we don't require that this response must be sent to a client (a kind of
251+
* "if a tree falls in a forest and nobody hears it" situation).
252+
*
253+
* Extend this class to refine existing API models. If you want to model new APIs,
254+
* extend `HttpResponse::Range` instead.
255+
*/
256+
class HttpResponse extends DataFlow::Node {
257+
HttpResponse::Range range;
258+
259+
HttpResponse() { this = range }
260+
261+
/** Gets the data-flow node that specifies the body of this HTTP response. */
262+
DataFlow::Node getBody() { result = range.getBody() }
263+
264+
/** Gets the mimetype of this HTTP response, if it can be statically determined. */
265+
string getMimetype() { result = range.getMimetype() }
266+
}
267+
268+
/** Provides a class for modeling new HTTP response APIs. */
269+
module HttpResponse {
270+
/**
271+
* A data-flow node that creates a HTTP response on a server.
272+
*
273+
* Note: we don't require that this response must be sent to a client (a kind of
274+
* "if a tree falls in a forest and nobody hears it" situation).
275+
*
276+
* Extend this class to model new APIs. If you want to refine existing API models,
277+
* extend `HttpResponse` instead.
278+
*/
279+
abstract class Range extends DataFlow::Node {
280+
/** Gets the data-flow node that specifies the body of this HTTP response. */
281+
abstract DataFlow::Node getBody();
282+
283+
/** Gets the data-flow node that specifies the content-type/mimetype of this HTTP response, if any. */
284+
abstract DataFlow::Node getMimetypeOrContentTypeArg();
285+
286+
/** Gets the default mimetype that should be used if `getMimetypeOrContentTypeArg` has no results. */
287+
abstract string getMimetypeDefault();
288+
289+
/** Gets the mimetype of this HTTP response, if it can be statically determined. */
290+
string getMimetype() {
291+
exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
292+
this.getMimetypeOrContentTypeArg().getALocalSource() = DataFlow::exprNode(strNode) and
293+
result = strNode.getExpr().getValueText().splitAt(";", 0)
294+
)
295+
or
296+
not exists(this.getMimetypeOrContentTypeArg()) and
297+
result = this.getMimetypeDefault()
298+
}
299+
}
300+
}
301+
302+
/**
303+
* A data-flow node that creates a HTTP redirect response on a server.
304+
*
305+
* Note: we don't require that this redirect must be sent to a client (a kind of
306+
* "if a tree falls in a forest and nobody hears it" situation).
307+
*
308+
* Extend this class to refine existing API models. If you want to model new APIs,
309+
* extend `HttpRedirectResponse::Range` instead.
310+
*/
311+
class HttpRedirectResponse extends HttpResponse {
312+
override HttpRedirectResponse::Range range;
313+
314+
HttpRedirectResponse() { this = range }
315+
316+
/** Gets the data-flow node that specifies the location of this HTTP redirect response. */
317+
DataFlow::Node getRedirectLocation() { result = range.getRedirectLocation() }
318+
}
319+
320+
/** Provides a class for modeling new HTTP redirect response APIs. */
321+
module HttpRedirectResponse {
322+
/**
323+
* A data-flow node that creates a HTTP redirect response on a server.
324+
*
325+
* Note: we don't require that this redirect must be sent to a client (a kind of
326+
* "if a tree falls in a forest and nobody hears it" situation).
327+
*
328+
* Extend this class to model new APIs. If you want to refine existing API models,
329+
* extend `HttpResponse` instead.
330+
*/
331+
abstract class Range extends HTTP::Server::HttpResponse::Range {
332+
/** Gets the data-flow node that specifies the location of this HTTP redirect response. */
333+
abstract DataFlow::Node getRedirectLocation();
334+
}
335+
}
336+
}
337+
}

ql/src/codeql_ruby/dataflow/internal/DataFlowPublic.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ class Node extends TNode {
3737
) {
3838
getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
3939
}
40+
41+
/**
42+
* Gets a local source node from which data may flow to this node in zero or more local data-flow steps.
43+
*/
44+
LocalSourceNode getALocalSource() { result.flowsTo(this) }
4045
}
4146

4247
/** A data-flow node corresponding to a call in the control-flow graph. */

0 commit comments

Comments
 (0)