Skip to content

Commit c319957

Browse files
committed
Ruby: add rb/log-inection query
1 parent cce39fb commit c319957

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Provides a taint-tracking configuration for reasoning about untrusted user input used in log entries.
3+
*/
4+
5+
import ruby
6+
import codeql.ruby.Concepts
7+
import codeql.ruby.DataFlow
8+
import codeql.ruby.TaintTracking
9+
import codeql.ruby.dataflow.RemoteFlowSources
10+
import codeql.ruby.frameworks.Core
11+
12+
/**
13+
* A data flow source for user input used in log entries.
14+
*/
15+
abstract class Source extends DataFlow::Node { }
16+
17+
/**
18+
* A data flow sink for user input used in log entries.
19+
*/
20+
abstract class Sink extends DataFlow::Node { }
21+
22+
/**
23+
* A sanitizer for malicious user input used in log entries.
24+
*/
25+
abstract class Sanitizer extends DataFlow::Node { }
26+
27+
/**
28+
* A taint-tracking configuration for untrusted user input used in log entries.
29+
*/
30+
class LogInjectionConfiguration extends TaintTracking::Configuration {
31+
LogInjectionConfiguration() { this = "LogInjection" }
32+
33+
override predicate isSource(DataFlow::Node source) { source instanceof Source }
34+
35+
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
36+
37+
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
38+
}
39+
40+
/**
41+
* A source of remote user controlled input.
42+
*/
43+
class RemoteSource extends Source instanceof RemoteFlowSource { }
44+
45+
/**
46+
* An input to a logging mechanism.
47+
*/
48+
class LoggingSink extends Sink {
49+
LoggingSink() { this = any(Logging logging).getAnInput() }
50+
}
51+
52+
/**
53+
* A call to `String#replace` that replaces `\n` is considered to sanitize the replaced string (reduce false positive).
54+
*/
55+
class StringReplaceSanitizer extends Sanitizer {
56+
StringReplaceSanitizer() {
57+
exists(string s | this.(StringSubstitutionCall).replaces(s, "") and s.regexpMatch("\\n")) and
58+
// exclude replacement methods that may not fully sanitize the string
59+
this.(StringSubstitutionCall).isGlobal()
60+
}
61+
}
62+
63+
/**
64+
* A call to an HTML escape method is considered to sanitize its input.
65+
*/
66+
class HtmlEscapingAsSanitizer extends Sanitizer {
67+
HtmlEscapingAsSanitizer() { this = any(HtmlEscaping esc).getOutput() }
68+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @name Log injection
3+
* @description Building log entries from user-controlled sources is vulnerable to
4+
* insertion of forged log entries by a malicious user.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @security-severity 7.8
8+
* @precision medium
9+
* @id rb/log-injection
10+
* @tags security
11+
* external/cwe/cwe-117
12+
*/
13+
14+
import ruby
15+
import DataFlow::PathGraph
16+
import codeql.ruby.security.LogInjectionQuery
17+
18+
from LogInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
19+
where config.hasFlowPath(source, sink)
20+
select sink.getNode(), source, sink, "$@ flows to log entry.", source.getNode(),
21+
"User-provided value"

0 commit comments

Comments
 (0)