Compare commits

...
Author SHA1 Message Date
semantic-release-bot 42ae75f001 chore(release): 9.10.1-alpha.2 [skip ci]
## [9.10.1-alpha.2](https://github.com/parse-community/parse-server/compare/9.10.1-alpha.1...9.10.1-alpha.2) (2026-07-14)

### Bug Fixes

* Creating a session can delete another user's session ([#10582](https://github.com/parse-community/parse-server/issues/10582)) ([0df8779](https://github.com/parse-community/parse-server/commit/0df8779c2ecccb055e615d42ee85ae2bce491670))
2026-07-14 16:31:56 +00:00
Manuel 0df8779c2e fix: Creating a session can delete another user's session (#10582) 2026-07-14 18:31:04 +02:00
Manuel d76845f058 test: Add _Installation non-master access control regression tests (#10578) 2026-07-14 15:12:14 +02:00
semantic-release-bot 6e87eb2544 chore(release): 9.10.1-alpha.1 [skip ci]
## [9.10.1-alpha.1](https://github.com/parse-community/parse-server/compare/9.10.0...9.10.1-alpha.1) (2026-07-13)

### Bug Fixes

* Bump follow-redirects from 1.15.11 to 1.16.0 ([#10577](https://github.com/parse-community/parse-server/issues/10577)) ([d577327](https://github.com/parse-community/parse-server/commit/d577327bff073b0436a158e9694feed8eed2f8e7))
2026-07-13 22:58:17 +00:00
Manuel d577327bff fix: Bump follow-redirects from 1.15.11 to 1.16.0 (#10577) 2026-07-14 00:57:28 +02:00
dependabot[bot] 7d4d135d3f refactor: Bump fast-xml-builder from 1.1.4 to 1.2.0 (#10457) 2026-07-13 14:12:01 +02:00
dependabot[bot] a7e792ac19 refactor: Bump @babel/plugin-transform-modules-systemjs from 7.29.0 to 7.29.7 (#10458) 2026-07-13 14:11:58 +02:00
dependabot[bot] a2ea125678 refactor: Bump fast-xml-parser from 5.5.9 to 5.8.0 (#10447) 2026-07-13 14:05:18 +02:00
dependabot[bot] 538b1d7088 refactor: Bump postcss from 8.4.47 to 8.5.14 (#10450) 2026-07-13 14:05:14 +02:00
dependabot[bot] 1b487e4c3f refactor: Bump @protobufjs/utf8 from 1.1.0 to 1.1.1 (#10461) 2026-07-13 14:05:10 +02:00
dependabot[bot] 3f4d0d7c3c refactor: Bump qs from 6.14.2 to 6.15.2 (#10476) 2026-07-13 14:05:06 +02:00
dependabot[bot] a972046b2e refactor: Bump @grpc/grpc-js from 1.14.3 to 1.14.4 (#10503) 2026-07-13 14:05:03 +02:00
Manuel b28dba8812 refactor: Bump typescript-eslint from 8.58.0 to 8.59.1 (#10575) 2026-07-13 13:49:35 +02:00
Manuel 0d260ff1ed refactor: Bump @babel/core from 7.29.0 to 7.29.6 (#10574) 2026-07-13 12:38:10 +02:00
dependabot[bot] 504f919415 refactor: Bump form-data (#10508) 2026-07-13 12:05:07 +02:00
dependabot[bot] 5c25152740 refactor: Bump markdown-it from 14.1.0 to 14.3.0 (#10510) 2026-07-13 05:22:04 +02:00
dependabot[bot] 2275feed3e refactor: Bump undici from 6.24.1 to 6.27.0 (#10524) 2026-07-13 04:31:19 +02:00
semantic-release-bot b5ca12fa5e chore(release): 9.10.0 [skip ci]
# [9.10.0](https://github.com/parse-community/parse-server/compare/9.9.0...9.10.0) (2026-07-13)

### Bug Fixes

* Cloud Code beforeFind trigger context is not isolated from prototype pollution ([#10570](https://github.com/parse-community/parse-server/issues/10570)) ([bea001e](https://github.com/parse-community/parse-server/commit/bea001e7ef9cf991e99fe1eb1e8e25ef71c530f7))
* Cloud Function multipart requests bypass the maxUploadSize limit ([#10498](https://github.com/parse-community/parse-server/issues/10498)) ([f12e1c3](https://github.com/parse-community/parse-server/commit/f12e1c3e31fb211bb7fe106a9a295ac7d0dd4ea7))
* Denial of service via exponential-time processing of deeply nested query operators ([GHSA-cgxm-vr2f-6fj8](https://github.com/parse-community/parse-server/security/advisories/GHSA-cgxm-vr2f-6fj8)) ([#10511](https://github.com/parse-community/parse-server/issues/10511)) ([1103c7a](https://github.com/parse-community/parse-server/commit/1103c7a890e0455ba3dccd4bc5db17efe1789c9a))
* Endpoints `/login` and `/verifyPassword` disclose MFA secrets and protected fields when `_User` get is denied ([GHSA-75v4-m273-5j49](https://github.com/parse-community/parse-server/security/advisories/GHSA-75v4-m273-5j49)) ([#10492](https://github.com/parse-community/parse-server/issues/10492)) ([83e90ed](https://github.com/parse-community/parse-server/commit/83e90edbe4224c81172a20e40fa986662c9394ca))
* GeoPoint distance queries fail with an internal server error on MongoDB 8.3 and later ([#10572](https://github.com/parse-community/parse-server/issues/10572)) ([b706c22](https://github.com/parse-community/parse-server/commit/b706c22cd9e47d91ec00d3f6050d161fd721e2e1))
* GraphQL "Did you mean" validation suggestions disclose schema to unauthenticated callers ([GHSA-8cph-rgr4-g5vj](https://github.com/parse-community/parse-server/security/advisories/GHSA-8cph-rgr4-g5vj)) ([#10467](https://github.com/parse-community/parse-server/issues/10467)) ([155123a](https://github.com/parse-community/parse-server/commit/155123ade9bc88cdf4807cf267ea1196f9274773))
* GraphQL error messages disclose pointer and relation target class names when public introspection is disabled ([GHSA-r2g6-4f6j-f6rf](https://github.com/parse-community/parse-server/security/advisories/GHSA-r2g6-4f6j-f6rf)) ([#10568](https://github.com/parse-community/parse-server/issues/10568)) ([cb9b542](https://github.com/parse-community/parse-server/commit/cb9b54264d4dae4c0f9306924c34d805130b600e))
* GraphQL error messages disclose required input field names when public introspection is disabled ([GHSA-2fgh-8j2g-w354](https://github.com/parse-community/parse-server/security/advisories/GHSA-2fgh-8j2g-w354)) ([#10566](https://github.com/parse-community/parse-server/issues/10566)) ([d96c945](https://github.com/parse-community/parse-server/commit/d96c945b6ddee652ecf27b240292e569367be550)), closes [GHSA-2f#8j2g-w354](https://github.com/GHSA-2f/issues/8j2g-w354) [/github.com/parse-community/parse-server/security/advisories/GHSA-2f#8j2g-w354](https://github.com//github.com/parse-community/parse-server/security/advisories/GHSA-2f/issues/8j2g-w354)
* GraphQL variable-coercion suggestions disclose schema to unauthenticated callers ([GHSA-9g8f-h8f3-hjcm](https://github.com/parse-community/parse-server/security/advisories/GHSA-9g8f-h8f3-hjcm)) ([#10563](https://github.com/parse-community/parse-server/issues/10563)) ([2625489](https://github.com/parse-community/parse-server/commit/2625489a27c07aed1283c5dc9c5e8f8f53a9e44c))
* LiveQuery discloses object data to a subscriber across an ACL read-access change ([GHSA-97pr-9hgg-3p8r](https://github.com/parse-community/parse-server/security/advisories/GHSA-97pr-9hgg-3p8r)) ([#10515](https://github.com/parse-community/parse-server/issues/10515)) ([e9c85df](https://github.com/parse-community/parse-server/commit/e9c85dfe40a866a55ebae3b6ae56285ac0a22e64))
* LiveQuery subscriptions leak when a client reuses a subscribe requestId ([#10499](https://github.com/parse-community/parse-server/issues/10499)) ([3fad4fb](https://github.com/parse-community/parse-server/commit/3fad4fb1c4b41f51dab96532245bb302d2be30e6))
* Middleware route checks do not match routing-equivalent path variants (trailing slash, case) ([#10501](https://github.com/parse-community/parse-server/issues/10501)) ([f861210](https://github.com/parse-community/parse-server/commit/f8612109e3175399b4f814efcc961128de6143a5))
* NumberOrBoolean config option (cluster) value not coerced from env/CLI ([#10531](https://github.com/parse-community/parse-server/issues/10531)) ([459786f](https://github.com/parse-community/parse-server/commit/459786fd41cc835ec650d951258f011737cdb4c9))
* Pre-authentication denial of service via client version header regex backtracking ([GHSA-38m6-82c8-4xfm](https://github.com/parse-community/parse-server/security/advisories/GHSA-38m6-82c8-4xfm)) ([#10463](https://github.com/parse-community/parse-server/issues/10463)) ([56c159e](https://github.com/parse-community/parse-server/commit/56c159ec962d729df09ccaa5cc2537751511e375))
* rateLimit on exact static routes is bypassed by appending a query string ([#10500](https://github.com/parse-community/parse-server/issues/10500)) ([880e8e6](https://github.com/parse-community/parse-server/commit/880e8e6929fd62ed3680b138613bcfdcd572db07))
* Relation `$relatedTo` query bypasses `protectedFields` and owning-object ACL ([GHSA-wmwx-jr2p-4j4r](https://github.com/parse-community/parse-server/security/advisories/GHSA-wmwx-jr2p-4j4r)) ([#10493](https://github.com/parse-community/parse-server/issues/10493)) ([43658f1](https://github.com/parse-community/parse-server/commit/43658f1fd83689b24a4350094f1071ac555ac9b3))
* Server option routeAllowList is bypassable through batch sub-requests ([GHSA-p84r-h6rx-f2xr](https://github.com/parse-community/parse-server/security/advisories/GHSA-p84r-h6rx-f2xr)) ([#10482](https://github.com/parse-community/parse-server/issues/10482)) ([552c6dd](https://github.com/parse-community/parse-server/commit/552c6dd754638c9f546fbceecd2ba0f7225a95d1))
* Stored XSS via malformed Content-Type bypassing file upload extension blocklist ([GHSA-r899-h629-j84r](https://github.com/parse-community/parse-server/security/advisories/GHSA-r899-h629-j84r)) ([#10521](https://github.com/parse-community/parse-server/issues/10521)) ([cce91e5](https://github.com/parse-community/parse-server/commit/cce91e554818492d1b153c46dc3b91fa6e0309bc))
* Stored XSS via non-standard file extension bypassing file upload extension blocklist ([GHSA-v8x7-r927-cc93](https://github.com/parse-community/parse-server/security/advisories/GHSA-v8x7-r927-cc93)) ([#10505](https://github.com/parse-community/parse-server/issues/10505)) ([be12a60](https://github.com/parse-community/parse-server/commit/be12a60d65b6e140481882037fb896b1f951df50))
* Stored XSS via trailing-dot filename bypassing file upload extension blocklist ([GHSA-7wqv-xjf3-x35v](https://github.com/parse-community/parse-server/security/advisories/GHSA-7wqv-xjf3-x35v)) ([#10489](https://github.com/parse-community/parse-server/issues/10489)) ([66484ce](https://github.com/parse-community/parse-server/commit/66484ce8fdd87a5d4c23bf9e40f7ea379b4dce79))

### Features

* Add option to disallow aggregation pipelines for the read-only master key ([#10517](https://github.com/parse-community/parse-server/issues/10517)) ([816078f](https://github.com/parse-community/parse-server/commit/816078fff7f95f333a99c1e2d7166a585742d466))
2026-07-13 01:05:22 +00:00
Manuel fb1d6fc186 build: Release (#10573) 2026-07-13 03:04:18 +02:00
GitHub Actions 534a6b92d0 empty commit to trigger CI 2026-07-13 00:44:39 +00:00
7 changed files with 849 additions and 561 deletions
+14
View File
@@ -1,3 +1,17 @@
## [9.10.1-alpha.2](https://github.com/parse-community/parse-server/compare/9.10.1-alpha.1...9.10.1-alpha.2) (2026-07-14)
### Bug Fixes
* Creating a session can delete another user's session ([#10582](https://github.com/parse-community/parse-server/issues/10582)) ([0df8779](https://github.com/parse-community/parse-server/commit/0df8779c2ecccb055e615d42ee85ae2bce491670))
## [9.10.1-alpha.1](https://github.com/parse-community/parse-server/compare/9.10.0...9.10.1-alpha.1) (2026-07-13)
### Bug Fixes
* Bump follow-redirects from 1.15.11 to 1.16.0 ([#10577](https://github.com/parse-community/parse-server/issues/10577)) ([d577327](https://github.com/parse-community/parse-server/commit/d577327bff073b0436a158e9694feed8eed2f8e7))
# [9.10.0-alpha.8](https://github.com/parse-community/parse-server/compare/9.10.0-alpha.7...9.10.0-alpha.8) (2026-07-13)
+30
View File
@@ -1,3 +1,33 @@
# [9.10.0](https://github.com/parse-community/parse-server/compare/9.9.0...9.10.0) (2026-07-13)
### Bug Fixes
* Cloud Code beforeFind trigger context is not isolated from prototype pollution ([#10570](https://github.com/parse-community/parse-server/issues/10570)) ([bea001e](https://github.com/parse-community/parse-server/commit/bea001e7ef9cf991e99fe1eb1e8e25ef71c530f7))
* Cloud Function multipart requests bypass the maxUploadSize limit ([#10498](https://github.com/parse-community/parse-server/issues/10498)) ([f12e1c3](https://github.com/parse-community/parse-server/commit/f12e1c3e31fb211bb7fe106a9a295ac7d0dd4ea7))
* Denial of service via exponential-time processing of deeply nested query operators ([GHSA-cgxm-vr2f-6fj8](https://github.com/parse-community/parse-server/security/advisories/GHSA-cgxm-vr2f-6fj8)) ([#10511](https://github.com/parse-community/parse-server/issues/10511)) ([1103c7a](https://github.com/parse-community/parse-server/commit/1103c7a890e0455ba3dccd4bc5db17efe1789c9a))
* Endpoints `/login` and `/verifyPassword` disclose MFA secrets and protected fields when `_User` get is denied ([GHSA-75v4-m273-5j49](https://github.com/parse-community/parse-server/security/advisories/GHSA-75v4-m273-5j49)) ([#10492](https://github.com/parse-community/parse-server/issues/10492)) ([83e90ed](https://github.com/parse-community/parse-server/commit/83e90edbe4224c81172a20e40fa986662c9394ca))
* GeoPoint distance queries fail with an internal server error on MongoDB 8.3 and later ([#10572](https://github.com/parse-community/parse-server/issues/10572)) ([b706c22](https://github.com/parse-community/parse-server/commit/b706c22cd9e47d91ec00d3f6050d161fd721e2e1))
* GraphQL "Did you mean" validation suggestions disclose schema to unauthenticated callers ([GHSA-8cph-rgr4-g5vj](https://github.com/parse-community/parse-server/security/advisories/GHSA-8cph-rgr4-g5vj)) ([#10467](https://github.com/parse-community/parse-server/issues/10467)) ([155123a](https://github.com/parse-community/parse-server/commit/155123ade9bc88cdf4807cf267ea1196f9274773))
* GraphQL error messages disclose pointer and relation target class names when public introspection is disabled ([GHSA-r2g6-4f6j-f6rf](https://github.com/parse-community/parse-server/security/advisories/GHSA-r2g6-4f6j-f6rf)) ([#10568](https://github.com/parse-community/parse-server/issues/10568)) ([cb9b542](https://github.com/parse-community/parse-server/commit/cb9b54264d4dae4c0f9306924c34d805130b600e))
* GraphQL error messages disclose required input field names when public introspection is disabled ([GHSA-2fgh-8j2g-w354](https://github.com/parse-community/parse-server/security/advisories/GHSA-2fgh-8j2g-w354)) ([#10566](https://github.com/parse-community/parse-server/issues/10566)) ([d96c945](https://github.com/parse-community/parse-server/commit/d96c945b6ddee652ecf27b240292e569367be550)), closes [GHSA-2f#8j2g-w354](https://github.com/GHSA-2f/issues/8j2g-w354) [/github.com/parse-community/parse-server/security/advisories/GHSA-2f#8j2g-w354](https://github.com//github.com/parse-community/parse-server/security/advisories/GHSA-2f/issues/8j2g-w354)
* GraphQL variable-coercion suggestions disclose schema to unauthenticated callers ([GHSA-9g8f-h8f3-hjcm](https://github.com/parse-community/parse-server/security/advisories/GHSA-9g8f-h8f3-hjcm)) ([#10563](https://github.com/parse-community/parse-server/issues/10563)) ([2625489](https://github.com/parse-community/parse-server/commit/2625489a27c07aed1283c5dc9c5e8f8f53a9e44c))
* LiveQuery discloses object data to a subscriber across an ACL read-access change ([GHSA-97pr-9hgg-3p8r](https://github.com/parse-community/parse-server/security/advisories/GHSA-97pr-9hgg-3p8r)) ([#10515](https://github.com/parse-community/parse-server/issues/10515)) ([e9c85df](https://github.com/parse-community/parse-server/commit/e9c85dfe40a866a55ebae3b6ae56285ac0a22e64))
* LiveQuery subscriptions leak when a client reuses a subscribe requestId ([#10499](https://github.com/parse-community/parse-server/issues/10499)) ([3fad4fb](https://github.com/parse-community/parse-server/commit/3fad4fb1c4b41f51dab96532245bb302d2be30e6))
* Middleware route checks do not match routing-equivalent path variants (trailing slash, case) ([#10501](https://github.com/parse-community/parse-server/issues/10501)) ([f861210](https://github.com/parse-community/parse-server/commit/f8612109e3175399b4f814efcc961128de6143a5))
* NumberOrBoolean config option (cluster) value not coerced from env/CLI ([#10531](https://github.com/parse-community/parse-server/issues/10531)) ([459786f](https://github.com/parse-community/parse-server/commit/459786fd41cc835ec650d951258f011737cdb4c9))
* Pre-authentication denial of service via client version header regex backtracking ([GHSA-38m6-82c8-4xfm](https://github.com/parse-community/parse-server/security/advisories/GHSA-38m6-82c8-4xfm)) ([#10463](https://github.com/parse-community/parse-server/issues/10463)) ([56c159e](https://github.com/parse-community/parse-server/commit/56c159ec962d729df09ccaa5cc2537751511e375))
* rateLimit on exact static routes is bypassed by appending a query string ([#10500](https://github.com/parse-community/parse-server/issues/10500)) ([880e8e6](https://github.com/parse-community/parse-server/commit/880e8e6929fd62ed3680b138613bcfdcd572db07))
* Relation `$relatedTo` query bypasses `protectedFields` and owning-object ACL ([GHSA-wmwx-jr2p-4j4r](https://github.com/parse-community/parse-server/security/advisories/GHSA-wmwx-jr2p-4j4r)) ([#10493](https://github.com/parse-community/parse-server/issues/10493)) ([43658f1](https://github.com/parse-community/parse-server/commit/43658f1fd83689b24a4350094f1071ac555ac9b3))
* Server option routeAllowList is bypassable through batch sub-requests ([GHSA-p84r-h6rx-f2xr](https://github.com/parse-community/parse-server/security/advisories/GHSA-p84r-h6rx-f2xr)) ([#10482](https://github.com/parse-community/parse-server/issues/10482)) ([552c6dd](https://github.com/parse-community/parse-server/commit/552c6dd754638c9f546fbceecd2ba0f7225a95d1))
* Stored XSS via malformed Content-Type bypassing file upload extension blocklist ([GHSA-r899-h629-j84r](https://github.com/parse-community/parse-server/security/advisories/GHSA-r899-h629-j84r)) ([#10521](https://github.com/parse-community/parse-server/issues/10521)) ([cce91e5](https://github.com/parse-community/parse-server/commit/cce91e554818492d1b153c46dc3b91fa6e0309bc))
* Stored XSS via non-standard file extension bypassing file upload extension blocklist ([GHSA-v8x7-r927-cc93](https://github.com/parse-community/parse-server/security/advisories/GHSA-v8x7-r927-cc93)) ([#10505](https://github.com/parse-community/parse-server/issues/10505)) ([be12a60](https://github.com/parse-community/parse-server/commit/be12a60d65b6e140481882037fb896b1f951df50))
* Stored XSS via trailing-dot filename bypassing file upload extension blocklist ([GHSA-7wqv-xjf3-x35v](https://github.com/parse-community/parse-server/security/advisories/GHSA-7wqv-xjf3-x35v)) ([#10489](https://github.com/parse-community/parse-server/issues/10489)) ([66484ce](https://github.com/parse-community/parse-server/commit/66484ce8fdd87a5d4c23bf9e40f7ea379b4dce79))
### Features
* Add option to disallow aggregation pipelines for the read-only master key ([#10517](https://github.com/parse-community/parse-server/issues/10517)) ([816078f](https://github.com/parse-community/parse-server/commit/816078fff7f95f333a99c1e2d7166a585742d466))
# [9.9.0](https://github.com/parse-community/parse-server/compare/9.8.0...9.9.0) (2026-05-01)
+620 -556
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "parse-server",
"version": "9.10.0-alpha.8",
"version": "9.10.1-alpha.2",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
@@ -33,7 +33,7 @@
"cors": "2.8.6",
"express": "5.2.1",
"express-rate-limit": "8.3.1",
"follow-redirects": "1.15.11",
"follow-redirects": "1.16.0",
"graphql": "16.13.2",
"graphql-list-fields": "2.0.4",
"graphql-relay": "0.10.2",
@@ -66,7 +66,7 @@
"@actions/core": "3.0.0",
"@apollo/client": "3.13.8",
"@babel/cli": "7.28.6",
"@babel/core": "7.29.0",
"@babel/core": "7.29.6",
"@babel/eslint-parser": "7.28.6",
"@babel/plugin-proposal-object-rest-spread": "7.20.7",
"@babel/plugin-transform-flow-strip-types": "7.27.1",
@@ -87,7 +87,7 @@
"eslint": "9.27.0",
"eslint-plugin-expect-type": "0.6.2",
"eslint-plugin-unused-imports": "4.4.1",
"form-data": "4.0.5",
"form-data": "4.0.6",
"globals": "17.3.0",
"graphql-tag": "2.12.6",
"jasmine": "6.1.0",
@@ -106,7 +106,7 @@
"prettier": "3.8.1",
"semantic-release": "25.0.3",
"typescript": "5.9.3",
"typescript-eslint": "8.58.0",
"typescript-eslint": "8.59.1",
"yaml": "2.8.3"
},
"scripts": {
+107
View File
@@ -1298,6 +1298,113 @@ describe('Installations', () => {
// TODO: Do we need to support _tombstone disabling of installations?
// TODO: Test deletion, badge increments
describe('access control for non-master clients', () => {
const anonymousHeaders = {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
it('blocks the find operation for an unauthenticated client', async () => {
await rest.create(config, auth.nobody(config), '_Installation', {
installationId: '12345678-abcd-abcd-abcd-123456789abc',
deviceType: 'android',
});
let error;
try {
await request({
method: 'GET',
headers: anonymousHeaders,
url: 'http://localhost:8378/1/installations',
});
fail('find should have been rejected');
return;
} catch (e) {
error = e;
}
expect(error.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
expect(error.data.error).toBe('Permission denied');
});
it('blocks the delete operation for an unauthenticated client', async () => {
const created = await rest.create(config, auth.nobody(config), '_Installation', {
installationId: '12345678-abcd-abcd-abcd-123456789abc',
deviceType: 'android',
});
let error;
try {
await request({
method: 'DELETE',
headers: anonymousHeaders,
url: 'http://localhost:8378/1/installations/' + created.response.objectId,
});
fail('delete should have been rejected');
return;
} catch (e) {
error = e;
}
expect(error.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
expect(error.data.error).toBe('Permission denied');
// The row is still present: the anonymous delete did not take effect.
const remaining = await database.adapter.find('_Installation', installationSchema, {}, {});
expect(remaining.length).toBe(1);
});
it('blocks the find operation for an authenticated non-master user', async () => {
// Even a logged-in user cannot enumerate installations, so another
// device's objectId cannot be discovered through an authenticated session.
const user = await Parse.User.signUp('installation-acl-user', 'pass-12345678');
await rest.create(config, auth.nobody(config), '_Installation', {
installationId: '12345678-abcd-abcd-abcd-123456789abc',
deviceType: 'android',
});
let error;
try {
await request({
method: 'GET',
headers: {
...anonymousHeaders,
'X-Parse-Session-Token': user.getSessionToken(),
},
url: 'http://localhost:8378/1/installations',
});
fail('find should have been rejected');
return;
} catch (e) {
error = e;
}
expect(error.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
expect(error.data.error).toBe('Permission denied');
});
it('blocks the delete operation for an authenticated non-master user', async () => {
const user = await Parse.User.signUp('installation-acl-user', 'pass-12345678');
const created = await rest.create(config, auth.nobody(config), '_Installation', {
installationId: '12345678-abcd-abcd-abcd-123456789abc',
deviceType: 'android',
});
let error;
try {
await request({
method: 'DELETE',
headers: {
...anonymousHeaders,
'X-Parse-Session-Token': user.getSessionToken(),
},
url: 'http://localhost:8378/1/installations/' + created.response.objectId,
});
fail('delete should have been rejected');
return;
} catch (e) {
error = e;
}
expect(error.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN);
expect(error.data.error).toBe('Permission denied');
// The row is still present: the authenticated non-master delete did not take effect.
const remaining = await database.adapter.find('_Installation', installationSchema, {}, {});
expect(remaining.length).toBe(1);
});
});
describe('deviceToken deduplication on new install (no installationId match)', () => {
const { randomUUID } = require('crypto');
const installationSchema = {
+65
View File
@@ -257,6 +257,71 @@ describe('Parse.Session', () => {
expect(newSession.createdWith.authProvider).toBeUndefined();
});
it('does not delete another user\'s session when creating a session via POST /classes/_Session', async () => {
const victim = await Parse.User.signUp('dedupvictim', 'password');
const attacker = await Parse.User.signUp('dedupattacker', 'password');
const victimId = victim.id;
const installationId = 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d';
// Victim logs in on a known installation, creating a session with that installationId.
const victimLogin = await request({
method: 'POST',
url: 'http://localhost:8378/1/login',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
'X-Parse-Installation-Id': installationId,
'Content-Type': 'application/json',
},
body: { username: 'dedupvictim', password: 'password' },
});
const victimSessionToken = victimLogin.data.sessionToken;
// Another user creates a session while naming the victim as `user` and supplying
// the victim's installationId. The session dedup must not delete the victim's session.
await request({
method: 'POST',
url: 'http://localhost:8378/1/classes/_Session',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
'X-Parse-Session-Token': attacker.getSessionToken(),
'Content-Type': 'application/json',
},
body: {
user: { __type: 'Pointer', className: '_User', objectId: victimId },
installationId,
sessionToken: 'r:someothertoken',
},
});
// The victim's session on that installation must still exist...
const sessions = await request({
method: 'GET',
url: 'http://localhost:8378/1/classes/_Session',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key': 'test',
},
});
const victimSession = sessions.data.results.find(
s => s.installationId === installationId && s.user && s.user.objectId === victimId
);
expect(victimSession).toBeDefined();
// ...and the victim's session token must still authenticate.
const meResponse = await request({
method: 'GET',
url: 'http://localhost:8378/1/users/me',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
'X-Parse-Session-Token': victimSessionToken,
},
});
expect(meResponse.data.objectId).toBe(victimId);
});
it('should reject expiresAt when updating a session via PUT', async () => {
const user = await Parse.User.signUp('sessionupdateuser1', 'password');
const sessionToken = user.getSessionToken();
+8
View File
@@ -1151,6 +1151,14 @@ RestWrite.prototype.deleteEmailResetTokenIfNeeded = function () {
};
RestWrite.prototype.destroyDuplicatedSessions = function () {
// Skip if the response is already set, matching the other write-pipeline steps
// (runDatabaseOperation, runAfterSaveTrigger). A non-master POST /classes/_Session
// create has handleSession() set this.response before this runs, so this guard
// prevents the dedup delete from acting on the client-supplied `user`/`installationId`
// rather than on the server-generated session data.
if (this.response) {
return;
}
// Only for _Session, and at creation time
if (this.className != '_Session' || this.query) {
return;