55import javascript
66
77/**
8- * A function composed from a collection of functions.
8+ * A call to a function that constructs a function composition `f(g(h(...)))` from a
9+ * series functions `f, g, h, ...`.
910 */
10- private class ComposedFunction extends DataFlow:: CallNode {
11- ComposedFunction ( ) {
12- exists ( string name |
13- name = "just-compose" or
14- name = "compose-function"
15- |
16- this = DataFlow:: moduleImport ( name ) .getACall ( )
17- )
18- or
19- this = LodashUnderscore:: member ( "flow" ) .getACall ( )
11+ class FunctionCompositionCall extends DataFlow:: CallNode {
12+ FunctionCompositionCall:: Range range ;
13+
14+ FunctionCompositionCall ( ) { this = range }
15+
16+ /**
17+ * Gets the `i`th function in the composition `f(g(h(...)))`, counting from left to right.
18+ *
19+ * Note that this is the opposite of the order in which the function are invoked,
20+ * that is, `g` occurs later than `f` in `f(g(...))` but is invoked before `f`.
21+ */
22+ DataFlow:: Node getOperandNode ( int i ) { result = range .getOperandNode ( i ) }
23+
24+ /** Gets a node holding one of the functions to be composed. */
25+ final DataFlow:: Node getAnOperandNode ( ) { result = getOperandNode ( _) }
26+
27+ /**
28+ * Gets the function flowing into the `i`th function in the composition `f(g(h(...)))`.
29+ *
30+ * Note that this is the opposite of the order in which the function are invoked,
31+ * that is, `g` occurs later than `f` in `f(g(...))` but is invoked before `f`.
32+ */
33+ final DataFlow:: FunctionNode getOperandFunction ( int i ) {
34+ result = getOperandNode ( i ) .getALocalSource ( )
2035 }
2136
37+ /** Gets any of the functions being composed. */
38+ final DataFlow:: Node getAnOperandFunction ( ) { result = getOperandFunction ( _) }
39+
40+ /** Gets the number of functions being composed. */
41+ int getNumOperand ( ) { result = range .getNumOperand ( ) }
42+ }
43+
44+ /**
45+ * Companion module to the `FunctionCompositionCall` class.
46+ */
47+ module FunctionCompositionCall {
2248 /**
23- * Gets the ith function in this composition.
49+ * Class that determines the set of values in `FunctionCompositionCall`.
50+ *
51+ * May be subclassed to classify more calls as function compositions.
2452 */
25- DataFlow:: FunctionNode getFunction ( int i ) { result .flowsTo ( getArgument ( i ) ) }
53+ abstract class Range extends DataFlow:: CallNode {
54+ /**
55+ * Gets the function flowing into the `i`th function in the composition `f(g(h(...)))`.
56+ */
57+ abstract DataFlow:: Node getOperandNode ( int i ) ;
58+
59+ /** Gets the number of functions being composed. */
60+ abstract int getNumOperand ( ) ;
61+ }
62+
63+ /**
64+ * A function composition call that accepts its operands in an array or
65+ * via the arguments list.
66+ *
67+ * For simplicity, we model every composition function as if it supported this.
68+ */
69+ abstract private class WithArrayOverloading extends Range {
70+ /** Gets the `i`th argument to the call or the `i`th array element passed into the call. */
71+ DataFlow:: Node getEffectiveArgument ( int i ) {
72+ result = getArgument ( 0 ) .( DataFlow:: ArrayCreationNode ) .getElement ( i )
73+ or
74+ not getArgument ( 0 ) instanceof DataFlow:: ArrayCreationNode and
75+ result = getArgument ( i )
76+ }
77+
78+ override int getNumOperand ( ) {
79+ result = getArgument ( 0 ) .( DataFlow:: ArrayCreationNode ) .getSize ( )
80+ or
81+ not getArgument ( 0 ) instanceof DataFlow:: ArrayCreationNode and
82+ result = getNumArgument ( )
83+ }
84+ }
85+
86+ /** A call whose arguments are functions `f,g,h` which are composed into `f(g(h(...))` */
87+ private class RightToLeft extends WithArrayOverloading {
88+ RightToLeft ( ) {
89+ this = DataFlow:: moduleImport ( [ "compose-function" ] ) .getACall ( )
90+ or
91+ this = DataFlow:: moduleMember ( [ "redux" , "ramda" ] , "compose" ) .getACall ( )
92+ or
93+ this = LodashUnderscore:: member ( "flowRight" ) .getACall ( )
94+ }
95+
96+ override DataFlow:: Node getOperandNode ( int i ) { result = getEffectiveArgument ( i ) }
97+ }
98+
99+ /** A call whose arguments are functions `f,g,h` which are composed into `f(g(h(...))` */
100+ private class LeftToRight extends WithArrayOverloading {
101+ LeftToRight ( ) {
102+ this = DataFlow:: moduleImport ( "just-compose" ) .getACall ( )
103+ or
104+ this = LodashUnderscore:: member ( "flow" ) .getACall ( )
105+ }
106+
107+ override DataFlow:: Node getOperandNode ( int i ) {
108+ result = getEffectiveArgument ( getNumOperand ( ) - i - 1 )
109+ }
110+ }
26111}
27112
28113/**
29114 * A taint step for a composed function.
30115 */
31116private class ComposedFunctionTaintStep extends TaintTracking:: AdditionalTaintStep {
32- ComposedFunction composed ;
117+ FunctionCompositionCall composed ;
33118 DataFlow:: CallNode call ;
34119
35120 ComposedFunctionTaintStep ( ) {
@@ -38,25 +123,24 @@ private class ComposedFunctionTaintStep extends TaintTracking::AdditionalTaintSt
38123 }
39124
40125 override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
41- exists ( int fnIndex , DataFlow:: FunctionNode fn | fn = composed .getFunction ( fnIndex ) |
126+ exists ( int fnIndex , DataFlow:: FunctionNode fn | fn = composed .getOperandFunction ( fnIndex ) |
127+ // flow into the first function
128+ fnIndex = composed .getNumOperand ( ) - 1 and
129+ exists ( int callArgIndex |
130+ pred = call .getArgument ( callArgIndex ) and
131+ succ = fn .getParameter ( callArgIndex )
132+ )
133+ or
134+ // flow through the composed functions
135+ exists ( DataFlow:: FunctionNode predFn | predFn = composed .getOperandFunction ( fnIndex + 1 ) |
136+ pred = predFn .getReturnNode ( ) and
137+ succ = fn .getParameter ( 0 )
138+ )
139+ or
42140 // flow out of the composed call
43- fnIndex = composed . getNumArgument ( ) - 1 and
44- pred = fn .getAReturn ( ) and
141+ fnIndex = 0 and
142+ pred = fn .getReturnNode ( ) and
45143 succ = this
46- or
47- if fnIndex = 0
48- then
49- // flow into the first composed function
50- exists ( int callArgIndex |
51- pred = call .getArgument ( callArgIndex ) and
52- succ = fn .getParameter ( callArgIndex )
53- )
54- else
55- // flow through the composed functions
56- exists ( DataFlow:: FunctionNode predFn | predFn = composed .getFunction ( fnIndex - 1 ) |
57- pred = predFn .getAReturn ( ) and
58- succ = fn .getParameter ( 0 )
59- )
60144 )
61145 }
62146}
0 commit comments