From 4ac4b7f71002ed4fbedbb901db1f6ed1e9ac5559 Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 30 Sep 2021 12:52:12 +1000 Subject: [PATCH] Merge pull request from GHSA-7pr3-p5fm-8r9x * fix: LQ deletes session token * add 4.10.4 * add changes --- CHANGELOG.md | 14 +++++--- package-lock.json | 2 +- package.json | 2 +- spec/ParseLiveQuery.spec.js | 46 +++++++++++++++++++++++++++ spec/ParseUser.spec.js | 45 ++++++++++++++++++++++++++ src/LiveQuery/ParseLiveQueryServer.js | 18 +++++++++++ 6 files changed, 121 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8a2a543e2..2c20053d9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Parse Server Changelog +## 4.10.4 +[Full Changelog](https://github.com/parse-community/parse-server/compare/4.10.3...4.10.4) + +### Security Fixes +- Strip out sessionToken when LiveQuery is used on Parse.User (Daniel Blyth) [GHSA-7pr3-p5fm-8r9x](https://github.com/parse-community/parse-server/security/advisories/GHSA-7pr3-p5fm-8r9x) + # 4.10.3 ## Security Fixes @@ -24,15 +30,15 @@ *Versions >4.5.2 and <4.10.0 are skipped.* -> ⚠️ A security incident caused a number of incorrect version tags to be pushed to the Parse Server repository. These version tags linked to a personal fork of a contributor who had write access to the repository. The code to which these tags linked has not been reviewed or approved by Parse Platform. Even though no releases were published with these incorrect versions, it was possible to define a Parse Server dependency that pointed to these version tags, for example if you defined this dependency: +> ⚠️ A security incident caused a number of incorrect version tags to be pushed to the Parse Server repository. These version tags linked to a personal fork of a contributor who had write access to the repository. The code to which these tags linked has not been reviewed or approved by Parse Platform. Even though no releases were published with these incorrect versions, it was possible to define a Parse Server dependency that pointed to these version tags, for example if you defined this dependency: > ```js > "parse-server": "git@github.com:parse-community/parse-server.git#4.9.3" > ``` -> +> > We have since deleted the incorrect version tags, but they may still show up if your personal fork on GitHub or locally. We do not know when these tags have been pushed to the Parse Server repository, but we first became aware of this issue on July 21, 2021. We are not aware of any malicious code or concerns related to privacy, security or legality (e.g. proprietary code). However, it has been reported that some functionality does not work as expected and the introduction of security vulnerabilities cannot be ruled out. > -> You may be also affected if you used the Bitnami image for Parse Server. Bitnami picked up the incorrect version tag `4.9.3` and published a new Bitnami image for Parse Server. -> +> You may be also affected if you used the Bitnami image for Parse Server. Bitnami picked up the incorrect version tag `4.9.3` and published a new Bitnami image for Parse Server. +> >**If you are using any of the affected versions, we urgently recommend to upgrade to version `4.10.0`.** # 4.5.2 diff --git a/package-lock.json b/package-lock.json index f92c2d2345..96fcc70b41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "4.10.3", + "version": "4.10.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index db72df4039..12874fce61 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "parse-server", - "version": "4.10.3", + "version": "4.10.4", "description": "An express module providing a Parse-compatible API server", "main": "lib/index.js", "repository": { diff --git a/spec/ParseLiveQuery.spec.js b/spec/ParseLiveQuery.spec.js index be2da8c866..e4ace5045f 100644 --- a/spec/ParseLiveQuery.spec.js +++ b/spec/ParseLiveQuery.spec.js @@ -928,6 +928,52 @@ describe('ParseLiveQuery', function () { done(); }); + it('should strip out session token in LiveQuery', async () => { + await reconfigureServer({ + liveQuery: { classNames: ['_User'] }, + startLiveQueryServer: true, + verbose: false, + silent: true, + }); + + const user = new Parse.User(); + user.setUsername('username'); + user.setPassword('password'); + user.set('foo', 'bar'); + + const query = new Parse.Query(Parse.User); + query.equalTo('foo', 'bar'); + const subscription = await query.subscribe(); + + const events = ['create', 'update', 'enter', 'leave', 'delete']; + const response = (obj, prev) => { + expect(obj.get('sessionToken')).toBeUndefined(); + expect(obj.sessionToken).toBeUndefined(); + expect(prev?.sessionToken).toBeUndefined(); + if (prev && prev.get) { + expect(prev.get('sessionToken')).toBeUndefined(); + } + }; + const calls = {}; + for (const key of events) { + calls[key] = response; + spyOn(calls, key).and.callThrough(); + subscription.on(key, calls[key]); + } + await user.signUp(); + user.unset('foo'); + await user.save(); + user.set('foo', 'bar'); + await user.save(); + user.set('yolo', 'bar'); + await user.save(); + await user.destroy(); + await new Promise(resolve => process.nextTick(resolve)); + for (const key of events) { + expect(calls[key]).toHaveBeenCalled(); + } + }); + afterEach(async function (done) { const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); client.close(); diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index 7eb882efc7..18ab1657b7 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -3929,6 +3929,51 @@ describe('Parse.User testing', () => { } }); + it('should strip out authdata in LiveQuery', async () => { + const provider = getMockFacebookProvider(); + Parse.User._registerAuthenticationProvider(provider); + + await reconfigureServer({ + liveQuery: { classNames: ['_User'] }, + startLiveQueryServer: true, + verbose: false, + silent: true, + }); + + const query = new Parse.Query(Parse.User); + query.doesNotExist('foo'); + const subscription = await query.subscribe(); + + const events = ['create', 'update', 'enter', 'leave', 'delete']; + const response = (obj, prev) => { + expect(obj.get('authData')).toBeUndefined(); + expect(obj.authData).toBeUndefined(); + expect(prev?.authData).toBeUndefined(); + if (prev && prev.get) { + expect(prev.get('authData')).toBeUndefined(); + } + }; + const calls = {}; + for (const key of events) { + calls[key] = response; + spyOn(calls, key).and.callThrough(); + subscription.on(key, calls[key]); + } + const user = await Parse.User._logInWith('facebook'); + + user.set('foo', 'bar'); + await user.save(); + user.unset('foo'); + await user.save(); + user.set('yolo', 'bar'); + await user.save(); + await user.destroy(); + await new Promise(resolve => process.nextTick(resolve)); + for (const key of events) { + expect(calls[key]).toHaveBeenCalled(); + } + }); + describe('issue #4897', () => { it_only_db('mongo')('should be able to login with a legacy user (no ACL)', async () => { // This issue is a side effect of the locked users and legacy users which don't have ACL's diff --git a/src/LiveQuery/ParseLiveQueryServer.js b/src/LiveQuery/ParseLiveQueryServer.js index 0f00635cab..7fb6418d25 100644 --- a/src/LiveQuery/ParseLiveQueryServer.js +++ b/src/LiveQuery/ParseLiveQueryServer.js @@ -179,6 +179,14 @@ class ParseLiveQueryServer { deletedParseObject = res.object.toJSON(); deletedParseObject.className = className; } + if ( + (deletedParseObject.className === '_User' || + deletedParseObject.className === '_Session') && + !client.hasMasterKey + ) { + delete deletedParseObject.sessionToken; + delete deletedParseObject.authData; + } client.pushDelete(requestId, deletedParseObject); }) .catch(error => { @@ -315,6 +323,16 @@ class ParseLiveQueryServer { originalParseObject = res.original.toJSON(); originalParseObject.className = res.original.className || className; } + if ( + (currentParseObject.className === '_User' || + currentParseObject.className === '_Session') && + !client.hasMasterKey + ) { + delete currentParseObject.sessionToken; + delete originalParseObject?.sessionToken; + delete currentParseObject.authData; + delete originalParseObject?.authData; + } const functionName = 'push' + message.event.charAt(0).toUpperCase() + message.event.slice(1); if (client[functionName]) {