@@ -4,45 +4,15 @@ import semmle.javascript.security.dataflow.ZipSlipQuery as ZipSlip
44import semmle.javascript.security.dataflow.TaintedPathCustomizations:: TaintedPath as TaintedPath
55
66/**
7- * An instance of `$.util.Zip`, but the argument to the constructor call is reachable from a remote flow source.
8- */
9- class XSJSZipInstanceDependingOnRemoteFlowSource extends XSJSZipInstance {
10- RemoteFlowSource remoteArgument ;
11-
12- XSJSZipInstanceDependingOnRemoteFlowSource ( ) {
13- this .getAnArgument ( ) .getALocalSource ( ) = remoteArgument
14- }
15-
16- RemoteFlowSource getRemoteArgument ( ) { result = remoteArgument }
17- }
18-
19- class XSJSRemoteFlowSourceToZipInstanceStep extends DataFlow:: SharedFlowStep {
20- override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
21- exists ( XSJSZipInstanceDependingOnRemoteFlowSource dollarUtilZip |
22- pred = dollarUtilZip .getRemoteArgument ( ) and
23- succ = dollarUtilZip
24- )
25- }
26- }
27-
28- class ForInLoopDomainToVariableStep extends DataFlow:: SharedFlowStep {
29- override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
30- exists ( ForInStmt forLoop |
31- pred = forLoop .getIterationDomain ( ) .flow ( ) and
32- succ = forLoop .getAnIterationVariable ( ) .getAnAccess ( ) .flow ( )
33- )
34- }
35- }
36-
37- /**
38- * A node that checks if the path that is being extracted is indeed the prefix of the entry, e.g.
7+ * A node that checks if the path that is being extracted is indeed the prefix of the entry.
8+ * e.g.
399 * ``` javascript
4010 * if (entryPath.indexOf("SomePrefix") === 0) {
4111 * // extract the file with the path.
4212 * }
4313 * ```
4414 */
45- class ZipEntryPathIndexOfCallEqualsZeroGuard extends TaintTracking :: SanitizerGuardNode {
15+ class ZipEntryPathIndexOfCallEqualsZeroGuard extends DataFlow :: Node {
4616 EqualityTest equalityTest ;
4717 MethodCallNode indexOfCall ;
4818 ForInStmt forLoop ;
@@ -55,53 +25,68 @@ class ZipEntryPathIndexOfCallEqualsZeroGuard extends TaintTracking::SanitizerGua
5525 equalityTest .getRightOperand ( ) .getIntValue ( ) = 0
5626 }
5727
58- override predicate sanitizes ( boolean outcome , Expr receiver ) {
28+ predicate blocksExpr ( boolean outcome , Expr receiver ) {
5929 exists ( DataFlow:: Node targetFilePath , DataFlow:: Node forLoopVariable |
6030 receiver = targetFilePath .asExpr ( ) and
6131 targetFilePath = indexOfCall .getReceiver ( ) and
6232 forLoopVariable = forLoop .getAnIterationVariable ( ) .getAnAccess ( ) .flow ( ) and
63- TaintedPath:: isAdditionalTaintedPathFlowStep ( forLoopVariable ,
64- targetFilePath .getALocalSource ( ) , _, _) and
33+ TaintedPath:: isAdditionalFlowStep ( forLoopVariable , _, targetFilePath .getALocalSource ( ) , _) and
6534 outcome = equalityTest .getPolarity ( )
6635 )
6736 }
6837}
6938
70- /**
71- * A class wraps `TaintedPath::BarrierGuardNode` by delegating its `sanitizes/0` to the `blocks/0` predicate.
72- * The characteristic predicate of this class is deliberately left out.
73- */
74- class TaintedPathSanitizerGuard extends TaintTracking:: SanitizerGuardNode {
75- TaintedPathSanitizerGuard ( ) { this = this }
76-
77- override predicate sanitizes ( boolean outcome , Expr receiver ) {
78- exists ( TaintedPath:: BarrierGuard node | node .blocksExpr ( outcome , receiver ) )
39+ module XSJSZipSlip implements DataFlow:: StateConfigSig {
40+ class FlowState extends string {
41+ FlowState ( ) { this in [ "$.util.Zip uninitialized" , "$.util.Zip initialized" ] }
7942 }
80- }
8143
82- class Configuration extends TaintTracking:: Configuration {
83- Configuration ( ) { this = "XSJS Zip Slip Query" }
44+ predicate isSource ( DataFlow:: Node node , FlowState state ) {
45+ node instanceof RemoteFlowSource and
46+ state = "$.util.Zip uninitialized"
47+ }
8448
85- override predicate isSource ( DataFlow:: Node start ) {
86- super .isSource ( start )
87- or
88- exists ( XSJSZipInstanceDependingOnRemoteFlowSource dollarUtilZip |
89- start = dollarUtilZip .getRemoteArgument ( )
90- )
49+ predicate isSink ( DataFlow:: Node node , FlowState state ) {
50+ node instanceof ZipSlip:: Sink and
51+ state = "$.util.Zip initialized"
9152 }
9253
93- override predicate isAdditionalTaintStep ( DataFlow:: Node src , DataFlow:: Node dst ) {
94- TaintedPath:: isAdditionalTaintedPathFlowStep ( src , dst , _, _)
54+ predicate isBarrier ( DataFlow:: Node node , FlowState state ) {
55+ (
56+ node = DataFlow:: MakeBarrierGuard< ZipEntryPathIndexOfCallEqualsZeroGuard > :: getABarrierNode ( )
57+ or
58+ node = DataFlow:: MakeBarrierGuard< TaintedPath:: BarrierGuard > :: getABarrierNode ( )
59+ ) and
60+ state = state
9561 }
9662
97- override predicate isSink ( DataFlow:: Node end ) {
98- super .isSink ( end )
63+ predicate isAdditionalFlowStep (
64+ DataFlow:: Node start , FlowState preState , DataFlow:: Node end , FlowState postState
65+ ) {
66+ /* 1. `$.util.Zip` initialized */
67+ start = start and
68+ preState = "$.util.Zip uninitialized" and
69+ end instanceof XSJSZipInstance and
70+ postState = "$.util.Zip initialized"
9971 or
100- end instanceof ZipSlip:: Sink
101- }
72+ /*
73+ * 2. Jump from a domain of a for-in statement to an access of the iteration variable.
74+ * e.g.
75+ * ``` javascript
76+ * for (var x in y) {
77+ * var z = x;
78+ * }
79+ * ```
80+ * This step jumps from `y` to `x` in the body of the for-in loop.
81+ */
10282
103- override predicate isSanitizerGuard ( TaintTracking:: SanitizerGuardNode node ) {
104- node instanceof ZipEntryPathIndexOfCallEqualsZeroGuard or
105- node instanceof TaintedPathSanitizerGuard
83+ exists ( ForInStmt forLoop |
84+ start = forLoop .getIterationDomain ( ) .flow ( ) and
85+ end = forLoop .getAnIterationVariable ( ) .getAnAccess ( ) .flow ( ) and
86+ preState = postState
87+ )
88+ or
89+ TaintedPath:: isAdditionalFlowStep ( start , _, end , _) and
90+ preState = postState
10691 }
10792}
0 commit comments