Compare commits

...
81 Commits
Author SHA1 Message Date
semantic-release-bot 586fdad67b chore(release): 6.5.11 [skip ci]
## [6.5.11](https://github.com/parse-community/parse-server/compare/6.5.10...6.5.11) (2024-10-23)

### Bug Fixes

* Security upgrade to path-to-regexp 6.2.2 ([#9365](https://github.com/parse-community/parse-server/issues/9365)) ([dfbafa7](https://github.com/parse-community/parse-server/commit/dfbafa702507da0d20667eabe8fdc835ca30c8e8))
2024-10-23 17:02:18 +00:00
Antoine Cormouls dfbafa7025 fix: Security upgrade to path-to-regexp 6.2.2 (#9365) 2024-10-23 19:00:44 +02:00
semantic-release-bot 1c2b448284 chore(release): 6.5.10 [skip ci]
## [6.5.10](https://github.com/parse-community/parse-server/compare/6.5.9...6.5.10) (2024-10-12)

### Bug Fixes

* Security upgrade express from 4.21.0 to 4.21.1 ([#9339](https://github.com/parse-community/parse-server/issues/9339)) ([97627ee](https://github.com/parse-community/parse-server/commit/97627ee450f5abe04c92609f4ae2e63c49060d6a))
2024-10-12 07:59:53 +00:00
Parse Platform 97627ee450 fix: Security upgrade express from 4.21.0 to 4.21.1 (#9339) 2024-10-12 09:57:36 +02:00
semantic-release-bot 144781a855 chore(release): 6.5.9 [skip ci]
## [6.5.9](https://github.com/parse-community/parse-server/compare/6.5.8...6.5.9) (2024-10-03)

### Bug Fixes

* Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) ([#9318](https://github.com/parse-community/parse-server/issues/9318)) ([1bfbccf](https://github.com/parse-community/parse-server/commit/1bfbccf9ee7ea77533b2b2aa7c4c69f3bd35e66f))
2024-10-03 19:32:32 +00:00
Manuel 1bfbccf9ee fix: Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) (#9318) 2024-10-03 21:28:41 +02:00
semantic-release-bot 12ce46db81 chore(release): 6.5.8 [skip ci]
## [6.5.8](https://github.com/parse-community/parse-server/compare/6.5.7...6.5.8) (2024-09-12)

### Bug Fixes

* Various vulnerabilities related to cross-site scripting ([#9310](https://github.com/parse-community/parse-server/issues/9310)) ([d5290d4](https://github.com/parse-community/parse-server/commit/d5290d46e5ff9237970ae1ac2d2df4051cbf53e5))
2024-09-12 12:56:07 +00:00
Parse Platform d5290d46e5 fix: Various vulnerabilities related to cross-site scripting (#9310) 2024-09-12 14:42:50 +02:00
semantic-release-bot 52729fd15c chore(release): 6.5.7 [skip ci]
## [6.5.7](https://github.com/parse-community/parse-server/compare/6.5.6...6.5.7) (2024-06-30)

### Bug Fixes

* SQL injection when using Parse Server with PostgreSQL; fixes security vulnerability [GHSA-c2hr-cqg6-8j6r](https://github.com/parse-community/parse-server/security/advisories/GHSA-c2hr-cqg6-8j6r) ([#9168](https://github.com/parse-community/parse-server/issues/9168)) ([f332d54](https://github.com/parse-community/parse-server/commit/f332d54577608c5ad927255e06d8c694e2e0ff5b))
2024-06-30 01:51:47 +00:00
Manuel f332d54577 fix: SQL injection when using Parse Server with PostgreSQL; fixes security vulnerability [GHSA-c2hr-cqg6-8j6r](https://github.com/parse-community/parse-server/security/advisories/GHSA-c2hr-cqg6-8j6r) (#9168) 2024-06-30 03:50:41 +02:00
Parse Platform 3012ff72bb refactor: Security upgrade ws from 8.16.0 to 8.17.1 (#9157) 2024-06-22 21:48:59 +02:00
Manuel 0d5e01c26b test: Disable OAuth 1 tests with Twitter API (#9162) 2024-06-22 15:16:13 +02:00
semantic-release-bot 09ead54626 chore(release): 6.5.6 [skip ci]
## [6.5.6](https://github.com/parse-community/parse-server/compare/6.5.5...6.5.6) (2024-05-16)

### Bug Fixes

* Facebook Limited Login not workind due to incorrect domain in JWT validation ([#9120](https://github.com/parse-community/parse-server/issues/9120)) ([0e92f76](https://github.com/parse-community/parse-server/commit/0e92f765e43d1e39285c5958d60cfd7fb76a3c90))
2024-05-16 07:12:08 +00:00
Chris 0e92f765e4 fix: Facebook Limited Login not workind due to incorrect domain in JWT validation (#9120) 2024-05-16 09:11:02 +02:00
Parse Platform acea93c8a6 refactor: Upgrade graphql-relay from 0.10.0 to 0.10.1 (#9096) 2024-04-19 15:46:43 +02:00
Parse Platform 490898e11d refactor: Upgrade @babel/eslint-parser from 7.23.3 to 7.24.1 (#9091) 2024-04-18 00:24:11 +02:00
Parse Platform 63db281976 refactor: Upgrade winston from 3.11.0 to 3.12.0 (#9054) 2024-03-26 06:32:51 +01:00
Parse Platform 72ba762390 refactor: Upgrade express from 4.18.2 to 4.18.3 (#9042) 2024-03-22 17:15:36 +01:00
Parse Platform eba0da47ba refactor: Upgrade @babel/eslint-parser from 7.21.8 to 7.23.3 (#8868) 2024-03-21 16:09:16 +01:00
Manuel dc53521243 ci: Fix auto-release (#9035) 2024-03-19 22:21:07 +01:00
semantic-release-bot 9dc0235b5d chore(release): 6.5.5 [skip ci]
## [6.5.5](https://github.com/parse-community/parse-server/compare/6.5.4...6.5.5) (2024-03-19)

### Bug Fixes

* Server crashes on invalid Cloud Function or Cloud Job name; fixes security vulnerability [GHSA-6hh7-46r2-vf29](https://github.com/parse-community/parse-server/security/advisories/GHSA-6hh7-46r2-vf29) ([#9023](https://github.com/parse-community/parse-server/issues/9023)) ([5ae6d6a](https://github.com/parse-community/parse-server/commit/5ae6d6a36d75c4511029f0ba5673ae4b2999179b))
2024-03-19 16:43:32 +00:00
Manuel 5ae6d6a36d fix: Server crashes on invalid Cloud Function or Cloud Job name; fixes security vulnerability [GHSA-6hh7-46r2-vf29](https://github.com/parse-community/parse-server/security/advisories/GHSA-6hh7-46r2-vf29) (#9023) 2024-03-19 17:42:24 +01:00
semantic-release-bot 3773203a37 chore(release): 6.5.4 [skip ci]
## [6.5.4](https://github.com/parse-community/parse-server/compare/6.5.3...6.5.4) (2024-03-16)

### Bug Fixes

* Server crashes when receiving an array of `Parse.Pointer` in the request body ([#9012](https://github.com/parse-community/parse-server/issues/9012)) ([8ff444d](https://github.com/parse-community/parse-server/commit/8ff444d42ef51bfe6808c4c3a5fe666bfe302ebe))
2024-03-16 16:20:47 +00:00
Jayson Ng 8ff444d42e fix: Server crashes when receiving an array of Parse.Pointer in the request body (#9012) 2024-03-16 17:19:54 +01:00
semantic-release-bot 9cb44c08cc chore(release): 6.5.3 [skip ci]
## [6.5.3](https://github.com/parse-community/parse-server/compare/6.5.2...6.5.3) (2024-03-16)

### Bug Fixes

* Security upgrade follow-redirects from 1.15.5 to 1.15.6 ([#9019](https://github.com/parse-community/parse-server/issues/9019)) ([422958e](https://github.com/parse-community/parse-server/commit/422958e246da6f13011776c8dde028a00fb821cb))
2024-03-16 13:41:15 +00:00
Manuel 09b6a95264 ci: Fix auto-release (#9021) 2024-03-16 14:40:12 +01:00
Parse Platform 422958e246 fix: Security upgrade follow-redirects from 1.15.5 to 1.15.6 (#9019) 2024-03-16 10:10:50 +01:00
Manuel b8535b3db9 ci: Fix LTS releases are published as pre-releases (#8989) 2024-03-05 20:00:09 +01:00
Corey 9282bc595c ci: Fix failing Docker release by removing arm/v6 and arm/v7 support (#8977) 2024-03-05 08:56:40 +01:00
Parse Platform 47184f0734 refactor: Upgrade graphql-list-fields from 2.0.2 to 2.0.4 (#8973) 2024-03-03 00:46:54 +01:00
Parse Platform d53c1f3668 refactor: Upgrade winston-daily-rotate-file from 4.7.1 to 5.0.0 (#8974) 2024-03-03 00:03:05 +01:00
semantic-release-bot d3ec2e2be7 chore(release): 6.5.2 [skip ci]
## [6.5.2](https://github.com/parse-community/parse-server/compare/6.5.1...6.5.2) (2024-03-02)

### Bug Fixes

* Security upgrade @parse/push-adapter from 5.1.0 to 5.1.1 ([#8975](https://github.com/parse-community/parse-server/issues/8975)) ([0fa0aab](https://github.com/parse-community/parse-server/commit/0fa0aabefe6bc9d356ee70be78dafc5fa22d4e17))
2024-03-02 21:48:11 +00:00
Parse Platform 0fa0aabefe fix: Security upgrade @parse/push-adapter from 5.1.0 to 5.1.1 (#8975) 2024-03-02 22:47:16 +01:00
semantic-release-bot 46761d3ae2 chore(release): 6.5.1 [skip ci]
## [6.5.1](https://github.com/parse-community/parse-server/compare/6.5.0...6.5.1) (2024-03-02)

### Bug Fixes

* Security upgrade @parse/push-adapter from 5.0.2 to 5.1.0 ([#8972](https://github.com/parse-community/parse-server/issues/8972)) ([bba24dd](https://github.com/parse-community/parse-server/commit/bba24dd8279ebb8e4084a5f00fbe3ab9fe6c01b4))
2024-03-02 20:43:02 +00:00
Parse Platform bba24dd827 fix: Security upgrade @parse/push-adapter from 5.0.2 to 5.1.0 (#8972) 2024-03-02 21:42:04 +01:00
Manuel 30258be121 docs: Remove incorrect change log entries (#8963) 2024-03-01 17:14:29 +01:00
semantic-release-bot 5f9a27fb8e chore(release): 6.5.0 [skip ci]
# [6.5.0](https://github.com/parse-community/parse-server/compare/6.4.0...6.5.0) (2024-03-01)

### Bug Fixes

* Context not passed to Cloud Code Trigger `beforeFind` when using `Parse.Query.include` ([#8765](https://github.com/parse-community/parse-server/issues/8765)) ([7d32d89](https://github.com/parse-community/parse-server/commit/7d32d8934f3ae7af7a7d8b9cc6a829c7d73973d3))
* Improve PostgreSQL injection detection; fixes security vulnerability [GHSA-6927-3vr9-fxf2](https://github.com/parse-community/parse-server/security/advisories/GHSA-6927-3vr9-fxf2) which affects Parse Server deployments using a Postgres database ([#8960](https://github.com/parse-community/parse-server/issues/8960)) ([a6e6549](https://github.com/parse-community/parse-server/commit/a6e654943536932904a69b51e513507fcf90a504))
* Parse Server option `fileUpload.fileExtensions` fails to determine file extension if filename contains multiple dots ([#8754](https://github.com/parse-community/parse-server/issues/8754)) ([3d6d50e](https://github.com/parse-community/parse-server/commit/3d6d50e0afff18b95fb906914e2cebd3839b517a))
* Security bump @babel/traverse from 7.20.5 to 7.23.2 ([#8777](https://github.com/parse-community/parse-server/issues/8777)) ([2d6b3d1](https://github.com/parse-community/parse-server/commit/2d6b3d18499179e99be116f25c0850d3f449509c))
* Security upgrade graphql from 16.6.0 to 16.8.1 ([#8758](https://github.com/parse-community/parse-server/issues/8758)) ([71dfd8a](https://github.com/parse-community/parse-server/commit/71dfd8a7ece8c0dd1a66d03bb9420cfd39f4f9b1))

### Features

* Add `$setOnInsert` operator to `Parse.Server.database.update` ([#8791](https://github.com/parse-community/parse-server/issues/8791)) ([f630a45](https://github.com/parse-community/parse-server/commit/f630a45aa5e87bc73a81fded061400c199b71a29))
* Add compatibility for MongoDB Atlas Serverless and AWS Amazon DocumentDB with collation options `enableCollationCaseComparison`, `transformEmailToLowercase`, `transformUsernameToLowercase` ([#8805](https://github.com/parse-community/parse-server/issues/8805)) ([09fbeeb](https://github.com/parse-community/parse-server/commit/09fbeebba8870e7cf371fb84371a254c7b368620))
* Add context to Cloud Code Triggers `beforeLogin` and `afterLogin` ([#8724](https://github.com/parse-community/parse-server/issues/8724)) ([a9c34ef](https://github.com/parse-community/parse-server/commit/a9c34ef1e2c78a42fb8b5fa8d569b7677c74919d))
* Allow setting `createdAt` and `updatedAt` during `Parse.Object` creation with maintenance key ([#8696](https://github.com/parse-community/parse-server/issues/8696)) ([77bbfb3](https://github.com/parse-community/parse-server/commit/77bbfb3f186f5651c33ba152f04cff95128eaf2d))
* Upgrade Parse Server Push Adapter to 5.0.2 ([#8813](https://github.com/parse-community/parse-server/issues/8813)) ([6ef1986](https://github.com/parse-community/parse-server/commit/6ef1986c03a1d84b7e11c05851e5bf9688d88740))

### Performance Improvements

* Improved IP validation performance for `masterKeyIPs`, `maintenanceKeyIPs` ([#8510](https://github.com/parse-community/parse-server/issues/8510)) ([b87daba](https://github.com/parse-community/parse-server/commit/b87daba0671a1b0b7b8d63bc671d665c91a04522))
2024-03-01 16:04:36 +00:00
Manuel 297faaece4 ci: Fix incorrect release branch config (#8962) 2024-03-01 17:03:43 +01:00
Manuel a6e6549435 fix: Improve PostgreSQL injection detection; fixes security vulnerability [GHSA-6927-3vr9-fxf2](https://github.com/parse-community/parse-server/security/advisories/GHSA-6927-3vr9-fxf2) which affects Parse Server deployments using a Postgres database (#8960) 2024-03-01 16:51:02 +01:00
Parse Platform 244e3431cf refactor: Upgrade redis from 4.6.12 to 4.6.13 (#8955) 2024-02-27 14:42:47 +01:00
Parse Platform 33c648dc81 refactor: Upgrade uuid from 9.0.0 to 9.0.1 (#8943) 2024-02-26 01:22:37 +01:00
Parse Platform 4524c35d51 refactor: Upgrade follow-redirects from 1.15.2 to 1.15.5 (#8931) 2024-02-24 17:30:05 +01:00
Parse Platform 70e0cb3744 refactor: Upgrade jwks-rsa from 2.1.5 to 3.1.0 (#8932) 2024-02-24 12:22:00 +01:00
Parse Platform 519dee9b89 refactor: Upgrade winston from 3.8.2 to 3.11.0 (#8933) 2024-02-23 18:18:24 +01:00
Parse Platform 897acb76a5 refactor: Upgrade semver from 7.5.2 to 7.5.4 (#8934) 2024-02-22 13:05:39 +01:00
Parse Platform e5de9daa18 refactor: Upgrade @parse/fs-files-adapter from 1.2.2 to 2.0.1 (#8930) 2024-02-20 18:17:11 +01:00
Parse Platform 223fde0f31 refactor: Upgrade pg-promise from 11.5.0 to 11.5.4 (#8924) 2024-02-17 00:41:11 +01:00
Parse Platform d0a5af33ca refactor: Upgrade otpauth from 9.1.2 to 9.2.2 (#8923) 2024-02-17 00:10:26 +01:00
Parse Platform 8fe0ae7a2c refactor: Upgrade ws from 8.13.0 to 8.16.0 (#8921) 2024-02-15 23:54:05 +01:00
Parse Platform 5179501885 refactor: Upgrade redis from 4.6.6 to 4.6.12 (#8922) 2024-02-15 19:35:22 +01:00
Parse Platform 933e8226df refactor: Upgrade lru-cache from 9.1.1 to 10.1.0 (#8870) 2024-01-08 12:15:33 +01:00
semantic-release-bot d3087ed69f chore(release): 6.5.0-alpha.2 [skip ci]
# [6.5.0-alpha.2](https://github.com/parse-community/parse-server/compare/6.5.0-alpha.1...6.5.0-alpha.2) (2023-11-19)

### Performance Improvements

* Improved IP validation performance for `masterKeyIPs`, `maintenanceKeyIPs` ([#8510](https://github.com/parse-community/parse-server/issues/8510)) ([b87daba](https://github.com/parse-community/parse-server/commit/b87daba0671a1b0b7b8d63bc671d665c91a04522))
2023-11-19 22:14:13 +00:00
Antoine Cormouls b87daba067 perf: Improved IP validation performance for masterKeyIPs, maintenanceKeyIPs (#8510) 2023-11-19 23:13:16 +01:00
Manuel 759731926f docs: Improve docs for Parse Server options masterKeyIps, maintenanceKeyIps (#8814) 2023-11-18 15:41:16 +01:00
semantic-release-bot 0762ba6b79 chore(release): 6.5.0-alpha.1 [skip ci]
# [6.5.0-alpha.1](https://github.com/parse-community/parse-server/compare/6.4.0...6.5.0-alpha.1) (2023-11-18)

### Bug Fixes

* Context not passed to Cloud Code Trigger `beforeFind` when using `Parse.Query.include` ([#8765](https://github.com/parse-community/parse-server/issues/8765)) ([7d32d89](https://github.com/parse-community/parse-server/commit/7d32d8934f3ae7af7a7d8b9cc6a829c7d73973d3))
* Parse Server option `fileUpload.fileExtensions` fails to determine file extension if filename contains multiple dots ([#8754](https://github.com/parse-community/parse-server/issues/8754)) ([3d6d50e](https://github.com/parse-community/parse-server/commit/3d6d50e0afff18b95fb906914e2cebd3839b517a))
* Security bump @babel/traverse from 7.20.5 to 7.23.2 ([#8777](https://github.com/parse-community/parse-server/issues/8777)) ([2d6b3d1](https://github.com/parse-community/parse-server/commit/2d6b3d18499179e99be116f25c0850d3f449509c))
* Security upgrade graphql from 16.6.0 to 16.8.1 ([#8758](https://github.com/parse-community/parse-server/issues/8758)) ([71dfd8a](https://github.com/parse-community/parse-server/commit/71dfd8a7ece8c0dd1a66d03bb9420cfd39f4f9b1))

### Features

* Add `$setOnInsert` operator to `Parse.Server.database.update` ([#8791](https://github.com/parse-community/parse-server/issues/8791)) ([f630a45](https://github.com/parse-community/parse-server/commit/f630a45aa5e87bc73a81fded061400c199b71a29))
* Add compatibility for MongoDB Atlas Serverless and AWS Amazon DocumentDB with collation options `enableCollationCaseComparison`, `transformEmailToLowercase`, `transformUsernameToLowercase` ([#8805](https://github.com/parse-community/parse-server/issues/8805)) ([09fbeeb](https://github.com/parse-community/parse-server/commit/09fbeebba8870e7cf371fb84371a254c7b368620))
* Add context to Cloud Code Triggers `beforeLogin` and `afterLogin` ([#8724](https://github.com/parse-community/parse-server/issues/8724)) ([a9c34ef](https://github.com/parse-community/parse-server/commit/a9c34ef1e2c78a42fb8b5fa8d569b7677c74919d))
* Allow setting `createdAt` and `updatedAt` during `Parse.Object` creation with maintenance key ([#8696](https://github.com/parse-community/parse-server/issues/8696)) ([77bbfb3](https://github.com/parse-community/parse-server/commit/77bbfb3f186f5651c33ba152f04cff95128eaf2d))
* Upgrade Parse Server Push Adapter to 5.0.2 ([#8813](https://github.com/parse-community/parse-server/issues/8813)) ([6ef1986](https://github.com/parse-community/parse-server/commit/6ef1986c03a1d84b7e11c05851e5bf9688d88740))
2023-11-18 13:54:18 +00:00
Manuel 6ef1986c03 feat: Upgrade Parse Server Push Adapter to 5.0.2 (#8813) 2023-11-18 14:53:21 +01:00
semantic-release-bot 988ecdac54 chore(release): 6.5.0-beta.1 [skip ci]
# [6.5.0-beta.1](https://github.com/parse-community/parse-server/compare/6.4.0...6.5.0-beta.1) (2023-11-16)

### Bug Fixes

* Context not passed to Cloud Code Trigger `beforeFind` when using `Parse.Query.include` ([#8765](https://github.com/parse-community/parse-server/issues/8765)) ([7d32d89](https://github.com/parse-community/parse-server/commit/7d32d8934f3ae7af7a7d8b9cc6a829c7d73973d3))
* Parse Server option `fileUpload.fileExtensions` fails to determine file extension if filename contains multiple dots ([#8754](https://github.com/parse-community/parse-server/issues/8754)) ([3d6d50e](https://github.com/parse-community/parse-server/commit/3d6d50e0afff18b95fb906914e2cebd3839b517a))
* Security bump @babel/traverse from 7.20.5 to 7.23.2 ([#8777](https://github.com/parse-community/parse-server/issues/8777)) ([2d6b3d1](https://github.com/parse-community/parse-server/commit/2d6b3d18499179e99be116f25c0850d3f449509c))
* Security upgrade graphql from 16.6.0 to 16.8.1 ([#8758](https://github.com/parse-community/parse-server/issues/8758)) ([71dfd8a](https://github.com/parse-community/parse-server/commit/71dfd8a7ece8c0dd1a66d03bb9420cfd39f4f9b1))

### Features

* Add `$setOnInsert` operator to `Parse.Server.database.update` ([#8791](https://github.com/parse-community/parse-server/issues/8791)) ([f630a45](https://github.com/parse-community/parse-server/commit/f630a45aa5e87bc73a81fded061400c199b71a29))
* Add compatibility for MongoDB Atlas Serverless and AWS Amazon DocumentDB with collation options `enableCollationCaseComparison`, `transformEmailToLowercase`, `transformUsernameToLowercase` ([#8805](https://github.com/parse-community/parse-server/issues/8805)) ([09fbeeb](https://github.com/parse-community/parse-server/commit/09fbeebba8870e7cf371fb84371a254c7b368620))
* Add context to Cloud Code Triggers `beforeLogin` and `afterLogin` ([#8724](https://github.com/parse-community/parse-server/issues/8724)) ([a9c34ef](https://github.com/parse-community/parse-server/commit/a9c34ef1e2c78a42fb8b5fa8d569b7677c74919d))
* Allow setting `createdAt` and `updatedAt` during `Parse.Object` creation with maintenance key ([#8696](https://github.com/parse-community/parse-server/issues/8696)) ([77bbfb3](https://github.com/parse-community/parse-server/commit/77bbfb3f186f5651c33ba152f04cff95128eaf2d))
2023-11-16 15:57:46 +00:00
Manuel 87059ae1d6 build: Release (#8810) 2023-11-16 16:56:44 +01:00
Manuel Trezza 42e2e12956 release 2023-11-16 16:43:24 +01:00
Manuel Trezza d58032d39d Merge branch 'beta' into build-release
* beta:
  chore(release): 6.4.0 [skip ci]
  release
  refactor: Server crash when uploading file without extension; fixes security vulnerability [GHSA-792q-q67h-w579](https://github.com/parse-community/parse-server/security/advisories/GHSA-792q-q67h-w579) (#8780)
  chore(release): 6.3.1 [skip ci]
  fix: Server crash when uploading file without extension; fixes security vulnerability [GHSA-792q-q67h-w579](https://github.com/parse-community/parse-server/security/advisories/GHSA-792q-q67h-w579) (#8781)
2023-11-16 16:41:43 +01:00
semantic-release-bot 5b1bb598e4 chore(release): 6.4.0 [skip ci]
# [6.4.0](https://github.com/parse-community/parse-server/compare/6.3.1...6.4.0) (2023-11-16)

### Bug Fixes

* Parse Server option `fileUpload.fileExtensions` does not work with an array of extensions ([#8688](https://github.com/parse-community/parse-server/issues/8688)) ([6a4a00c](https://github.com/parse-community/parse-server/commit/6a4a00ca7af1163ea74b047b85cd6817366b824b))
* Redis 4 does not reconnect after unhandled error ([#8706](https://github.com/parse-community/parse-server/issues/8706)) ([2b3d4e5](https://github.com/parse-community/parse-server/commit/2b3d4e5d3c85cd142f85af68dec51a8523548d49))
* Remove config logging when launching Parse Server via CLI ([#8710](https://github.com/parse-community/parse-server/issues/8710)) ([ae68f0c](https://github.com/parse-community/parse-server/commit/ae68f0c31b741eeb83379c905c7ddfaa124436ec))
* Server does not start via CLI when `auth` option is set ([#8666](https://github.com/parse-community/parse-server/issues/8666)) ([4e2000b](https://github.com/parse-community/parse-server/commit/4e2000bc563324389584ace3c090a5c1a7796a64))

### Features

* Add conditional email verification via dynamic Parse Server options `verifyUserEmails`, `sendUserEmailVerification` that now accept functions ([#8425](https://github.com/parse-community/parse-server/issues/8425)) ([44acd6d](https://github.com/parse-community/parse-server/commit/44acd6d9ed157ad4842200c9d01f9c77a05fec3a))
* Add property `Parse.Server.version` to determine current version of Parse Server in Cloud Code ([#8670](https://github.com/parse-community/parse-server/issues/8670)) ([a9d376b](https://github.com/parse-community/parse-server/commit/a9d376b61f5b07806eafbda91c4e36c322f09298))
* Add TOTP authentication adapter ([#8457](https://github.com/parse-community/parse-server/issues/8457)) ([cc079a4](https://github.com/parse-community/parse-server/commit/cc079a40f6849a0e9bc6fdc811e8649ecb67b589))

### Performance Improvements

* Improve performance of recursive pointer iterations ([#8741](https://github.com/parse-community/parse-server/issues/8741)) ([45a3ed0](https://github.com/parse-community/parse-server/commit/45a3ed0fcf2c0170607505a1550fb15896e705fd))
2023-11-16 15:26:18 +00:00
Manuel 2612a387ce build: Release (#8809) 2023-11-16 16:25:13 +01:00
Manuel Trezza c6355cda73 release 2023-11-16 15:58:24 +01:00
Manuel Trezza 90aac622b0 Merge branch 'release' into build-release
* release:
  chore(release): 6.3.1 [skip ci]
  fix: Server crash when uploading file without extension; fixes security vulnerability [GHSA-792q-q67h-w579](https://github.com/parse-community/parse-server/security/advisories/GHSA-792q-q67h-w579) (#8781)
2023-11-16 15:57:43 +01:00
semantic-release-bot 4b3ce20300 chore(release): 6.4.0-alpha.8 [skip ci]
# [6.4.0-alpha.8](https://github.com/parse-community/parse-server/compare/6.4.0-alpha.7...6.4.0-alpha.8) (2023-11-13)

### Features

* Add compatibility for MongoDB Atlas Serverless and AWS Amazon DocumentDB with collation options `enableCollationCaseComparison`, `transformEmailToLowercase`, `transformUsernameToLowercase` ([#8805](https://github.com/parse-community/parse-server/issues/8805)) ([09fbeeb](https://github.com/parse-community/parse-server/commit/09fbeebba8870e7cf371fb84371a254c7b368620))
2023-11-13 22:33:43 +00:00
Mattia Faraci 09fbeebba8 feat: Add compatibility for MongoDB Atlas Serverless and AWS Amazon DocumentDB with collation options enableCollationCaseComparison, transformEmailToLowercase, transformUsernameToLowercase (#8805) 2023-11-13 23:32:47 +01:00
Manuel 80b987d00d test: Improve test for $setOnInsert (#8793) 2023-10-25 20:32:58 +02:00
semantic-release-bot 9e0094980f chore(release): 6.4.0-alpha.7 [skip ci]
# [6.4.0-alpha.7](https://github.com/parse-community/parse-server/compare/6.4.0-alpha.6...6.4.0-alpha.7) (2023-10-25)

### Features

* Add `$setOnInsert` operator to `Parse.Server.database.update` ([#8791](https://github.com/parse-community/parse-server/issues/8791)) ([f630a45](https://github.com/parse-community/parse-server/commit/f630a45aa5e87bc73a81fded061400c199b71a29))
2023-10-25 17:14:37 +00:00
Manuel f630a45aa5 feat: Add $setOnInsert operator to Parse.Server.database.update (#8791) 2023-10-25 19:13:27 +02:00
Manuel ea57a7706d refactor: Server crash when uploading file without extension; fixes security vulnerability [GHSA-792q-q67h-w579](https://github.com/parse-community/parse-server/security/advisories/GHSA-792q-q67h-w579) (#8780) 2023-10-21 01:03:31 +02:00
Manuel fe02d3e8aa refactor: Server crash when uploading file without extension; fixes security vulnerability [GHSA-792q-q67h-w579](https://github.com/parse-community/parse-server/security/advisories/GHSA-792q-q67h-w579) (#8779) 2023-10-21 01:03:02 +02:00
semantic-release-bot b0c012e835 chore(release): 6.3.1 [skip ci]
## [6.3.1](https://github.com/parse-community/parse-server/compare/6.3.0...6.3.1) (2023-10-20)

### Bug Fixes

* Server crash when uploading file without extension; fixes security vulnerability [GHSA-792q-q67h-w579](https://github.com/parse-community/parse-server/security/advisories/GHSA-792q-q67h-w579) ([#8781](https://github.com/parse-community/parse-server/issues/8781)) ([fd86278](https://github.com/parse-community/parse-server/commit/fd86278919556d3682e7e2c856dfccd5beffbfc0))
2023-10-20 23:02:48 +00:00
Manuel fd86278919 fix: Server crash when uploading file without extension; fixes security vulnerability [GHSA-792q-q67h-w579](https://github.com/parse-community/parse-server/security/advisories/GHSA-792q-q67h-w579) (#8781) 2023-10-21 01:01:35 +02:00
semantic-release-bot 5dd3aa0d48 chore(release): 6.4.0-alpha.6 [skip ci]
# [6.4.0-alpha.6](https://github.com/parse-community/parse-server/compare/6.4.0-alpha.5...6.4.0-alpha.6) (2023-10-18)

### Bug Fixes

* Security bump @babel/traverse from 7.20.5 to 7.23.2 ([#8777](https://github.com/parse-community/parse-server/issues/8777)) ([2d6b3d1](https://github.com/parse-community/parse-server/commit/2d6b3d18499179e99be116f25c0850d3f449509c))
2023-10-18 21:46:43 +00:00
dependabot[bot] 2d6b3d1849 fix: Security bump @babel/traverse from 7.20.5 to 7.23.2 (#8777) 2023-10-18 23:45:24 +02:00
Doug Drechsel 93af48a8b4 ci: Add ability to exclude tests via ID in testExclusionList.json (#8774) 2023-10-18 22:39:41 +02:00
semantic-release-bot 5462834240 chore(release): 6.4.0-alpha.5 [skip ci]
# [6.4.0-alpha.5](https://github.com/parse-community/parse-server/compare/6.4.0-alpha.4...6.4.0-alpha.5) (2023-10-14)

### Bug Fixes

* Context not passed to Cloud Code Trigger `beforeFind` when using `Parse.Query.include` ([#8765](https://github.com/parse-community/parse-server/issues/8765)) ([7d32d89](https://github.com/parse-community/parse-server/commit/7d32d8934f3ae7af7a7d8b9cc6a829c7d73973d3))
2023-10-14 00:58:51 +00:00
Rikard Teodorsson 7d32d8934f fix: Context not passed to Cloud Code Trigger beforeFind when using Parse.Query.include (#8765) 2023-10-14 02:57:47 +02:00
dependabot[bot] 8d3117e0bc refactor: Bump postcss from 8.4.20 to 8.4.31 (#8771) 2023-10-07 13:29:46 +02:00
semantic-release-bot a2a98b1684 chore(release): 6.4.0-alpha.4 [skip ci]
# [6.4.0-alpha.4](https://github.com/parse-community/parse-server/compare/6.4.0-alpha.3...6.4.0-alpha.4) (2023-09-29)

### Features

* Allow setting `createdAt` and `updatedAt` during `Parse.Object` creation with maintenance key ([#8696](https://github.com/parse-community/parse-server/issues/8696)) ([77bbfb3](https://github.com/parse-community/parse-server/commit/77bbfb3f186f5651c33ba152f04cff95128eaf2d))
2023-09-29 20:18:59 +00:00
Wes 77bbfb3f18 feat: Allow setting createdAt and updatedAt during Parse.Object creation with maintenance key (#8696) 2023-09-29 22:17:48 +02:00
40 changed files with 4091 additions and 1134 deletions
+4 -4
View File
@@ -123,14 +123,14 @@ jobs:
uses: actions/checkout@v2
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Build docker image
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64
platforms: linux/amd64, linux/arm64/v8
check-lock-file-version:
name: NPM Lock File Version
timeout-minutes: 5
+7 -7
View File
@@ -56,18 +56,18 @@ jobs:
ref: ${{ needs.release.outputs.current_tag }}
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Log into Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v3
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
flavor: |
@@ -75,10 +75,10 @@ jobs:
tags: |
type=semver,pattern={{version}},value=${{ needs.release.outputs.current_tag }}
- name: Build and push Docker image
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64, linux/arm/v6, linux/arm/v7, linux/arm64/v8
platforms: linux/amd64, linux/arm64/v8
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
@@ -112,4 +112,4 @@ jobs:
uses: peaceiris/actions-gh-pages@v3.7.3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
publish_dir: ./docs
+6 -6
View File
@@ -28,18 +28,18 @@ jobs:
ref: ${{ github.event.inputs.ref }}
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Log into Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v3
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
flavor: |
@@ -48,10 +48,10 @@ jobs:
type=semver,enable=true,pattern={{version}},value=${{ github.event.inputs.ref }}
type=raw,enable=${{ github.event.inputs.ref == '' }},value=latest
- name: Build and push Docker image
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64, linux/arm/v6, linux/arm/v7, linux/arm64/v8
platforms: linux/amd64, linux/arm64/v8
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+2 -2
View File
@@ -1,7 +1,7 @@
############################################################
# Build stage
############################################################
FROM node:lts-alpine AS build
FROM node:18-alpine AS build
RUN apk --no-cache add git
WORKDIR /tmp
@@ -24,7 +24,7 @@ RUN npm ci --omit=dev --ignore-scripts \
############################################################
# Release stage
############################################################
FROM node:lts-alpine AS release
FROM node:18-alpine AS release
VOLUME /parse-server/cloud /parse-server/config
+8 -6
View File
@@ -358,12 +358,14 @@ The client keys used with Parse are no longer necessary with Parse Server. If yo
## Access Scopes
| Scope | Internal data | Custom data | Restricted by CLP, ACL | Key |
|----------------|---------------|-------------|------------------------|---------------------|
| Internal | r/w | r/w | no | `maintenanceKey` |
| Master | -/- | r/w | no | `masterKey` |
| ReadOnlyMaster | -/- | r/- | no | `readOnlyMasterKey` |
| Session | -/- | r/w | yes | `sessionToken` |
| Scope | Internal data | Read-only data <sub>(1)</sub> | Custom data | Restricted by CLP, ACL | Key |
|----------------|---------------|-------------------------------|-------------|------------------------|---------------------|
| Internal | r/w | r/w | r/w | no | `maintenanceKey` |
| Master | -/- | r/- | r/w | no | `masterKey` |
| ReadOnlyMaster | -/- | r/- | r/- | no | `readOnlyMasterKey` |
| Session | -/- | r/- | r/w | yes | `sessionToken` |
<sub>(1) `Parse.Object.createdAt`, `Parse.Object.updatedAt`.</sub>
## Email Verification and Password Reset
+60
View File
@@ -1,3 +1,63 @@
# [6.5.0-alpha.2](https://github.com/parse-community/parse-server/compare/6.5.0-alpha.1...6.5.0-alpha.2) (2023-11-19)
### Performance Improvements
* Improved IP validation performance for `masterKeyIPs`, `maintenanceKeyIPs` ([#8510](https://github.com/parse-community/parse-server/issues/8510)) ([b87daba](https://github.com/parse-community/parse-server/commit/b87daba0671a1b0b7b8d63bc671d665c91a04522))
# [6.5.0-alpha.1](https://github.com/parse-community/parse-server/compare/6.4.0...6.5.0-alpha.1) (2023-11-18)
### Bug Fixes
* Context not passed to Cloud Code Trigger `beforeFind` when using `Parse.Query.include` ([#8765](https://github.com/parse-community/parse-server/issues/8765)) ([7d32d89](https://github.com/parse-community/parse-server/commit/7d32d8934f3ae7af7a7d8b9cc6a829c7d73973d3))
* Parse Server option `fileUpload.fileExtensions` fails to determine file extension if filename contains multiple dots ([#8754](https://github.com/parse-community/parse-server/issues/8754)) ([3d6d50e](https://github.com/parse-community/parse-server/commit/3d6d50e0afff18b95fb906914e2cebd3839b517a))
* Security bump @babel/traverse from 7.20.5 to 7.23.2 ([#8777](https://github.com/parse-community/parse-server/issues/8777)) ([2d6b3d1](https://github.com/parse-community/parse-server/commit/2d6b3d18499179e99be116f25c0850d3f449509c))
* Security upgrade graphql from 16.6.0 to 16.8.1 ([#8758](https://github.com/parse-community/parse-server/issues/8758)) ([71dfd8a](https://github.com/parse-community/parse-server/commit/71dfd8a7ece8c0dd1a66d03bb9420cfd39f4f9b1))
### Features
* Add `$setOnInsert` operator to `Parse.Server.database.update` ([#8791](https://github.com/parse-community/parse-server/issues/8791)) ([f630a45](https://github.com/parse-community/parse-server/commit/f630a45aa5e87bc73a81fded061400c199b71a29))
* Add compatibility for MongoDB Atlas Serverless and AWS Amazon DocumentDB with collation options `enableCollationCaseComparison`, `transformEmailToLowercase`, `transformUsernameToLowercase` ([#8805](https://github.com/parse-community/parse-server/issues/8805)) ([09fbeeb](https://github.com/parse-community/parse-server/commit/09fbeebba8870e7cf371fb84371a254c7b368620))
* Add context to Cloud Code Triggers `beforeLogin` and `afterLogin` ([#8724](https://github.com/parse-community/parse-server/issues/8724)) ([a9c34ef](https://github.com/parse-community/parse-server/commit/a9c34ef1e2c78a42fb8b5fa8d569b7677c74919d))
* Allow setting `createdAt` and `updatedAt` during `Parse.Object` creation with maintenance key ([#8696](https://github.com/parse-community/parse-server/issues/8696)) ([77bbfb3](https://github.com/parse-community/parse-server/commit/77bbfb3f186f5651c33ba152f04cff95128eaf2d))
* Upgrade Parse Server Push Adapter to 5.0.2 ([#8813](https://github.com/parse-community/parse-server/issues/8813)) ([6ef1986](https://github.com/parse-community/parse-server/commit/6ef1986c03a1d84b7e11c05851e5bf9688d88740))
# [6.4.0-alpha.8](https://github.com/parse-community/parse-server/compare/6.4.0-alpha.7...6.4.0-alpha.8) (2023-11-13)
### Features
* Add compatibility for MongoDB Atlas Serverless and AWS Amazon DocumentDB with collation options `enableCollationCaseComparison`, `transformEmailToLowercase`, `transformUsernameToLowercase` ([#8805](https://github.com/parse-community/parse-server/issues/8805)) ([09fbeeb](https://github.com/parse-community/parse-server/commit/09fbeebba8870e7cf371fb84371a254c7b368620))
# [6.4.0-alpha.7](https://github.com/parse-community/parse-server/compare/6.4.0-alpha.6...6.4.0-alpha.7) (2023-10-25)
### Features
* Add `$setOnInsert` operator to `Parse.Server.database.update` ([#8791](https://github.com/parse-community/parse-server/issues/8791)) ([f630a45](https://github.com/parse-community/parse-server/commit/f630a45aa5e87bc73a81fded061400c199b71a29))
# [6.4.0-alpha.6](https://github.com/parse-community/parse-server/compare/6.4.0-alpha.5...6.4.0-alpha.6) (2023-10-18)
### Bug Fixes
* Security bump @babel/traverse from 7.20.5 to 7.23.2 ([#8777](https://github.com/parse-community/parse-server/issues/8777)) ([2d6b3d1](https://github.com/parse-community/parse-server/commit/2d6b3d18499179e99be116f25c0850d3f449509c))
# [6.4.0-alpha.5](https://github.com/parse-community/parse-server/compare/6.4.0-alpha.4...6.4.0-alpha.5) (2023-10-14)
### Bug Fixes
* Context not passed to Cloud Code Trigger `beforeFind` when using `Parse.Query.include` ([#8765](https://github.com/parse-community/parse-server/issues/8765)) ([7d32d89](https://github.com/parse-community/parse-server/commit/7d32d8934f3ae7af7a7d8b9cc6a829c7d73973d3))
# [6.4.0-alpha.4](https://github.com/parse-community/parse-server/compare/6.4.0-alpha.3...6.4.0-alpha.4) (2023-09-29)
### Features
* Allow setting `createdAt` and `updatedAt` during `Parse.Object` creation with maintenance key ([#8696](https://github.com/parse-community/parse-server/issues/8696)) ([77bbfb3](https://github.com/parse-community/parse-server/commit/77bbfb3f186f5651c33ba152f04cff95128eaf2d))
# [6.4.0-alpha.3](https://github.com/parse-community/parse-server/compare/6.4.0-alpha.2...6.4.0-alpha.3) (2023-09-23)
+17
View File
@@ -1,3 +1,20 @@
# [6.5.0-beta.1](https://github.com/parse-community/parse-server/compare/6.4.0...6.5.0-beta.1) (2023-11-16)
### Bug Fixes
* Context not passed to Cloud Code Trigger `beforeFind` when using `Parse.Query.include` ([#8765](https://github.com/parse-community/parse-server/issues/8765)) ([7d32d89](https://github.com/parse-community/parse-server/commit/7d32d8934f3ae7af7a7d8b9cc6a829c7d73973d3))
* Parse Server option `fileUpload.fileExtensions` fails to determine file extension if filename contains multiple dots ([#8754](https://github.com/parse-community/parse-server/issues/8754)) ([3d6d50e](https://github.com/parse-community/parse-server/commit/3d6d50e0afff18b95fb906914e2cebd3839b517a))
* Security bump @babel/traverse from 7.20.5 to 7.23.2 ([#8777](https://github.com/parse-community/parse-server/issues/8777)) ([2d6b3d1](https://github.com/parse-community/parse-server/commit/2d6b3d18499179e99be116f25c0850d3f449509c))
* Security upgrade graphql from 16.6.0 to 16.8.1 ([#8758](https://github.com/parse-community/parse-server/issues/8758)) ([71dfd8a](https://github.com/parse-community/parse-server/commit/71dfd8a7ece8c0dd1a66d03bb9420cfd39f4f9b1))
### Features
* Add `$setOnInsert` operator to `Parse.Server.database.update` ([#8791](https://github.com/parse-community/parse-server/issues/8791)) ([f630a45](https://github.com/parse-community/parse-server/commit/f630a45aa5e87bc73a81fded061400c199b71a29))
* Add compatibility for MongoDB Atlas Serverless and AWS Amazon DocumentDB with collation options `enableCollationCaseComparison`, `transformEmailToLowercase`, `transformUsernameToLowercase` ([#8805](https://github.com/parse-community/parse-server/issues/8805)) ([09fbeeb](https://github.com/parse-community/parse-server/commit/09fbeebba8870e7cf371fb84371a254c7b368620))
* Add context to Cloud Code Triggers `beforeLogin` and `afterLogin` ([#8724](https://github.com/parse-community/parse-server/issues/8724)) ([a9c34ef](https://github.com/parse-community/parse-server/commit/a9c34ef1e2c78a42fb8b5fa8d569b7677c74919d))
* Allow setting `createdAt` and `updatedAt` during `Parse.Object` creation with maintenance key ([#8696](https://github.com/parse-community/parse-server/issues/8696)) ([77bbfb3](https://github.com/parse-community/parse-server/commit/77bbfb3f186f5651c33ba152f04cff95128eaf2d))
# [6.4.0-beta.1](https://github.com/parse-community/parse-server/compare/6.3.0...6.4.0-beta.1) (2023-09-16)
+111
View File
@@ -1,3 +1,114 @@
## [6.5.11](https://github.com/parse-community/parse-server/compare/6.5.10...6.5.11) (2024-10-23)
### Bug Fixes
* Security upgrade to path-to-regexp 6.2.2 ([#9365](https://github.com/parse-community/parse-server/issues/9365)) ([dfbafa7](https://github.com/parse-community/parse-server/commit/dfbafa702507da0d20667eabe8fdc835ca30c8e8))
## [6.5.10](https://github.com/parse-community/parse-server/compare/6.5.9...6.5.10) (2024-10-12)
### Bug Fixes
* Security upgrade express from 4.21.0 to 4.21.1 ([#9339](https://github.com/parse-community/parse-server/issues/9339)) ([97627ee](https://github.com/parse-community/parse-server/commit/97627ee450f5abe04c92609f4ae2e63c49060d6a))
## [6.5.9](https://github.com/parse-community/parse-server/compare/6.5.8...6.5.9) (2024-10-03)
### Bug Fixes
* Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) ([#9318](https://github.com/parse-community/parse-server/issues/9318)) ([1bfbccf](https://github.com/parse-community/parse-server/commit/1bfbccf9ee7ea77533b2b2aa7c4c69f3bd35e66f))
## [6.5.8](https://github.com/parse-community/parse-server/compare/6.5.7...6.5.8) (2024-09-12)
### Bug Fixes
* Various vulnerabilities related to cross-site scripting ([#9310](https://github.com/parse-community/parse-server/issues/9310)) ([d5290d4](https://github.com/parse-community/parse-server/commit/d5290d46e5ff9237970ae1ac2d2df4051cbf53e5))
## [6.5.7](https://github.com/parse-community/parse-server/compare/6.5.6...6.5.7) (2024-06-30)
### Bug Fixes
* SQL injection when using Parse Server with PostgreSQL; fixes security vulnerability [GHSA-c2hr-cqg6-8j6r](https://github.com/parse-community/parse-server/security/advisories/GHSA-c2hr-cqg6-8j6r) ([#9168](https://github.com/parse-community/parse-server/issues/9168)) ([f332d54](https://github.com/parse-community/parse-server/commit/f332d54577608c5ad927255e06d8c694e2e0ff5b))
## [6.5.6](https://github.com/parse-community/parse-server/compare/6.5.5...6.5.6) (2024-05-16)
### Bug Fixes
* Facebook Limited Login not workind due to incorrect domain in JWT validation ([#9120](https://github.com/parse-community/parse-server/issues/9120)) ([0e92f76](https://github.com/parse-community/parse-server/commit/0e92f765e43d1e39285c5958d60cfd7fb76a3c90))
## [6.5.5](https://github.com/parse-community/parse-server/compare/6.5.4...6.5.5) (2024-03-19)
### Bug Fixes
* Server crashes on invalid Cloud Function or Cloud Job name; fixes security vulnerability [GHSA-6hh7-46r2-vf29](https://github.com/parse-community/parse-server/security/advisories/GHSA-6hh7-46r2-vf29) ([#9023](https://github.com/parse-community/parse-server/issues/9023)) ([5ae6d6a](https://github.com/parse-community/parse-server/commit/5ae6d6a36d75c4511029f0ba5673ae4b2999179b))
## [6.5.4](https://github.com/parse-community/parse-server/compare/6.5.3...6.5.4) (2024-03-16)
### Bug Fixes
* Server crashes when receiving an array of `Parse.Pointer` in the request body ([#9012](https://github.com/parse-community/parse-server/issues/9012)) ([8ff444d](https://github.com/parse-community/parse-server/commit/8ff444d42ef51bfe6808c4c3a5fe666bfe302ebe))
## [6.5.3](https://github.com/parse-community/parse-server/compare/6.5.2...6.5.3) (2024-03-16)
### Bug Fixes
* Security upgrade follow-redirects from 1.15.5 to 1.15.6 ([#9019](https://github.com/parse-community/parse-server/issues/9019)) ([422958e](https://github.com/parse-community/parse-server/commit/422958e246da6f13011776c8dde028a00fb821cb))
## [6.5.2](https://github.com/parse-community/parse-server/compare/6.5.1...6.5.2) (2024-03-02)
### Bug Fixes
* Security upgrade @parse/push-adapter from 5.1.0 to 5.1.1 ([#8975](https://github.com/parse-community/parse-server/issues/8975)) ([0fa0aab](https://github.com/parse-community/parse-server/commit/0fa0aabefe6bc9d356ee70be78dafc5fa22d4e17))
## [6.5.1](https://github.com/parse-community/parse-server/compare/6.5.0...6.5.1) (2024-03-02)
### Bug Fixes
* Security upgrade @parse/push-adapter from 5.0.2 to 5.1.0 ([#8972](https://github.com/parse-community/parse-server/issues/8972)) ([bba24dd](https://github.com/parse-community/parse-server/commit/bba24dd8279ebb8e4084a5f00fbe3ab9fe6c01b4))
# [6.5.0](https://github.com/parse-community/parse-server/compare/6.4.0...6.5.0) (2024-03-01)
### Bug Fixes
* Improve PostgreSQL injection detection; fixes security vulnerability [GHSA-6927-3vr9-fxf2](https://github.com/parse-community/parse-server/security/advisories/GHSA-6927-3vr9-fxf2) which affects Parse Server deployments using a Postgres database ([#8960](https://github.com/parse-community/parse-server/issues/8960)) ([a6e6549](https://github.com/parse-community/parse-server/commit/a6e654943536932904a69b51e513507fcf90a504))
# [6.4.0](https://github.com/parse-community/parse-server/compare/6.3.1...6.4.0) (2023-11-16)
### Bug Fixes
* Parse Server option `fileUpload.fileExtensions` does not work with an array of extensions ([#8688](https://github.com/parse-community/parse-server/issues/8688)) ([6a4a00c](https://github.com/parse-community/parse-server/commit/6a4a00ca7af1163ea74b047b85cd6817366b824b))
* Redis 4 does not reconnect after unhandled error ([#8706](https://github.com/parse-community/parse-server/issues/8706)) ([2b3d4e5](https://github.com/parse-community/parse-server/commit/2b3d4e5d3c85cd142f85af68dec51a8523548d49))
* Remove config logging when launching Parse Server via CLI ([#8710](https://github.com/parse-community/parse-server/issues/8710)) ([ae68f0c](https://github.com/parse-community/parse-server/commit/ae68f0c31b741eeb83379c905c7ddfaa124436ec))
* Server does not start via CLI when `auth` option is set ([#8666](https://github.com/parse-community/parse-server/issues/8666)) ([4e2000b](https://github.com/parse-community/parse-server/commit/4e2000bc563324389584ace3c090a5c1a7796a64))
### Features
* Add conditional email verification via dynamic Parse Server options `verifyUserEmails`, `sendUserEmailVerification` that now accept functions ([#8425](https://github.com/parse-community/parse-server/issues/8425)) ([44acd6d](https://github.com/parse-community/parse-server/commit/44acd6d9ed157ad4842200c9d01f9c77a05fec3a))
* Add property `Parse.Server.version` to determine current version of Parse Server in Cloud Code ([#8670](https://github.com/parse-community/parse-server/issues/8670)) ([a9d376b](https://github.com/parse-community/parse-server/commit/a9d376b61f5b07806eafbda91c4e36c322f09298))
* Add TOTP authentication adapter ([#8457](https://github.com/parse-community/parse-server/issues/8457)) ([cc079a4](https://github.com/parse-community/parse-server/commit/cc079a40f6849a0e9bc6fdc811e8649ecb67b589))
### Performance Improvements
* Improve performance of recursive pointer iterations ([#8741](https://github.com/parse-community/parse-server/issues/8741)) ([45a3ed0](https://github.com/parse-community/parse-server/commit/45a3ed0fcf2c0170607505a1550fb15896e705fd))
## [6.3.1](https://github.com/parse-community/parse-server/compare/6.3.0...6.3.1) (2023-10-20)
### Bug Fixes
* Server crash when uploading file without extension; fixes security vulnerability [GHSA-792q-q67h-w579](https://github.com/parse-community/parse-server/security/advisories/GHSA-792q-q67h-w579) ([#8781](https://github.com/parse-community/parse-server/issues/8781)) ([fd86278](https://github.com/parse-community/parse-server/commit/fd86278919556d3682e7e2c856dfccd5beffbfc0))
# [6.3.0](https://github.com/parse-community/parse-server/compare/6.2.2...6.3.0) (2023-09-16)
+2901 -994
View File
File diff suppressed because it is too large Load Diff
+23 -23
View File
@@ -1,6 +1,6 @@
{
"name": "parse-server",
"version": "6.4.0-alpha.3",
"version": "6.5.11",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
@@ -19,50 +19,49 @@
],
"license": "Apache-2.0",
"dependencies": {
"@babel/eslint-parser": "7.21.8",
"@babel/eslint-parser": "7.24.1",
"@graphql-tools/merge": "8.4.1",
"@graphql-tools/schema": "9.0.4",
"@graphql-tools/utils": "8.12.0",
"@graphql-yoga/node": "2.6.0",
"@parse/fs-files-adapter": "1.2.2",
"@parse/push-adapter": "4.2.0",
"@parse/fs-files-adapter": "2.0.1",
"@parse/push-adapter": "5.1.1",
"bcryptjs": "2.4.3",
"body-parser": "1.20.2",
"body-parser": "1.20.3",
"commander": "10.0.1",
"cors": "2.8.5",
"deepcopy": "2.1.0",
"express": "4.18.2",
"express": "4.21.1",
"express-rate-limit": "6.7.0",
"follow-redirects": "1.15.2",
"follow-redirects": "1.15.6",
"graphql": "16.8.1",
"graphql-list-fields": "2.0.2",
"graphql-relay": "0.10.0",
"graphql-list-fields": "2.0.4",
"graphql-relay": "0.10.1",
"graphql-tag": "2.12.6",
"intersect": "1.0.1",
"ip-range-check": "0.2.0",
"jsonwebtoken": "9.0.0",
"jwks-rsa": "2.1.5",
"jwks-rsa": "3.1.0",
"ldapjs": "2.3.3",
"lodash": "4.17.21",
"lru-cache": "9.1.1",
"lru-cache": "10.1.0",
"mime": "3.0.0",
"mongodb": "4.10.0",
"mustache": "4.2.0",
"otpauth": "9.1.2",
"parse": "4.1.0",
"path-to-regexp": "6.2.1",
"otpauth": "9.2.2",
"parse": "4.2.0",
"path-to-regexp": "6.2.2",
"pg-monitor": "2.0.0",
"pg-promise": "11.5.0",
"pg-promise": "11.5.4",
"pluralize": "8.0.0",
"rate-limit-redis": "3.0.2",
"redis": "4.6.6",
"semver": "7.5.2",
"redis": "4.6.13",
"semver": "7.5.4",
"subscriptions-transport-ws": "0.11.0",
"tv4": "1.3.0",
"uuid": "9.0.0",
"winston": "3.8.2",
"winston-daily-rotate-file": "4.7.1",
"ws": "8.13.0"
"uuid": "9.0.1",
"winston": "3.12.0",
"winston-daily-rotate-file": "5.0.0",
"ws": "8.17.1"
},
"devDependencies": {
"@actions/core": "1.9.1",
@@ -125,8 +124,9 @@
"test:mongodb:4.4.13": "npm run test:mongodb --dbversion=4.4.13",
"test:mongodb:5.3.2": "npm run test:mongodb --dbversion=5.3.2",
"test:mongodb:6.0.2": "npm run test:mongodb --dbversion=6.0.2",
"test:postgres:testonly": "cross-env PARSE_SERVER_TEST_DB=postgres PARSE_SERVER_TEST_DATABASE_URI=postgres://postgres:password@localhost:5432/parse_server_postgres_adapter_test_database npm run testonly",
"posttest:mongodb": "mongodb-runner stop",
"pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start",
"pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=6.0.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start",
"testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
"test": "npm run testonly",
"posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner stop",
+4 -15
View File
@@ -24,11 +24,11 @@ const templates = {
async function config() {
// Get branch
const branch = ref.split('/').pop().split('-')[0];
const branch = ref.split('/').pop();
console.log(`Running on branch: ${branch}`);
// Set changelog file
const changelogFile = `./changelogs/CHANGELOG_${branch}.md`;
const changelogFile = `./changelogs/CHANGELOG_release.md`;
console.log(`Changelog file output to: ${changelogFile}`);
// Load template file contents
@@ -39,9 +39,8 @@ async function config() {
'release',
{ name: 'alpha', prerelease: true },
{ name: 'beta', prerelease: true },
'next-major',
// Long-Term-Support branches; defined as GLOB pattern
'release-+([0-9]).x.x',
// Long-Term-Support branches
{ name: 'release-6.x.x', range: '6.x.x', channel: '6.x.x' },
],
dryRun: false,
debug: true,
@@ -85,16 +84,6 @@ async function config() {
labels: ['type:ci'],
releasedLabels: ['state:released<%= nextRelease.channel ? `-\${nextRelease.channel}` : "" %>']
}],
// Back-merge module runs last because if it fails it should not impede the release process
[
"@saithodev/semantic-release-backmerge",
{
"branches": [
{ from: "beta", to: "alpha" },
{ from: "release", to: "beta" },
]
}
],
],
};
+1
View File
@@ -15,6 +15,7 @@
"equal": true,
"expectAsync": true,
"notEqual": true,
"it_id": true,
"it_only_db": true,
"it_only_mongodb_version": true,
"it_only_postgres_version": true,
+8 -8
View File
@@ -2081,7 +2081,7 @@ describe('facebook limited auth adapter', () => {
it('should use algorithm from key header to verify id_token', async () => {
const fakeClaim = {
iss: 'https://facebook.com',
iss: 'https://www.facebook.com',
aud: 'secret',
exp: Date.now(),
sub: 'the_user_id',
@@ -2145,7 +2145,7 @@ describe('facebook limited auth adapter', () => {
it('(using client id as string) should verify id_token', async () => {
const fakeClaim = {
iss: 'https://facebook.com',
iss: 'https://www.facebook.com',
aud: 'secret',
exp: Date.now(),
sub: 'the_user_id',
@@ -2172,7 +2172,7 @@ describe('facebook limited auth adapter', () => {
it('(using client id as array) should verify id_token', async () => {
const fakeClaim = {
iss: 'https://facebook.com',
iss: 'https://www.facebook.com',
aud: 'secret',
exp: Date.now(),
sub: 'the_user_id',
@@ -2199,7 +2199,7 @@ describe('facebook limited auth adapter', () => {
it('(using client id as array with multiple items) should verify id_token', async () => {
const fakeClaim = {
iss: 'https://facebook.com',
iss: 'https://www.facebook.com',
aud: 'secret',
exp: Date.now(),
sub: 'the_user_id',
@@ -2250,7 +2250,7 @@ describe('facebook limited auth adapter', () => {
fail();
} catch (e) {
expect(e.message).toBe(
'id token not issued by correct OpenID provider - expected: https://facebook.com | from: https://not.facebook.com'
'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com'
);
}
});
@@ -2286,7 +2286,7 @@ describe('facebook limited auth adapter', () => {
fail();
} catch (e) {
expect(e.message).toBe(
'id token not issued by correct OpenID provider - expected: https://facebook.com | from: https://not.facebook.com'
'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com'
);
}
});
@@ -2320,7 +2320,7 @@ describe('facebook limited auth adapter', () => {
fail();
} catch (e) {
expect(e.message).toBe(
'id token not issued by correct OpenID provider - expected: https://facebook.com | from: https://not.facebook.com'
'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com'
);
}
});
@@ -2378,7 +2378,7 @@ describe('facebook limited auth adapter', () => {
it('should throw error with with invalid user id', async () => {
const fakeClaim = {
iss: 'https://facebook.com',
iss: 'https://www.facebook.com',
aud: 'invalid_client_id',
sub: 'a_different_user_id',
};
+25
View File
@@ -2510,6 +2510,31 @@ describe('beforeFind hooks', () => {
expect(res2.get('pointerFieldArray')[0].get('aField')).toBe('aFieldValue');
expect(spy).toHaveBeenCalledTimes(2);
});
it('should have access to context in include query in beforeFind hook', async () => {
let beforeFindTestObjectCalled = false;
let beforeFindTestObject2Called = false;
const obj1 = new Parse.Object('TestObject');
const obj2 = new Parse.Object('TestObject2');
obj2.set('aField', 'aFieldValue');
await obj2.save();
obj1.set('pointerField', obj2);
await obj1.save();
Parse.Cloud.beforeFind('TestObject', req => {
expect(req.context).toBeDefined();
expect(req.context.a).toEqual('a');
beforeFindTestObjectCalled = true;
});
Parse.Cloud.beforeFind('TestObject2', req => {
expect(req.context).toBeDefined();
expect(req.context.a).toEqual('a');
beforeFindTestObject2Called = true;
});
const query = new Parse.Query('TestObject');
await query.include('pointerField').find({ context: { a: 'a' } });
expect(beforeFindTestObjectCalled).toBeTrue();
expect(beforeFindTestObject2Called).toBeTrue();
});
});
describe('afterFind hooks', () => {
+254
View File
@@ -1,3 +1,4 @@
const Config = require('../lib/Config');
const DatabaseController = require('../lib/Controllers/DatabaseController.js');
const validateQuery = DatabaseController._validateQuery;
@@ -361,6 +362,259 @@ describe('DatabaseController', function () {
done();
});
});
describe('enableCollationCaseComparison', () => {
const dummyStorageAdapter = {
find: () => Promise.resolve([]),
watch: () => Promise.resolve(),
getAllClasses: () => Promise.resolve([]),
};
beforeEach(() => {
Config.get(Parse.applicationId).schemaCache.clear();
});
it('should force caseInsensitive to false with enableCollationCaseComparison option', async () => {
const databaseController = new DatabaseController(dummyStorageAdapter, {
enableCollationCaseComparison: true,
});
const spy = spyOn(dummyStorageAdapter, 'find');
spy.and.callThrough();
await databaseController.find('SomeClass', {}, { caseInsensitive: true });
expect(spy.calls.all()[0].args[3].caseInsensitive).toEqual(false);
});
it('should support caseInsensitive without enableCollationCaseComparison option', async () => {
const databaseController = new DatabaseController(dummyStorageAdapter, {});
const spy = spyOn(dummyStorageAdapter, 'find');
spy.and.callThrough();
await databaseController.find('_User', {}, { caseInsensitive: true });
expect(spy.calls.all()[0].args[3].caseInsensitive).toEqual(true);
});
it_only_db('mongo')(
'should create insensitive indexes without enableCollationCaseComparison',
async () => {
await reconfigureServer({
databaseURI: 'mongodb://localhost:27017/enableCollationCaseComparisonFalse',
databaseAdapter: undefined,
});
const user = new Parse.User();
await user.save({
username: 'example',
password: 'password',
email: 'example@example.com',
});
const schemas = await Parse.Schema.all();
const UserSchema = schemas.find(({ className }) => className === '_User');
expect(UserSchema.indexes).toEqual({
_id_: { _id: 1 },
username_1: { username: 1 },
case_insensitive_username: { username: 1 },
case_insensitive_email: { email: 1 },
email_1: { email: 1 },
});
}
);
it_only_db('mongo')(
'should not create insensitive indexes with enableCollationCaseComparison',
async () => {
await reconfigureServer({
enableCollationCaseComparison: true,
databaseURI: 'mongodb://localhost:27017/enableCollationCaseComparisonTrue',
databaseAdapter: undefined,
});
const user = new Parse.User();
await user.save({
username: 'example',
password: 'password',
email: 'example@example.com',
});
const schemas = await Parse.Schema.all();
const UserSchema = schemas.find(({ className }) => className === '_User');
expect(UserSchema.indexes).toEqual({
_id_: { _id: 1 },
username_1: { username: 1 },
email_1: { email: 1 },
});
}
);
});
describe('convertEmailToLowercase', () => {
const dummyStorageAdapter = {
createObject: () => Promise.resolve({ ops: [{}] }),
findOneAndUpdate: () => Promise.resolve({}),
watch: () => Promise.resolve(),
getAllClasses: () =>
Promise.resolve([
{
className: '_User',
fields: { email: 'String' },
indexes: {},
classLevelPermissions: { protectedFields: {} },
},
]),
};
const dates = {
createdAt: { iso: undefined, __type: 'Date' },
updatedAt: { iso: undefined, __type: 'Date' },
};
it('should not transform email to lower case without convertEmailToLowercase option on create', async () => {
const databaseController = new DatabaseController(dummyStorageAdapter, {});
const spy = spyOn(dummyStorageAdapter, 'createObject');
spy.and.callThrough();
await databaseController.create('_User', {
email: 'EXAMPLE@EXAMPLE.COM',
});
expect(spy.calls.all()[0].args[2]).toEqual({
email: 'EXAMPLE@EXAMPLE.COM',
...dates,
});
});
it('should transform email to lower case with convertEmailToLowercase option on create', async () => {
const databaseController = new DatabaseController(dummyStorageAdapter, {
convertEmailToLowercase: true,
});
const spy = spyOn(dummyStorageAdapter, 'createObject');
spy.and.callThrough();
await databaseController.create('_User', {
email: 'EXAMPLE@EXAMPLE.COM',
});
expect(spy.calls.all()[0].args[2]).toEqual({
email: 'example@example.com',
...dates,
});
});
it('should not transform email to lower case without convertEmailToLowercase option on update', async () => {
const databaseController = new DatabaseController(dummyStorageAdapter, {});
const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate');
spy.and.callThrough();
await databaseController.update('_User', { id: 'example' }, { email: 'EXAMPLE@EXAMPLE.COM' });
expect(spy.calls.all()[0].args[3]).toEqual({
email: 'EXAMPLE@EXAMPLE.COM',
});
});
it('should transform email to lower case with convertEmailToLowercase option on update', async () => {
const databaseController = new DatabaseController(dummyStorageAdapter, {
convertEmailToLowercase: true,
});
const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate');
spy.and.callThrough();
await databaseController.update('_User', { id: 'example' }, { email: 'EXAMPLE@EXAMPLE.COM' });
expect(spy.calls.all()[0].args[3]).toEqual({
email: 'example@example.com',
});
});
it('should not find a case insensitive user by email with convertEmailToLowercase', async () => {
await reconfigureServer({ convertEmailToLowercase: true });
const user = new Parse.User();
await user.save({ username: 'EXAMPLE', email: 'EXAMPLE@EXAMPLE.COM', password: 'password' });
const query = new Parse.Query(Parse.User);
query.equalTo('email', 'EXAMPLE@EXAMPLE.COM');
const result = await query.find({ useMasterKey: true });
expect(result.length).toEqual(0);
const query2 = new Parse.Query(Parse.User);
query2.equalTo('email', 'example@example.com');
const result2 = await query2.find({ useMasterKey: true });
expect(result2.length).toEqual(1);
});
});
describe('convertUsernameToLowercase', () => {
const dummyStorageAdapter = {
createObject: () => Promise.resolve({ ops: [{}] }),
findOneAndUpdate: () => Promise.resolve({}),
watch: () => Promise.resolve(),
getAllClasses: () =>
Promise.resolve([
{
className: '_User',
fields: { username: 'String' },
indexes: {},
classLevelPermissions: { protectedFields: {} },
},
]),
};
const dates = {
createdAt: { iso: undefined, __type: 'Date' },
updatedAt: { iso: undefined, __type: 'Date' },
};
it('should not transform username to lower case without convertUsernameToLowercase option on create', async () => {
const databaseController = new DatabaseController(dummyStorageAdapter, {});
const spy = spyOn(dummyStorageAdapter, 'createObject');
spy.and.callThrough();
await databaseController.create('_User', {
username: 'EXAMPLE',
});
expect(spy.calls.all()[0].args[2]).toEqual({
username: 'EXAMPLE',
...dates,
});
});
it('should transform username to lower case with convertUsernameToLowercase option on create', async () => {
const databaseController = new DatabaseController(dummyStorageAdapter, {
convertUsernameToLowercase: true,
});
const spy = spyOn(dummyStorageAdapter, 'createObject');
spy.and.callThrough();
await databaseController.create('_User', {
username: 'EXAMPLE',
});
expect(spy.calls.all()[0].args[2]).toEqual({
username: 'example',
...dates,
});
});
it('should not transform username to lower case without convertUsernameToLowercase option on update', async () => {
const databaseController = new DatabaseController(dummyStorageAdapter, {});
const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate');
spy.and.callThrough();
await databaseController.update('_User', { id: 'example' }, { username: 'EXAMPLE' });
expect(spy.calls.all()[0].args[3]).toEqual({
username: 'EXAMPLE',
});
});
it('should transform username to lower case with convertUsernameToLowercase option on update', async () => {
const databaseController = new DatabaseController(dummyStorageAdapter, {
convertUsernameToLowercase: true,
});
const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate');
spy.and.callThrough();
await databaseController.update('_User', { id: 'example' }, { username: 'EXAMPLE' });
expect(spy.calls.all()[0].args[3]).toEqual({
username: 'example',
});
});
it('should not find a case insensitive user by username with convertUsernameToLowercase', async () => {
await reconfigureServer({ convertUsernameToLowercase: true });
const user = new Parse.User();
await user.save({ username: 'EXAMPLE', password: 'password' });
const query = new Parse.Query(Parse.User);
query.equalTo('username', 'EXAMPLE');
const result = await query.find({ useMasterKey: true });
expect(result.length).toEqual(0);
const query2 = new Parse.Query(Parse.User);
query2.equalTo('username', 'example');
const result2 = await query2.find({ useMasterKey: true });
expect(result2.length).toEqual(1);
});
});
});
function buildCLP(pointerNames) {
+105 -20
View File
@@ -1,10 +1,19 @@
const middlewares = require('../lib/middlewares');
const AppCache = require('../lib/cache').AppCache;
const { BlockList } = require('net');
const AppCachePut = (appId, config) =>
AppCache.put(appId, {
...config,
maintenanceKeyIpsStore: new Map(),
masterKeyIpsStore: new Map(),
});
describe('middlewares', () => {
let fakeReq, fakeRes;
beforeEach(() => {
fakeReq = {
ip: '127.0.0.1',
originalUrl: 'http://example.com/parse/',
url: 'http://example.com/',
body: {
@@ -16,7 +25,7 @@ describe('middlewares', () => {
},
};
fakeRes = jasmine.createSpyObj('fakeRes', ['end', 'status']);
AppCache.put(fakeReq.body._ApplicationId, {});
AppCachePut(fakeReq.body._ApplicationId, {});
});
afterEach(() => {
@@ -35,7 +44,7 @@ describe('middlewares', () => {
});
it('should give invalid response when keys are configured but no key supplied', () => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
restAPIKey: 'restAPIKey',
});
@@ -44,7 +53,7 @@ describe('middlewares', () => {
});
it('should give invalid response when keys are configured but supplied key is incorrect', () => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
restAPIKey: 'restAPIKey',
});
@@ -54,7 +63,7 @@ describe('middlewares', () => {
});
it('should give invalid response when keys are configured but different key is supplied', () => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
restAPIKey: 'restAPIKey',
});
@@ -64,7 +73,7 @@ describe('middlewares', () => {
});
it('should succeed when any one of the configured keys supplied', done => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
clientKey: 'clientKey',
masterKey: 'masterKey',
restAPIKey: 'restAPIKey',
@@ -77,7 +86,7 @@ describe('middlewares', () => {
});
it('should succeed when client key supplied but empty', done => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
clientKey: '',
masterKey: 'masterKey',
restAPIKey: 'restAPIKey',
@@ -90,7 +99,7 @@ describe('middlewares', () => {
});
it('should succeed when no keys are configured and none supplied', done => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
});
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
@@ -117,7 +126,7 @@ describe('middlewares', () => {
otherKey => otherKey !== infoKey && otherKey !== 'javascriptKey'
);
it(`it should pull ${bodyKey} into req.info`, done => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
masterKeyIps: ['0.0.0.0/0'],
});
fakeReq.ip = '127.0.0.1';
@@ -138,7 +147,7 @@ describe('middlewares', () => {
it('should not succeed and log if the ip does not belong to masterKeyIps list', async () => {
const logger = require('../lib/logger').logger;
spyOn(logger, 'error').and.callFake(() => {});
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['10.0.0.1'],
});
@@ -152,7 +161,7 @@ describe('middlewares', () => {
});
it('should not succeed if the ip does not belong to masterKeyIps list', async () => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['10.0.0.1'],
});
@@ -165,7 +174,7 @@ describe('middlewares', () => {
it('should not succeed if the ip does not belong to maintenanceKeyIps list', async () => {
const logger = require('../lib/logger').logger;
spyOn(logger, 'error').and.callFake(() => {});
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
maintenanceKey: 'masterKey',
maintenanceKeyIps: ['10.0.0.0', '10.0.0.1'],
});
@@ -179,7 +188,7 @@ describe('middlewares', () => {
});
it('should succeed if the ip does belong to masterKeyIps list', async () => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['10.0.0.1'],
});
@@ -190,7 +199,7 @@ describe('middlewares', () => {
});
it('should allow any ip to use masterKey if masterKeyIps is empty', async () => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['0.0.0.0/0'],
});
@@ -221,7 +230,7 @@ describe('middlewares', () => {
});
it('should set default Access-Control-Allow-Headers if allowHeaders are empty', () => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
allowHeaders: undefined,
});
const headers = {};
@@ -234,7 +243,7 @@ describe('middlewares', () => {
allowCrossDomain(fakeReq, res, () => {});
expect(headers['Access-Control-Allow-Headers']).toContain(middlewares.DEFAULT_ALLOWED_HEADERS);
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
allowHeaders: [],
});
allowCrossDomain(fakeReq, res, () => {});
@@ -242,7 +251,7 @@ describe('middlewares', () => {
});
it('should append custom headers to Access-Control-Allow-Headers if allowHeaders provided', () => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
allowHeaders: ['Header-1', 'Header-2'],
});
const headers = {};
@@ -258,7 +267,7 @@ describe('middlewares', () => {
});
it('should set default Access-Control-Allow-Origin if allowOrigin is empty', () => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
allowOrigin: undefined,
});
const headers = {};
@@ -273,7 +282,7 @@ describe('middlewares', () => {
});
it('should set custom origin to Access-Control-Allow-Origin if allowOrigin is provided', () => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
allowOrigin: 'https://parseplatform.org/',
});
const headers = {};
@@ -317,7 +326,7 @@ describe('middlewares', () => {
});
it('should use user provided on field userFromJWT', done => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
});
fakeReq.userFromJWT = 'fake-user';
@@ -328,11 +337,87 @@ describe('middlewares', () => {
});
it('should give invalid response when upload file without x-parse-application-id in header', () => {
AppCache.put(fakeReq.body._ApplicationId, {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
});
fakeReq.body = Buffer.from('fake-file');
middlewares.handleParseHeaders(fakeReq, fakeRes);
expect(fakeRes.status).toHaveBeenCalledWith(403);
});
it('should match address', () => {
const ipv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334';
const anotherIpv6 = '::ffff:101.10.0.1';
const ipv4 = '192.168.0.101';
const localhostV6 = '::1';
const localhostV62 = '::ffff:127.0.0.1';
const localhostV4 = '127.0.0.1';
const v6 = [ipv6, anotherIpv6];
v6.forEach(ip => {
expect(middlewares.checkIp(ip, ['::/0'], new Map())).toBe(true);
expect(middlewares.checkIp(ip, ['::'], new Map())).toBe(true);
expect(middlewares.checkIp(ip, ['0.0.0.0'], new Map())).toBe(false);
expect(middlewares.checkIp(ip, ['0.0.0.0/0'], new Map())).toBe(false);
expect(middlewares.checkIp(ip, ['123.123.123.123'], new Map())).toBe(false);
});
expect(middlewares.checkIp(ipv6, [anotherIpv6], new Map())).toBe(false);
expect(middlewares.checkIp(ipv6, [ipv6], new Map())).toBe(true);
expect(middlewares.checkIp(ipv6, ['2001:db8:85a3:0:0:8a2e:0:0/100'], new Map())).toBe(true);
expect(middlewares.checkIp(ipv4, ['::'], new Map())).toBe(false);
expect(middlewares.checkIp(ipv4, ['::/0'], new Map())).toBe(false);
expect(middlewares.checkIp(ipv4, ['0.0.0.0'], new Map())).toBe(true);
expect(middlewares.checkIp(ipv4, ['0.0.0.0/0'], new Map())).toBe(true);
expect(middlewares.checkIp(ipv4, ['123.123.123.123'], new Map())).toBe(false);
expect(middlewares.checkIp(ipv4, [ipv4], new Map())).toBe(true);
expect(middlewares.checkIp(ipv4, ['192.168.0.0/24'], new Map())).toBe(true);
expect(middlewares.checkIp(localhostV4, ['::1'], new Map())).toBe(false);
expect(middlewares.checkIp(localhostV6, ['::1'], new Map())).toBe(true);
// ::ffff:127.0.0.1 is a padded ipv4 address but not ::1
expect(middlewares.checkIp(localhostV62, ['::1'], new Map())).toBe(false);
// ::ffff:127.0.0.1 is a padded ipv4 address and is a match for 127.0.0.1
expect(middlewares.checkIp(localhostV62, ['127.0.0.1'], new Map())).toBe(true);
});
it('should match address with cache', () => {
const ipv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334';
const cache1 = new Map();
const spyBlockListCheck = spyOn(BlockList.prototype, 'check').and.callThrough();
expect(middlewares.checkIp(ipv6, ['::'], cache1)).toBe(true);
expect(cache1.get('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe(undefined);
expect(cache1.get('allowAllIpv6')).toBe(true);
expect(spyBlockListCheck).toHaveBeenCalledTimes(0);
const cache2 = new Map();
expect(middlewares.checkIp('::1', ['::1'], cache2)).toBe(true);
expect(cache2.get('::1')).toBe(true);
expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
expect(middlewares.checkIp('::1', ['::1'], cache2)).toBe(true);
expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
spyBlockListCheck.calls.reset();
const cache3 = new Map();
expect(middlewares.checkIp('127.0.0.1', ['127.0.0.1'], cache3)).toBe(true);
expect(cache3.get('127.0.0.1')).toBe(true);
expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
expect(middlewares.checkIp('127.0.0.1', ['127.0.0.1'], cache3)).toBe(true);
expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
spyBlockListCheck.calls.reset();
const cache4 = new Map();
const ranges = ['127.0.0.1', '192.168.0.0/24'];
// should not cache negative match
expect(middlewares.checkIp('123.123.123.123', ranges, cache4)).toBe(false);
expect(cache4.get('123.123.123.123')).toBe(undefined);
expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
spyBlockListCheck.calls.reset();
// should not cache cidr
expect(middlewares.checkIp('192.168.0.101', ranges, cache4)).toBe(true);
expect(cache4.get('192.168.0.101')).toBe(undefined);
expect(spyBlockListCheck).toHaveBeenCalledTimes(1);
});
});
+55
View File
@@ -254,6 +254,61 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
expect(obj.get('foo').test.date[0] instanceof Date).toBeTrue();
});
it('upserts with $setOnInsert', async () => {
const uuid = require('uuid');
const uuid1 = uuid.v4();
const uuid2 = uuid.v4();
const schema = {
className: 'MyClass',
fields: {
x: { type: 'Number' },
count: { type: 'Number' },
},
classLevelPermissions: {},
};
const myClassSchema = new Parse.Schema(schema.className);
myClassSchema.setCLP(schema.classLevelPermissions);
await myClassSchema.save();
const query = {
x: 1,
};
const update = {
objectId: {
__op: 'SetOnInsert',
amount: uuid1,
},
count: {
__op: 'Increment',
amount: 1,
},
};
await Parse.Server.database.update(
'MyClass',
query,
update,
{ upsert: true },
);
update.objectId.amount = uuid2;
await Parse.Server.database.update(
'MyClass',
query,
update,
{ upsert: true },
);
const res = await Parse.Server.database.find(
schema.className,
{},
{},
);
expect(res.length).toBe(1);
expect(res[0].objectId).toBe(uuid1);
expect(res[0].count).toBe(2);
expect(res[0].x).toBe(1);
});
it('handles updating a single object with array, object date', done => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
+2 -2
View File
@@ -87,7 +87,7 @@ describe('OAuth', function () {
done();
}
it('GET request for a resource that requires OAuth should fail with invalid credentials', done => {
xit('GET request for a resource that requires OAuth should fail with invalid credentials', done => {
/*
This endpoint has been chosen to make a request to an endpoint that requires OAuth which fails due to missing authentication.
Any other endpoint from the Twitter API that requires OAuth can be used instead in case the currently used endpoint deprecates.
@@ -105,7 +105,7 @@ describe('OAuth', function () {
});
});
it('POST request for a resource that requires OAuth should fail with invalid credentials', done => {
xit('POST request for a resource that requires OAuth should fail with invalid credentials', done => {
/*
This endpoint has been chosen to make a request to an endpoint that requires OAuth which fails due to missing authentication.
Any other endpoint from the Twitter API that requires OAuth can be used instead in case the currently used endpoint deprecates.
+18
View File
@@ -1267,6 +1267,24 @@ describe('miscellaneous', function () {
});
});
it('test cloud function query parameters with array of pointers', async () => {
Parse.Cloud.define('echoParams', req => {
return req.params;
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-Javascript-Key': 'test',
};
const response = await request({
method: 'POST',
headers: headers,
url: 'http://localhost:8378/1/functions/echoParams',
body: '{"arr": [{ "__type": "Pointer", "className": "PointerTest" }]}',
});
const res = response.data.result;
expect(res.arr.length).toEqual(1);
});
it('can handle null params in cloud functions (regression test for #1742)', done => {
Parse.Cloud.define('func', request => {
expect(request.params.nullParam).toEqual(null);
+28
View File
@@ -1432,6 +1432,34 @@ describe('Parse.File testing', () => {
}
});
it('allows file without extension', async () => {
await reconfigureServer({
fileUpload: {
enableForPublic: true,
fileExtensions: ['^[^hH][^tT][^mM][^lL]?$'],
},
});
const headers = {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const values = ['filenamewithoutextension'];
for (const value of values) {
await expectAsync(
request({
method: 'POST',
headers: headers,
url: `http://localhost:8378/1/files/${value}`,
body: '<html></html>\n',
}).catch(e => {
throw new Error(e.data.error);
})
).toBeResolved();
}
});
it('works with array', async () => {
await reconfigureServer({
fileUpload: {
+33
View File
@@ -694,3 +694,36 @@ describe('triggers', () => {
expect(req.context).toBeUndefined();
});
});
describe('sanitizing names', () => {
const invalidNames = [
`test'%3bdeclare%20@q%20varchar(99)%3bset%20@q%3d'%5c%5cxxxxxxxxxxxxxxx.yyyyy'%2b'fy.com%5cxus'%3b%20exec%20master.dbo.xp_dirtree%20@q%3b--%20`,
`test.function.name`,
];
it('should not crash server and return error on invalid Cloud Function name', async () => {
for (const invalidName of invalidNames) {
let error;
try {
await Parse.Cloud.run(invalidName);
} catch (err) {
error = err;
}
expect(error).toBeDefined();
expect(error.message).toMatch(/Invalid function/);
}
});
it('should not crash server and return error on invalid Cloud Job name', async () => {
for (const invalidName of invalidNames) {
let error;
try {
await Parse.Cloud.startJob(invalidName);
} catch (err) {
error = err;
}
expect(error).toBeDefined();
expect(error.message).toMatch(/Invalid job/);
}
});
});
+26 -2
View File
@@ -103,6 +103,7 @@ const defaultConfiguration = {
restAPIKey: 'rest',
webhookKey: 'hook',
masterKey: 'test',
maintenanceKey: 'testing',
readOnlyMasterKey: 'read-only-test',
fileKey: 'test',
directAccess: true,
@@ -250,8 +251,8 @@ afterEach(function (done) {
})
.then(() => Parse.User.logOut())
.then(
() => {},
() => {}
() => { },
() => { }
) // swallow errors
.then(() => {
// Connection close events are not immediate on node 10+... wait a bit
@@ -427,6 +428,29 @@ global.it_exclude_dbs = excluded => {
}
};
let testExclusionList = [];
try {
// Fetch test exclusion list
testExclusionList = require('./testExclusionList.json');
console.log(`Using test exclusion list with ${testExclusionList.length} entries`);
} catch(error) {
if(error.code !== 'MODULE_NOT_FOUND') {
throw error;
}
}
// Disable test if its UUID is found in testExclusionList
global.it_id = (id, func) => {
if (testExclusionList.includes(id)) {
return xit;
} else {
if(func === undefined)
return it;
else
return func;
}
};
global.it_only_db = db => {
if (
process.env.PARSE_SERVER_TEST_DB === db ||
+113
View File
@@ -136,6 +136,119 @@ describe('rest create', () => {
});
});
describe('with maintenance key', () => {
let req;
async function getObject(id) {
const res = await request({
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest'
},
method: 'GET',
url: `http://localhost:8378/1/classes/TestObject/${id}`
});
return res.data;
}
beforeEach(() => {
req = {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
'X-Parse-Maintenance-Key': 'testing'
},
method: 'POST',
url: 'http://localhost:8378/1/classes/TestObject'
};
});
it('allows createdAt', async () => {
const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' };
req.body = { createdAt };
const res = await request(req);
expect(res.data.createdAt).toEqual(createdAt.iso);
});
it('allows createdAt and updatedAt', async () => {
const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' };
const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' };
req.body = { createdAt, updatedAt };
const res = await request(req);
const obj = await getObject(res.data.objectId);
expect(obj.createdAt).toEqual(createdAt.iso);
expect(obj.updatedAt).toEqual(updatedAt.iso);
});
it('allows createdAt, updatedAt, and additional field', async () => {
const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' };
const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' };
req.body = { createdAt, updatedAt, testing: 123 };
const res = await request(req);
const obj = await getObject(res.data.objectId);
expect(obj.createdAt).toEqual(createdAt.iso);
expect(obj.updatedAt).toEqual(updatedAt.iso);
expect(obj.testing).toEqual(123);
});
it('cannot set updatedAt dated before createdAt', async () => {
const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' };
const updatedAt = { __type: 'Date', iso: '2018-12-01T00:00:00.000Z' };
req.body = { createdAt, updatedAt };
try {
await request(req);
fail();
}
catch (err) {
expect(err.data.code).toEqual(Parse.Error.VALIDATION_ERROR);
}
});
it('cannot set updatedAt without createdAt', async () => {
const updatedAt = { __type: 'Date', iso: '2018-12-01T00:00:00.000Z' };
req.body = { updatedAt };
const res = await request(req);
const obj = await getObject(res.data.objectId);
expect(obj.updatedAt).not.toEqual(updatedAt.iso);
});
it('handles bad types for createdAt and updatedAt', async () => {
const createdAt = 12345;
const updatedAt = true;
req.body = { createdAt, updatedAt };
try {
await request(req);
fail();
}
catch (err) {
expect(err.data.code).toEqual(Parse.Error.INCORRECT_TYPE);
}
});
it('cannot set createdAt or updatedAt without maintenance key', async () => {
const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' };
const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' };
req.body = { createdAt, updatedAt };
delete req.headers['X-Parse-Maintenance-Key'];
const res = await request(req);
expect(res.data.createdAt).not.toEqual(createdAt.iso);
expect(res.data.updatedAt).not.toEqual(updatedAt.iso);
});
});
it('handles array, object, date', done => {
const now = new Date();
const obj = {
+70
View File
@@ -1,6 +1,51 @@
const request = require('../lib/request');
describe('Vulnerabilities', () => {
describe('(GHSA-8xq9-g7ch-35hg) Custom object ID allows to acquire role privilege', () => {
beforeAll(async () => {
await reconfigureServer({ allowCustomObjectId: true });
Parse.allowCustomObjectId = true;
});
afterAll(async () => {
await reconfigureServer({ allowCustomObjectId: false });
Parse.allowCustomObjectId = false;
});
it('denies user creation with poisoned object ID', async () => {
await expectAsync(
new Parse.User({ id: 'role:a', username: 'a', password: '123' }).save()
).toBeRejectedWith(new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.'));
});
describe('existing sessions for users with poisoned object ID', () => {
/** @type {Parse.User} */
let poisonedUser;
/** @type {Parse.User} */
let innocentUser;
beforeAll(async () => {
const parseServer = await global.reconfigureServer();
const databaseController = parseServer.config.databaseController;
[poisonedUser, innocentUser] = await Promise.all(
['role:abc', 'abc'].map(async id => {
// Create the users directly on the db to bypass the user creation check
await databaseController.create('_User', { objectId: id });
// Use the master key to create a session for them to bypass the session check
return Parse.User.loginAs(id);
})
);
});
it('refuses session token of user with poisoned object ID', async () => {
await expectAsync(
new Parse.Query(Parse.User).find({ sessionToken: poisonedUser.getSessionToken() })
).toBeRejectedWith(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.'));
await new Parse.Query(Parse.User).find({ sessionToken: innocentUser.getSessionToken() });
});
});
});
describe('Object prototype pollution', () => {
it('denies object prototype to be polluted with keyword "constructor"', async () => {
const headers = {
@@ -459,3 +504,28 @@ describe('Vulnerabilities', () => {
});
});
});
describe('Postgres regex sanitizater', () => {
it('sanitizes the regex correctly to prevent Injection', async () => {
const user = new Parse.User();
user.set('username', 'username');
user.set('password', 'password');
user.set('email', 'email@example.com');
await user.signUp();
const response = await request({
method: 'GET',
url:
"http://localhost:8378/1/classes/_User?where[username][$regex]=A'B'%3BSELECT+PG_SLEEP(3)%3B--",
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
});
expect(response.status).toBe(200);
expect(response.data.results).toEqual(jasmine.any(Array));
expect(response.data.results.length).toBe(0);
});
});
+1 -1
View File
@@ -7,7 +7,7 @@ const jwt = require('jsonwebtoken');
const httpsRequest = require('./httpsRequest');
const authUtils = require('./utils');
const TOKEN_ISSUER = 'https://facebook.com';
const TOKEN_ISSUER = 'https://www.facebook.com';
function getAppSecretPath(authData, options = {}) {
const appSecret = options.appSecret;
@@ -986,6 +986,13 @@ function transformUpdateOperator({ __op, amount, objects }, flatten) {
return { __op: '$inc', arg: amount };
}
case 'SetOnInsert':
if (flatten) {
return amount;
} else {
return { __op: '$setOnInsert', arg: amount };
}
case 'Add':
case 'AddUnique':
if (!(objects instanceof Array)) {
@@ -2614,16 +2614,16 @@ function isAnyValueRegexStartsWith(values) {
});
}
function createLiteralRegex(remaining) {
function createLiteralRegex(remaining: string) {
return remaining
.split('')
.map(c => {
const regex = RegExp('[0-9 ]|\\p{L}', 'u'); // Support all unicode letter chars
const regex = RegExp('[0-9 ]|\\p{L}', 'u'); // Support all Unicode letter chars
if (c.match(regex) !== null) {
// don't escape alphanumeric characters
// Don't escape alphanumeric characters
return c;
}
// escape everything else (single quotes with single quotes, everything else with a backslash)
// Escape everything else (single quotes with single quotes, everything else with a backslash)
return c === `'` ? `''` : `\\${c}`;
})
.join('');
@@ -2633,14 +2633,14 @@ function literalizeRegexPart(s: string) {
const matcher1 = /\\Q((?!\\E).*)\\E$/;
const result1: any = s.match(matcher1);
if (result1 && result1.length > 1 && result1.index > -1) {
// process regex that has a beginning and an end specified for the literal text
// Process Regex that has a beginning and an end specified for the literal text
const prefix = s.substring(0, result1.index);
const remaining = result1[1];
return literalizeRegexPart(prefix) + createLiteralRegex(remaining);
}
// process regex that has a beginning specified for the literal text
// Process Regex that has a beginning specified for the literal text
const matcher2 = /\\Q((?!\\E).*)$/;
const result2: any = s.match(matcher2);
if (result2 && result2.length > 1 && result2.index > -1) {
@@ -2650,14 +2650,18 @@ function literalizeRegexPart(s: string) {
return literalizeRegexPart(prefix) + createLiteralRegex(remaining);
}
// remove all instances of \Q and \E from the remaining text & escape single quotes
// Remove problematic chars from remaining text
return s
// Remove all instances of \Q and \E
.replace(/([^\\])(\\E)/, '$1')
.replace(/([^\\])(\\Q)/, '$1')
.replace(/^\\E/, '')
.replace(/^\\Q/, '')
.replace(/([^'])'/, `$1''`)
.replace(/^'([^'])/, `''$1`);
// Ensure even number of single quote sequences by adding an extra single quote if needed;
// this ensures that every single quote is escaped
.replace(/'+/g, match => {
return match.length % 2 === 0 ? match : match + "'";
});
}
var GeoPointCoder = {
+5
View File
@@ -173,6 +173,11 @@ const getAuthForSessionToken = async function ({
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Session token is expired.');
}
const obj = session.user;
if (typeof obj['objectId'] === 'string' && obj['objectId'].startsWith('role:')) {
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.');
}
delete obj.password;
obj['className'] = '_User';
obj['sessionToken'] = sessionToken;
+40 -15
View File
@@ -279,6 +279,9 @@ const flattenUpdateOperatorsForCreate = object => {
}
object[key] = object[key].amount;
break;
case 'SetOnInsert':
object[key] = object[key].amount;
break;
case 'Add':
if (!(object[key].objects instanceof Array)) {
throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array');
@@ -365,6 +368,22 @@ const relationSchema = {
fields: { relatedId: { type: 'String' }, owningId: { type: 'String' } },
};
const convertEmailToLowercase = (object, className, options) => {
if (className === '_User' && options.convertEmailToLowercase) {
if (typeof object['email'] === 'string') {
object['email'] = object['email'].toLowerCase();
}
}
};
const convertUsernameToLowercase = (object, className, options) => {
if (className === '_User' && options.convertUsernameToLowercase) {
if (typeof object['username'] === 'string') {
object['username'] = object['username'].toLowerCase();
}
}
};
class DatabaseController {
adapter: StorageAdapter;
schemaCache: any;
@@ -570,6 +589,8 @@ class DatabaseController {
}
}
update = transformObjectACL(update);
convertEmailToLowercase(update, className, this.options);
convertUsernameToLowercase(update, className, this.options);
transformAuthData(className, update, schema);
if (validateOnly) {
return this.adapter.find(className, schema, query, {}).then(result => {
@@ -819,6 +840,8 @@ class DatabaseController {
const originalObject = object;
object = transformObjectACL(object);
convertEmailToLowercase(object, className, this.options);
convertUsernameToLowercase(object, className, this.options);
object.createdAt = { iso: object.createdAt, __type: 'Date' };
object.updatedAt = { iso: object.updatedAt, __type: 'Date' };
@@ -1212,7 +1235,7 @@ class DatabaseController {
keys,
readPreference,
hint,
caseInsensitive,
caseInsensitive: this.options.enableCollationCaseComparison ? false : caseInsensitive,
explain,
};
Object.keys(sort).forEach(fieldName => {
@@ -1716,25 +1739,27 @@ class DatabaseController {
throw error;
});
await this.adapter
.ensureIndex('_User', requiredUserFields, ['username'], 'case_insensitive_username', true)
.catch(error => {
logger.warn('Unable to create case insensitive username index: ', error);
throw error;
});
if (!this.options.enableCollationCaseComparison) {
await this.adapter
.ensureIndex('_User', requiredUserFields, ['username'], 'case_insensitive_username', true)
.catch(error => {
logger.warn('Unable to create case insensitive username index: ', error);
throw error;
});
await this.adapter
.ensureIndex('_User', requiredUserFields, ['email'], 'case_insensitive_email', true)
.catch(error => {
logger.warn('Unable to create case insensitive email index: ', error);
throw error;
});
}
await this.adapter.ensureUniqueness('_User', requiredUserFields, ['email']).catch(error => {
logger.warn('Unable to ensure uniqueness for user email addresses: ', error);
throw error;
});
await this.adapter
.ensureIndex('_User', requiredUserFields, ['email'], 'case_insensitive_email', true)
.catch(error => {
logger.warn('Unable to create case insensitive email index: ', error);
throw error;
});
await this.adapter.ensureUniqueness('_Role', requiredRoleFields, ['name']).catch(error => {
logger.warn('Unable to ensure uniqueness for role name: ', error);
throw error;
@@ -1817,7 +1842,7 @@ class DatabaseController {
keyUpdate &&
typeof keyUpdate === 'object' &&
keyUpdate.__op &&
['Add', 'AddUnique', 'Remove', 'Increment'].indexOf(keyUpdate.__op) > -1
['Add', 'AddUnique', 'Remove', 'Increment', 'SetOnInsert'].indexOf(keyUpdate.__op) > -1
) {
// only valid ops that produce an actionable result
// the op may have happened on a keypath
+24 -3
View File
@@ -139,6 +139,20 @@ module.exports.ParseServerOptions = {
help: 'A collection prefix for the classes',
default: '',
},
convertEmailToLowercase: {
env: 'PARSE_SERVER_CONVERT_EMAIL_TO_LOWERCASE',
help:
'Optional. If set to `true`, the `email` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `email` property is stored as set, without any case modifications. Default is `false`.',
action: parsers.booleanParser,
default: false,
},
convertUsernameToLowercase: {
env: 'PARSE_SERVER_CONVERT_USERNAME_TO_LOWERCASE',
help:
'Optional. If set to `true`, the `username` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `username` property is stored as set, without any case modifications. Default is `false`.',
action: parsers.booleanParser,
default: false,
},
customPages: {
env: 'PARSE_SERVER_CUSTOM_PAGES',
help: 'custom pages for password validation and reset',
@@ -203,6 +217,13 @@ module.exports.ParseServerOptions = {
action: parsers.booleanParser,
default: true,
},
enableCollationCaseComparison: {
env: 'PARSE_SERVER_ENABLE_COLLATION_CASE_COMPARISON',
help:
'Optional. If set to `true`, the collation rule of case comparison for queries and indexes is enabled. Enable this option to run Parse Server with MongoDB Atlas Serverless or AWS Amazon DocumentDB. If `false`, the collation rule of case comparison is disabled. Default is `false`.',
action: parsers.booleanParser,
default: false,
},
enableExpressErrorHandler: {
env: 'PARSE_SERVER_ENABLE_EXPRESS_ERROR_HANDLER',
help: 'Enables the default express error handler for all errors',
@@ -317,13 +338,13 @@ module.exports.ParseServerOptions = {
maintenanceKey: {
env: 'PARSE_SERVER_MAINTENANCE_KEY',
help:
'(Optional) The maintenance key is used for modifying internal fields of Parse Server.<br><br>\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.',
'(Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.<br><br>\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.',
required: true,
},
maintenanceKeyIps: {
env: 'PARSE_SERVER_MAINTENANCE_KEY_IPS',
help:
"(Optional) Restricts the use of maintenance key permissions to a list of IP addresses.<br><br>This option accepts a list of single IP addresses, for example:<br>`['10.0.0.1', '10.0.0.2']`<br><br>You can also use CIDR notation to specify an IP address range, for example:<br>`['10.0.1.0/24']`<br><br>Special cases:<br>- Setting an empty array `[]` means that `maintenanceKey` cannot be used even in Parse Server Cloud Code.<br>- Setting `['0.0.0.0/0']` means disabling the filter and the maintenance key can be used from any IP address.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server itself, is allowed to use the maintenance key.",
"(Optional) Restricts the use of maintenance key permissions to a list of IP addresses or ranges.<br><br>This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.<br><br><b>Special scenarios:</b><br>- Setting an empty array `[]` means that the maintenance key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.<br>- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the maintenance key and effectively disables the IP filter.<br><br><b>Considerations:</b><br>- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.<br>- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.<br>- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.<br>- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the maintenance key.",
action: parsers.arrayParser,
default: ['127.0.0.1', '::1'],
},
@@ -335,7 +356,7 @@ module.exports.ParseServerOptions = {
masterKeyIps: {
env: 'PARSE_SERVER_MASTER_KEY_IPS',
help:
"(Optional) Restricts the use of master key permissions to a list of IP addresses.<br><br>This option accepts a list of single IP addresses, for example:<br>`['10.0.0.1', '10.0.0.2']`<br><br>You can also use CIDR notation to specify an IP address range, for example:<br>`['10.0.1.0/24']`<br><br>Special cases:<br>- Setting an empty array `[]` means that `masterKey` cannot be used even in Parse Server Cloud Code.<br>- Setting `['0.0.0.0/0']` means disabling the filter and the master key can be used from any IP address.<br><br>To connect Parse Dashboard from a different server requires to add the IP address of the server that hosts Parse Dashboard because Parse Dashboard uses the master key.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server itself, is allowed to use the master key.",
"(Optional) Restricts the use of master key permissions to a list of IP addresses or ranges.<br><br>This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.<br><br><b>Special scenarios:</b><br>- Setting an empty array `[]` means that the master key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.<br>- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the master key and effectively disables the IP filter.<br><br><b>Considerations:</b><br>- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.<br>- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.<br>- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.<br>- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the master key.",
action: parsers.arrayParser,
default: ['127.0.0.1', '::1'],
},
+6 -3
View File
@@ -28,6 +28,8 @@
* @property {String} cloud Full path to your cloud code main.js
* @property {Number|Boolean} cluster Run with cluster, optionally set the number of processes default to os.cpus().length
* @property {String} collectionPrefix A collection prefix for the classes
* @property {Boolean} convertEmailToLowercase Optional. If set to `true`, the `email` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `email` property is stored as set, without any case modifications. Default is `false`.
* @property {Boolean} convertUsernameToLowercase Optional. If set to `true`, the `username` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `username` property is stored as set, without any case modifications. Default is `false`.
* @property {CustomPagesOptions} customPages custom pages for password validation and reset
* @property {Adapter<StorageAdapter>} databaseAdapter Adapter module for the database; any options that are not explicitly described here are passed directly to the database client.
* @property {DatabaseOptions} databaseOptions Options to pass to the database client
@@ -39,6 +41,7 @@
* @property {Boolean} emailVerifyTokenReuseIfValid Set to `true` if a email verification token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.<br><br>Default is `false`.<br>Requires option `verifyUserEmails: true`.
* @property {Number} emailVerifyTokenValidityDuration Set the validity duration of the email verification token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.<br><br>For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).<br><br>Default is `undefined`.<br>Requires option `verifyUserEmails: true`.
* @property {Boolean} enableAnonymousUsers Enable (or disable) anonymous users, defaults to true
* @property {Boolean} enableCollationCaseComparison Optional. If set to `true`, the collation rule of case comparison for queries and indexes is enabled. Enable this option to run Parse Server with MongoDB Atlas Serverless or AWS Amazon DocumentDB. If `false`, the collation rule of case comparison is disabled. Default is `false`.
* @property {Boolean} enableExpressErrorHandler Enables the default express error handler for all errors
* @property {Boolean} encodeParseObjectInCloudFunction If set to `true`, a `Parse.Object` that is in the payload when calling a Cloud Function will be converted to an instance of `Parse.Object`. If `false`, the object will not be converted and instead be a plain JavaScript object, which contains the raw data of a `Parse.Object` but is not an actual instance of `Parse.Object`. Default is `false`. <br><br>ℹ️ The expected behavior would be that the object is converted to an instance of `Parse.Object`, so you would normally set this option to `true`. The default is `false` because this is a temporary option that has been introduced to avoid a breaking change when fixing a bug where JavaScript objects are not converted to actual instances of `Parse.Object`.
* @property {String} encryptionKey Key for encrypting your files
@@ -60,10 +63,10 @@
* @property {String} logLevel Sets the level for logs
* @property {LogLevels} logLevels (Optional) Overrides the log levels used internally by Parse Server to log events.
* @property {String} logsFolder Folder for the logs (defaults to './logs'); set to null to disable file based logging
* @property {String} maintenanceKey (Optional) The maintenance key is used for modifying internal fields of Parse Server.<br><br>⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.
* @property {String[]} maintenanceKeyIps (Optional) Restricts the use of maintenance key permissions to a list of IP addresses.<br><br>This option accepts a list of single IP addresses, for example:<br>`['10.0.0.1', '10.0.0.2']`<br><br>You can also use CIDR notation to specify an IP address range, for example:<br>`['10.0.1.0/24']`<br><br>Special cases:<br>- Setting an empty array `[]` means that `maintenanceKey` cannot be used even in Parse Server Cloud Code.<br>- Setting `['0.0.0.0/0']` means disabling the filter and the maintenance key can be used from any IP address.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server itself, is allowed to use the maintenance key.
* @property {String} maintenanceKey (Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.<br><br>⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.
* @property {String[]} maintenanceKeyIps (Optional) Restricts the use of maintenance key permissions to a list of IP addresses or ranges.<br><br>This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.<br><br><b>Special scenarios:</b><br>- Setting an empty array `[]` means that the maintenance key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.<br>- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the maintenance key and effectively disables the IP filter.<br><br><b>Considerations:</b><br>- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.<br>- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.<br>- When setting the option via an environment variable the notation is a comma-separated string, for example `"0.0.0.0/0,::0"`.<br>- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the maintenance key.
* @property {String} masterKey Your Parse Master Key
* @property {String[]} masterKeyIps (Optional) Restricts the use of master key permissions to a list of IP addresses.<br><br>This option accepts a list of single IP addresses, for example:<br>`['10.0.0.1', '10.0.0.2']`<br><br>You can also use CIDR notation to specify an IP address range, for example:<br>`['10.0.1.0/24']`<br><br>Special cases:<br>- Setting an empty array `[]` means that `masterKey` cannot be used even in Parse Server Cloud Code.<br>- Setting `['0.0.0.0/0']` means disabling the filter and the master key can be used from any IP address.<br><br>To connect Parse Dashboard from a different server requires to add the IP address of the server that hosts Parse Dashboard because Parse Dashboard uses the master key.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server itself, is allowed to use the master key.
* @property {String[]} masterKeyIps (Optional) Restricts the use of master key permissions to a list of IP addresses or ranges.<br><br>This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.<br><br><b>Special scenarios:</b><br>- Setting an empty array `[]` means that the master key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.<br>- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the master key and effectively disables the IP filter.<br><br><b>Considerations:</b><br>- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.<br>- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.<br>- When setting the option via an environment variable the notation is a comma-separated string, for example `"0.0.0.0/0,::0"`.<br>- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the master key.
* @property {Number} maxLimit Max value for limit option on queries, defaults to unlimited
* @property {Number|String} maxLogFiles Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null)
* @property {String} maxUploadSize Max file size for uploads, defaults to 20mb
+13 -4
View File
@@ -47,16 +47,16 @@ export interface ParseServerOptions {
appId: string;
/* Your Parse Master Key */
masterKey: string;
/* (Optional) The maintenance key is used for modifying internal fields of Parse Server.<br><br>⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server. */
/* (Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.<br><br>⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server. */
maintenanceKey: string;
/* URL to your parse server with http:// or https://.
:ENV: PARSE_SERVER_URL */
serverURL: string;
/* (Optional) Restricts the use of master key permissions to a list of IP addresses.<br><br>This option accepts a list of single IP addresses, for example:<br>`['10.0.0.1', '10.0.0.2']`<br><br>You can also use CIDR notation to specify an IP address range, for example:<br>`['10.0.1.0/24']`<br><br>Special cases:<br>- Setting an empty array `[]` means that `masterKey` cannot be used even in Parse Server Cloud Code.<br>- Setting `['0.0.0.0/0']` means disabling the filter and the master key can be used from any IP address.<br><br>To connect Parse Dashboard from a different server requires to add the IP address of the server that hosts Parse Dashboard because Parse Dashboard uses the master key.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server itself, is allowed to use the master key.
/* (Optional) Restricts the use of master key permissions to a list of IP addresses or ranges.<br><br>This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.<br><br><b>Special scenarios:</b><br>- Setting an empty array `[]` means that the master key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.<br>- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the master key and effectively disables the IP filter.<br><br><b>Considerations:</b><br>- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.<br>- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.<br>- When setting the option via an environment variable the notation is a comma-separated string, for example `"0.0.0.0/0,::0"`.<br>- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the master key.
:DEFAULT: ["127.0.0.1","::1"] */
masterKeyIps: ?(string[]);
/* (Optional) Restricts the use of maintenance key permissions to a list of IP addresses.<br><br>This option accepts a list of single IP addresses, for example:<br>`['10.0.0.1', '10.0.0.2']`<br><br>You can also use CIDR notation to specify an IP address range, for example:<br>`['10.0.1.0/24']`<br><br>Special cases:<br>- Setting an empty array `[]` means that `maintenanceKey` cannot be used even in Parse Server Cloud Code.<br>- Setting `['0.0.0.0/0']` means disabling the filter and the maintenance key can be used from any IP address.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server itself, is allowed to use the maintenance key.
:DEFAULT: ["127.0.0.1","::1"] */
/* (Optional) Restricts the use of maintenance key permissions to a list of IP addresses or ranges.<br><br>This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.<br><br><b>Special scenarios:</b><br>- Setting an empty array `[]` means that the maintenance key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.<br>- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the maintenance key and effectively disables the IP filter.<br><br><b>Considerations:</b><br>- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.<br>- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.<br>- When setting the option via an environment variable the notation is a comma-separated string, for example `"0.0.0.0/0,::0"`.<br>- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.<br><br>Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the maintenance key.
:DEFAULT: ["127.0.0.1","::1"] */
maintenanceKeyIps: ?(string[]);
/* Sets the app name */
appName: ?string;
@@ -103,6 +103,15 @@ export interface ParseServerOptions {
databaseOptions: ?DatabaseOptions;
/* Adapter module for the database; any options that are not explicitly described here are passed directly to the database client. */
databaseAdapter: ?Adapter<StorageAdapter>;
/* Optional. If set to `true`, the collation rule of case comparison for queries and indexes is enabled. Enable this option to run Parse Server with MongoDB Atlas Serverless or AWS Amazon DocumentDB. If `false`, the collation rule of case comparison is disabled. Default is `false`.
:DEFAULT: false */
enableCollationCaseComparison: ?boolean;
/* Optional. If set to `true`, the `email` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `email` property is stored as set, without any case modifications. Default is `false`.
:DEFAULT: false */
convertEmailToLowercase: ?boolean;
/* Optional. If set to `true`, the `username` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `username` property is stored as set, without any case modifications. Default is `false`.
:DEFAULT: false */
convertUsernameToLowercase: ?boolean;
/* Full path to your cloud code main.js */
cloud: ?string;
/* A collection prefix for the classes
+2
View File
@@ -75,6 +75,8 @@ class ParseServer {
const allControllers = controllers.getControllers(options);
options.state = 'initialized';
this.config = Config.put(Object.assign({}, options, allControllers));
this.config.masterKeyIpsStore = new Map();
this.config.maintenanceKeyIpsStore = new Map();
logging.setLogger(allControllers.loggerController);
}
+7 -1
View File
@@ -478,6 +478,7 @@ _UnsafeRestQuery.prototype.replaceInQuery = async function () {
className: inQueryValue.className,
restWhere: inQueryValue.where,
restOptions: additionalOptions,
context: this.context,
});
return subquery.execute().then(response => {
transformInQuery(inQueryObject, subquery.className, response.results);
@@ -537,6 +538,7 @@ _UnsafeRestQuery.prototype.replaceNotInQuery = async function () {
className: notInQueryValue.className,
restWhere: notInQueryValue.where,
restOptions: additionalOptions,
context: this.context,
});
return subquery.execute().then(response => {
@@ -609,6 +611,7 @@ _UnsafeRestQuery.prototype.replaceSelect = async function () {
className: selectValue.query.className,
restWhere: selectValue.query.where,
restOptions: additionalOptions,
context: this.context,
});
return subquery.execute().then(response => {
@@ -671,6 +674,7 @@ _UnsafeRestQuery.prototype.replaceDontSelect = async function () {
className: dontSelectValue.query.className,
restWhere: dontSelectValue.query.where,
restOptions: additionalOptions,
context: this.context,
});
return subquery.execute().then(response => {
@@ -860,6 +864,7 @@ _UnsafeRestQuery.prototype.handleInclude = function () {
this.auth,
this.response,
this.include[0],
this.context,
this.restOptions
);
if (pathResponse.then) {
@@ -946,7 +951,7 @@ _UnsafeRestQuery.prototype.handleAuthAdapters = async function () {
// Adds included values to the response.
// Path is a list of field names.
// Returns a promise for an augmented response.
function includePath(config, auth, response, path, restOptions = {}) {
function includePath(config, auth, response, path, context, restOptions = {}) {
var pointers = findPointers(response.results, path);
if (pointers.length == 0) {
return response;
@@ -1026,6 +1031,7 @@ function includePath(config, auth, response, path, restOptions = {}) {
className,
restWhere: where,
restOptions: includeRestOptions,
context: context,
});
return query.execute({ op: 'get' }).then(results => {
results.className = className;
+31 -2
View File
@@ -368,9 +368,36 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () {
};
// Add default fields
this.data.updatedAt = this.updatedAt;
if (!this.query) {
this.data.createdAt = this.updatedAt;
// allow customizing createdAt and updatedAt when using maintenance key
if (
this.auth.isMaintenance &&
this.data.createdAt &&
this.data.createdAt.__type === 'Date'
) {
this.data.createdAt = this.data.createdAt.iso;
if (this.data.updatedAt && this.data.updatedAt.__type === 'Date') {
const createdAt = new Date(this.data.createdAt);
const updatedAt = new Date(this.data.updatedAt.iso);
if (updatedAt < createdAt) {
throw new Parse.Error(
Parse.Error.VALIDATION_ERROR,
'updatedAt cannot occur before createdAt'
);
}
this.data.updatedAt = this.data.updatedAt.iso;
}
// if no updatedAt is provided, set it to createdAt to match default behavior
else {
this.data.updatedAt = this.data.createdAt;
}
} else {
this.data.updatedAt = this.updatedAt;
this.data.createdAt = this.updatedAt;
}
// Only assign new objectId if we are creating new object
if (!this.data.objectId) {
@@ -382,6 +409,8 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () {
});
}
} else if (schema) {
this.data.updatedAt = this.updatedAt;
Object.keys(this.data).forEach(fieldName => {
setRequiredFieldIfNeeded(fieldName, false);
});
+7
View File
@@ -106,6 +106,13 @@ export class ClassesRouter extends PromiseRouter {
}
handleCreate(req) {
if (
this.className(req) === '_User' &&
typeof req.body?.objectId === 'string' &&
req.body.objectId.startsWith('role:')
) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.');
}
return rest.create(
req.config,
req.auth,
+2 -2
View File
@@ -159,9 +159,9 @@ export class FilesRouter {
} else if (contentType && contentType.includes('/')) {
extension = contentType.split('/')[1];
}
extension = extension.split(' ').join('');
extension = extension?.split(' ')?.join('');
if (!isValidExtension(extension)) {
if (extension && !isValidExtension(extension)) {
next(
new Parse.Error(
Parse.Error.FILE_SAVE_ERROR,
+1 -1
View File
@@ -12,7 +12,7 @@ import { logger } from '../logger';
function parseObject(obj, config) {
if (Array.isArray(obj)) {
return obj.map(item => {
return parseObject(item);
return parseObject(item, config);
});
} else if (obj && obj.__type == 'Date') {
return Object.assign(new Date(obj.iso), obj);
+44 -3
View File
@@ -10,9 +10,9 @@ import PostgresStorageAdapter from './Adapters/Storage/Postgres/PostgresStorageA
import rateLimit from 'express-rate-limit';
import { RateLimitOptions } from './Options/Definitions';
import { pathToRegexp } from 'path-to-regexp';
import ipRangeCheck from 'ip-range-check';
import RedisStore from 'rate-limit-redis';
import { createClient } from 'redis';
import { BlockList, isIPv4 } from 'net';
export const DEFAULT_ALLOWED_HEADERS =
'X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, X-Parse-Request-Id, Content-Type, Pragma, Cache-Control';
@@ -23,6 +23,46 @@ const getMountForRequest = function (req) {
return req.protocol + '://' + req.get('host') + mountPath;
};
const getBlockList = (ipRangeList, store) => {
if (store.get('blockList')) return store.get('blockList');
const blockList = new BlockList();
ipRangeList.forEach(fullIp => {
if (fullIp === '::/0' || fullIp === '::') {
store.set('allowAllIpv6', true);
return;
}
if (fullIp === '0.0.0.0/0' || fullIp === '0.0.0.0') {
store.set('allowAllIpv4', true);
return;
}
const [ip, mask] = fullIp.split('/');
if (!mask) {
blockList.addAddress(ip, isIPv4(ip) ? 'ipv4' : 'ipv6');
} else {
blockList.addSubnet(ip, Number(mask), isIPv4(ip) ? 'ipv4' : 'ipv6');
}
});
store.set('blockList', blockList);
return blockList;
};
export const checkIp = (ip, ipRangeList, store) => {
const incomingIpIsV4 = isIPv4(ip);
const blockList = getBlockList(ipRangeList, store);
if (store.get(ip)) return true;
if (store.get('allowAllIpv4') && incomingIpIsV4) return true;
if (store.get('allowAllIpv6') && !incomingIpIsV4) return true;
const result = blockList.check(ip, incomingIpIsV4 ? 'ipv4' : 'ipv6');
// If the ip is in the list, we store the result in the store
// so we have a optimized path for the next request
if (ipRangeList.includes(ip) && result) {
store.set(ip, result);
}
return result;
};
// Checks that the request is authorized for this app and checks user
// auth too.
// The bodyparser should run before this middleware.
@@ -183,7 +223,7 @@ export function handleParseHeaders(req, res, next) {
const isMaintenance =
req.config.maintenanceKey && info.maintenanceKey === req.config.maintenanceKey;
if (isMaintenance) {
if (ipRangeCheck(clientIp, req.config.maintenanceKeyIps || [])) {
if (checkIp(clientIp, req.config.maintenanceKeyIps || [], req.config.maintenanceKeyIpsStore)) {
req.auth = new auth.Auth({
config: req.config,
installationId: info.installationId,
@@ -199,7 +239,8 @@ export function handleParseHeaders(req, res, next) {
}
let isMaster = info.masterKey === req.config.masterKey;
if (isMaster && !ipRangeCheck(clientIp, req.config.masterKeyIps || [])) {
if (isMaster && !checkIp(clientIp, req.config.masterKeyIps || [], req.config.masterKeyIpsStore)) {
const log = req.config?.loggerController || defaultLogger;
log.error(
`Request using master key rejected as the request IP address '${clientIp}' is not set in Parse Server option 'masterKeyIps'.`
+7 -1
View File
@@ -86,6 +86,12 @@ const Category = {
};
function getStore(category, name, applicationId) {
const invalidNameRegex = /['"`]/;
if (invalidNameRegex.test(name)) {
// Prevent a malicious user from injecting properties into the store
return {};
}
const path = name.split('.');
path.splice(-1); // remove last component
applicationId = applicationId || Parse.applicationId;
@@ -94,7 +100,7 @@ function getStore(category, name, applicationId) {
for (const component of path) {
store = store[component];
if (!store) {
return undefined;
return {};
}
}
return store;