@@ -90,40 +90,69 @@ private class FeatureDTDLOAD extends Feature, TDTDLOAD {
9090 override string getConstantName ( ) { result = "DTDLOAD" }
9191}
9292
93+ private API:: Node parseOptionsModule ( ) {
94+ result = API:: getTopLevelMember ( "Nokogiri" ) .getMember ( "XML" ) .getMember ( "ParseOptions" )
95+ or
96+ result = API:: getTopLevelMember ( "LibXML" ) .getMember ( "XML" ) .getMember ( "Options" )
97+ or
98+ result = API:: getTopLevelMember ( "XML" ) .getMember ( "Options" )
99+ }
100+
93101private DataFlow:: LocalSourceNode trackFeature ( Feature f , boolean enable , TypeTracker t ) {
94102 t .start ( ) and
95103 (
96- result .asExpr ( ) .getExpr ( ) .( IntegerLiteral ) .getValue ( ) .bitAnd ( f .getValue ( ) ) = f .getValue ( ) and
97- enable = true
104+ // An integer literal with the feature-bit enabled/disabled
105+ exists ( int bitValue |
106+ bitValue = result .asExpr ( ) .getExpr ( ) .( IntegerLiteral ) .getValue ( ) .bitAnd ( f .getValue ( ) )
107+ |
108+ if bitValue = 0 then enable = false else enable = true
109+ )
98110 or
111+ // Use of a constant f
99112 enable = true and
100- result =
101- API:: getTopLevelMember ( "Nokogiri" )
102- .getMember ( "XML" )
103- .getMember ( "ParseOptions" )
104- .getMember ( f .getConstantName ( ) )
105- .getAUse ( )
113+ result = parseOptionsModule ( ) .getMember ( f .getConstantName ( ) ) .getAUse ( )
106114 or
107- enable = true and
108- result =
109- [ API:: getTopLevelMember ( "LibXML" ) .getMember ( "XML" ) , API:: getTopLevelMember ( "XML" ) ]
110- .getMember ( "Options" )
111- .getMember ( f .getConstantName ( ) )
112- .getAUse ( )
115+ // If a feature is enabled in any of the operands of the `|` and `|=` operators
116+ // then the feature is also enabled in the result of the operators.
117+ exists ( CfgNodes:: ExprNodes:: OperationCfgNode operation |
118+ operation = result .asExpr ( ) .( CfgNodes:: ExprNodes:: OperationCfgNode ) and
119+ (
120+ operation .getExpr ( ) instanceof BitwiseOrExpr or
121+ operation .getExpr ( ) instanceof AssignBitwiseOrExpr
122+ )
123+ |
124+ enable = true and
125+ operation .getAnOperand ( ) = trackFeature ( f , true ) .asExpr ( )
126+ or
127+ enable = false and
128+ operation .getAnOperand ( ) = trackFeature ( f , false ) .asExpr ( ) and
129+ forall ( DataFlow:: Node n | n .asExpr ( ) = operation .getAnOperand ( ) | n != trackFeature ( f , true ) )
130+ )
113131 or
114- (
115- result .asExpr ( ) .getExpr ( ) instanceof BitwiseOrExpr or
116- result .asExpr ( ) .getExpr ( ) instanceof AssignBitwiseOrExpr or
117- result .asExpr ( ) .getExpr ( ) instanceof BitwiseAndExpr or
118- result .asExpr ( ) .getExpr ( ) instanceof AssignBitwiseAndExpr
119- ) and
120- result .asExpr ( ) .( CfgNodes:: ExprNodes:: OperationCfgNode ) .getAnOperand ( ) =
121- trackFeature ( f , enable ) .asExpr ( )
132+ // If a feature is disabled in any of the operands of the `&` and `&=` operators
133+ // then the feature is also disabled in the result of the operators.
134+ exists ( CfgNodes:: ExprNodes:: OperationCfgNode operation |
135+ operation = result .asExpr ( ) .( CfgNodes:: ExprNodes:: OperationCfgNode ) and
136+ (
137+ operation .getExpr ( ) instanceof BitwiseAndExpr or
138+ operation .getExpr ( ) instanceof AssignBitwiseAndExpr
139+ )
140+ |
141+ enable = false and
142+ operation .getAnOperand ( ) = trackFeature ( f , false ) .asExpr ( )
143+ or
144+ enable = true and
145+ operation .getAnOperand ( ) = trackFeature ( f , true ) .asExpr ( ) and
146+ forall ( DataFlow:: Node n | n .asExpr ( ) = operation .getAnOperand ( ) | n != trackFeature ( f , false ) )
147+ )
122148 or
149+ // The complement operator toggles a feature from enabled to disabled and vice-versa
123150 result .asExpr ( ) .getExpr ( ) instanceof ComplementExpr and
124151 result .asExpr ( ) .( CfgNodes:: ExprNodes:: OperationCfgNode ) .getAnOperand ( ) =
125152 trackFeature ( f , enable .booleanNot ( ) ) .asExpr ( )
126153 or
154+ // Nokogiri has a ParseOptions class that is a wrapper around the bit-fields and
155+ // provides methods for querying and updating the fields.
127156 result =
128157 API:: getTopLevelMember ( "Nokogiri" )
129158 .getMember ( "XML" )
@@ -132,6 +161,9 @@ private DataFlow::LocalSourceNode trackFeature(Feature f, boolean enable, TypeTr
132161 result .asExpr ( ) .( CfgNodes:: ExprNodes:: CallCfgNode ) .getArgument ( 0 ) =
133162 trackFeature ( f , enable ) .asExpr ( )
134163 or
164+ // The Nokogiri ParseOptions class has methods for setting/unsetting features.
165+ // The method names are the lowercase variants of the constant names, with a "no"
166+ // prefix for unsetting a feature.
135167 exists ( CfgNodes:: ExprNodes:: CallCfgNode call |
136168 enable = true and
137169 call .getExpr ( ) .( MethodCall ) .getMethodName ( ) = f .getConstantName ( ) .toLowerCase ( )
@@ -140,16 +172,13 @@ private DataFlow::LocalSourceNode trackFeature(Feature f, boolean enable, TypeTr
140172 call .getExpr ( ) .( MethodCall ) .getMethodName ( ) = "no" + f .getConstantName ( ) .toLowerCase ( )
141173 |
142174 (
143- result .asExpr ( ) = call
144- or
175+ // these methods update the receiver
145176 result .flowsTo ( any ( DataFlow:: Node n | n .asExpr ( ) = call .getReceiver ( ) ) )
177+ or
178+ // in addition they return the (updated) receiver to allow chaining calls.
179+ result .asExpr ( ) = call
146180 )
147181 )
148- or
149- exists ( CfgNodes:: ExprNodes:: CallCfgNode call |
150- trackFeature ( f , enable ) .asExpr ( ) = call .getReceiver ( ) and
151- result .asExpr ( ) = call
152- )
153182 )
154183 or
155184 exists ( TypeTracker t2 | result = trackFeature ( f , enable , t2 ) .track ( t2 , t ) )
0 commit comments