Skip to content

Commit 5a6da82

Browse files
committed
Ruby: Avoid FP in TaintedFormatString query
Kernel#printf supports two call signatures: printf(String, *args) printf(IO, String, *args) We want to identify the String argument, which is the format string. Previously we would return the 0th and 1st arguments, which gives some FPs when the 1st arg is not a format string. We now try to rule out the trivial case by checking if arg 0 has a string value, and then assuming it is the format string. Otherwise we fall back to returning both arguments. This still has some false positive potential, but less than previously.
1 parent 5dcf0ad commit 5a6da82

File tree

3 files changed

+47
-33
lines changed

3 files changed

+47
-33
lines changed

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import codeql.ruby.dataflow.RemoteFlowSources
88
import codeql.ruby.ApiGraphs
99
import codeql.ruby.TaintTracking
1010
private import codeql.ruby.frameworks.Files::IO
11+
private import codeql.ruby.controlflow.CfgNodes
1112

1213
/**
1314
* A call to `printf` or `sprintf`.
@@ -39,7 +40,15 @@ class KernelPrintfCall extends PrintfStyleCall {
3940
// Kernel#printf supports two signatures:
4041
// printf(io, string, ...)
4142
// printf(string, ...)
42-
override DataFlow::Node getFormatString() { result = this.getArgument([0, 1]) }
43+
override DataFlow::Node getFormatString() {
44+
// Because `printf` has two different signatures, we can't be sure which
45+
// argument is the format string, so we use a heuristic:
46+
// If the first argument has a string value, then we assume it is the format string.
47+
// Otherwise we treat both the first and second args as the format string.
48+
if this.getArgument(0).getExprNode().getConstantValue().isString(_)
49+
then result = this.getArgument(0)
50+
else result = this.getArgument([0, 1])
51+
}
4352
}
4453

4554
/**

ruby/ql/test/query-tests/security/cwe-134/TaintedFormatString.expected

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ edges
33
| tainted_format_string.rb:5:19:5:24 | call to params : | tainted_format_string.rb:5:19:5:33 | ...[...] |
44
| tainted_format_string.rb:10:23:10:28 | call to params : | tainted_format_string.rb:10:23:10:37 | ...[...] |
55
| tainted_format_string.rb:11:30:11:35 | call to params : | tainted_format_string.rb:11:30:11:44 | ...[...] |
6-
| tainted_format_string.rb:13:23:13:28 | call to params : | tainted_format_string.rb:13:23:13:37 | ...[...] |
7-
| tainted_format_string.rb:14:30:14:35 | call to params : | tainted_format_string.rb:14:30:14:44 | ...[...] |
8-
| tainted_format_string.rb:16:27:16:32 | call to params : | tainted_format_string.rb:16:27:16:41 | ...[...] |
9-
| tainted_format_string.rb:17:20:17:25 | call to params : | tainted_format_string.rb:17:20:17:34 | ...[...] |
10-
| tainted_format_string.rb:23:19:23:24 | call to params : | tainted_format_string.rb:23:19:23:33 | ...[...] |
11-
| tainted_format_string.rb:28:32:28:37 | call to params : | tainted_format_string.rb:28:32:28:46 | ...[...] : |
12-
| tainted_format_string.rb:28:32:28:46 | ...[...] : | tainted_format_string.rb:28:12:28:46 | ... + ... |
13-
| tainted_format_string.rb:31:30:31:35 | call to params : | tainted_format_string.rb:31:30:31:44 | ...[...] : |
14-
| tainted_format_string.rb:31:30:31:44 | ...[...] : | tainted_format_string.rb:31:12:31:46 | "A log message: #{...}" |
6+
| tainted_format_string.rb:18:23:18:28 | call to params : | tainted_format_string.rb:18:23:18:37 | ...[...] |
7+
| tainted_format_string.rb:19:30:19:35 | call to params : | tainted_format_string.rb:19:30:19:44 | ...[...] |
8+
| tainted_format_string.rb:21:27:21:32 | call to params : | tainted_format_string.rb:21:27:21:41 | ...[...] |
9+
| tainted_format_string.rb:22:20:22:25 | call to params : | tainted_format_string.rb:22:20:22:34 | ...[...] |
10+
| tainted_format_string.rb:28:19:28:24 | call to params : | tainted_format_string.rb:28:19:28:33 | ...[...] |
11+
| tainted_format_string.rb:33:32:33:37 | call to params : | tainted_format_string.rb:33:32:33:46 | ...[...] : |
12+
| tainted_format_string.rb:33:32:33:46 | ...[...] : | tainted_format_string.rb:33:12:33:46 | ... + ... |
13+
| tainted_format_string.rb:36:30:36:35 | call to params : | tainted_format_string.rb:36:30:36:44 | ...[...] : |
14+
| tainted_format_string.rb:36:30:36:44 | ...[...] : | tainted_format_string.rb:36:12:36:46 | "A log message: #{...}" |
1515
nodes
1616
| tainted_format_string.rb:4:12:4:17 | call to params : | semmle.label | call to params : |
1717
| tainted_format_string.rb:4:12:4:26 | ...[...] | semmle.label | ...[...] |
@@ -21,32 +21,32 @@ nodes
2121
| tainted_format_string.rb:10:23:10:37 | ...[...] | semmle.label | ...[...] |
2222
| tainted_format_string.rb:11:30:11:35 | call to params : | semmle.label | call to params : |
2323
| tainted_format_string.rb:11:30:11:44 | ...[...] | semmle.label | ...[...] |
24-
| tainted_format_string.rb:13:23:13:28 | call to params : | semmle.label | call to params : |
25-
| tainted_format_string.rb:13:23:13:37 | ...[...] | semmle.label | ...[...] |
26-
| tainted_format_string.rb:14:30:14:35 | call to params : | semmle.label | call to params : |
27-
| tainted_format_string.rb:14:30:14:44 | ...[...] | semmle.label | ...[...] |
28-
| tainted_format_string.rb:16:27:16:32 | call to params : | semmle.label | call to params : |
29-
| tainted_format_string.rb:16:27:16:41 | ...[...] | semmle.label | ...[...] |
30-
| tainted_format_string.rb:17:20:17:25 | call to params : | semmle.label | call to params : |
31-
| tainted_format_string.rb:17:20:17:34 | ...[...] | semmle.label | ...[...] |
32-
| tainted_format_string.rb:23:19:23:24 | call to params : | semmle.label | call to params : |
33-
| tainted_format_string.rb:23:19:23:33 | ...[...] | semmle.label | ...[...] |
34-
| tainted_format_string.rb:28:12:28:46 | ... + ... | semmle.label | ... + ... |
35-
| tainted_format_string.rb:28:32:28:37 | call to params : | semmle.label | call to params : |
36-
| tainted_format_string.rb:28:32:28:46 | ...[...] : | semmle.label | ...[...] : |
37-
| tainted_format_string.rb:31:12:31:46 | "A log message: #{...}" | semmle.label | "A log message: #{...}" |
38-
| tainted_format_string.rb:31:30:31:35 | call to params : | semmle.label | call to params : |
39-
| tainted_format_string.rb:31:30:31:44 | ...[...] : | semmle.label | ...[...] : |
24+
| tainted_format_string.rb:18:23:18:28 | call to params : | semmle.label | call to params : |
25+
| tainted_format_string.rb:18:23:18:37 | ...[...] | semmle.label | ...[...] |
26+
| tainted_format_string.rb:19:30:19:35 | call to params : | semmle.label | call to params : |
27+
| tainted_format_string.rb:19:30:19:44 | ...[...] | semmle.label | ...[...] |
28+
| tainted_format_string.rb:21:27:21:32 | call to params : | semmle.label | call to params : |
29+
| tainted_format_string.rb:21:27:21:41 | ...[...] | semmle.label | ...[...] |
30+
| tainted_format_string.rb:22:20:22:25 | call to params : | semmle.label | call to params : |
31+
| tainted_format_string.rb:22:20:22:34 | ...[...] | semmle.label | ...[...] |
32+
| tainted_format_string.rb:28:19:28:24 | call to params : | semmle.label | call to params : |
33+
| tainted_format_string.rb:28:19:28:33 | ...[...] | semmle.label | ...[...] |
34+
| tainted_format_string.rb:33:12:33:46 | ... + ... | semmle.label | ... + ... |
35+
| tainted_format_string.rb:33:32:33:37 | call to params : | semmle.label | call to params : |
36+
| tainted_format_string.rb:33:32:33:46 | ...[...] : | semmle.label | ...[...] : |
37+
| tainted_format_string.rb:36:12:36:46 | "A log message: #{...}" | semmle.label | "A log message: #{...}" |
38+
| tainted_format_string.rb:36:30:36:35 | call to params : | semmle.label | call to params : |
39+
| tainted_format_string.rb:36:30:36:44 | ...[...] : | semmle.label | ...[...] : |
4040
subpaths
4141
#select
4242
| tainted_format_string.rb:4:12:4:26 | ...[...] | tainted_format_string.rb:4:12:4:17 | call to params : | tainted_format_string.rb:4:12:4:26 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:4:12:4:17 | call to params | User-provided value |
4343
| tainted_format_string.rb:5:19:5:33 | ...[...] | tainted_format_string.rb:5:19:5:24 | call to params : | tainted_format_string.rb:5:19:5:33 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:5:19:5:24 | call to params | User-provided value |
4444
| tainted_format_string.rb:10:23:10:37 | ...[...] | tainted_format_string.rb:10:23:10:28 | call to params : | tainted_format_string.rb:10:23:10:37 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:10:23:10:28 | call to params | User-provided value |
4545
| tainted_format_string.rb:11:30:11:44 | ...[...] | tainted_format_string.rb:11:30:11:35 | call to params : | tainted_format_string.rb:11:30:11:44 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:11:30:11:35 | call to params | User-provided value |
46-
| tainted_format_string.rb:13:23:13:37 | ...[...] | tainted_format_string.rb:13:23:13:28 | call to params : | tainted_format_string.rb:13:23:13:37 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:13:23:13:28 | call to params | User-provided value |
47-
| tainted_format_string.rb:14:30:14:44 | ...[...] | tainted_format_string.rb:14:30:14:35 | call to params : | tainted_format_string.rb:14:30:14:44 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:14:30:14:35 | call to params | User-provided value |
48-
| tainted_format_string.rb:16:27:16:41 | ...[...] | tainted_format_string.rb:16:27:16:32 | call to params : | tainted_format_string.rb:16:27:16:41 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:16:27:16:32 | call to params | User-provided value |
49-
| tainted_format_string.rb:17:20:17:34 | ...[...] | tainted_format_string.rb:17:20:17:25 | call to params : | tainted_format_string.rb:17:20:17:34 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:17:20:17:25 | call to params | User-provided value |
50-
| tainted_format_string.rb:23:19:23:33 | ...[...] | tainted_format_string.rb:23:19:23:24 | call to params : | tainted_format_string.rb:23:19:23:33 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:23:19:23:24 | call to params | User-provided value |
51-
| tainted_format_string.rb:28:12:28:46 | ... + ... | tainted_format_string.rb:28:32:28:37 | call to params : | tainted_format_string.rb:28:12:28:46 | ... + ... | $@ flows here and is used in a format string. | tainted_format_string.rb:28:32:28:37 | call to params | User-provided value |
52-
| tainted_format_string.rb:31:12:31:46 | "A log message: #{...}" | tainted_format_string.rb:31:30:31:35 | call to params : | tainted_format_string.rb:31:12:31:46 | "A log message: #{...}" | $@ flows here and is used in a format string. | tainted_format_string.rb:31:30:31:35 | call to params | User-provided value |
46+
| tainted_format_string.rb:18:23:18:37 | ...[...] | tainted_format_string.rb:18:23:18:28 | call to params : | tainted_format_string.rb:18:23:18:37 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:18:23:18:28 | call to params | User-provided value |
47+
| tainted_format_string.rb:19:30:19:44 | ...[...] | tainted_format_string.rb:19:30:19:35 | call to params : | tainted_format_string.rb:19:30:19:44 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:19:30:19:35 | call to params | User-provided value |
48+
| tainted_format_string.rb:21:27:21:41 | ...[...] | tainted_format_string.rb:21:27:21:32 | call to params : | tainted_format_string.rb:21:27:21:41 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:21:27:21:32 | call to params | User-provided value |
49+
| tainted_format_string.rb:22:20:22:34 | ...[...] | tainted_format_string.rb:22:20:22:25 | call to params : | tainted_format_string.rb:22:20:22:34 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:22:20:22:25 | call to params | User-provided value |
50+
| tainted_format_string.rb:28:19:28:33 | ...[...] | tainted_format_string.rb:28:19:28:24 | call to params : | tainted_format_string.rb:28:19:28:33 | ...[...] | $@ flows here and is used in a format string. | tainted_format_string.rb:28:19:28:24 | call to params | User-provided value |
51+
| tainted_format_string.rb:33:12:33:46 | ... + ... | tainted_format_string.rb:33:32:33:37 | call to params : | tainted_format_string.rb:33:12:33:46 | ... + ... | $@ flows here and is used in a format string. | tainted_format_string.rb:33:32:33:37 | call to params | User-provided value |
52+
| tainted_format_string.rb:36:12:36:46 | "A log message: #{...}" | tainted_format_string.rb:36:30:36:35 | call to params : | tainted_format_string.rb:36:12:36:46 | "A log message: #{...}" | $@ flows here and is used in a format string. | tainted_format_string.rb:36:30:36:35 | call to params | User-provided value |

ruby/ql/test/query-tests/security/cwe-134/tainted_format_string.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ def show
1010
printf(IO.new(1), params[:format], arg) # BAD
1111
Kernel.printf(IO.new(1), params[:format], arg) # BAD
1212

13+
printf("%s", params[:format]) # GOOD
14+
Kernel.printf("%s", params[:format]) # GOOD
15+
fmt = "%s"
16+
printf(fmt, params[:format]) # GOOD
17+
1318
printf(IO.new(1), params[:format]) # GOOD [FALSE POSITIVE]
1419
Kernel.printf(IO.new(1), params[:format]) # GOOD [FALSE POSITIVE]
1520

0 commit comments

Comments
 (0)