-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathFormatInvalid.ql
107 lines (94 loc) · 3.79 KB
/
FormatInvalid.ql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/**
* @name Invalid string formatting
* @description Calling 'string.Format()' with either an invalid format string or incorrect
* number of arguments may result in dropped arguments or a 'System.FormatException'.
* @kind path-problem
* @problem.severity error
* @precision high
* @id cs/invalid-string-formatting
* @tags reliability
* maintainability
*/
import csharp
import semmle.code.csharp.frameworks.system.Text
import semmle.code.csharp.frameworks.Format
import FormatFlow::PathGraph
module FormatInvalidConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLiteral }
predicate isSink(DataFlow::Node n) {
exists(FormatStringParseCall c | n.asExpr() = c.getFormatExpr())
}
}
module FormatInvalid = DataFlow::Global<FormatInvalidConfig>;
module FormatLiteralConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLiteral }
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
// Add flow via `System.Text.CompositeFormat.Parse`.
exists(ParseFormatStringCall call |
pred.asExpr() = call.getFormatExpr() and
succ.asExpr() = call
)
}
predicate isSink(DataFlow::Node n) { exists(FormatCall c | n.asExpr() = c.getFormatExpr()) }
}
module FormatLiteral = DataFlow::Global<FormatLiteralConfig>;
module FormatFlow =
DataFlow::MergePathGraph<FormatInvalid::PathNode, FormatLiteral::PathNode,
FormatInvalid::PathGraph, FormatLiteral::PathGraph>;
private predicate invalidFormatString(
InvalidFormatString src, FormatInvalid::PathNode source, FormatInvalid::PathNode sink, string msg,
FormatStringParseCall call, string callString
) {
source.getNode().asExpr() = src and
sink.getNode().asExpr() = call.getFormatExpr() and
FormatInvalid::flowPath(source, sink) and
msg = "Invalid format string used in $@ formatting call." and
callString = "this"
}
private predicate unusedArgument(
FormatCall call, FormatLiteral::PathNode source, FormatLiteral::PathNode sink, string msg,
ValidFormatString src, string srcString, Expr unusedExpr, string unusedString
) {
exists(int unused |
source.getNode().asExpr() = src and
sink.getNode().asExpr() = call.getFormatExpr() and
FormatLiteral::flowPath(source, sink) and
unused = call.getASuppliedArgument() and
not unused = src.getAnInsert() and
not src.getValue() = "" and
msg = "The $@ ignores $@." and
srcString = "format string" and
unusedExpr = call.getSuppliedExpr(unused) and
unusedString = "this supplied value"
)
}
private predicate missingArgument(
FormatCall call, FormatLiteral::PathNode source, FormatLiteral::PathNode sink, string msg,
ValidFormatString src, string srcString
) {
exists(int used, int supplied |
source.getNode().asExpr() = src and
sink.getNode().asExpr() = call.getFormatExpr() and
FormatLiteral::flowPath(source, sink) and
used = src.getAnInsert() and
supplied = call.getSuppliedArguments() and
used >= supplied and
msg = "Argument '{" + used + "}' has not been supplied to $@ format string." and
srcString = "this"
)
}
from
Element alert, FormatFlow::PathNode source, FormatFlow::PathNode sink, string msg, Element extra1,
string extra1String, Element extra2, string extra2String
where
invalidFormatString(alert, source.asPathNode1(), sink.asPathNode1(), msg, extra1, extra1String) and
extra2 = extra1 and
extra2String = extra1String
or
unusedArgument(alert, source.asPathNode2(), sink.asPathNode2(), msg, extra1, extra1String, extra2,
extra2String)
or
missingArgument(alert, source.asPathNode2(), sink.asPathNode2(), msg, extra1, extra1String) and
extra2 = extra1 and
extra2String = extra1String
select alert, source, sink, msg, extra1, extra1String, extra2, extra2String