Skip to content

Commit 50eed3c

Browse files
authored
fix: Remote code execution via MongoDB BSON parser through prototype pollution; fixes security vulnerability [GHSA-prm5-8g2m-24gg](GHSA-prm5-8g2m-24gg) (parse-community#8295)
1 parent 12e174b commit 50eed3c

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

spec/vulnerabilities.spec.js

+38
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,44 @@ describe('Vulnerabilities', () => {
279279
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
280280
expect(text.error).toBe('Prohibited keyword in request data: {"value":"aValue[123]*"}.');
281281
});
282+
283+
it('denies BSON type code data in file metadata', async () => {
284+
const str = 'Hello World!';
285+
const data = [];
286+
for (let i = 0; i < str.length; i++) {
287+
data.push(str.charCodeAt(i));
288+
}
289+
const file = new Parse.File('hello.txt', data, 'text/plain');
290+
file.addMetadata('obj', {
291+
_bsontype: 'Code',
292+
code: 'delete Object.prototype.evalFunctions',
293+
});
294+
await expectAsync(file.save()).toBeRejectedWith(
295+
new Parse.Error(
296+
Parse.Error.INVALID_KEY_NAME,
297+
`Prohibited keyword in request data: {"key":"_bsontype","value":"Code"}.`
298+
)
299+
);
300+
});
301+
302+
it('denies BSON type code data in file tags', async () => {
303+
const str = 'Hello World!';
304+
const data = [];
305+
for (let i = 0; i < str.length; i++) {
306+
data.push(str.charCodeAt(i));
307+
}
308+
const file = new Parse.File('hello.txt', data, 'text/plain');
309+
file.addTag('obj', {
310+
_bsontype: 'Code',
311+
code: 'delete Object.prototype.evalFunctions',
312+
});
313+
await expectAsync(file.save()).toBeRejectedWith(
314+
new Parse.Error(
315+
Parse.Error.INVALID_KEY_NAME,
316+
`Prohibited keyword in request data: {"key":"_bsontype","value":"Code"}.`
317+
)
318+
);
319+
});
282320
});
283321

284322
describe('Ignore non-matches', () => {

src/Routers/FilesRouter.js

+18
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import mime from 'mime';
77
import logger from '../logger';
88
const triggers = require('../triggers');
99
const http = require('http');
10+
const Utils = require('../Utils');
1011

1112
const downloadFileFromURI = uri => {
1213
return new Promise((res, rej) => {
@@ -140,6 +141,23 @@ export class FilesRouter {
140141
const base64 = req.body.toString('base64');
141142
const file = new Parse.File(filename, { base64 }, contentType);
142143
const { metadata = {}, tags = {} } = req.fileData || {};
144+
if (req.config && req.config.requestKeywordDenylist) {
145+
// Scan request data for denied keywords
146+
for (const keyword of req.config.requestKeywordDenylist) {
147+
const match =
148+
Utils.objectContainsKeyValue(metadata, keyword.key, keyword.value) ||
149+
Utils.objectContainsKeyValue(tags, keyword.key, keyword.value);
150+
if (match) {
151+
next(
152+
new Parse.Error(
153+
Parse.Error.INVALID_KEY_NAME,
154+
`Prohibited keyword in request data: ${JSON.stringify(keyword)}.`
155+
)
156+
);
157+
return;
158+
}
159+
}
160+
}
143161
file.setTags(tags);
144162
file.setMetadata(metadata);
145163
const fileSize = Buffer.byteLength(req.body);

0 commit comments

Comments
 (0)