|
4 | 4 | * provide concrete subclasses. |
5 | 5 | */ |
6 | 6 |
|
| 7 | +private import codeql_ruby.AST |
| 8 | +private import codeql_ruby.CFG |
7 | 9 | private import codeql_ruby.DataFlow |
8 | 10 | private import codeql_ruby.Frameworks |
| 11 | +private import codeql_ruby.dataflow.RemoteFlowSources |
9 | 12 |
|
10 | 13 | /** |
11 | 14 | * A data-flow node that executes SQL statements. |
@@ -35,3 +38,300 @@ module SqlExecution { |
35 | 38 | abstract DataFlow::Node getSql(); |
36 | 39 | } |
37 | 40 | } |
| 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 | +} |
0 commit comments