Skip to content

Commit 4e284cf

Browse files
committed
fix: Block dot-notation updates to authData sub-fields and harden login provider checks
1 parent 9667fd7 commit 4e284cf

File tree

3 files changed

+60
-5
lines changed

3 files changed

+60
-5
lines changed

spec/vulnerabilities.spec.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3010,6 +3010,56 @@ describe('(GHSA-fjxm-vhvc-gcmj) LiveQuery Operator Type Confusion', () => {
30103010
});
30113011
});
30123012

3013+
describe('authData dot-notation injection and login crash', () => {
3014+
it('rejects dotted update key that targets authData sub-field', async () => {
3015+
const user = new Parse.User();
3016+
user.setUsername('dotuser');
3017+
user.setPassword('pass1234');
3018+
await user.signUp();
3019+
3020+
const res = await request({
3021+
method: 'PUT',
3022+
url: `http://localhost:8378/1/users/${user.id}`,
3023+
headers: {
3024+
'Content-Type': 'application/json',
3025+
'X-Parse-Application-Id': 'test',
3026+
'X-Parse-REST-API-Key': 'rest',
3027+
'X-Parse-Session-Token': user.getSessionToken(),
3028+
},
3029+
body: JSON.stringify({ 'authData.anonymous".id': 'injected' }),
3030+
}).catch(e => e);
3031+
expect(res.status).toBe(400);
3032+
});
3033+
3034+
it('login does not crash when stored authData has unknown provider', async () => {
3035+
const user = new Parse.User();
3036+
user.setUsername('dotuser2');
3037+
user.setPassword('pass1234');
3038+
await user.signUp();
3039+
await Parse.User.logOut();
3040+
3041+
// Inject unknown provider directly in database to simulate corrupted data
3042+
const config = Config.get('test');
3043+
await config.database.update(
3044+
'_User',
3045+
{ objectId: user.id },
3046+
{ authData: { unknown_provider: { id: 'bad' } } }
3047+
);
3048+
3049+
// Login should not crash with 500
3050+
const login = await request({
3051+
method: 'GET',
3052+
url: `http://localhost:8378/1/login?username=dotuser2&password=pass1234`,
3053+
headers: {
3054+
'X-Parse-Application-Id': 'test',
3055+
'X-Parse-REST-API-Key': 'rest',
3056+
},
3057+
}).catch(e => e);
3058+
expect(login.status).toBe(200);
3059+
expect(login.data.sessionToken).toBeDefined();
3060+
});
3061+
});
3062+
30133063
describe('(GHSA-r3xq-68wh-gwvh) Password reset single-use token bypass via concurrent requests', () => {
30143064
let sendPasswordResetEmail;
30153065

src/Auth.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -523,10 +523,15 @@ const checkIfUserHasProvidedConfiguredProvidersForLogin = (
523523
userAuthData = {},
524524
config
525525
) => {
526-
const savedUserProviders = Object.keys(userAuthData).map(provider => ({
527-
name: provider,
528-
adapter: config.authDataManager.getValidatorForProvider(provider).adapter,
529-
}));
526+
const savedUserProviders = Object.keys(userAuthData)
527+
.map(provider => {
528+
const validator = config.authDataManager.getValidatorForProvider(provider);
529+
if (!validator || !validator.adapter) {
530+
return null;
531+
}
532+
return { name: provider, adapter: validator.adapter };
533+
})
534+
.filter(Boolean);
530535

531536
const hasProvidedASoloProvider = savedUserProviders.some(
532537
provider =>

src/Controllers/DatabaseController.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ class DatabaseController {
603603
})
604604
.then(schema => {
605605
Object.keys(update).forEach(fieldName => {
606-
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
606+
if (fieldName.match(/^authData\./)) {
607607
throw new Parse.Error(
608608
Parse.Error.INVALID_KEY_NAME,
609609
`Invalid field name for update: ${fieldName}`

0 commit comments

Comments
 (0)