Skip to content

Commit cb58be4

Browse files
committed
fix-release-7vr6
1 parent 1506273 commit cb58be4

File tree

6 files changed

+101
-34
lines changed

6 files changed

+101
-34
lines changed

.eslintrc.json

+3
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,8 @@
2525
"space-infix-ops": "error",
2626
"no-useless-escape": "off",
2727
"require-atomic-updates": "off"
28+
},
29+
"globals": {
30+
"Parse": true
2831
}
2932
}

spec/vulnerabilities.spec.js

+65
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,71 @@ describe('Vulnerabilities', () => {
138138
);
139139
});
140140

141+
it('denies creating global config with polluted data', async () => {
142+
const headers = {
143+
'Content-Type': 'application/json',
144+
'X-Parse-Application-Id': 'test',
145+
'X-Parse-Master-Key': 'test',
146+
};
147+
const params = {
148+
method: 'PUT',
149+
url: 'http://localhost:8378/1/config',
150+
json: true,
151+
body: {
152+
params: {
153+
welcomeMesssage: 'Welcome to Parse',
154+
foo: { _bsontype: 'Code', code: 'shell' },
155+
},
156+
},
157+
headers,
158+
};
159+
const response = await request(params).catch(e => e);
160+
expect(response.status).toBe(400);
161+
const text = JSON.parse(response.text);
162+
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
163+
expect(text.error).toBe(
164+
'Prohibited keyword in request data: {"key":"_bsontype","value":"Code"}.'
165+
);
166+
});
167+
168+
it('denies direct database write wih prohibited keys', async () => {
169+
const Config = require('../lib/Config');
170+
const config = Config.get(Parse.applicationId);
171+
const user = {
172+
objectId: '1234567890',
173+
username: 'hello',
174+
password: 'pass',
175+
_session_token: 'abc',
176+
foo: { _bsontype: 'Code', code: 'shell' },
177+
};
178+
await expectAsync(config.database.create('_User', user)).toBeRejectedWith(
179+
new Parse.Error(
180+
Parse.Error.INVALID_KEY_NAME,
181+
'Prohibited keyword in request data: {"key":"_bsontype","value":"Code"}.'
182+
)
183+
);
184+
});
185+
186+
it('denies direct database update wih prohibited keys', async () => {
187+
const Config = require('../lib/Config');
188+
const config = Config.get(Parse.applicationId);
189+
const user = {
190+
objectId: '1234567890',
191+
username: 'hello',
192+
password: 'pass',
193+
_session_token: 'abc',
194+
foo: { _bsontype: 'Code', code: 'shell' },
195+
};
196+
await expectAsync(
197+
config.database.update('_User', { _id: user.objectId }, user)
198+
).toBeRejectedWith(
199+
new Parse.Error(
200+
Parse.Error.INVALID_KEY_NAME,
201+
'Prohibited keyword in request data: {"key":"_bsontype","value":"Code"}.'
202+
)
203+
);
204+
});
205+
141206
it('denies creating a hook with polluted data', async () => {
142207
const express = require('express');
143208
const bodyParser = require('body-parser');

src/Controllers/DatabaseController.js

+10
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,11 @@ class DatabaseController {
475475
validateOnly: boolean = false,
476476
validSchemaController: SchemaController.SchemaController
477477
): Promise<any> {
478+
try {
479+
Utils.checkProhibitedKeywords(this.options, update);
480+
} catch (error) {
481+
return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, error));
482+
}
478483
const originalQuery = query;
479484
const originalUpdate = update;
480485
// Make a copy of the object, so we don't mutate the incoming data.
@@ -805,6 +810,11 @@ class DatabaseController {
805810
validateOnly: boolean = false,
806811
validSchemaController: SchemaController.SchemaController
807812
): Promise<any> {
813+
try {
814+
Utils.checkProhibitedKeywords(this.options, object);
815+
} catch (error) {
816+
return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, error));
817+
}
808818
// Make a copy of the object, so we don't mutate the incoming data.
809819
const originalObject = object;
810820
object = transformObjectACL(object);

src/RestWrite.js

+5-18
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ function RestWrite(config, auth, className, query, data, originalData, clientSDK
6464
}
6565
}
6666

67-
this.checkProhibitedKeywords(data);
68-
6967
// When the operation is complete, this.response may have several
7068
// fields.
7169
// response: the actual data to be returned
@@ -298,7 +296,11 @@ RestWrite.prototype.runBeforeSaveTrigger = function () {
298296
delete this.data.objectId;
299297
}
300298
}
301-
this.checkProhibitedKeywords(this.data);
299+
try {
300+
Utils.checkProhibitedKeywords(this.config, this.data);
301+
} catch (error) {
302+
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, error);
303+
}
302304
});
303305
};
304306

@@ -1756,20 +1758,5 @@ RestWrite.prototype._updateResponseWithData = function (response, data) {
17561758
return response;
17571759
};
17581760

1759-
RestWrite.prototype.checkProhibitedKeywords = function (data) {
1760-
if (this.config.requestKeywordDenylist) {
1761-
// Scan request data for denied keywords
1762-
for (const keyword of this.config.requestKeywordDenylist) {
1763-
const match = Utils.objectContainsKeyValue(data, keyword.key, keyword.value);
1764-
if (match) {
1765-
throw new Parse.Error(
1766-
Parse.Error.INVALID_KEY_NAME,
1767-
`Prohibited keyword in request data: ${JSON.stringify(keyword)}.`
1768-
);
1769-
}
1770-
}
1771-
}
1772-
};
1773-
17741761
export default RestWrite;
17751762
module.exports = RestWrite;

src/Routers/FilesRouter.js

+6-16
Original file line numberDiff line numberDiff line change
@@ -175,22 +175,12 @@ export class FilesRouter {
175175
const base64 = req.body.toString('base64');
176176
const file = new Parse.File(filename, { base64 }, contentType);
177177
const { metadata = {}, tags = {} } = req.fileData || {};
178-
if (req.config && req.config.requestKeywordDenylist) {
179-
// Scan request data for denied keywords
180-
for (const keyword of req.config.requestKeywordDenylist) {
181-
const match =
182-
Utils.objectContainsKeyValue(metadata, keyword.key, keyword.value) ||
183-
Utils.objectContainsKeyValue(tags, keyword.key, keyword.value);
184-
if (match) {
185-
next(
186-
new Parse.Error(
187-
Parse.Error.INVALID_KEY_NAME,
188-
`Prohibited keyword in request data: ${JSON.stringify(keyword)}.`
189-
)
190-
);
191-
return;
192-
}
193-
}
178+
try {
179+
Utils.checkProhibitedKeywords(config, metadata);
180+
Utils.checkProhibitedKeywords(config, tags);
181+
} catch (error) {
182+
next(new Parse.Error(Parse.Error.INVALID_KEY_NAME, error));
183+
return;
194184
}
195185
file.setTags(tags);
196186
file.setMetadata(metadata);

src/Utils.js

+12
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,18 @@ class Utils {
358358
}
359359
return false;
360360
}
361+
362+
static checkProhibitedKeywords(config, data) {
363+
if (config?.requestKeywordDenylist) {
364+
// Scan request data for denied keywords
365+
for (const keyword of config.requestKeywordDenylist) {
366+
const match = Utils.objectContainsKeyValue(data, keyword.key, keyword.value);
367+
if (match) {
368+
throw `Prohibited keyword in request data: ${JSON.stringify(keyword)}.`;
369+
}
370+
}
371+
}
372+
}
361373
}
362374

363375
module.exports = Utils;

0 commit comments

Comments
 (0)