@@ -19,16 +19,18 @@ private class ConcreteObjectPrototype extends ObjectPrototype {
1919}
2020
2121/** A taint-tracking configuration for reasoning about prototype-polluting assignments. */
22- class Configuration extends TaintTracking :: Configuration {
23- Configuration ( ) { this = "PrototypePollutingAssignment" }
22+ module PrototypePollutingAssignmentConfig implements DataFlow :: StateConfigSig {
23+ class FlowState = DataFlow :: FlowLabel ;
2424
25- override predicate isSource ( DataFlow:: Node node ) { node instanceof Source }
25+ predicate isSource ( DataFlow:: Node node , DataFlow:: FlowLabel label ) {
26+ node instanceof Source and label .isTaint ( )
27+ }
2628
27- override predicate isSink ( DataFlow:: Node node , DataFlow:: FlowLabel lbl ) {
29+ predicate isSink ( DataFlow:: Node node , DataFlow:: FlowLabel lbl ) {
2830 node .( Sink ) .getAFlowLabel ( ) = lbl
2931 }
3032
31- override predicate isSanitizer ( DataFlow:: Node node ) {
33+ predicate isBarrier ( DataFlow:: Node node ) {
3234 node instanceof Sanitizer
3335 or
3436 // Concatenating with a string will in practice prevent the string `__proto__` from arising.
@@ -53,17 +55,24 @@ class Configuration extends TaintTracking::Configuration {
5355 not replace .getRawReplacement ( ) .getStringValue ( ) = ""
5456 )
5557 )
58+ or
59+ node = DataFlow:: MakeBarrierGuard< BarrierGuard > :: getABarrierNode ( )
5660 }
5761
58- override predicate isSanitizerOut ( DataFlow:: Node node , DataFlow:: FlowLabel lbl ) {
62+ predicate isBarrierOut ( DataFlow:: Node node , DataFlow:: FlowLabel lbl ) {
5963 // Suppress the value-preserving step src -> dst in `extend(dst, src)`. This is modeled as a value-preserving
6064 // step because it preserves all properties, but the destination is not actually Object.prototype.
6165 node = any ( ExtendCall call ) .getASourceOperand ( ) and
6266 lbl instanceof ObjectPrototype
6367 }
6468
65- override predicate isAdditionalFlowStep (
66- DataFlow:: Node pred , DataFlow:: Node succ , DataFlow:: FlowLabel inlbl , DataFlow:: FlowLabel outlbl
69+ predicate isBarrierIn ( DataFlow:: Node node , DataFlow:: FlowLabel lbl ) {
70+ // FIXME: This should only be an in-barrier for the corresponding flow state, but flow-state specific in-barriers are not supported right now.
71+ isSource ( node , lbl )
72+ }
73+
74+ predicate isAdditionalFlowStep (
75+ DataFlow:: Node pred , DataFlow:: FlowLabel inlbl , DataFlow:: Node succ , DataFlow:: FlowLabel outlbl
6776 ) {
6877 // Step from x -> obj[x] while switching to the ObjectPrototype label
6978 // (If `x` can have the value `__proto__` then the result can be Object.prototype)
@@ -91,7 +100,80 @@ class Configuration extends TaintTracking::Configuration {
91100 outlbl instanceof ObjectPrototype
92101 )
93102 or
94- DataFlow:: localFieldStep ( pred , succ ) and inlbl = outlbl
103+ // TODO: local field step becomes a jump step, resulting in FPs (closure-lib)
104+ // TODO: localFieldStep is too expensive with dataflow2
105+ // DataFlow::localFieldStep(pred, succ)
106+ none ( )
107+ or
108+ inlbl .isTaint ( ) and
109+ TaintTracking:: defaultTaintStep ( pred , succ ) and
110+ inlbl = outlbl
111+ }
112+
113+ DataFlow:: FlowFeature getAFeature ( ) { result instanceof DataFlow:: FeatureHasSourceCallContext }
114+
115+ predicate isBarrier ( DataFlow:: Node node , DataFlow:: FlowLabel lbl ) {
116+ lbl .isTaint ( ) and
117+ TaintTracking:: defaultSanitizer ( node )
118+ or
119+ // Don't propagate into the receiver, as the method lookups will generally fail on Object.prototype.
120+ node instanceof DataFlow:: ThisNode and
121+ lbl instanceof ObjectPrototype
122+ or
123+ node = DataFlow:: MakeLabeledBarrierGuard< BarrierGuard > :: getABarrierNode ( lbl )
124+ }
125+ }
126+
127+ /** Taint-tracking for reasoning about prototype-polluting assignments. */
128+ module PrototypePollutingAssignmentFlow =
129+ DataFlow:: GlobalWithState< PrototypePollutingAssignmentConfig > ;
130+
131+ /**
132+ * Holds if the given `source, sink` pair should not be reported, as we don't have enough
133+ * confidence in the alert given that source is a library input.
134+ */
135+ bindingset [ source, sink]
136+ predicate isIgnoredLibraryFlow ( ExternalInputSource source , Sink sink ) {
137+ exists ( source ) and
138+ // filter away paths that start with library inputs and end with a write to a fixed property.
139+ exists ( DataFlow:: PropWrite write | sink = write .getBase ( ) |
140+ // fixed property name
141+ exists ( write .getPropertyName ( ) )
142+ or
143+ // non-string property name (likely number)
144+ exists ( Expr prop | prop = write .getPropertyNameExpr ( ) |
145+ not prop .analyze ( ) .getAType ( ) = TTString ( )
146+ )
147+ )
148+ }
149+
150+ /**
151+ * DEPRECATED. Use the `PrototypePollutingAssignmentFlow` module instead.
152+ */
153+ deprecated class Configuration extends TaintTracking:: Configuration {
154+ Configuration ( ) { this = "PrototypePollutingAssignment" }
155+
156+ override predicate isSource ( DataFlow:: Node node ) { node instanceof Source }
157+
158+ override predicate isSink ( DataFlow:: Node node , DataFlow:: FlowLabel lbl ) {
159+ node .( Sink ) .getAFlowLabel ( ) = lbl
160+ }
161+
162+ override predicate isSanitizer ( DataFlow:: Node node ) {
163+ PrototypePollutingAssignmentConfig:: isBarrier ( node )
164+ }
165+
166+ override predicate isSanitizerOut ( DataFlow:: Node node , DataFlow:: FlowLabel lbl ) {
167+ // Suppress the value-preserving step src -> dst in `extend(dst, src)`. This is modeled as a value-preserving
168+ // step because it preserves all properties, but the destination is not actually Object.prototype.
169+ node = any ( ExtendCall call ) .getASourceOperand ( ) and
170+ lbl instanceof ObjectPrototype
171+ }
172+
173+ override predicate isAdditionalFlowStep (
174+ DataFlow:: Node pred , DataFlow:: Node succ , DataFlow:: FlowLabel inlbl , DataFlow:: FlowLabel outlbl
175+ ) {
176+ PrototypePollutingAssignmentConfig:: isAdditionalFlowStep ( pred , inlbl , succ , outlbl )
95177 }
96178
97179 override predicate hasFlowPath ( DataFlow:: SourcePathNode source , DataFlow:: SinkPathNode sink ) {
@@ -174,51 +256,49 @@ private predicate isPropertyPresentOnObjectPrototype(string prop) {
174256}
175257
176258/** A check of form `e.prop` where `prop` is not present on `Object.prototype`. */
177- private class PropertyPresenceCheck extends TaintTracking:: LabeledSanitizerGuardNode ,
178- DataFlow:: ValueNode
179- {
259+ private class PropertyPresenceCheck extends BarrierGuardLegacy , DataFlow:: ValueNode {
180260 override PropAccess astNode ;
181261
182262 PropertyPresenceCheck ( ) {
183263 astNode = any ( ConditionGuardNode c ) .getTest ( ) and // restrict size of charpred
184264 not isPropertyPresentOnObjectPrototype ( astNode .getPropertyName ( ) )
185265 }
186266
187- override predicate sanitizes ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
267+ override predicate blocksExpr ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
188268 e = astNode .getBase ( ) and
189269 outcome = true and
190270 label instanceof ObjectPrototype
191271 }
192272}
193273
194274/** A check of form `"prop" in e` where `prop` is not present on `Object.prototype`. */
195- private class InExprCheck extends TaintTracking :: LabeledSanitizerGuardNode , DataFlow:: ValueNode {
275+ private class InExprCheck extends BarrierGuardLegacy , DataFlow:: ValueNode {
196276 override InExpr astNode ;
197277
198278 InExprCheck ( ) {
199279 not isPropertyPresentOnObjectPrototype ( astNode .getLeftOperand ( ) .getStringValue ( ) )
200280 }
201281
202- override predicate sanitizes ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
282+ override predicate blocksExpr ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
203283 e = astNode .getRightOperand ( ) and
204284 outcome = true and
205285 label instanceof ObjectPrototype
206286 }
207287}
208288
209289/** A check of form `e instanceof X`, which is always false for `Object.prototype`. */
210- private class InstanceofCheck extends TaintTracking :: LabeledSanitizerGuardNode , DataFlow:: ValueNode {
290+ private class InstanceofCheck extends BarrierGuardLegacy , DataFlow:: ValueNode {
211291 override InstanceofExpr astNode ;
212292
213- override predicate sanitizes ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
293+ override predicate blocksExpr ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
214294 e = astNode .getLeftOperand ( ) and
215295 outcome = true and
216296 label instanceof ObjectPrototype
217297 }
218298}
219299
220300/** A check of form `typeof e === "string"`. */
221- private class TypeofCheck extends TaintTracking :: LabeledSanitizerGuardNode , DataFlow:: ValueNode {
301+ private class TypeofCheck extends BarrierGuardLegacy , DataFlow:: ValueNode {
222302 override EqualityTest astNode ;
223303 Expr operand ;
224304 boolean polarity ;
@@ -231,28 +311,28 @@ private class TypeofCheck extends TaintTracking::LabeledSanitizerGuardNode, Data
231311 )
232312 }
233313
234- override predicate sanitizes ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
314+ override predicate blocksExpr ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
235315 polarity = outcome and
236316 e = operand and
237317 label instanceof ObjectPrototype
238318 }
239319}
240320
241321/** A guard that checks whether `x` is a number. */
242- class NumberGuard extends TaintTracking :: SanitizerGuardNode instanceof DataFlow:: CallNode {
322+ class NumberGuard extends BarrierGuardLegacy instanceof DataFlow:: CallNode {
243323 Expr x ;
244324 boolean polarity ;
245325
246326 NumberGuard ( ) { TaintTracking:: isNumberGuard ( this , x , polarity ) }
247327
248- override predicate sanitizes ( boolean outcome , Expr e ) { e = x and outcome = polarity }
328+ override predicate blocksExpr ( boolean outcome , Expr e ) { e = x and outcome = polarity }
249329}
250330
251331/** A call to `Array.isArray`, which is false for `Object.prototype`. */
252- private class IsArrayCheck extends TaintTracking :: LabeledSanitizerGuardNode , DataFlow:: CallNode {
332+ private class IsArrayCheck extends BarrierGuardLegacy , DataFlow:: CallNode {
253333 IsArrayCheck ( ) { this = DataFlow:: globalVarRef ( "Array" ) .getAMemberCall ( "isArray" ) }
254334
255- override predicate sanitizes ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
335+ override predicate blocksExpr ( boolean outcome , Expr e , DataFlow:: FlowLabel label ) {
256336 e = this .getArgument ( 0 ) .asExpr ( ) and
257337 outcome = true and
258338 label instanceof ObjectPrototype
@@ -262,12 +342,12 @@ private class IsArrayCheck extends TaintTracking::LabeledSanitizerGuardNode, Dat
262342/**
263343 * Sanitizer guard of form `x !== "__proto__"`.
264344 */
265- private class EqualityCheck extends TaintTracking :: SanitizerGuardNode , DataFlow:: ValueNode {
345+ private class EqualityCheck extends BarrierGuardLegacy , DataFlow:: ValueNode {
266346 override EqualityTest astNode ;
267347
268348 EqualityCheck ( ) { astNode .getAnOperand ( ) .getStringValue ( ) = "__proto__" }
269349
270- override predicate sanitizes ( boolean outcome , Expr e ) {
350+ override predicate blocksExpr ( boolean outcome , Expr e ) {
271351 e = astNode .getAnOperand ( ) and
272352 outcome = astNode .getPolarity ( ) .booleanNot ( )
273353 }
@@ -276,10 +356,10 @@ private class EqualityCheck extends TaintTracking::SanitizerGuardNode, DataFlow:
276356/**
277357 * Sanitizer guard of the form `x.includes("__proto__")`.
278358 */
279- private class IncludesCheck extends TaintTracking :: LabeledSanitizerGuardNode , InclusionTest {
359+ private class IncludesCheck extends BarrierGuardLegacy , InclusionTest {
280360 IncludesCheck ( ) { this .getContainedNode ( ) .mayHaveStringValue ( "__proto__" ) }
281361
282- override predicate sanitizes ( boolean outcome , Expr e ) {
362+ override predicate blocksExpr ( boolean outcome , Expr e ) {
283363 e = this .getContainerNode ( ) .asExpr ( ) and
284364 outcome = this .getPolarity ( ) .booleanNot ( )
285365 }
@@ -288,7 +368,7 @@ private class IncludesCheck extends TaintTracking::LabeledSanitizerGuardNode, In
288368/**
289369 * A sanitizer guard that checks tests whether `x` is included in a list like `["__proto__"].includes(x)`.
290370 */
291- private class DenyListInclusionGuard extends TaintTracking :: SanitizerGuardNode , InclusionTest {
371+ private class DenyListInclusionGuard extends BarrierGuardLegacy , InclusionTest {
292372 DenyListInclusionGuard ( ) {
293373 this .getContainerNode ( )
294374 .getALocalSource ( )
@@ -297,7 +377,7 @@ private class DenyListInclusionGuard extends TaintTracking::SanitizerGuardNode,
297377 .mayHaveStringValue ( "__proto__" )
298378 }
299379
300- override predicate sanitizes ( boolean outcome , Expr e ) {
380+ override predicate blocksExpr ( boolean outcome , Expr e ) {
301381 e = this .getContainedNode ( ) .asExpr ( ) and
302382 outcome = super .getPolarity ( ) .booleanNot ( )
303383 }
0 commit comments