Skip to content

Commit 78b59fb

Browse files
authored
Merge pull request from GHSA-2xm2-xj2q-qgpj
* Test case and fixes * Change requestTimeout default to 5s * Document new function argument
1 parent ef2e54c commit 78b59fb

File tree

6 files changed

+62
-12
lines changed

6 files changed

+62
-12
lines changed

spec/ParseLiveQuery.spec.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,48 @@ describe('ParseLiveQuery', function () {
784784
});
785785
});
786786

787+
it('should not broadcast event to client with invalid session token - avisory GHSA-2xm2-xj2q-qgpj', async done => {
788+
await reconfigureServer({
789+
liveQuery: {
790+
classNames: ['TestObject'],
791+
},
792+
liveQueryServerOptions: {
793+
cacheTimeout: 100,
794+
},
795+
startLiveQueryServer: true,
796+
verbose: false,
797+
silent: true,
798+
cacheTTL: 100,
799+
});
800+
const user = new Parse.User();
801+
user.setUsername('username');
802+
user.setPassword('password');
803+
await user.signUp();
804+
const obj1 = new Parse.Object('TestObject');
805+
const obj1ACL = new Parse.ACL();
806+
obj1ACL.setPublicReadAccess(false);
807+
obj1ACL.setReadAccess(user, true);
808+
obj1.setACL(obj1ACL);
809+
const obj2 = new Parse.Object('TestObject');
810+
const obj2ACL = new Parse.ACL();
811+
obj2ACL.setPublicReadAccess(false);
812+
obj2ACL.setReadAccess(user, true);
813+
obj2.setACL(obj2ACL);
814+
const query = new Parse.Query('TestObject');
815+
const subscription = await query.subscribe();
816+
subscription.on('create', obj => {
817+
if (obj.id !== obj1.id) {
818+
done.fail('should not fire');
819+
}
820+
});
821+
await obj1.save();
822+
await Parse.User.logOut();
823+
await new Promise(resolve => setTimeout(resolve, 200));
824+
await obj2.save();
825+
await new Promise(resolve => setTimeout(resolve, 200));
826+
done();
827+
});
828+
787829
afterEach(async function (done) {
788830
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
789831
client.close();

src/LiveQuery/ParseLiveQueryServer.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ class ParseLiveQueryServer {
3030
// The subscriber we use to get object update from publisher
3131
subscriber: Object;
3232

33-
constructor(server: any, config: any = {}) {
33+
constructor(server: any, config: any = {}, parseServerConfig: any = {}) {
3434
this.server = server;
3535
this.clients = new Map();
3636
this.subscriptions = new Map();
37+
this.config = config;
3738

3839
config.appId = config.appId || Parse.applicationId;
3940
config.masterKey = config.masterKey || Parse.masterKey;
@@ -54,13 +55,15 @@ class ParseLiveQueryServer {
5455

5556
// The cache controller is a proper cache controller
5657
// with access to User and Roles
57-
this.cacheController = getCacheController(config);
58+
this.cacheController = getCacheController(parseServerConfig);
59+
60+
config.cacheTimeout = config.cacheTimeout || 5 * 1000; // 5s
5861

5962
// This auth cache stores the promises for each auth resolution.
6063
// The main benefit is to be able to reuse the same user / session token resolution.
6164
this.authCache = new LRU({
6265
max: 500, // 500 concurrent
63-
maxAge: 60 * 60 * 1000, // 1h
66+
maxAge: config.cacheTimeout,
6467
});
6568
// Initialize websocket server
6669
this.parseWebSocketServer = new ParseWebSocketServer(
@@ -510,12 +513,11 @@ class ParseLiveQueryServer {
510513
// There was an error with the session token
511514
const result = {};
512515
if (error && error.code === Parse.Error.INVALID_SESSION_TOKEN) {
513-
// Store a resolved promise with the error for 10 minutes
514516
result.error = error;
515517
this.authCache.set(
516518
sessionToken,
517519
Promise.resolve(result),
518-
60 * 10 * 1000
520+
this.config.cacheTimeout
519521
);
520522
} else {
521523
this.authCache.del(sessionToken);

src/Options/Definitions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ module.exports.LiveQueryServerOptions = {
478478
cacheTimeout: {
479479
env: 'PARSE_LIVE_QUERY_SERVER_CACHE_TIMEOUT',
480480
help:
481-
"Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details, defaults to 30 * 24 * 60 * 60 * 1000 ms (~30 days).",
481+
"Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details, defaults to 5 * 1000 ms (5 seconds).",
482482
action: parsers.numberParser('cacheTimeout'),
483483
},
484484
keyPairs: {

src/Options/docs.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
/**
101101
* @interface LiveQueryServerOptions
102102
* @property {String} appId This string should match the appId in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same appId.
103-
* @property {Number} cacheTimeout Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details, defaults to 30 * 24 * 60 * 60 * 1000 ms (~30 days).
103+
* @property {Number} cacheTimeout Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details, defaults to 5 * 1000 ms (5 seconds).
104104
* @property {Any} keyPairs A JSON object that serves as a whitelist of keys. It is used for validating clients when they try to connect to the LiveQuery server. Check the following Security section and our protocol specification for details.
105105
* @property {String} logLevel This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE, defaults to INFO.
106106
* @property {String} masterKey This string should match the masterKey in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey.

src/Options/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ export interface LiveQueryServerOptions {
260260
keyPairs: ?any;
261261
/* Number of milliseconds between ping/pong frames. The WebSocket server sends ping/pong frames to the clients to keep the WebSocket alive. This value defines the interval of the ping/pong frame from the server to clients, defaults to 10 * 1000 ms (10 s).*/
262262
websocketTimeout: ?number;
263-
/* Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details, defaults to 30 * 24 * 60 * 60 * 1000 ms (~30 days).*/
263+
/* Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details, defaults to 5 * 1000 ms (5 seconds).*/
264264
cacheTimeout: ?number;
265265
/* This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE, defaults to INFO.*/
266266
logLevel: ?string;

src/ParseServer.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,8 @@ class ParseServer {
298298
if (options.startLiveQueryServer || options.liveQueryServerOptions) {
299299
this.liveQueryServer = ParseServer.createLiveQueryServer(
300300
server,
301-
options.liveQueryServerOptions
301+
options.liveQueryServerOptions,
302+
options
302303
);
303304
}
304305
/* istanbul ignore next */
@@ -324,16 +325,21 @@ class ParseServer {
324325
* Helper method to create a liveQuery server
325326
* @static
326327
* @param {Server} httpServer an optional http server to pass
327-
* @param {LiveQueryServerOptions} config options fot he liveQueryServer
328+
* @param {LiveQueryServerOptions} config options for the liveQueryServer
329+
* @param {ParseServerOptions} options options for the ParseServer
328330
* @returns {ParseLiveQueryServer} the live query server instance
329331
*/
330-
static createLiveQueryServer(httpServer, config: LiveQueryServerOptions) {
332+
static createLiveQueryServer(
333+
httpServer,
334+
config: LiveQueryServerOptions,
335+
options: ParseServerOptions
336+
) {
331337
if (!httpServer || (config && config.port)) {
332338
var app = express();
333339
httpServer = require('http').createServer(app);
334340
httpServer.listen(config.port);
335341
}
336-
return new ParseLiveQueryServer(httpServer, config);
342+
return new ParseLiveQueryServer(httpServer, config, options);
337343
}
338344

339345
static verifyServerUrl(callback) {

0 commit comments

Comments
 (0)