Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions changelogs/CHANGELOG_alpha.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
## [9.10.6-alpha.6](https://github.com/parse-community/parse-server/compare/9.10.6-alpha.5...9.10.6-alpha.6) (2026-10-21)


### Bug Fixes

* Object create returns field values to a caller without read access ([GHSA-qq3r-ppqq-rh6m](https://github.com/parse-community/parse-server/security/advisories/GHSA-qq3r-ppqq-rh6m)) ([#10813](https://github.com/parse-community/parse-server/issues/10813)) ([d95b14d](https://github.com/parse-community/parse-server/commit/d95b14d08819e859c4385532510d1c8d6e787112))

## [9.10.6-alpha.5](https://github.com/parse-community/parse-server/compare/9.10.6-alpha.4...9.10.6-alpha.5) (2026-10-20)


### Bug Fixes

* Object write with a crafted dot notation key can pollute a shared prototype when the class has a trigger or LiveQuery ([GHSA-95hp-82c6-25q5](https://github.com/parse-community/parse-server/security/advisories/GHSA-95hp-82c6-25q5)) ([#10810](https://github.com/parse-community/parse-server/issues/10810)) ([9e3da78](https://github.com/parse-community/parse-server/commit/9e3da782be8e2225a06d87851ee450da6a0b57a4))

## [9.10.6-alpha.4](https://github.com/parse-community/parse-server/compare/9.10.6-alpha.3...9.10.6-alpha.4) (2026-10-20)


### Bug Fixes

* Session upgrade endpoint creates an additional session from an already revocable session token ([GHSA-2j2x-c587-q33g](https://github.com/parse-community/parse-server/security/advisories/GHSA-2j2x-c587-q33g)) ([#10808](https://github.com/parse-community/parse-server/issues/10808)) ([5bcb56b](https://github.com/parse-community/parse-server/commit/5bcb56b1e77326f33d7b89787ee4cbc3870e6cec))

## [9.10.6-alpha.3](https://github.com/parse-community/parse-server/compare/9.10.6-alpha.2...9.10.6-alpha.3) (2026-10-19)


### Bug Fixes

* Object write with a null dot notation key can corrupt a field or make reads fail on MongoDB ([GHSA-655c-2xf3-77x9](https://github.com/parse-community/parse-server/security/advisories/GHSA-655c-2xf3-77x9)) ([#10804](https://github.com/parse-community/parse-server/issues/10804)) ([b405b63](https://github.com/parse-community/parse-server/commit/b405b637a6adaff21ac27d2b4cf4c164ce54e39a))

## [9.10.6-alpha.2](https://github.com/parse-community/parse-server/compare/9.10.6-alpha.1...9.10.6-alpha.2) (2026-10-19)


### Bug Fixes

* Object update with a dot notation key below a pointer field can terminate the server process ([GHSA-wc3c-6843-q7mv](https://github.com/parse-community/parse-server/security/advisories/GHSA-wc3c-6843-q7mv)) ([#10802](https://github.com/parse-community/parse-server/issues/10802)) ([946b670](https://github.com/parse-community/parse-server/commit/946b670186cc4b3932a5a738f3264404de8a0aa5))

## [9.10.6-alpha.1](https://github.com/parse-community/parse-server/compare/9.10.5...9.10.6-alpha.1) (2026-10-18)


### Bug Fixes

* Object update returns stored field values to a caller without read access ([GHSA-2rcp-jc67-vq74](https://github.com/parse-community/parse-server/security/advisories/GHSA-2rcp-jc67-vq74)) ([#10798](https://github.com/parse-community/parse-server/issues/10798)) ([4f110a9](https://github.com/parse-community/parse-server/commit/4f110a9cf262375b7432dc1a630200272a8232ed))

## [9.10.5-alpha.11](https://github.com/parse-community/parse-server/compare/9.10.5-alpha.10...9.10.5-alpha.11) (2026-10-16)


Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "parse-server",
"version": "9.10.5",
"version": "9.10.6-alpha.6",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
Expand Down
2 changes: 2 additions & 0 deletions spec/ParseACL.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,7 @@ describe('Parse.ACL', () => {
'TestObject',
{},
{
get: { '*': true },
create: {
'*': true,
},
Expand Down Expand Up @@ -959,6 +960,7 @@ describe('Parse.ACL', () => {
'TestObject',
{},
{
get: { '*': true },
create: { '*': true },
update: { '*': true },
addField: { '*': true },
Expand Down
59 changes: 59 additions & 0 deletions spec/ParseRole.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
const RestQuery = require('../lib/RestQuery');
const Auth = require('../lib/Auth').Auth;
const Config = require('../lib/Config');
const request = require('../lib/request');

function testLoadRoles(config, done) {
const rolesNames = ['FooRole', 'BarRole', 'BazRole'];
Expand Down Expand Up @@ -197,6 +198,64 @@ describe('Parse Role testing', () => {
});
});

it('stops role resolution at a cycle through a role with object ID __proto__', async () => {
const auth = new Auth({ config: Config.get('test'), isMaster: true });
let lookups = 0;
spyOn(auth, 'getRolesByIds').and.callFake(async () => {
lookups++;
if (lookups > 10) {
throw new Error('Role resolution did not stop at the cycle.');
}
return [{ objectId: '__proto__', name: 'CyclicRole' }];
});
await expectAsync(
auth._getAllRolesNamesForRoleIds(['__proto__'], ['CyclicRole'])
).toBeResolvedTo(['CyclicRole']);
expect(lookups).toBe(1);
});

it('resolves the roles of a member of a self-cyclic role with object ID __proto__', async () => {
await reconfigureServer({ allowCustomObjectId: true });
const user = await createTestUser();
await request({
method: 'POST',
url: 'http://localhost:8378/1/classes/_Role',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test',
'Content-Type': 'application/json',
},
body: {
objectId: '__proto__',
name: 'CyclicRole',
ACL: {},
users: {
__op: 'AddRelation',
objects: [{ __type: 'Pointer', className: '_User', objectId: user.id }],
},
roles: {
__op: 'AddRelation',
objects: [{ __type: 'Pointer', className: '_Role', objectId: '__proto__' }],
},
},
});
const getRolesForUser = spyOn(Auth.prototype, 'getRolesForUser').and.callThrough();
const getRolesByIds = Auth.prototype.getRolesByIds;
let lookups = 0;
spyOn(Auth.prototype, 'getRolesByIds').and.callFake(async function (...args) {
lookups++;
if (lookups > 10) {
throw new Error('Role resolution did not stop at the cycle.');
}
return getRolesByIds.apply(this, args);
});
await expectAsync(
new Parse.Query('TestObject').find({ sessionToken: user.getSessionToken() })
).toBeResolved();
expect(getRolesForUser).toHaveBeenCalled();
expect(lookups).toBeLessThanOrEqual(1);
});

it('should recursively load roles', done => {
testLoadRoles(Config.get('test'), done);
});
Expand Down
16 changes: 16 additions & 0 deletions spec/Utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -468,4 +468,20 @@ describe('Utils', () => {
});
}
});

describe('safeToJSON', () => {
it('returns the serialized object', () => {
const object = { toJSON: () => ({ a: 1 }) };
expect(Utils.safeToJSON(object)).toEqual({ a: 1 });
});

it('returns undefined when serialization throws', () => {
const object = {
toJSON: () => {
throw new Error('Cannot create a pointer to an unsaved ParseObject');
},
};
expect(Utils.safeToJSON(object)).toBeUndefined();
});
});
});
4 changes: 3 additions & 1 deletion spec/schemas.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2847,7 +2847,9 @@ describe('schemas', () => {
await obj.save({ key: 1 }, { useMasterKey: true });
obj.increment('key', 10);
const objectAgain = await obj.save();
expect(objectAgain.get('key')).toBe(11);
expect(objectAgain.get('key')).toBeUndefined();
const stored = await new Parse.Query('AClass').get(obj.id, { useMasterKey: true });
expect(stored.get('key')).toBe(11);
});

it('regression test for #2246', done => {
Expand Down
Loading
Loading