Skip to content

Commit d0fe8e9

Browse files
committed
Reject non-string installationId; add tests
Prevent non-string installationId values from causing a server error by validating the type in RestWrite: remove invalid installationId on create so the existing "must specify ID" check runs, and throw Parse.Error.INVALID_JSON when installationId exists but is not a string (avoids crashes from .toLowerCase()). Add unit tests to cover create/update failures for non-string installationId inputs and ensure master key cannot clear installationId via null.
1 parent 5289c0b commit d0fe8e9

2 files changed

Lines changed: 90 additions & 3 deletions

File tree

spec/ParseInstallation.spec.js

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,53 @@ describe('Installations', () => {
636636
});
637637
});
638638

639-
it('master key cannot clear installationId', done => {
639+
it('create fails when installationId is a non-string object', done => {
640+
const input = {
641+
installationId: { foo: 'bar' },
642+
deviceType: 'ios',
643+
};
644+
rest
645+
.create(config, auth.nobody(config), '_Installation', input)
646+
.then(() => {
647+
fail('Creating the installation should have failed.');
648+
done();
649+
})
650+
.catch(error => {
651+
expect(error.code).toEqual(Parse.Error.INVALID_JSON);
652+
done();
653+
});
654+
});
655+
656+
it('update fails when installationId is a non-string object', done => {
657+
const installId = '12345678-abcd-abcd-abcd-123456789abc';
658+
const input = {
659+
installationId: installId,
660+
deviceType: 'ios',
661+
};
662+
rest
663+
.create(config, auth.nobody(config), '_Installation', input)
664+
.then(() => database.adapter.find('_Installation', installationSchema, {}, {}))
665+
.then(results => {
666+
expect(results.length).toEqual(1);
667+
return rest.update(
668+
config,
669+
auth.nobody(config),
670+
'_Installation',
671+
{ objectId: results[0].objectId },
672+
{ installationId: { foo: 'bar' } }
673+
);
674+
})
675+
.then(() => {
676+
fail('Updating the installation should have failed.');
677+
done();
678+
})
679+
.catch(error => {
680+
expect(error.code).toEqual(Parse.Error.INVALID_JSON);
681+
done();
682+
});
683+
});
684+
685+
it('master key cannot clear installationId via Delete op', done => {
640686
const installId = '12345678-abcd-abcd-abcd-123456789abc';
641687
const input = {
642688
installationId: installId,
@@ -665,6 +711,35 @@ describe('Installations', () => {
665711
});
666712
});
667713

714+
it('master key cannot clear installationId via null', done => {
715+
const installId = '12345678-abcd-abcd-abcd-123456789abc';
716+
const input = {
717+
installationId: installId,
718+
deviceType: 'ios',
719+
};
720+
rest
721+
.create(config, auth.master(config), '_Installation', input)
722+
.then(() => database.adapter.find('_Installation', installationSchema, {}, {}))
723+
.then(results => {
724+
expect(results.length).toEqual(1);
725+
return rest.update(
726+
config,
727+
auth.master(config),
728+
'_Installation',
729+
{ objectId: results[0].objectId },
730+
{ installationId: null }
731+
);
732+
})
733+
.then(() => {
734+
fail('Master key clearing of installationId via null should have been rejected.');
735+
done();
736+
})
737+
.catch(error => {
738+
expect(error.code).toEqual(136);
739+
done();
740+
});
741+
});
742+
668743
it('update fails to change deviceType', done => {
669744
const installId = '12345678-abcd-abcd-abcd-123456789abc';
670745
let input = {

src/RestWrite.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,11 +1309,23 @@ RestWrite.prototype.handleInstallation = function () {
13091309
if (this.query) {
13101310
throw new Parse.Error(136, 'installationId may not be changed in this operation');
13111311
}
1312-
// Create path: drop the operator/null so the "must specify ID"
1313-
// guard below fires with the correct 135 error.
1312+
// Create path: drop the invalid value so the existing "must specify
1313+
// ID" guard below can run. If no alternative ID (deviceToken,
1314+
// auth.installationId) is supplied the create is rejected with
1315+
// error 135; otherwise the create proceeds with the remaining ID.
13141316
delete this.data.installationId;
13151317
}
13161318

1319+
// Any remaining non-string installationId (object, array, number, etc.)
1320+
// would crash on `.toLowerCase()` below; reject it as a Parse error
1321+
// rather than letting it surface as a 500.
1322+
if (
1323+
this.data.installationId !== undefined &&
1324+
typeof this.data.installationId !== 'string'
1325+
) {
1326+
throw new Parse.Error(Parse.Error.INVALID_JSON, 'installationId must be a string');
1327+
}
1328+
13171329
if (
13181330
!this.query &&
13191331
!this.data.deviceToken &&

0 commit comments

Comments
 (0)