From 73b0f9a339b81f5d757725dc557955a7b670a3ec Mon Sep 17 00:00:00 2001 From: Fabian Strachanski Date: Wed, 10 Jul 2019 15:47:23 +0200 Subject: [PATCH] Merge pull request from GHSA-8w3j-g983-8jh5 * Add Test and Authenticator for ghsa-8w3j-g983-8jh5 * fix for ghsa-8w3j-g983-8jh5 * nit whitespace not sure why lint isn't catching... --- spec/ParseUser.spec.js | 37 +++++++++++++++++++++++++++++++++++++ spec/helper.js | 20 ++++++++++++++++++++ src/RestWrite.js | 21 +++++++++++---------- 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index f21403e89a..2eeba7b73b 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -3847,4 +3847,41 @@ describe('Parse.User testing', () => { } ); }); + + it('should validate credentials first and check if account already linked afterwards (GHSA-8w3j-g983-8jh5)', async done => { + // Add User to Database with authData + const database = Config.get(Parse.applicationId).database; + const collection = await database.adapter._adaptiveCollection('_User'); + await collection.insertOne({ + _id: 'ABCDEF1234', + name: '', + email: '', + username: '', + _hashed_password: '', + _auth_data_custom: { + id: 'linkedID', // Already linked userid + }, + sessionToken: '', + }); + const provider = { + getAuthType: () => 'custom', + restoreAuthentication: () => true, + }; // AuthProvider checks if password is 'password' + Parse.User._registerAuthenticationProvider(provider); + + // Try to link second user with wrong password + try { + const user = await Parse.AnonymousUtils.logIn(); + await user._linkWith(provider.getAuthType(), { + authData: { id: 'linkedID', password: 'wrong' }, + }); + } catch (error) { + // This should throw Parse.Error.SESSION_MISSING and not Parse.Error.ACCOUNT_ALREADY_LINKED + expect(error.code).toEqual(Parse.Error.SESSION_MISSING); + done(); + return; + } + fail(); + done(); + }); }); diff --git a/spec/helper.js b/spec/helper.js index 844ca6777a..82901ac593 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -118,6 +118,7 @@ const defaultConfiguration = { }, auth: { // Override the facebook provider + custom: mockCustom(), facebook: mockFacebook(), myoauth: { module: path.resolve(__dirname, 'myoauth'), // relative path as it's run from src @@ -348,6 +349,24 @@ function range(n) { return answer; } +function mockCustomAuthenticator(id, password) { + const custom = {}; + custom.validateAuthData = function(authData) { + if (authData.id === id && authData.password.startsWith(password)) { + return Promise.resolve(); + } + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'not validated'); + }; + custom.validateAppId = function() { + return Promise.resolve(); + }; + return custom; +} + +function mockCustom() { + return mockCustomAuthenticator('fastrde', 'password'); +} + function mockFacebookAuthenticator(id, token) { const facebook = {}; facebook.validateAuthData = function(authData) { @@ -406,6 +425,7 @@ global.jequal = jequal; global.range = range; global.reconfigureServer = reconfigureServer; global.defaultConfiguration = defaultConfiguration; +global.mockCustomAuthenticator = mockCustomAuthenticator; global.mockFacebookAuthenticator = mockFacebookAuthenticator; global.jfail = function(err) { fail(JSON.stringify(err)); diff --git a/src/RestWrite.js b/src/RestWrite.js index 2622b62d40..69e9c3d4c7 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -449,17 +449,10 @@ RestWrite.prototype.handleAuthData = function(authData) { let results; return this.findUsersWithAuthData(authData).then(async r => { results = this.filteredObjectsByACL(r); - if (results.length > 1) { - // More than 1 user with the passed id's - throw new Parse.Error( - Parse.Error.ACCOUNT_ALREADY_LINKED, - 'this auth is already used' - ); - } - this.storage['authProvider'] = Object.keys(authData).join(','); + if (results.length == 1) { + this.storage['authProvider'] = Object.keys(authData).join(','); - if (results.length > 0) { const userResult = results[0]; const mutatedAuthData = {}; Object.keys(authData).forEach(provider => { @@ -543,7 +536,15 @@ RestWrite.prototype.handleAuthData = function(authData) { } } } - return this.handleAuthDataValidation(authData); + return this.handleAuthDataValidation(authData).then(() => { + if (results.length > 1) { + // More than 1 user with the passed id's + throw new Parse.Error( + Parse.Error.ACCOUNT_ALREADY_LINKED, + 'this auth is already used' + ); + } + }); }); };