@@ -90,40 +90,69 @@ private class FeatureDTDLOAD extends Feature, TDTDLOAD {
90
90
override string getConstantName ( ) { result = "DTDLOAD" }
91
91
}
92
92
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
+
93
101
private DataFlow:: LocalSourceNode trackFeature ( Feature f , boolean enable , TypeTracker t ) {
94
102
t .start ( ) and
95
103
(
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
+ )
98
110
or
111
+ // Use of a constant f
99
112
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 ( )
106
114
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
+ )
113
131
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
+ )
122
148
or
149
+ // The complement operator toggles a feature from enabled to disabled and vice-versa
123
150
result .asExpr ( ) .getExpr ( ) instanceof ComplementExpr and
124
151
result .asExpr ( ) .( CfgNodes:: ExprNodes:: OperationCfgNode ) .getAnOperand ( ) =
125
152
trackFeature ( f , enable .booleanNot ( ) ) .asExpr ( )
126
153
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.
127
156
result =
128
157
API:: getTopLevelMember ( "Nokogiri" )
129
158
.getMember ( "XML" )
@@ -132,6 +161,9 @@ private DataFlow::LocalSourceNode trackFeature(Feature f, boolean enable, TypeTr
132
161
result .asExpr ( ) .( CfgNodes:: ExprNodes:: CallCfgNode ) .getArgument ( 0 ) =
133
162
trackFeature ( f , enable ) .asExpr ( )
134
163
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.
135
167
exists ( CfgNodes:: ExprNodes:: CallCfgNode call |
136
168
enable = true and
137
169
call .getExpr ( ) .( MethodCall ) .getMethodName ( ) = f .getConstantName ( ) .toLowerCase ( )
@@ -140,16 +172,13 @@ private DataFlow::LocalSourceNode trackFeature(Feature f, boolean enable, TypeTr
140
172
call .getExpr ( ) .( MethodCall ) .getMethodName ( ) = "no" + f .getConstantName ( ) .toLowerCase ( )
141
173
|
142
174
(
143
- result .asExpr ( ) = call
144
- or
175
+ // these methods update the receiver
145
176
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
146
180
)
147
181
)
148
- or
149
- exists ( CfgNodes:: ExprNodes:: CallCfgNode call |
150
- trackFeature ( f , enable ) .asExpr ( ) = call .getReceiver ( ) and
151
- result .asExpr ( ) = call
152
- )
153
182
)
154
183
or
155
184
exists ( TypeTracker t2 | result = trackFeature ( f , enable , t2 ) .track ( t2 , t ) )
0 commit comments