Skip to content

Commit a2b924b

Browse files
committed
move model of printf style calls to StringFormatters.qll
1 parent dbf2673 commit a2b924b

File tree

3 files changed

+73
-76
lines changed

3 files changed

+73
-76
lines changed

ruby/ql/lib/codeql/ruby/Frameworks.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ private import codeql.ruby.frameworks.HttpClients
2323
private import codeql.ruby.frameworks.XmlParsing
2424
private import codeql.ruby.frameworks.ActionDispatch
2525
private import codeql.ruby.frameworks.PosixSpawn
26+
private import codeql.ruby.frameworks.StringFormatters
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Provides classes for modeling string formatting libraries.
3+
*/
4+
5+
private import codeql.ruby.ast.Call
6+
private import codeql.ruby.DataFlow
7+
private import codeql.ruby.ApiGraphs
8+
private import codeql.ruby.frameworks.core.IO
9+
10+
/**
11+
* A call to `printf` or `sprintf`.
12+
*/
13+
abstract class PrintfStyleCall extends DataFlow::CallNode {
14+
// We assume that most printf-like calls have the signature f(format_string, args...)
15+
/**
16+
* Gets the format string of this call.
17+
*/
18+
DataFlow::Node getFormatString() { result = this.getArgument(0) }
19+
20+
/**
21+
* Gets then `n`th formatted argument of this call.
22+
*/
23+
DataFlow::Node getFormatArgument(int n) { n >= 0 and result = this.getArgument(n + 1) }
24+
}
25+
26+
/**
27+
* A call to `Kernel.printf`.
28+
*/
29+
class KernelPrintfCall extends PrintfStyleCall {
30+
KernelPrintfCall() {
31+
this = API::getTopLevelMember("Kernel").getAMethodCall("printf")
32+
or
33+
this.asExpr().getExpr() instanceof UnknownMethodCall and
34+
this.getMethodName() = "printf"
35+
}
36+
37+
// Kernel#printf supports two signatures:
38+
// printf(io, string, ...)
39+
// printf(string, ...)
40+
override DataFlow::Node getFormatString() {
41+
// Because `printf` has two different signatures, we can't be sure which
42+
// argument is the format string, so we use a heuristic:
43+
// If the first argument has a string value, then we assume it is the format string.
44+
// Otherwise we treat both the first and second args as the format string.
45+
if this.getArgument(0).getExprNode().getConstantValue().isString(_)
46+
then result = this.getArgument(0)
47+
else result = this.getArgument([0, 1])
48+
}
49+
}
50+
51+
/**
52+
* A call to `Kernel.sprintf`.
53+
*/
54+
class KernelSprintfCall extends PrintfStyleCall {
55+
KernelSprintfCall() {
56+
this = API::getTopLevelMember("Kernel").getAMethodCall("sprintf")
57+
or
58+
this.asExpr().getExpr() instanceof UnknownMethodCall and
59+
this.getMethodName() = "sprintf"
60+
}
61+
}
62+
63+
/**
64+
* A call to `IO#printf`.
65+
*/
66+
class IOPrintfCall extends PrintfStyleCall {
67+
IOPrintfCall() {
68+
this.getReceiver() instanceof IO::IOInstance and this.getMethodName() = "printf"
69+
}
70+
}

ruby/ql/lib/codeql/ruby/security/TaintedFormatStringSpecific.qll

Lines changed: 2 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2,82 +2,8 @@
22
* Provides Ruby-specific imports and classes needed for `TaintedFormatStringQuery` and `TaintedFormatStringCustomizations`.
33
*/
44

5-
import codeql.ruby.AST
5+
import codeql.ruby.frameworks.StringFormatters
66
import codeql.ruby.DataFlow
77
import codeql.ruby.dataflow.RemoteFlowSources
8-
import codeql.ruby.ApiGraphs
98
import codeql.ruby.TaintTracking
10-
private import codeql.ruby.frameworks.Files
11-
private import codeql.ruby.frameworks.core.IO
12-
private import codeql.ruby.controlflow.CfgNodes
13-
14-
/**
15-
* A call to `printf` or `sprintf`.
16-
*/
17-
abstract class PrintfStyleCall extends DataFlow::CallNode {
18-
// We assume that most printf-like calls have the signature f(format_string, args...)
19-
/**
20-
* Gets the format string of this call.
21-
*/
22-
DataFlow::Node getFormatString() { result = this.getArgument(0) }
23-
24-
/**
25-
* Gets then `n`th formatted argument of this call.
26-
*/
27-
DataFlow::Node getFormatArgument(int n) { n >= 0 and result = this.getArgument(n + 1) }
28-
29-
/** Holds if this call returns the formatted string. */
30-
predicate returnsFormatted() { any() }
31-
}
32-
33-
/**
34-
* A call to `Kernel.printf`.
35-
*/
36-
class KernelPrintfCall extends PrintfStyleCall {
37-
KernelPrintfCall() {
38-
this = API::getTopLevelMember("Kernel").getAMethodCall("printf")
39-
or
40-
this.asExpr().getExpr() instanceof UnknownMethodCall and
41-
this.getMethodName() = "printf"
42-
}
43-
44-
// Kernel#printf supports two signatures:
45-
// printf(io, string, ...)
46-
// printf(string, ...)
47-
override DataFlow::Node getFormatString() {
48-
// Because `printf` has two different signatures, we can't be sure which
49-
// argument is the format string, so we use a heuristic:
50-
// If the first argument has a string value, then we assume it is the format string.
51-
// Otherwise we treat both the first and second args as the format string.
52-
if this.getArgument(0).getExprNode().getConstantValue().isString(_)
53-
then result = this.getArgument(0)
54-
else result = this.getArgument([0, 1])
55-
}
56-
57-
override predicate returnsFormatted() { none() }
58-
}
59-
60-
/**
61-
* A call to `Kernel.sprintf`.
62-
*/
63-
class KernelSprintfCall extends PrintfStyleCall {
64-
KernelSprintfCall() {
65-
this = API::getTopLevelMember("Kernel").getAMethodCall("sprintf")
66-
or
67-
this.asExpr().getExpr() instanceof UnknownMethodCall and
68-
this.getMethodName() = "sprintf"
69-
}
70-
71-
override predicate returnsFormatted() { any() }
72-
}
73-
74-
/**
75-
* A call to `IO#printf`.
76-
*/
77-
class IOPrintfCall extends PrintfStyleCall {
78-
IOPrintfCall() {
79-
this.getReceiver() instanceof IO::IOInstance and this.getMethodName() = "printf"
80-
}
81-
82-
override predicate returnsFormatted() { none() }
83-
}
9+
import codeql.ruby.DataFlow

0 commit comments

Comments
 (0)