Skip to content

Commit 16aee6e

Browse files
authored
Merge pull request #4842 from hvitved/csharp/format-method-no-insertion-param
C#: Recognize format methods without insertion parameters
2 parents 49f902d + d53faa8 commit 16aee6e

File tree

12 files changed

+30
-14
lines changed

12 files changed

+30
-14
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* For string formatting methods, such as `System.Console.WriteLine(string format, params object[] arg)`, we now also recognize overloads without insertion parameters as string formatting methods. For example, `System.Console.WriteLine(string value)` is now also a member of the class `FormatMethod` in `frameworks/Format.qll`.

csharp/ql/src/API Abuse/FormatInvalid.ql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@ import semmle.code.csharp.frameworks.Format
1414
import FormatFlow
1515

1616
from FormatCall s, InvalidFormatString src, PathNode source, PathNode sink
17-
where hasFlowPath(src, source, s, sink)
17+
where
18+
hasFlowPath(src, source, s, sink) and
19+
s.hasInsertions()
1820
select src, source, sink, "Invalid format string used in $@ formatting call.", s, "this"

csharp/ql/src/API Abuse/FormatInvalidBad.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ class Bad
44
{
55
string GenerateEmptyClass(string c)
66
{
7-
return string.Format("class {0} { }");
7+
return string.Format("class {0} { }", "C");
88
}
99
}

csharp/ql/src/Security Features/CWE-134/UncontrolledFormatString.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class FormatStringConfiguration extends TaintTracking::Configuration {
2727
}
2828

2929
override predicate isSink(DataFlow::Node sink) {
30-
sink.asExpr() = any(FormatCall call).getFormatExpr()
30+
sink.asExpr() = any(FormatCall call | call.hasInsertions()).getFormatExpr()
3131
}
3232
}
3333

csharp/ql/src/semmle/code/csharp/frameworks/Format.qll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,13 @@ class FormatMethod extends Method {
1313
exists(Class declType | declType = this.getDeclaringType() |
1414
this.getParameter(0).getType() instanceof SystemIFormatProviderInterface and
1515
this.getParameter(1).getType() instanceof StringType and
16-
this.getNumberOfParameters() >= 3 and
1716
(
1817
this = any(SystemStringClass c).getFormatMethod()
1918
or
2019
this = any(SystemTextStringBuilderClass c).getAppendFormatMethod()
2120
)
2221
or
2322
this.getParameter(0).getType() instanceof StringType and
24-
this.getNumberOfParameters() >= 2 and
2523
(
2624
this = any(SystemStringClass c).getFormatMethod()
2725
or
@@ -220,6 +218,9 @@ class FormatCall extends MethodCall {
220218
/** Gets the argument number of the first supplied insert. */
221219
int getFirstArgument() { result = this.getFormatArgument() + 1 }
222220

221+
/** Holds if this call has one or more insertions. */
222+
predicate hasInsertions() { exists(this.getArgument(this.getFirstArgument())) }
223+
223224
/** Holds if the arguments are supplied in an array, not individually. */
224225
predicate hasArrayExpr() {
225226
this.getNumberOfArguments() = this.getFirstArgument() + 1 and

csharp/ql/test/query-tests/API Abuse/FormatInvalid/FormatInvalid.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ void FormatMethodTests()
115115
System.Diagnostics.Debug.Assert(true, "Error", "}", ps);
116116
sw.Write("}", 0);
117117
System.Diagnostics.Debug.Print("}", ps);
118+
119+
Console.WriteLine("}"); // GOOD
118120
}
119121

120122
System.IO.StringWriter sw;

csharp/ql/test/query-tests/API Abuse/FormatInvalid/FormatInvalid.expected

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ nodes
5151
| FormatInvalid.cs:115:56:115:58 | [assertion success] "}" | semmle.label | [assertion success] "}" |
5252
| FormatInvalid.cs:116:18:116:20 | "}" | semmle.label | "}" |
5353
| FormatInvalid.cs:117:40:117:42 | "}" | semmle.label | "}" |
54+
| FormatInvalid.cs:119:27:119:29 | "}" | semmle.label | "}" |
5455
| FormatInvalidBad.cs:7:30:7:44 | "class {0} { }" | semmle.label | "class {0} { }" |
5556
| FormatInvalidGood.cs:7:30:7:46 | "class {0} {{ }}" | semmle.label | "class {0} {{ }}" |
5657
edges
@@ -96,4 +97,4 @@ edges
9697
| FormatInvalid.cs:115:57:115:58 | "}" | FormatInvalid.cs:115:56:115:58 | [assertion success] "}" | FormatInvalid.cs:115:56:115:58 | [assertion success] "}" | Invalid format string used in $@ formatting call. | FormatInvalid.cs:115:9:115:63 | call to method Assert | this |
9798
| FormatInvalid.cs:116:19:116:20 | "}" | FormatInvalid.cs:116:18:116:20 | "}" | FormatInvalid.cs:116:18:116:20 | "}" | Invalid format string used in $@ formatting call. | FormatInvalid.cs:116:9:116:24 | call to method Write | this |
9899
| FormatInvalid.cs:117:41:117:42 | "}" | FormatInvalid.cs:117:40:117:42 | "}" | FormatInvalid.cs:117:40:117:42 | "}" | Invalid format string used in $@ formatting call. | FormatInvalid.cs:117:9:117:47 | call to method Print | this |
99-
| FormatInvalidBad.cs:7:41:7:44 | "class {0} { }" | FormatInvalidBad.cs:7:30:7:44 | "class {0} { }" | FormatInvalidBad.cs:7:30:7:44 | "class {0} { }" | Invalid format string used in $@ formatting call. | FormatInvalidBad.cs:7:16:7:45 | call to method Format | this |
100+
| FormatInvalidBad.cs:7:41:7:44 | "class {0} { }" | FormatInvalidBad.cs:7:30:7:44 | "class {0} { }" | FormatInvalidBad.cs:7:30:7:44 | "class {0} { }" | Invalid format string used in $@ formatting call. | FormatInvalidBad.cs:7:16:7:50 | call to method Format | this |

csharp/ql/test/query-tests/API Abuse/FormatInvalid/FormatInvalidBad.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ class Bad
44
{
55
string GenerateEmptyClass(string c)
66
{
7-
return string.Format("class {0} { }");
7+
return string.Format("class {0} { }", "C");
88
}
99
}

csharp/ql/test/query-tests/API Abuse/FormatMissingArgument/FormatMissingArgument.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ void TestFormatMissingArgument()
2020
String.Format("{0} {1} {2} {3}", 0, 1, 2, 3);
2121

2222
helper("{1}");
23+
24+
// BAD: Missing {0}
25+
Console.WriteLine("{0}");
2326
}
2427

2528
void helper(string format)

csharp/ql/test/query-tests/API Abuse/FormatMissingArgument/FormatMissingArgument.expected

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@ nodes
55
| FormatMissingArgument.cs:17:23:17:35 | "{0} {1} {2}" | semmle.label | "{0} {1} {2}" |
66
| FormatMissingArgument.cs:20:23:20:39 | "{0} {1} {2} {3}" | semmle.label | "{0} {1} {2} {3}" |
77
| FormatMissingArgument.cs:22:16:22:20 | "{1}" : String | semmle.label | "{1}" : String |
8-
| FormatMissingArgument.cs:25:24:25:29 | format : String | semmle.label | format : String |
9-
| FormatMissingArgument.cs:28:23:28:28 | access to parameter format | semmle.label | access to parameter format |
8+
| FormatMissingArgument.cs:25:27:25:31 | "{0}" | semmle.label | "{0}" |
9+
| FormatMissingArgument.cs:28:24:28:29 | format : String | semmle.label | format : String |
10+
| FormatMissingArgument.cs:31:23:31:28 | access to parameter format | semmle.label | access to parameter format |
1011
| FormatMissingArgumentBad.cs:7:27:7:41 | "Hello {0} {1}" | semmle.label | "Hello {0} {1}" |
1112
| FormatMissingArgumentBad.cs:8:27:8:41 | "Hello {1} {2}" | semmle.label | "Hello {1} {2}" |
1213
| FormatMissingArgumentGood.cs:7:27:7:41 | "Hello {0} {1}" | semmle.label | "Hello {0} {1}" |
1314
edges
14-
| FormatMissingArgument.cs:22:16:22:20 | "{1}" : String | FormatMissingArgument.cs:25:24:25:29 | format : String |
15-
| FormatMissingArgument.cs:25:24:25:29 | format : String | FormatMissingArgument.cs:28:23:28:28 | access to parameter format |
15+
| FormatMissingArgument.cs:22:16:22:20 | "{1}" : String | FormatMissingArgument.cs:28:24:28:29 | format : String |
16+
| FormatMissingArgument.cs:28:24:28:29 | format : String | FormatMissingArgument.cs:31:23:31:28 | access to parameter format |
1617
#select
1718
| FormatMissingArgument.cs:11:9:11:31 | call to method Format | FormatMissingArgument.cs:11:23:11:27 | "{1}" | FormatMissingArgument.cs:11:23:11:27 | "{1}" | Argument '{1}' has not been supplied to $@ format string. | FormatMissingArgument.cs:11:23:11:27 | "{1}" | this |
1819
| FormatMissingArgument.cs:14:9:14:38 | call to method Format | FormatMissingArgument.cs:14:23:14:31 | "{2} {3}" | FormatMissingArgument.cs:14:23:14:31 | "{2} {3}" | Argument '{2}' has not been supplied to $@ format string. | FormatMissingArgument.cs:14:23:14:31 | "{2} {3}" | this |
1920
| FormatMissingArgument.cs:14:9:14:38 | call to method Format | FormatMissingArgument.cs:14:23:14:31 | "{2} {3}" | FormatMissingArgument.cs:14:23:14:31 | "{2} {3}" | Argument '{3}' has not been supplied to $@ format string. | FormatMissingArgument.cs:14:23:14:31 | "{2} {3}" | this |
20-
| FormatMissingArgument.cs:28:9:28:32 | call to method Format | FormatMissingArgument.cs:22:16:22:20 | "{1}" : String | FormatMissingArgument.cs:28:23:28:28 | access to parameter format | Argument '{1}' has not been supplied to $@ format string. | FormatMissingArgument.cs:22:16:22:20 | "{1}" | this |
21+
| FormatMissingArgument.cs:25:9:25:32 | call to method WriteLine | FormatMissingArgument.cs:25:27:25:31 | "{0}" | FormatMissingArgument.cs:25:27:25:31 | "{0}" | Argument '{0}' has not been supplied to $@ format string. | FormatMissingArgument.cs:25:27:25:31 | "{0}" | this |
22+
| FormatMissingArgument.cs:31:9:31:32 | call to method Format | FormatMissingArgument.cs:22:16:22:20 | "{1}" : String | FormatMissingArgument.cs:31:23:31:28 | access to parameter format | Argument '{1}' has not been supplied to $@ format string. | FormatMissingArgument.cs:22:16:22:20 | "{1}" | this |
2123
| FormatMissingArgumentBad.cs:7:9:7:49 | call to method WriteLine | FormatMissingArgumentBad.cs:7:27:7:41 | "Hello {0} {1}" | FormatMissingArgumentBad.cs:7:27:7:41 | "Hello {0} {1}" | Argument '{1}' has not been supplied to $@ format string. | FormatMissingArgumentBad.cs:7:27:7:41 | "Hello {0} {1}" | this |
2224
| FormatMissingArgumentBad.cs:8:9:8:55 | call to method WriteLine | FormatMissingArgumentBad.cs:8:27:8:41 | "Hello {1} {2}" | FormatMissingArgumentBad.cs:8:27:8:41 | "Hello {1} {2}" | Argument '{2}' has not been supplied to $@ format string. | FormatMissingArgumentBad.cs:8:27:8:41 | "Hello {1} {2}" | this |

0 commit comments

Comments
 (0)