@@ -10,7 +10,8 @@ module ExceptionXss {
1010 import DomBasedXssCustomizations:: DomBasedXss as DomBasedXssCustom
1111 import ReflectedXssCustomizations:: ReflectedXss as ReflectedXssCustom
1212 import Xss as Xss
13-
13+ private import semmle.javascript.dataflow.InferredTypes
14+
1415 /**
1516 * Holds if `node` is unlikely to cause an exception containing sensitive information to be thrown.
1617 */
@@ -24,16 +25,29 @@ module ExceptionXss {
2425 node = DataFlow:: globalVarRef ( "console" ) .getAMemberCall ( _) .getAnArgument ( )
2526 }
2627
28+ /**
29+ * Holds if `t` is `null` or `undefined`.
30+ */
31+ private predicate isNullOrUndefined ( InferredType t ) {
32+ t = TTNull ( ) or
33+ t = TTUndefined ( )
34+ }
35+
2736 /**
2837 * Holds if `node` can possibly cause an exception containing sensitive information to be thrown.
2938 */
3039 predicate canThrowSensitiveInformation ( DataFlow:: Node node ) {
31- not isUnlikelyToThrowSensitiveInformation ( node ) and
40+ not isUnlikelyToThrowSensitiveInformation ( node ) and
3241 (
3342 // in the case of reflective calls the below ensures that both InvokeNodes have no known callee.
34- forex ( DataFlow:: InvokeNode call | node = call .getAnArgument ( ) | not exists ( call .getACallee ( ) ) )
43+ forex ( DataFlow:: InvokeNode call | call .getAnArgument ( ) = node | not exists ( call .getACallee ( ) ) )
3544 or
3645 node .asExpr ( ) .getEnclosingStmt ( ) instanceof ThrowStmt
46+ or
47+ exists ( DataFlow:: PropRef prop |
48+ node = DataFlow:: valueNode ( prop .getPropertyNameExpr ( ) ) and
49+ forex ( InferredType t | t = prop .getBase ( ) .analyze ( ) .getAType ( ) | isNullOrUndefined ( t ) )
50+ )
3751 )
3852 }
3953
@@ -47,6 +61,55 @@ module ExceptionXss {
4761 NotYetThrown ( ) { this = "NotYetThrown" }
4862 }
4963
64+ /**
65+ * A callback that is the last argument to some call, and the callback has the form:
66+ * `function (err, value) {if (err) {...} ... }`
67+ */
68+ class Callback extends DataFlow:: FunctionNode {
69+ DataFlow:: ParameterNode errorParameter ;
70+
71+ Callback ( ) {
72+ exists ( DataFlow:: CallNode call | call .getLastArgument ( ) .getAFunctionValue ( ) = this ) and
73+ this .getNumParameter ( ) = 2 and
74+ errorParameter = this .getParameter ( 0 ) and
75+ exists ( IfStmt ifStmt |
76+ ifStmt = this .getFunction ( ) .getBodyStmt ( 0 ) and
77+ errorParameter .flowsToExpr ( ifStmt .getCondition ( ) )
78+ )
79+ }
80+
81+ /**
82+ * Get the parameter in the callback that contains an error.
83+ * In the current implementation this is always the first parameter.
84+ */
85+ DataFlow:: Node getErrorParam ( ) { result = errorParameter }
86+ }
87+
88+ /**
89+ * Gets the error parameter for a callback that is supplied to the same call as `pred` is an argument to.
90+ * For example: `outerCall(foo, <pred>, bar, (<result>, val) => { ... })`.
91+ */
92+ DataFlow:: Node getCallbackErrorParam ( DataFlow:: Node pred ) {
93+ exists ( DataFlow:: CallNode call , Callback callback |
94+ pred = call .getAnArgument ( ) and
95+ call .getLastArgument ( ) = callback and
96+ result = callback .getErrorParam ( ) and
97+ not pred = callback
98+ )
99+ }
100+
101+ /**
102+ * Gets the data-flow node to which any exceptions thrown by
103+ * this expression will propagate.
104+ * This predicate adds, on top of `Expr::getExceptionTarget`, exceptions
105+ * propagated by callbacks.
106+ */
107+ private DataFlow:: Node getExceptionTarget ( DataFlow:: Node pred ) {
108+ result = pred .asExpr ( ) .getExceptionTarget ( )
109+ or
110+ result = getCallbackErrorParam ( pred )
111+ }
112+
50113 /**
51114 * A taint-tracking configuration for reasoning about XSS with possible exceptional flow.
52115 * Flow labels are used to ensure that we only report taint-flow that has been thrown in
@@ -69,12 +132,15 @@ module ExceptionXss {
69132 DataFlow:: Node pred , DataFlow:: Node succ , DataFlow:: FlowLabel inlbl ,
70133 DataFlow:: FlowLabel outlbl
71134 ) {
72- inlbl instanceof NotYetThrown and ( outlbl .isTaint ( ) or outlbl instanceof NotYetThrown ) and
73- succ = pred .asExpr ( ) .getExceptionTarget ( ) and
74- canThrowSensitiveInformation ( pred )
135+ inlbl instanceof NotYetThrown and
136+ ( outlbl .isTaint ( ) or outlbl instanceof NotYetThrown ) and
137+ canThrowSensitiveInformation ( pred ) and
138+ succ = getExceptionTarget ( pred )
75139 or
76140 // All the usual taint-flow steps apply on data-flow before it has been thrown in an exception.
77- this .isAdditionalFlowStep ( pred , succ ) and inlbl instanceof NotYetThrown and outlbl instanceof NotYetThrown
141+ this .isAdditionalFlowStep ( pred , succ ) and
142+ inlbl instanceof NotYetThrown and
143+ outlbl instanceof NotYetThrown
78144 }
79145 }
80146}
0 commit comments