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 ( )
35+ }
36+
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+ module FunctionCompositionCall {
45+ abstract class Range extends DataFlow:: CallNode {
46+ /**
47+ * Gets the function flowing into the `i`th function in the composition `f(g(h(...)))`.
48+ */
49+ abstract DataFlow:: Node getOperandNode ( int i ) ;
50+
51+ /** Gets the number of functions being composed. */
52+ abstract int getNumOperand ( ) ;
2053 }
2154
2255 /**
23- * Gets the ith function in this composition.
56+ * A function composition call that accepts its operands in an array or
57+ * via the arguments list.
58+ *
59+ * For simplicity, we model every composition function as if it supported this.
2460 */
25- DataFlow:: FunctionNode getFunction ( int i ) { result .flowsTo ( getArgument ( i ) ) }
61+ private abstract class WithArrayOverloading extends Range {
62+ /** Gets the `i`th argument to the call or the `i`th array element passed into the call. */
63+ DataFlow:: Node getEffectiveArgument ( int i ) {
64+ result = getArgument ( 0 ) .( DataFlow:: ArrayCreationNode ) .getElement ( i )
65+ or
66+ not getArgument ( 0 ) instanceof DataFlow:: ArrayCreationNode and
67+ result = getArgument ( i )
68+ }
69+
70+ override int getNumOperand ( ) {
71+ result = getArgument ( 0 ) .( DataFlow:: ArrayCreationNode ) .getSize ( )
72+ or
73+ not getArgument ( 0 ) instanceof DataFlow:: ArrayCreationNode and
74+ result = getNumArgument ( )
75+ }
76+ }
77+
78+ /** A call whose arguments are functions `f,g,h` which are composed into `f(g(h(...))` */
79+ private class RightToLeft extends WithArrayOverloading {
80+ RightToLeft ( ) {
81+ this = DataFlow:: moduleImport ( [ "compose-function" ] ) .getACall ( )
82+ or
83+ this = DataFlow:: moduleMember ( [ "redux" , "ramda" ] , "compose" ) .getACall ( )
84+ or
85+ this = LodashUnderscore:: member ( "flowRight" ) .getACall ( )
86+ }
87+
88+ override DataFlow:: Node getOperandNode ( int i ) {
89+ result = getEffectiveArgument ( i )
90+ }
91+ }
92+
93+ /** A call whose arguments are functions `f,g,h` which are composed into `f(g(h(...))` */
94+ private class LeftToRight extends WithArrayOverloading {
95+ LeftToRight ( ) {
96+ this = DataFlow:: moduleImport ( "just-compose" ) .getACall ( )
97+ or
98+ this = LodashUnderscore:: member ( "flow" ) .getACall ( )
99+ }
100+
101+ override DataFlow:: Node getOperandNode ( int i ) {
102+ result = getEffectiveArgument ( getNumOperand ( ) - i - 1 )
103+ }
104+ }
26105}
27106
28107/**
29108 * A taint step for a composed function.
30109 */
31110private class ComposedFunctionTaintStep extends TaintTracking:: AdditionalTaintStep {
32- ComposedFunction composed ;
111+ FunctionCompositionCall composed ;
33112 DataFlow:: CallNode call ;
34113
35114 ComposedFunctionTaintStep ( ) {
@@ -38,25 +117,24 @@ private class ComposedFunctionTaintStep extends TaintTracking::AdditionalTaintSt
38117 }
39118
40119 override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
41- exists ( int fnIndex , DataFlow:: FunctionNode fn | fn = composed .getFunction ( fnIndex ) |
120+ exists ( int fnIndex , DataFlow:: FunctionNode fn | fn = composed .getOperandFunction ( fnIndex ) |
42121 // flow out of the composed call
43- fnIndex = composed . getNumArgument ( ) - 1 and
44- pred = fn .getAReturn ( ) and
122+ fnIndex = 0 and
123+ pred = fn .getReturnNode ( ) and
45124 succ = this
46125 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- )
126+ // flow into the first function
127+ fnIndex = composed .getNumOperand ( ) - 1 and
128+ exists ( int callArgIndex |
129+ pred = call .getArgument ( callArgIndex ) and
130+ succ = fn .getParameter ( callArgIndex )
131+ )
132+ or
133+ // flow through the composed functions
134+ exists ( DataFlow:: FunctionNode predFn | predFn = composed .getOperandFunction ( fnIndex + 1 ) |
135+ pred = predFn .getReturnNode ( ) and
136+ succ = fn .getParameter ( 0 )
137+ )
60138 )
61139 }
62140}
0 commit comments