5
5
import javascript
6
6
7
7
/**
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, ...`.
9
10
*/
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 ( )
20
35
}
21
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
+ /**
45
+ * Companion module to the `FunctionCompositionCall` class.
46
+ */
47
+ module FunctionCompositionCall {
22
48
/**
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.
24
52
*/
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
+ }
26
111
}
27
112
28
113
/**
29
114
* A taint step for a composed function.
30
115
*/
31
116
private class ComposedFunctionTaintStep extends TaintTracking:: AdditionalTaintStep {
32
- ComposedFunction composed ;
117
+ FunctionCompositionCall composed ;
33
118
DataFlow:: CallNode call ;
34
119
35
120
ComposedFunctionTaintStep ( ) {
@@ -38,25 +123,24 @@ private class ComposedFunctionTaintStep extends TaintTracking::AdditionalTaintSt
38
123
}
39
124
40
125
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
42
140
// 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
45
143
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
- )
60
144
)
61
145
}
62
146
}
0 commit comments