11
11
*/
12
12
13
13
import csharp
14
+ import semmle.code.csharp.frameworks.system.Text
14
15
import semmle.code.csharp.frameworks.Format
15
- import FormatInvalid:: PathGraph
16
+ import FormatFlow:: PathGraph
17
+
18
+ abstract private class FormatStringParseCall extends MethodCall {
19
+ abstract Expr getFormatExpr ( ) ;
20
+ }
21
+
22
+ private class OrdinaryFormatCall extends FormatStringParseCall instanceof FormatCall {
23
+ override Expr getFormatExpr ( ) { result = FormatCall .super .getFormatExpr ( ) }
24
+ }
25
+
26
+ private class ParseFormatStringCall extends FormatStringParseCall {
27
+ ParseFormatStringCall ( ) {
28
+ this .getTarget ( ) = any ( SystemTextCompositeFormatClass x ) .getParseMethod ( )
29
+ }
30
+
31
+ override Expr getFormatExpr ( ) { result = this .getArgument ( 0 ) }
32
+ }
16
33
17
34
module FormatInvalidConfig implements DataFlow:: ConfigSig {
18
35
predicate isSource ( DataFlow:: Node n ) { n .asExpr ( ) instanceof StringLiteral }
19
36
20
- predicate isSink ( DataFlow:: Node n ) { exists ( FormatCall c | n .asExpr ( ) = c .getFormatExpr ( ) ) }
37
+ predicate isSink ( DataFlow:: Node n ) {
38
+ exists ( FormatStringParseCall c | n .asExpr ( ) = c .getFormatExpr ( ) )
39
+ }
21
40
}
22
41
23
42
module FormatInvalid = DataFlow:: Global< FormatInvalidConfig > ;
24
43
44
+ module FormatLiteralConfig implements DataFlow:: ConfigSig {
45
+ predicate isSource ( DataFlow:: Node n ) { n .asExpr ( ) instanceof StringLiteral }
46
+
47
+ predicate isAdditionalFlowStep ( DataFlow:: Node pred , DataFlow:: Node succ ) {
48
+ // Add flow via `System.Text.CompositeFormat.Parse`.
49
+ exists ( ParseFormatStringCall call |
50
+ pred .asExpr ( ) = call .getFormatExpr ( ) and
51
+ succ .asExpr ( ) = call
52
+ )
53
+ }
54
+
55
+ predicate isSink ( DataFlow:: Node n ) { exists ( FormatCall c | n .asExpr ( ) = c .getFormatExpr ( ) ) }
56
+ }
57
+
58
+ module FormatLiteral = DataFlow:: Global< FormatLiteralConfig > ;
59
+
60
+ module FormatFlow =
61
+ DataFlow:: MergePathGraph< FormatInvalid:: PathNode , FormatLiteral:: PathNode ,
62
+ FormatInvalid:: PathGraph , FormatLiteral:: PathGraph > ;
63
+
25
64
private predicate invalidFormatString (
26
65
InvalidFormatString src , FormatInvalid:: PathNode source , FormatInvalid:: PathNode sink , string msg ,
27
- FormatCall call , string callString
66
+ FormatStringParseCall call , string callString
28
67
) {
29
68
source .getNode ( ) .asExpr ( ) = src and
30
69
sink .getNode ( ) .asExpr ( ) = call .getFormatExpr ( ) and
@@ -34,13 +73,13 @@ private predicate invalidFormatString(
34
73
}
35
74
36
75
private predicate unusedArgument (
37
- FormatCall call , FormatInvalid :: PathNode source , FormatInvalid :: PathNode sink , string msg ,
76
+ FormatCall call , FormatLiteral :: PathNode source , FormatLiteral :: PathNode sink , string msg ,
38
77
ValidFormatString src , string srcString , Expr unusedExpr , string unusedString
39
78
) {
40
79
exists ( int unused |
41
80
source .getNode ( ) .asExpr ( ) = src and
42
81
sink .getNode ( ) .asExpr ( ) = call .getFormatExpr ( ) and
43
- FormatInvalid :: flowPath ( source , sink ) and
82
+ FormatLiteral :: flowPath ( source , sink ) and
44
83
unused = call .getASuppliedArgument ( ) and
45
84
not unused = src .getAnInsert ( ) and
46
85
not src .getValue ( ) = "" and
@@ -52,13 +91,13 @@ private predicate unusedArgument(
52
91
}
53
92
54
93
private predicate missingArgument (
55
- FormatCall call , FormatInvalid :: PathNode source , FormatInvalid :: PathNode sink , string msg ,
94
+ FormatCall call , FormatLiteral :: PathNode source , FormatLiteral :: PathNode sink , string msg ,
56
95
ValidFormatString src , string srcString
57
96
) {
58
97
exists ( int used , int supplied |
59
98
source .getNode ( ) .asExpr ( ) = src and
60
99
sink .getNode ( ) .asExpr ( ) = call .getFormatExpr ( ) and
61
- FormatInvalid :: flowPath ( source , sink ) and
100
+ FormatLiteral :: flowPath ( source , sink ) and
62
101
used = src .getAnInsert ( ) and
63
102
supplied = call .getSuppliedArguments ( ) and
64
103
used >= supplied and
@@ -68,16 +107,17 @@ private predicate missingArgument(
68
107
}
69
108
70
109
from
71
- Element alert , FormatInvalid :: PathNode source , FormatInvalid :: PathNode sink , string msg ,
72
- Element extra1 , string extra1String , Element extra2 , string extra2String
110
+ Element alert , FormatFlow :: PathNode source , FormatFlow :: PathNode sink , string msg , Element extra1 ,
111
+ string extra1String , Element extra2 , string extra2String
73
112
where
74
- invalidFormatString ( alert , source , sink , msg , extra1 , extra1String ) and
113
+ invalidFormatString ( alert , source . asPathNode1 ( ) , sink . asPathNode1 ( ) , msg , extra1 , extra1String ) and
75
114
extra2 = extra1 and
76
115
extra2String = extra1String
77
116
or
78
- unusedArgument ( alert , source , sink , msg , extra1 , extra1String , extra2 , extra2String )
117
+ unusedArgument ( alert , source .asPathNode2 ( ) , sink .asPathNode2 ( ) , msg , extra1 , extra1String , extra2 ,
118
+ extra2String )
79
119
or
80
- missingArgument ( alert , source , sink , msg , extra1 , extra1String ) and
120
+ missingArgument ( alert , source . asPathNode2 ( ) , sink . asPathNode2 ( ) , msg , extra1 , extra1String ) and
81
121
extra2 = extra1 and
82
122
extra2String = extra1String
83
123
select alert , source , sink , msg , extra1 , extra1String , extra2 , extra2String
0 commit comments