Skip to content

Commit 3e8bc8b

Browse files
authored
Merge pull request #8224 from github/hmac/http-to-file-access
Ruby: Add rb/http-to-file-access query
2 parents 6c18e1d + b1ae548 commit 3e8bc8b

File tree

16 files changed

+264
-20
lines changed

16 files changed

+264
-20
lines changed

config/identical-files.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,5 +533,13 @@
533533
"TaintedFormatStringCustomizations Ruby/JS": [
534534
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringCustomizations.qll",
535535
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringCustomizations.qll"
536+
],
537+
"HttpToFileAccessQuery JS/Ruby": [
538+
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessQuery.qll",
539+
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessQuery.qll"
540+
],
541+
"HttpToFileAccessCustomizations JS/Ruby": [
542+
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessCustomizations.qll",
543+
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessCustomizations.qll"
536544
]
537545
}

javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessCustomizations.qll

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44
* for adding your own.
55
*/
66

7-
import javascript
8-
import semmle.javascript.security.dataflow.RemoteFlowSources
9-
7+
/**
8+
* Provides default sources, sinks and sanitizers for reasoning about
9+
* writing user-controlled data to files, as well as extension points
10+
* for adding your own.
11+
*/
1012
module HttpToFileAccess {
13+
import HttpToFileAccessSpecific
14+
1115
/**
1216
* A data flow source for writing user-controlled data to files.
1317
*/
@@ -23,18 +27,6 @@ module HttpToFileAccess {
2327
*/
2428
abstract class Sanitizer extends DataFlow::Node { }
2529

26-
/**
27-
* An access to a user-controlled HTTP request input, considered as a flow source for writing user-controlled data to files
28-
*/
29-
private class RequestInputAccessAsSource extends Source {
30-
RequestInputAccessAsSource() { this instanceof HTTP::RequestInputAccess }
31-
}
32-
33-
/** A response from a server, considered as a flow source for writing user-controlled data to files. */
34-
private class ServerResponseAsSource extends Source {
35-
ServerResponseAsSource() { this = any(ClientRequest r).getAResponseDataNode() }
36-
}
37-
3830
/** A sink that represents file access method (write, append) argument */
3931
class FileAccessAsSink extends Sink {
4032
FileAccessAsSink() { exists(FileSystemWriteAccess src | this = src.getADataNode()) }

javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessQuery.qll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
* `HttpToFileAccessCustomizations` should be imported instead.
77
*/
88

9-
import javascript
10-
import HttpToFileAccessCustomizations::HttpToFileAccess
9+
private import HttpToFileAccessCustomizations::HttpToFileAccess
1110

1211
/**
1312
* A taint tracking configuration for writing user-controlled data to files.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Provides imports and classes needed for `HttpToFileAccessQuery` and `HttpToFileAccessCustomizations`.
3+
*/
4+
5+
import javascript
6+
import semmle.javascript.security.dataflow.RemoteFlowSources
7+
private import HttpToFileAccessCustomizations::HttpToFileAccess
8+
9+
/**
10+
* An access to a user-controlled HTTP request input, considered as a flow source for writing user-controlled data to files
11+
*/
12+
private class RequestInputAccessAsSource extends Source {
13+
RequestInputAccessAsSource() { this instanceof HTTP::RequestInputAccess }
14+
}
15+
16+
/** A response from a server, considered as a flow source for writing user-controlled data to files. */
17+
private class ServerResponseAsSource extends Source {
18+
ServerResponseAsSource() { this = any(ClientRequest r).getAResponseDataNode() }
19+
}

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

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,44 @@ module HTTP {
290290
}
291291
}
292292

293+
/**
294+
* An access to a user-controlled HTTP request input. For example, the URL or body of a request.
295+
* Instances of this class automatically become `RemoteFlowSource`s.
296+
*
297+
* Extend this class to refine existing API models. If you want to model new APIs,
298+
* extend `RequestInputAccess::Range` instead.
299+
*/
300+
class RequestInputAccess extends DataFlow::Node instanceof RequestInputAccess::Range {
301+
/**
302+
* Gets a string that describes the type of this input.
303+
*
304+
* This is typically the name of the method that gives rise to this input.
305+
*/
306+
string getSourceType() { result = super.getSourceType() }
307+
}
308+
309+
/** Provides a class for modeling new HTTP request inputs. */
310+
module RequestInputAccess {
311+
/**
312+
* An access to a user-controlled HTTP request input.
313+
*
314+
* Extend this class to model new APIs. If you want to refine existing API models,
315+
* extend `RequestInputAccess` instead.
316+
*/
317+
abstract class Range extends DataFlow::Node {
318+
/**
319+
* Gets a string that describes the type of this input.
320+
*
321+
* This is typically the name of the method that gives rise to this input.
322+
*/
323+
abstract string getSourceType();
324+
}
325+
}
326+
327+
private class RequestInputAccessAsRemoteFlowSource extends RemoteFlowSource::Range instanceof RequestInputAccess {
328+
override string getSourceType() { result = this.(RequestInputAccess).getSourceType() }
329+
}
330+
293331
/**
294332
* A function that will handle incoming HTTP requests.
295333
*
@@ -343,7 +381,7 @@ module HTTP {
343381
}
344382

345383
/** A parameter that will receive parts of the url when handling an incoming request. */
346-
private class RoutedParameter extends RemoteFlowSource::Range, DataFlow::ParameterNode {
384+
private class RoutedParameter extends RequestInputAccess::Range, DataFlow::ParameterNode {
347385
RequestHandler handler;
348386

349387
RoutedParameter() { this.getParameter() = handler.getARoutedParameter() }

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ private import codeql.ruby.ast.internal.Module
1111
private import codeql.ruby.ApiGraphs
1212
private import ActionView
1313
private import codeql.ruby.frameworks.ActionDispatch
14+
private import codeql.ruby.Concepts
1415

1516
/**
1617
* A `ClassDeclaration` for a class that extends `ActionController::Base`.
@@ -126,7 +127,7 @@ abstract class ParamsCall extends MethodCall {
126127
* A `RemoteFlowSource::Range` to represent accessing the
127128
* ActionController parameters available via the `params` method.
128129
*/
129-
class ParamsSource extends RemoteFlowSource::Range {
130+
class ParamsSource extends HTTP::Server::RequestInputAccess::Range {
130131
ParamsSource() { this.asExpr().getExpr() instanceof ParamsCall }
131132

132133
override string getSourceType() { result = "ActionController::Metal#params" }
@@ -143,7 +144,7 @@ abstract class CookiesCall extends MethodCall {
143144
* A `RemoteFlowSource::Range` to represent accessing the
144145
* ActionController parameters available via the `cookies` method.
145146
*/
146-
class CookiesSource extends RemoteFlowSource::Range {
147+
class CookiesSource extends HTTP::Server::RequestInputAccess::Range {
147148
CookiesSource() { this.asExpr().getExpr() instanceof CookiesCall }
148149

149150
override string getSourceType() { result = "ActionController::Metal#cookies" }
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Provides default sources, sinks and sanitizers for reasoning about
3+
* writing user-controlled data to files, as well as extension points
4+
* for adding your own.
5+
*/
6+
7+
/**
8+
* Provides default sources, sinks and sanitizers for reasoning about
9+
* writing user-controlled data to files, as well as extension points
10+
* for adding your own.
11+
*/
12+
module HttpToFileAccess {
13+
import HttpToFileAccessSpecific
14+
15+
/**
16+
* A data flow source for writing user-controlled data to files.
17+
*/
18+
abstract class Source extends DataFlow::Node { }
19+
20+
/**
21+
* A data flow sink for writing user-controlled data to files.
22+
*/
23+
abstract class Sink extends DataFlow::Node { }
24+
25+
/**
26+
* A sanitizer for writing user-controlled data to files.
27+
*/
28+
abstract class Sanitizer extends DataFlow::Node { }
29+
30+
/** A sink that represents file access method (write, append) argument */
31+
class FileAccessAsSink extends Sink {
32+
FileAccessAsSink() { exists(FileSystemWriteAccess src | this = src.getADataNode()) }
33+
}
34+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Provides a taint tracking configuration for reasoning about writing user-controlled data to files.
3+
*
4+
* Note, for performance reasons: only import this file if
5+
* `HttpToFileAccess::Configuration` is needed, otherwise
6+
* `HttpToFileAccessCustomizations` should be imported instead.
7+
*/
8+
9+
private import HttpToFileAccessCustomizations::HttpToFileAccess
10+
11+
/**
12+
* A taint tracking configuration for writing user-controlled data to files.
13+
*/
14+
class Configuration extends TaintTracking::Configuration {
15+
Configuration() { this = "HttpToFileAccess" }
16+
17+
override predicate isSource(DataFlow::Node source) { source instanceof Source }
18+
19+
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
20+
21+
override predicate isSanitizer(DataFlow::Node node) {
22+
super.isSanitizer(node) or
23+
node instanceof Sanitizer
24+
}
25+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Provides imports and classes needed for `HttpToFileAccessQuery` and `HttpToFileAccessCustomizations`.
3+
*/
4+
5+
import ruby
6+
import codeql.ruby.DataFlow
7+
import codeql.ruby.dataflow.RemoteFlowSources
8+
import codeql.ruby.Concepts
9+
import codeql.ruby.TaintTracking
10+
private import HttpToFileAccessCustomizations::HttpToFileAccess
11+
12+
/**
13+
* An access to a user-controlled HTTP request input, considered as a flow source for writing user-controlled data to files
14+
*/
15+
private class RequestInputAccessAsSource extends Source instanceof HTTP::Server::RequestInputAccess {
16+
}
17+
18+
/** A response from an outgoing HTTP request, considered as a flow source for writing user-controlled data to files. */
19+
private class HttpResponseAsSource extends Source {
20+
HttpResponseAsSource() { this = any(HTTP::Client::Request r).getResponseBody() }
21+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: newQuery
3+
---
4+
* Added a new query, `rb/http-to-file-access`. The query finds cases where data from remote user input is written to a file.

0 commit comments

Comments
 (0)