Skip to content

Commit 0401b26

Browse files
committed
JS: handle CloudFunctions
1 parent 49a746b commit 0401b26

File tree

1 file changed

+170
-92
lines changed

1 file changed

+170
-92
lines changed

javascript/ql/src/semmle/javascript/frameworks/Firebase.qll

Lines changed: 170 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
/**
2+
* Provides classes and predicates for reasoning about code using the Firebase API.
3+
*/
14
import javascript
25

36
module Firebase {
4-
57
/** Gets a reference to the firebase API object. */
68
private DataFlow::SourceNode firebase(DataFlow::TypeTracker t) {
79
t.start() and
@@ -18,7 +20,7 @@ module Firebase {
1820
)
1921
}
2022

21-
/** Gets a reference to the firebase API object. */
23+
/** Gets a reference to the `firebase/app` or `firebase-admin` API object. */
2224
DataFlow::SourceNode firebase() {
2325
result = firebase(_)
2426
}
@@ -31,7 +33,7 @@ module Firebase {
3133
result = initApp(t2).track(t2, t)
3234
)
3335
}
34-
36+
3537
/**
3638
* Gets a reference to a firebase app, either the `firebase` object or an
3739
* app created explicitly with `initializeApp()`.
@@ -40,96 +42,188 @@ module Firebase {
4042
result = firebase(_) or result = initApp(_)
4143
}
4244

43-
/** Gets a reference to a firebase database object, such as `firebase.database()`. */
44-
private DataFlow::SourceNode database(DataFlow::TypeTracker t) {
45-
result = app().getAMethodCall("database") and t.start()
46-
or
47-
exists (DataFlow::TypeTracker t2 |
48-
result = database(t2).track(t2, t)
49-
)
50-
}
51-
52-
/** Gets a reference to a firebase database object, such as `firebase.database()`. */
53-
DataFlow::SourceNode database() {
54-
result = database(_)
55-
}
56-
57-
/** Gets a node that refers to a `Reference` object, such as `firebase.database().ref()`. */
58-
DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
59-
t.start() and
60-
(
61-
exists (string name | result = database().getAMethodCall(name) |
62-
name = "ref" or
63-
name = "refFromURL"
45+
module Database {
46+
47+
/** Gets a reference to a firebase database object, such as `firebase.database()`. */
48+
private DataFlow::SourceNode database(DataFlow::TypeTracker t) {
49+
result = app().getAMethodCall("database") and t.start()
50+
or
51+
exists (DataFlow::TypeTracker t2 |
52+
result = database(t2).track(t2, t)
53+
)
54+
}
55+
56+
/** Gets a reference to a firebase database object, such as `firebase.database()`. */
57+
DataFlow::SourceNode database() {
58+
result = database(_)
59+
}
60+
61+
/** Gets a node that refers to a `Reference` object, such as `firebase.database().ref()`. */
62+
DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
63+
t.start() and
64+
(
65+
exists (string name | result = database().getAMethodCall(name) |
66+
name = "ref" or
67+
name = "refFromURL"
68+
)
69+
or
70+
exists (string name | result = ref(_).getAMethodCall(name) |
71+
name = "push" or
72+
name = "child"
73+
)
74+
or
75+
exists (string name | result = ref(_).getAPropertyRead(name) |
76+
name = "parent" or
77+
name = "root"
78+
)
79+
or
80+
result = snapshot().getAPropertyRead("ref")
6481
)
6582
or
66-
exists (string name | result = ref(_).getAMethodCall(name) |
67-
name = "push" or
68-
name = "child"
83+
exists (DataFlow::TypeTracker t2 |
84+
result = ref(t2).track(t2, t)
85+
)
86+
}
87+
88+
/** Gets a node that refers to a `Reference` object, such as `firebase.database().ref()`. */
89+
DataFlow::SourceNode ref() {
90+
result = ref(_)
91+
}
92+
93+
/** Gets a node that refers to a `Query` or `Reference` object. */
94+
DataFlow::SourceNode query(DataFlow::TypeTracker t) {
95+
t.start() and
96+
(
97+
result = ref(t) // a Reference can be used as a Query
98+
or
99+
exists (string name | result = query(_).getAMethodCall(name) |
100+
name = "endAt" or
101+
name = "limitTo" + any(string s) or
102+
name = "orderBy" + any(string s) or
103+
name = "startAt"
104+
)
69105
)
70106
or
71-
exists (string name | result = ref(_).getAPropertyRead(name) |
72-
name = "parent" or
73-
name = "root"
107+
exists (DataFlow::TypeTracker t2 |
108+
result = query(t2).track(t2, t)
74109
)
110+
}
111+
112+
/** Gets a node that refers to a `Query` or `Reference` object. */
113+
DataFlow::SourceNode query() {
114+
result = query(_)
115+
}
116+
117+
/**
118+
* A call of form `query.on(...)` or `query.once(...)`.
119+
*/
120+
class QueryListenCall extends DataFlow::MethodCallNode {
121+
QueryListenCall() {
122+
this = query().getAMethodCall() and
123+
(getMethodName() = "on" or getMethodName() = "once")
124+
}
125+
126+
DataFlow::Node getCallbackNode() {
127+
result = getArgument(1)
128+
}
129+
}
130+
131+
/**
132+
* Gets a node that is passed as the callback to a `Reference.transaction` call.
133+
*/
134+
DataFlow::SourceNode transactionCallback(DataFlow::TypeTracker t) {
135+
t.start() and
136+
result = ref().getAMethodCall("transaction").getArgument(0).getALocalSource()
75137
or
76-
result = snapshot().getAPropertyRead("ref")
77-
)
78-
or
79-
exists (DataFlow::TypeTracker t2 |
80-
result = ref(t2).track(t2, t)
81-
)
138+
exists (DataFlow::TypeTracker t2 |
139+
result = transactionCallback(t2).backtrack(t2, t)
140+
)
141+
}
142+
143+
/**
144+
* Gets a node that is passed as the callback to a `Reference.transaction` call.
145+
*/
146+
DataFlow::SourceNode transactionCallback() {
147+
result = transactionCallback(_)
148+
}
82149
}
83150

84-
/** Gets a node that refers to a `Reference` object, such as `firebase.database().ref()`. */
85-
DataFlow::SourceNode ref() {
86-
result = ref(_)
87-
}
151+
/**
152+
* Provides predicates for reasoning about the the Firebase Cloud Functions API,
153+
* sometimes referred to just as just "Firebase Functions".
154+
*/
155+
module CloudFunctions {
156+
/** Gets a reference to the Cloud Functions namespace. */
157+
DataFlow::SourceNode namespace(DataFlow::TypeTracker t) {
158+
t.start() and
159+
result = DataFlow::moduleImport("firebase-functions")
160+
or
161+
exists (DataFlow::TypeTracker t2 |
162+
result = namespace(t2).track(t2, t)
163+
)
164+
}
165+
166+
/** Gets a reference to the Cloud Functions namespace. */
167+
DataFlow::SourceNode namespace() {
168+
result = namespace(_)
169+
}
88170

89-
/** Gets a node that refers to a `Query` or `Reference` object. */
90-
DataFlow::SourceNode query(DataFlow::TypeTracker t) {
91-
t.start() and
92-
(
93-
result = ref(t) // a Reference can be used as a Query
171+
/** Gets a reference to a Cloud Functions database object. */
172+
DataFlow::SourceNode database(DataFlow::TypeTracker t) {
173+
t.start() and
174+
result = namespace().getAPropertyRead("database")
94175
or
95-
exists (string name | result = query(_).getAMethodCall(name) |
96-
name = "endAt" or
97-
name = "limitTo" + any(string s) or
98-
name = "orderBy" + any(string s) or
99-
name = "startAt"
176+
exists (DataFlow::TypeTracker t2 |
177+
result = database(t2).track(t2, t)
100178
)
101-
)
102-
or
103-
exists (DataFlow::TypeTracker t2 |
104-
result = query(t2).track(t2, t)
105-
)
106-
}
179+
}
107180

108-
/** Gets a node that refers to a `Query` or `Reference` object. */
109-
DataFlow::SourceNode query() {
110-
result = query(_)
111-
}
112-
113-
/**
114-
* A call of form `query.on(...)` or `query.once(...)`.
115-
*/
116-
class QueryListenCall extends DataFlow::MethodCallNode {
117-
QueryListenCall() {
118-
this = query().getAMethodCall() and
119-
(getMethodName() = "on" or getMethodName() = "once")
181+
/** Gets a reference to a Cloud Functions database object. */
182+
DataFlow::SourceNode database() {
183+
result = database(_)
184+
}
185+
186+
/** Gets a dataflow node holding a `RefBuilder` object. */
187+
DataFlow::SourceNode refBuilder(DataFlow::TypeTracker t) {
188+
t.start() and
189+
result = database().getAMethodCall("ref")
190+
or
191+
exists (DataFlow::TypeTracker t2 |
192+
result = refBuilder(t2).track(t2, t)
193+
)
120194
}
121195

122-
DataFlow::Node getCallbackNode() {
123-
result = getArgument(1)
196+
/** Gets a dataflow node holding a `RefBuilder` object. */
197+
DataFlow::SourceNode ref() {
198+
result = refBuilder(_)
124199
}
125-
}
126200

201+
/** Gets a call that registers a listener on a `RefBuilder`, such as `ref.onCreate(...)`. */
202+
class RefBuilderListenCall extends DataFlow::MethodCallNode {
203+
RefBuilderListenCall() {
204+
this = ref().getAMethodCall() and
205+
getMethodName() = "on" + any(string s)
206+
}
207+
208+
/**
209+
* Gets the dataflow node holding the listener callback.
210+
*/
211+
DataFlow::Node getCallbackNode() {
212+
result = getArgument(0)
213+
}
214+
}
215+
}
216+
127217
/**
128218
* Gets a value that will be invoked with a `DataSnapshot` value as its first parameter.
129219
*/
130220
DataFlow::SourceNode snapshotCallback(DataFlow::TypeTracker t) {
131221
t.start() and
132-
result = any(QueryListenCall call).getCallbackNode().getALocalSource()
222+
(
223+
result = any(Database::QueryListenCall call).getCallbackNode().getALocalSource()
224+
or
225+
result = any(CloudFunctions::RefBuilderListenCall call).getCallbackNode().getALocalSource()
226+
)
133227
or
134228
exists (DataFlow::TypeTracker t2 |
135229
result = snapshotCallback(t2).backtrack(t2, t)
@@ -151,7 +245,7 @@ module Firebase {
151245
(
152246
result = snapshotCallback().(DataFlow::FunctionNode).getParameter(0)
153247
or
154-
result instanceof QueryListenCall // returns promise
248+
result instanceof Database::QueryListenCall // returns promise
155249
or
156250
result = snapshot(_).getAMethodCall("child")
157251
or
@@ -161,7 +255,7 @@ module Firebase {
161255
promiseTaintStep(snapshot(t), result)
162256
or
163257
exists (DataFlow::TypeTracker t2 |
164-
result = ref(t2).track(t2, t)
258+
result = snapshot(t2).track(t2, t)
165259
)
166260
}
167261

@@ -174,32 +268,16 @@ module Firebase {
174268
}
175269

176270
/**
177-
* Gets a node that is passed as the callback to a `Reference.transaction` call.
271+
* A reference to a value obtained from a Firebase database.
178272
*/
179-
DataFlow::SourceNode transactionCallback(DataFlow::TypeTracker t) {
180-
t.start() and
181-
result = ref().getAMethodCall("transaction").getArgument(0).getALocalSource()
182-
or
183-
exists (DataFlow::TypeTracker t2 |
184-
result = transactionCallback(t2).backtrack(t2, t)
185-
)
186-
}
187-
188-
/**
189-
* Gets a node that is passed as the callback to a `Reference.transaction` call.
190-
*/
191-
DataFlow::SourceNode transactionCallback() {
192-
result = transactionCallback(_)
193-
}
194-
195273
class FirebaseVal extends RemoteFlowSource {
196274
FirebaseVal() {
197275
exists (string name | this = snapshot().getAMethodCall(name) |
198276
name = "val" or
199277
name = "exportVal"
200278
)
201279
or
202-
this = transactionCallback().(DataFlow::FunctionNode).getParameter(0)
280+
this = Database::transactionCallback().(DataFlow::FunctionNode).getParameter(0)
203281
}
204282

205283
override string getSourceType() {

0 commit comments

Comments
 (0)