Skip to content

Commit 2e69bc7

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 60537fe + 308668c commit 2e69bc7

File tree

4 files changed

+80
-4
lines changed

4 files changed

+80
-4
lines changed

CHANGELOG.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ Jump directly to a version:
44

55
| 4.x |
66
|--------------------------------------|
7-
| [**4.10.2 (latest release)**](#4102) |
7+
| [**4.10.3 (latest release)**](#4103) |
8+
| [4.10.2](#4102) |
89
| [4.10.1](#4101) |
910
| [4.10.0](#4100) |
1011
| [4.5.2](#452) |
@@ -93,14 +94,16 @@ Jump directly to a version:
9394
___
9495

9596
## Unreleased (Master Branch)
96-
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.10.2...master)
97+
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.10.3...master)
98+
9799
### Breaking Changes
98100
- Improved schema caching through database real-time hooks. Reduces DB queries, decreases Parse Query execution time and fixes a potential schema memory leak. If multiple Parse Server instances connect to the same DB (for example behind a load balancer), set the [Parse Server Option](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html) `databaseOptions.enableSchemaHooks: true` to enable this feature and keep the schema in sync across all instances. Failing to do so will cause a schema change to not propagate to other instances and re-syncing will only happen when these instances restart. The options `enableSingleSchemaCache` and `schemaCacheTTL` have been removed. To use this feature with MongoDB, a replica set cluster with [change stream](https://docs.mongodb.com/manual/changeStreams/#availability) support is required. (Diamond Lewis, SebC) [#7214](https://github.com/parse-community/parse-server/issues/7214)
99101
- Added file upload restriction. File upload is now only allowed for authenticated users by default for improved security. To allow file upload also for Anonymous Users or Public, set the `fileUpload` parameter in the [Parse Server Options](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html) (dblythy, Manuel Trezza) [#7071](https://github.com/parse-community/parse-server/pull/7071)
100102
- Removed [parse-server-simple-mailgun-adapter](https://github.com/parse-community/parse-server-simple-mailgun-adapter) dependency; to continue using the adapter it has to be explicitly installed (Manuel Trezza) [#7321](https://github.com/parse-community/parse-server/pull/7321)
101103
- Remove support for MongoDB 3.6 which has reached its End-of-Life date and PostgreSQL 10 (Manuel Trezza) [#7315](https://github.com/parse-community/parse-server/pull/7315)
102104
- Remove support for Node 10 which has reached its End-of-Life date (Manuel Trezza) [#7314](https://github.com/parse-community/parse-server/pull/7314)
103105
- Remove S3 Files Adapter from Parse Server, instead install separately as `@parse/s3-files-adapter` (Manuel Trezza) [#7324](https://github.com/parse-community/parse-server/pull/7324)
106+
104107
### Notable Changes
105108
- Added Parse Server Security Check to report weak security settings (Manuel Trezza, dblythy) [#7247](https://github.com/parse-community/parse-server/issues/7247)
106109
- EXPERIMENTAL: Added new page router with placeholder rendering and localization of custom and feature pages such as password reset and email verification (Manuel Trezza) [#7128](https://github.com/parse-community/parse-server/pull/7128)
@@ -147,6 +150,12 @@ ___
147150
- Add CI check to add changelog entry (Manuel Trezza) [#7512](https://github.com/parse-community/parse-server/pull/7512)
148151
- Refactor: uniform issue templates across repos (Manuel Trezza) [#7528](https://github.com/parse-community/parse-server/pull/7528)
149152

153+
## 4.10.3
154+
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.10.2...4.10.3)
155+
156+
### Security Fixes
157+
- Validate `explain` query parameter to avoid a server crash due to MongoDB bug [NODE-3463](https://jira.mongodb.org/browse/NODE-3463) (Kartal Kaan Bozdogan) [GHSA-xqp8-w826-hh6x](https://github.com/parse-community/parse-server/security/advisories/GHSA-xqp8-w826-hh6x)
158+
150159
## 4.10.2
151160
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.10.1...4.10.2)
152161

spec/ParseQuery.spec.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,54 @@ describe('Parse.Query testing', () => {
3737
});
3838
});
3939

40+
it_only_db('mongo')('gracefully handles invalid explain values', async () => {
41+
// Note that anything that is not truthy (like 0) does not cause an exception, as they get swallowed up by ClassesRouter::optionsFromBody
42+
const values = [1, 'yolo', { a: 1 }, [1, 2, 3]];
43+
for (const value of values) {
44+
try {
45+
await request({
46+
method: 'GET',
47+
url: `http://localhost:8378/1/classes/_User?explain=${value}`,
48+
json: true,
49+
headers: masterKeyHeaders,
50+
});
51+
fail('request did not throw');
52+
} catch (e) {
53+
// Expect that Parse Server did not crash
54+
expect(e.code).not.toEqual('ECONNRESET');
55+
// Expect that Parse Server validates the explain value and does not crash;
56+
// see https://jira.mongodb.org/browse/NODE-3463
57+
equal(e.data.code, Parse.Error.INVALID_QUERY);
58+
equal(e.data.error, 'Invalid value for explain');
59+
}
60+
// get queries (of the form '/classes/:className/:objectId' cannot have the explain key, see ClassesRouter.js)
61+
// so it is enough that we test find queries
62+
}
63+
});
64+
65+
it_only_db('mongo')('supports valid explain values', async () => {
66+
const values = [
67+
false,
68+
true,
69+
'queryPlanner',
70+
'executionStats',
71+
'allPlansExecution',
72+
// 'queryPlannerExtended' is excluded as it only applies to MongoDB Data Lake which is currently not available in our CI environment
73+
];
74+
for (const value of values) {
75+
const response = await request({
76+
method: 'GET',
77+
url: `http://localhost:8378/1/classes/_User?explain=${value}`,
78+
json: true,
79+
headers: masterKeyHeaders,
80+
});
81+
expect(response.status).toBe(200);
82+
if (value) {
83+
expect(response.data.results.ok).toBe(1);
84+
}
85+
}
86+
});
87+
4088
it('searching for null', function (done) {
4189
const baz = new TestObject({ foo: null });
4290
const qux = new TestObject({ foo: 'qux' });

src/Adapters/Storage/Mongo/MongoStorageAdapter.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,23 @@ const mongoSchemaFromFieldsAndClassNameAndCLP = (
108108
return mongoObject;
109109
};
110110

111+
function validateExplainValue(explain) {
112+
if (explain) {
113+
// The list of allowed explain values is from node-mongodb-native/lib/explain.js
114+
const explainAllowedValues = [
115+
'queryPlanner',
116+
'queryPlannerExtended',
117+
'executionStats',
118+
'allPlansExecution',
119+
false,
120+
true,
121+
];
122+
if (!explainAllowedValues.includes(explain)) {
123+
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Invalid value for explain');
124+
}
125+
}
126+
}
127+
111128
export class MongoStorageAdapter implements StorageAdapter {
112129
// Private
113130
_uri: string;
@@ -578,6 +595,7 @@ export class MongoStorageAdapter implements StorageAdapter {
578595
query: QueryType,
579596
{ skip, limit, sort, keys, readPreference, hint, caseInsensitive, explain }: QueryOptions
580597
): Promise<any> {
598+
validateExplainValue(explain);
581599
schema = convertParseSchemaToMongoSchema(schema);
582600
const mongoWhere = transformWhere(className, query, schema);
583601
const mongoSort = _.mapKeys(sort, (value, fieldName) =>
@@ -756,6 +774,7 @@ export class MongoStorageAdapter implements StorageAdapter {
756774
hint: ?mixed,
757775
explain?: boolean
758776
) {
777+
validateExplainValue(explain);
759778
let isPointerField = false;
760779
pipeline = pipeline.map(stage => {
761780
if (stage.$group) {

src/RestQuery.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ RestQuery.prototype.runFind = function (options = {}) {
657657
return this.config.database
658658
.find(this.className, this.restWhere, findOptions, this.auth)
659659
.then(results => {
660-
if (this.className === '_User' && findOptions.explain !== true) {
660+
if (this.className === '_User' && !findOptions.explain) {
661661
for (var result of results) {
662662
cleanResultAuthData(result);
663663
}
@@ -866,7 +866,7 @@ function includePath(config, auth, response, path, restOptions = {}) {
866866
return set;
867867
}
868868
}
869-
if (i == (keyPath.length - 1)) {
869+
if (i == keyPath.length - 1) {
870870
set.add(keyPath[i]);
871871
}
872872
return set;

0 commit comments

Comments
 (0)