1010 * external/cwe/cwe-611
1111 */
1212
13+ // TODO: currently the file name is Xerces-specific but the query ID isn't.
14+ // Decide which design to go with.
1315import cpp
1416import semmle.code.cpp.ir.dataflow.DataFlow
1517import DataFlow:: PathGraph
1618import semmle.code.cpp.ir.IR
1719
18- class AbstractDOMParser extends Class {
19- AbstractDOMParser ( ) { this .hasName ( "AbstractDOMParser" ) }
20+ /**
21+ * A flow state representing a possible configuration of an XML object.
22+ */
23+ abstract class XXEFlowState extends string {
24+ bindingset [ this ]
25+ XXEFlowState ( ) { any ( ) } // required constructor
26+ }
27+
28+ /**
29+ * An `Expr` that changes the configuration of an XML object, transforming the
30+ * `XXEFlowState` that flows through it.
31+ */
32+ abstract class XXEFlowStateTranformer extends Expr {
33+ /**
34+ * Gets the flow state that `flowstate` is transformed into.
35+ *
36+ * Due to limitations of the implementation the result must always map to
37+ * itself, that is, it must be that:
38+ * ```
39+ * transform(tranform(x)) = tranform(x)
40+ * ```
41+ */
42+ abstract XXEFlowState transform ( XXEFlowState flowstate ) ;
43+ }
44+
45+ /**
46+ * The `AbstractDOMParser` class.
47+ */
48+ class AbstractDOMParserClass extends Class {
49+ AbstractDOMParserClass ( ) { this .hasName ( "AbstractDOMParser" ) }
50+ }
51+
52+ /**
53+ * The `XercesDOMParser` class.
54+ */
55+ class XercesDOMParserClass extends Class {
56+ XercesDOMParserClass ( ) { this .hasName ( "XercesDOMParser" ) }
57+ }
58+
59+ /**
60+ * Gets a valid flow state for `XercesDOMParser` flow.
61+ *
62+ * These flow states take the form `XercesDOM-A-B`, where:
63+ * - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
64+ * - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
65+ */
66+ predicate encodeXercesDOMFlowState ( string flowstate , int a , int b ) {
67+ flowstate = "XercesDOM-0-0" and a = 0 and b = 0
68+ or
69+ flowstate = "XercesDOM-0-1" and a = 0 and b = 1
70+ or
71+ flowstate = "XercesDOM-1-0" and a = 1 and b = 0
72+ or
73+ flowstate = "XercesDOM-1-1" and a = 1 and b = 1
2074}
2175
22- class XercesDOMParser extends Class {
23- XercesDOMParser ( ) { this .hasName ( "XercesDOMParser" ) }
76+ /**
77+ * A flow state representing the configuration of a `XercesDOMParser` object.
78+ */
79+ class XercesDOMParserFlowState extends XXEFlowState {
80+ XercesDOMParserFlowState ( ) { encodeXercesDOMFlowState ( this , _, _) }
2481}
2582
26- class DisableDefaultEntityResolution extends Function {
27- DisableDefaultEntityResolution ( ) {
28- this .getDeclaringType ( ) instanceof AbstractDOMParser and
29- this .hasName ( "setDisableDefaultEntityResolution" )
83+ /**
84+ * The qualifier of a call to `AbstractDOMParser.setDisableDefaultEntityResolution`.
85+ */
86+ class DisableDefaultEntityResolutionTranformer extends XXEFlowStateTranformer {
87+ Expr newValue ;
88+
89+ DisableDefaultEntityResolutionTranformer ( ) {
90+ exists ( Call call , Function f |
91+ call .getTarget ( ) = f and
92+ f .getDeclaringType ( ) instanceof AbstractDOMParserClass and
93+ f .hasName ( "setDisableDefaultEntityResolution" ) and
94+ this = call .getQualifier ( ) and
95+ newValue = call .getArgument ( 0 )
96+ )
97+ }
98+
99+ final override XXEFlowState transform ( XXEFlowState flowstate ) {
100+ exists ( int a , int b |
101+ encodeXercesDOMFlowState ( flowstate , a , b ) and
102+ (
103+ newValue .getValue ( ) .toInt ( ) = 1 and // true
104+ encodeXercesDOMFlowState ( result , 1 , b )
105+ or
106+ not newValue .getValue ( ) .toInt ( ) = 1 and // false or unknown
107+ encodeXercesDOMFlowState ( result , 0 , b )
108+ )
109+ )
30110 }
31111}
32112
33- class SetCreateEntityReferenceNodes extends Function {
34- SetCreateEntityReferenceNodes ( ) {
35- this .getDeclaringType ( ) instanceof AbstractDOMParser and
36- this .hasName ( "setCreateEntityReferenceNodes" )
113+ /**
114+ * The qualifier of a call to `AbstractDOMParser.setDisableDefaultEntityResolution`.
115+ */
116+ class CreateEntityReferenceNodesTranformer extends XXEFlowStateTranformer {
117+ Expr newValue ;
118+
119+ CreateEntityReferenceNodesTranformer ( ) {
120+ exists ( Call call , Function f |
121+ call .getTarget ( ) = f and
122+ f .getDeclaringType ( ) instanceof AbstractDOMParserClass and
123+ f .hasName ( "setCreateEntityReferenceNodes" ) and
124+ this = call .getQualifier ( ) and
125+ newValue = call .getArgument ( 0 )
126+ )
127+ }
128+
129+ final override XXEFlowState transform ( XXEFlowState flowstate ) {
130+ exists ( int a , int b |
131+ encodeXercesDOMFlowState ( flowstate , a , b ) and
132+ (
133+ newValue .getValue ( ) .toInt ( ) = 1 and // true
134+ encodeXercesDOMFlowState ( result , a , 1 )
135+ or
136+ not newValue .getValue ( ) .toInt ( ) = 1 and // false or unknown
137+ encodeXercesDOMFlowState ( result , a , 0 )
138+ )
139+ )
37140 }
38141}
39142
40- class Parse extends Function {
41- Parse ( ) {
42- this .getDeclaringType ( ) instanceof AbstractDOMParser and
143+ /**
144+ * The `AbstractDOMParser.parse` method.
145+ */
146+ class ParseFunction extends Function {
147+ ParseFunction ( ) {
148+ this .getDeclaringType ( ) instanceof AbstractDOMParserClass and
43149 this .hasName ( "parse" )
44150 }
45151}
46152
47153/*
48- class CreateLSParser extends Function {
49- CreateLSParser() { this.hasName("createLSParser") }
50- }
51-
52- class SetSecurityManager extends Function {
53- SetSecurityManager() { this.hasQualifiedName(_, "AbstractDOMParser", "setSecurityManager") }
54- }
154+ * class CreateLSParser extends Function {
155+ * CreateLSParser() { this.hasName("createLSParser") }
156+ * }
157+ *
158+ * class SetSecurityManager extends Function {
159+ * SetSecurityManager() { this.hasQualifiedName(_, "AbstractDOMParser", "setSecurityManager") }
160+ * }
161+ *
162+ * class SAXParser extends Class {
163+ * SAXParser() { this.hasName("SAXParser") }
164+ * }
165+ */
55166
56- class SAXParser extends Class {
57- SAXParser() { this.hasName("SAXParser") }
58- }
59- */
167+ /**
168+ * Configuration for tracking Xerces library XML objects and their states.
169+ */
60170class XercesXXEConfiguration extends DataFlow:: Configuration {
61171 XercesXXEConfiguration ( ) { this = "XercesXXEConfiguration" }
62172
63- override predicate isSource ( DataFlow:: Node node /* , string flowstate*/ ) {
173+ override predicate isSource ( DataFlow:: Node node , string flowstate ) {
64174 // source is the write on `this` of a call to the XercesDOMParser
65175 // constructor.
66176 exists ( CallInstruction call |
67- call .getStaticCallTarget ( ) = any ( XercesDOMParser c ) .getAConstructor ( ) and
68- node .asInstruction ( ) .( WriteSideEffectInstruction ) .getDestinationAddress ( ) =
69- call .getThisArgument ( ) /* and
70- flowstate = "XercesDOM"*/
71- )
72- /*exists(Call call |
73- call.getTarget() = any(XercesDOMParser c).getAConstructor() and
74- node.asExpr() = call
75- )*/
76- /* or
77- exists(Call call |
78- call.getTarget() instanceof CreateLSParser and
79- call = node.asExpr() and
80- flowstate = "XercesDOM"
81- )
82- or
83- exists(CallInstruction call |
177+ call .getStaticCallTarget ( ) = any ( XercesDOMParserClass c ) .getAConstructor ( ) and
84178 node .asInstruction ( ) .( WriteSideEffectInstruction ) .getDestinationAddress ( ) =
85179 call .getThisArgument ( ) and
86- call.getStaticCallTarget().(Constructor).getDeclaringType() instanceof SAXParser and
87- flowstate = "SAXParser"
88- )*/
89- }
180+ encodeXercesDOMFlowState ( flowstate , 0 , 1 ) // default configuration
181+ )
182+ /*
183+ * or
184+ * exists(Call call |
185+ * call.getTarget() instanceof CreateLSParser and
186+ * call = node.asExpr() and
187+ * flowstate = "XercesDOM"
188+ * )
189+ * or
190+ * exists(CallInstruction call |
191+ * node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
192+ * call.getThisArgument() and
193+ * call.getStaticCallTarget().(Constructor).getDeclaringType() instanceof SAXParser and
194+ * flowstate = "SAXParser"
195+ * )
196+ */
197+
198+ }
90199
91- override predicate isSink ( DataFlow:: Node node ) {
200+ override predicate isSink ( DataFlow:: Node node , string flowstate ) {
92201 // sink is the read of the qualifier of a call to `parse`.
93- exists ( Call call /*, ReadSideEffectInstruction instr*/ |
94- call .getTarget ( ) instanceof Parse and
202+ exists ( Call call |
203+ call .getTarget ( ) instanceof ParseFunction and
95204 call .getQualifier ( ) = node .asConvertedExpr ( )
96- /*instr.getArgumentDef().getUnconvertedResultExpression( ) and
97- node.asOperand() = instr.getSideEffectOperand()*/
98- )
205+ ) and
206+ flowstate instanceof XercesDOMParserFlowState and
207+ not encodeXercesDOMFlowState ( flowstate , 1 , 1 ) // safe configuration
99208 }
100209
101- /* override predicate isAdditionalFlowStep(
210+ override predicate isAdditionalFlowStep (
102211 DataFlow:: Node node1 , string state1 , DataFlow:: Node node2 , string state2
103212 ) {
104- exists(Call call |
105- node1.asConvertedExpr() = call.getQualifier() and
106- node2.asDefiningArgument() = call.getQualifier() and
107- (
108- call.getTarget() instanceof DisableDefaultEntityResolution and
109- state1 = "XercesDOM" and
110- state2 = "XercesDOM-DDER"
111- or
112- call.getTarget() instanceof SetCreateEntityReferenceNodes and
113- state1 = "XercesDOM" and
114- state2 = "XercesDOM-SCERN"
115- )
116- )
117- }*/
213+ // create additional flow steps for `XXEFlowStateTranformer`s
214+ state2 = node2 .asConvertedExpr ( ) .( XXEFlowStateTranformer ) .transform ( state1 ) and
215+ DataFlow:: simpleLocalFlowStep ( node1 , node2 )
216+ /*
217+ * exists(CallInstruction call |
218+ * node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
219+ * call.getThisArgument() and
220+ * call.getStaticCallTarget().(Constructor).getDeclaringType() instanceof SAXParser and
221+ * flowstate = "SAXParser"
222+ * )
223+ */
118224
119- /*override predicate isBarrier(DataFlow::Node node, string flowstate) {
120- exists(Call call |
121- (
122- flowstate = "XercesDOM-DDER" and
123- call.getTarget() instanceof SetCreateEntityReferenceNodes
124- or
125- flowstate = "XercesDOM-SCERN" and
126- call.getTarget() instanceof DisableDefaultEntityResolution
127- ) and
128- call.getQualifier() = node.asDefiningArgument()
129- )
130- or
131- exists(Call setSecurityManager |
132- // todo: security manager setup
133- flowstate = TODO
134- setSecurityManager.getQualifier() = node.asDefiningArgument() and
135- setSecurityManager.getTarget() instanceof SetSecurityManager
136- )
137- }*/
225+ }
226+
227+ override predicate isBarrierOut ( DataFlow:: Node node , string flowstate ) {
228+ // when the flowstate is transformed at a call node, block the original
229+ // flowstate value.
230+ node .asConvertedExpr ( ) .( XXEFlowStateTranformer ) .transform ( flowstate ) != flowstate
231+ /*
232+ * or
233+ * exists(Call setSecurityManager |
234+ * // todo: security manager setup
235+ * flowstate = TODO
236+ * setSecurityManager.getQualifier() = node.asDefiningArgument() and
237+ * setSecurityManager.getTarget() instanceof SetSecurityManager
238+ * )
239+ */
240+
241+ }
138242}
139243
140244/*
@@ -152,4 +256,5 @@ class XercesXXEConfiguration extends DataFlow::Configuration {
152256from XercesXXEConfiguration conf , DataFlow:: PathNode source , DataFlow:: PathNode sink
153257where conf .hasFlowPath ( source , sink )
154258select sink , source , sink ,
155- "This $@ is not configured to prevent an External Entity Expansion (XXE) attack." , source , "XML parser"
259+ "This $@ is not configured to prevent an External Entity Expansion (XXE) attack." , source ,
260+ "XML parser"
0 commit comments