## Vulnerability Key Information Summary ### Vulnerability Overview **Vulnerability Name**: Cloud Function validator bypass via prototype chain traversal **CVE ID**: GHSA-vpj2-4q7w-5qq8 **Vulnerability Type**: Security validation bypass caused by prototype pollution Attackers can bypass the `requireUser` and `requireMaster` validation mechanisms of cloud functions by appending `.prototype.constructor` or `.prototype` suffixes to function names, enabling unauthorized calls to protected cloud functions. --- ### Affected Scope - **Affected Versions**: Parse Server 9.7.1-alpha.4 to 9.7.0-alpha.11 - **Fixed Version**: commit dc59e27 --- ### Remediation Solution **Core Fix Code** (`src/triggers.js`): ```javascript function getStore(category, name, applicationId) { // ... store = store[component]; // Fix: Add prototype chain check to prevent prototype pollution if (!store || Object.getPrototypeOf(store) !== null) { return createStore(); } // ... } ``` **Fix Principle**: By checking `Object.getPrototypeOf(store) !== null`, the fix ensures that the retrieved storage object does not originate from properties on the prototype chain, preventing pollution attacks through prototype properties such as `__proto__` and `constructor`. --- ### POC/Test Code **Test Case 1: Bypassing `requireUser` validation via `.prototype.constructor`** ```javascript it('rejects prototype.constructor traversal on function keyword master', async () => { Parse.Cloud.define('protectedFn', function () { return 'secret'; }, { requireUser: true }); const response = await request({ headers, method: 'POST', url: 'http://localhost:8378/1/functions/protectedFn.prototype.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'); }); ``` **Test Case 2: Bypassing validation via `.prototype`** ```javascript it('rejects prototype traversal without constructor suffix', async () => { Parse.Cloud.define('protectedFn2', function () { return 'secret'; }, { requireUser: true }); const response = await request({ headers, method: 'POST', url: 'http://localhost:8378/1/functions/protectedFn2.prototype', 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'); }); ``` **Test Case 3: Bypassing `requireMaster` validation** ```javascript it('enforces requireMaster validator against prototype.constructor bypass', async () => { Parse.Cloud.define('masterOnlyFn', function () { return 'admin data'; }, { requireMaster: true }); const response = await request({ headers, method: 'POST', url: 'http://localhost:8378/1/functions/masterOnlyFn.prototype.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'); }); ``` **Normal Call Verification (ensuring the fix does not affect normal functionality)** ```javascript it('enforces validator when calling function normally', async () => { Parse.Cloud.define('protectedFn3', function () { return 'secret'; }, { requireUser: true }); const response = await request({ headers, method: 'POST', url: 'http://localhost:8378/1/functions/protectedFn3', body: JSON.stringify({}), }).catch(e => e); expect(response.status).toBe(400); const text = JSON.parse(response.text); expect(text.code).toBe(Parse.Error.VALIDATION_ERROR); }); ```