mirror of
https://github.com/parse-community/parse-server
synced 2026-08-09 13:03:18 +00:00
fix: Cloud function dispatch crashes server via prototype chain traversal ([GHSA-4263-jgmp-7pf4](https://github.com/parse-community/parse-server/security/advisories/GHSA-4263-jgmp-7pf4)) (#10210)
This commit is contained in:
@@ -251,6 +251,71 @@ describe('Vulnerabilities', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('(GHSA-4263-jgmp-7pf4) Cloud function prototype chain dispatch via registered function', () => {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
Parse.Cloud.define('legitimateFunction', () => 'ok');
|
||||
});
|
||||
|
||||
it('rejects prototype chain traversal from a registered function name', async () => {
|
||||
const response = await request({
|
||||
headers,
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8378/1/functions/legitimateFunction.__proto__.__proto__.constructor',
|
||||
body: JSON.stringify({}),
|
||||
}).catch(e => e);
|
||||
expect(response.status).toBe(400);
|
||||
const text = JSON.parse(response.text);
|
||||
expect(text.code).toBe(Parse.Error.SCRIPT_FAILED);
|
||||
expect(text.error).toContain('Invalid function');
|
||||
});
|
||||
|
||||
it('rejects prototype chain traversal via single __proto__ from a registered function', async () => {
|
||||
const response = await request({
|
||||
headers,
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8378/1/functions/legitimateFunction.__proto__.constructor',
|
||||
body: JSON.stringify({}),
|
||||
}).catch(e => e);
|
||||
expect(response.status).toBe(400);
|
||||
const text = JSON.parse(response.text);
|
||||
expect(text.code).toBe(Parse.Error.SCRIPT_FAILED);
|
||||
expect(text.error).toContain('Invalid function');
|
||||
});
|
||||
|
||||
it('does not crash the server when prototype chain traversal is attempted', async () => {
|
||||
const maliciousNames = [
|
||||
'legitimateFunction.__proto__.__proto__.constructor',
|
||||
'legitimateFunction.__proto__.constructor',
|
||||
'legitimateFunction.constructor',
|
||||
'legitimateFunction.__proto__',
|
||||
];
|
||||
for (const name of maliciousNames) {
|
||||
const response = await request({
|
||||
headers,
|
||||
method: 'POST',
|
||||
url: `http://localhost:8378/1/functions/${encodeURIComponent(name)}`,
|
||||
body: JSON.stringify({}),
|
||||
}).catch(e => e);
|
||||
expect(response.status).toBe(400);
|
||||
}
|
||||
// Verify server is still responsive after all attempts
|
||||
const healthResponse = await request({
|
||||
headers,
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8378/1/functions/legitimateFunction',
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
expect(healthResponse.status).toBe(200);
|
||||
expect(JSON.parse(healthResponse.text).result).toBe('ok');
|
||||
});
|
||||
});
|
||||
|
||||
describe('(GHSA-3v4q-4q9g-x83q) Prototype pollution via application ID in trigger store', () => {
|
||||
const prototypeProperties = ['constructor', 'toString', 'valueOf', 'hasOwnProperty', '__proto__'];
|
||||
|
||||
|
||||
@@ -109,6 +109,9 @@ function getStore(category, name, applicationId) {
|
||||
_triggerStore[applicationId] = _triggerStore[applicationId] || baseStore();
|
||||
let store = _triggerStore[applicationId][category];
|
||||
for (const component of path) {
|
||||
if (!Object.prototype.hasOwnProperty.call(store, component)) {
|
||||
return createStore();
|
||||
}
|
||||
store = store[component];
|
||||
if (!store) {
|
||||
return createStore();
|
||||
@@ -137,6 +140,9 @@ function remove(category, name, applicationId) {
|
||||
function get(category, name, applicationId) {
|
||||
const lastComponent = name.split('.').splice(-1);
|
||||
const store = getStore(category, name, applicationId);
|
||||
if (!Object.prototype.hasOwnProperty.call(store, lastComponent)) {
|
||||
return undefined;
|
||||
}
|
||||
return store[lastComponent];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user