1111 */
1212
1313import csharp
14+ import semmle.code.csharp.frameworks.system.Text
1415import 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+ }
1633
1734module FormatInvalidConfig implements DataFlow:: ConfigSig {
1835 predicate isSource ( DataFlow:: Node n ) { n .asExpr ( ) instanceof StringLiteral }
1936
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+ }
2140}
2241
2342module FormatInvalid = DataFlow:: Global< FormatInvalidConfig > ;
2443
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+
2564private predicate invalidFormatString (
2665 InvalidFormatString src , FormatInvalid:: PathNode source , FormatInvalid:: PathNode sink , string msg ,
27- FormatCall call , string callString
66+ FormatStringParseCall call , string callString
2867) {
2968 source .getNode ( ) .asExpr ( ) = src and
3069 sink .getNode ( ) .asExpr ( ) = call .getFormatExpr ( ) and
@@ -34,13 +73,13 @@ private predicate invalidFormatString(
3473}
3574
3675private predicate unusedArgument (
37- FormatCall call , FormatInvalid :: PathNode source , FormatInvalid :: PathNode sink , string msg ,
76+ FormatCall call , FormatLiteral :: PathNode source , FormatLiteral :: PathNode sink , string msg ,
3877 ValidFormatString src , string srcString , Expr unusedExpr , string unusedString
3978) {
4079 exists ( int unused |
4180 source .getNode ( ) .asExpr ( ) = src and
4281 sink .getNode ( ) .asExpr ( ) = call .getFormatExpr ( ) and
43- FormatInvalid :: flowPath ( source , sink ) and
82+ FormatLiteral :: flowPath ( source , sink ) and
4483 unused = call .getASuppliedArgument ( ) and
4584 not unused = src .getAnInsert ( ) and
4685 not src .getValue ( ) = "" and
@@ -52,13 +91,13 @@ private predicate unusedArgument(
5291}
5392
5493private predicate missingArgument (
55- FormatCall call , FormatInvalid :: PathNode source , FormatInvalid :: PathNode sink , string msg ,
94+ FormatCall call , FormatLiteral :: PathNode source , FormatLiteral :: PathNode sink , string msg ,
5695 ValidFormatString src , string srcString
5796) {
5897 exists ( int used , int supplied |
5998 source .getNode ( ) .asExpr ( ) = src and
6099 sink .getNode ( ) .asExpr ( ) = call .getFormatExpr ( ) and
61- FormatInvalid :: flowPath ( source , sink ) and
100+ FormatLiteral :: flowPath ( source , sink ) and
62101 used = src .getAnInsert ( ) and
63102 supplied = call .getSuppliedArguments ( ) and
64103 used >= supplied and
@@ -68,16 +107,17 @@ private predicate missingArgument(
68107}
69108
70109from
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
73112where
74- invalidFormatString ( alert , source , sink , msg , extra1 , extra1String ) and
113+ invalidFormatString ( alert , source . asPathNode1 ( ) , sink . asPathNode1 ( ) , msg , extra1 , extra1String ) and
75114 extra2 = extra1 and
76115 extra2String = extra1String
77116 or
78- unusedArgument ( alert , source , sink , msg , extra1 , extra1String , extra2 , extra2String )
117+ unusedArgument ( alert , source .asPathNode2 ( ) , sink .asPathNode2 ( ) , msg , extra1 , extra1String , extra2 ,
118+ extra2String )
79119 or
80- missingArgument ( alert , source , sink , msg , extra1 , extra1String ) and
120+ missingArgument ( alert , source . asPathNode2 ( ) , sink . asPathNode2 ( ) , msg , extra1 , extra1String ) and
81121 extra2 = extra1 and
82122 extra2String = extra1String
83123select alert , source , sink , msg , extra1 , extra1String , extra2 , extra2String
0 commit comments