Skip to content

Commit

Permalink
fix: Remote code execution via MongoDB BSON parser through prototype …
Browse files Browse the repository at this point in the history
…pollution; fixes security vulnerability [GHSA-prm5-8g2m-24gg](GHSA-prm5-8g2m-24gg) (#8296)
  • Loading branch information
mtrezza authored Nov 7, 2022
1 parent 041e604 commit 47cfeee
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 0 deletions.
38 changes: 38 additions & 0 deletions spec/vulnerabilities.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,44 @@ describe('Vulnerabilities', () => {
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe('Prohibited keyword in request data: {"value":"aValue[123]*"}.');
});

it('denies BSON type code data in file metadata', async () => {
const str = 'Hello World!';
const data = [];
for (let i = 0; i < str.length; i++) {
data.push(str.charCodeAt(i));
}
const file = new Parse.File('hello.txt', data, 'text/plain');
file.addMetadata('obj', {
_bsontype: 'Code',
code: 'delete Object.prototype.evalFunctions',
});
await expectAsync(file.save()).toBeRejectedWith(
new Parse.Error(
Parse.Error.INVALID_KEY_NAME,
`Prohibited keyword in request data: {"key":"_bsontype","value":"Code"}.`
)
);
});

it('denies BSON type code data in file tags', async () => {
const str = 'Hello World!';
const data = [];
for (let i = 0; i < str.length; i++) {
data.push(str.charCodeAt(i));
}
const file = new Parse.File('hello.txt', data, 'text/plain');
file.addTag('obj', {
_bsontype: 'Code',
code: 'delete Object.prototype.evalFunctions',
});
await expectAsync(file.save()).toBeRejectedWith(
new Parse.Error(
Parse.Error.INVALID_KEY_NAME,
`Prohibited keyword in request data: {"key":"_bsontype","value":"Code"}.`
)
);
});
});

describe('Ignore non-matches', () => {
Expand Down
18 changes: 18 additions & 0 deletions src/Routers/FilesRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import mime from 'mime';
import logger from '../logger';
const triggers = require('../triggers');
const http = require('http');
const Utils = require('../Utils');

const downloadFileFromURI = uri => {
return new Promise((res, rej) => {
Expand Down Expand Up @@ -118,6 +119,23 @@ export class FilesRouter {
const base64 = req.body.toString('base64');
const file = new Parse.File(filename, { base64 }, contentType);
const { metadata = {}, tags = {} } = req.fileData || {};
if (req.config && req.config.requestKeywordDenylist) {
// Scan request data for denied keywords
for (const keyword of req.config.requestKeywordDenylist) {
const match =
Utils.objectContainsKeyValue(metadata, keyword.key, keyword.value) ||
Utils.objectContainsKeyValue(tags, keyword.key, keyword.value);
if (match) {
next(
new Parse.Error(
Parse.Error.INVALID_KEY_NAME,
`Prohibited keyword in request data: ${JSON.stringify(keyword)}.`
)
);
return;
}
}
}
file.setTags(tags);
file.setMetadata(metadata);
const fileSize = Buffer.byteLength(req.body);
Expand Down

0 comments on commit 47cfeee

Please sign in to comment.