VYPR
High severityNVD Advisory· Published Nov 10, 2022· Updated Apr 23, 2025

Parse Server Prototype pollution and Injection via Cloud Code Webhooks or Cloud Code Triggers

CVE-2022-41878

Description

Parse Server is an open source backend that can be deployed to any infrastructure that can run Node.js. In versions prior to 5.3.2 or 4.10.19, keywords that are specified in the Parse Server option requestKeywordDenylist can be injected via Cloud Code Webhooks or Triggers. This will result in the keyword being saved to the database, bypassing the requestKeywordDenylist option. This issue is fixed in versions 4.10.19, and 5.3.2. If upgrade is not possible, the following Workarounds may be applied: Configure your firewall to only allow trusted servers to make request to the Parse Server Cloud Code Webhooks API, or block the API completely if you are not using the feature.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
parse-servernpm
< 4.10.194.10.19
parse-servernpm
>= 5.0.0, < 5.3.25.3.2

Affected products

1

Patches

2
6728da1e3591

fix: Parse Server option `requestKeywordDenylist` can be bypassed via Cloud Code Webhooks or Triggers; fixes security vulnerability [GHSA-xprv-wvh7-qqqx](https://github.com/parse-community/parse-server/security/advisories/GHSA-xprv-wvh7-qqqx) (#8302)

2 files changed · +67 12
  • spec/vulnerabilities.spec.js+50 0 modified
    @@ -109,6 +109,56 @@ describe('Vulnerabilities', () => {
           );
         });
     
    +    it('denies creating a cloud trigger with polluted data', async () => {
    +      Parse.Cloud.beforeSave('TestObject', ({ object }) => {
    +        object.set('obj', {
    +          constructor: {
    +            prototype: {
    +              dummy: 0,
    +            },
    +          },
    +        });
    +      });
    +      await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith(
    +        new Parse.Error(
    +          Parse.Error.INVALID_KEY_NAME,
    +          'Prohibited keyword in request data: {"key":"constructor"}.'
    +        )
    +      );
    +    });
    +
    +    it('denies creating a hook with polluted data', async () => {
    +      const express = require('express');
    +      const bodyParser = require('body-parser');
    +      const port = 34567;
    +      const hookServerURL = 'http://localhost:' + port;
    +      const app = express();
    +      app.use(bodyParser.json({ type: '*/*' }));
    +      const server = await new Promise(resolve => {
    +        const res = app.listen(port, undefined, () => resolve(res));
    +      });
    +      app.post('/BeforeSave', function (req, res) {
    +        const object = Parse.Object.fromJSON(req.body.object);
    +        object.set('hello', 'world');
    +        object.set('obj', {
    +          constructor: {
    +            prototype: {
    +              dummy: 0,
    +            },
    +          },
    +        });
    +        res.json({ success: object });
    +      });
    +      await Parse.Hooks.createTrigger('TestObject', 'beforeSave', hookServerURL + '/BeforeSave');
    +      await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith(
    +        new Parse.Error(
    +          Parse.Error.INVALID_KEY_NAME,
    +          'Prohibited keyword in request data: {"key":"constructor"}.'
    +        )
    +      );
    +      await new Promise(resolve => server.close(resolve));
    +    });
    +
         it('allows BSON type code data in write request with custom denylist', async () => {
           await reconfigureServer({
             requestKeywordDenylist: [],
    
  • src/RestWrite.js+17 12 modified
    @@ -64,18 +64,7 @@ function RestWrite(config, auth, className, query, data, originalData, clientSDK
         }
       }
     
    -  if (this.config.requestKeywordDenylist) {
    -    // Scan request data for denied keywords
    -    for (const keyword of this.config.requestKeywordDenylist) {
    -      const match = Utils.objectContainsKeyValue(data, keyword.key, keyword.value);
    -      if (match) {
    -        throw new Parse.Error(
    -          Parse.Error.INVALID_KEY_NAME,
    -          `Prohibited keyword in request data: ${JSON.stringify(keyword)}.`
    -        );
    -      }
    -    }
    -  }
    +  this.checkProhibitedKeywords(data);
     
       // When the operation is complete, this.response may have several
       // fields.
    @@ -292,6 +281,7 @@ RestWrite.prototype.runBeforeSaveTrigger = function () {
               delete this.data.objectId;
             }
           }
    +      this.checkProhibitedKeywords(this.data);
         });
     };
     
    @@ -1728,5 +1718,20 @@ RestWrite.prototype._updateResponseWithData = function (response, data) {
       return response;
     };
     
    +RestWrite.prototype.checkProhibitedKeywords = function (data) {
    +  if (this.config.requestKeywordDenylist) {
    +    // Scan request data for denied keywords
    +    for (const keyword of this.config.requestKeywordDenylist) {
    +      const match = Utils.objectContainsKeyValue(data, keyword.key, keyword.value);
    +      if (match) {
    +        throw new Parse.Error(
    +          Parse.Error.INVALID_KEY_NAME,
    +          `Prohibited keyword in request data: ${JSON.stringify(keyword)}.`
    +        );
    +      }
    +    }
    +  }
    +};
    +
     export default RestWrite;
     module.exports = RestWrite;
    
0a2d412e2659

fix: Parse Server option `requestKeywordDenylist` can be bypassed via Cloud Code Webhooks or Triggers; fixes security vulnerability [GHSA-xprv-wvh7-qqqx](https://github.com/parse-community/parse-server/security/advisories/GHSA-xprv-wvh7-qqqx) (#8301)

2 files changed · +67 12
  • spec/vulnerabilities.spec.js+50 0 modified
    @@ -109,6 +109,56 @@ describe('Vulnerabilities', () => {
           );
         });
     
    +    it('denies creating a cloud trigger with polluted data', async () => {
    +      Parse.Cloud.beforeSave('TestObject', ({ object }) => {
    +        object.set('obj', {
    +          constructor: {
    +            prototype: {
    +              dummy: 0,
    +            },
    +          },
    +        });
    +      });
    +      await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith(
    +        new Parse.Error(
    +          Parse.Error.INVALID_KEY_NAME,
    +          'Prohibited keyword in request data: {"key":"constructor"}.'
    +        )
    +      );
    +    });
    +
    +    it('denies creating a hook with polluted data', async () => {
    +      const express = require('express');
    +      const bodyParser = require('body-parser');
    +      const port = 34567;
    +      const hookServerURL = 'http://localhost:' + port;
    +      const app = express();
    +      app.use(bodyParser.json({ type: '*/*' }));
    +      const server = await new Promise(resolve => {
    +        const res = app.listen(port, undefined, () => resolve(res));
    +      });
    +      app.post('/BeforeSave', function (req, res) {
    +        const object = Parse.Object.fromJSON(req.body.object);
    +        object.set('hello', 'world');
    +        object.set('obj', {
    +          constructor: {
    +            prototype: {
    +              dummy: 0,
    +            },
    +          },
    +        });
    +        res.json({ success: object });
    +      });
    +      await Parse.Hooks.createTrigger('TestObject', 'beforeSave', hookServerURL + '/BeforeSave');
    +      await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith(
    +        new Parse.Error(
    +          Parse.Error.INVALID_KEY_NAME,
    +          'Prohibited keyword in request data: {"key":"constructor"}.'
    +        )
    +      );
    +      await new Promise(resolve => server.close(resolve));
    +    });
    +
         it('allows BSON type code data in write request with custom denylist', async () => {
           await reconfigureServer({
             requestKeywordDenylist: [],
    
  • src/RestWrite.js+17 12 modified
    @@ -62,18 +62,7 @@ function RestWrite(config, auth, className, query, data, originalData, clientSDK
         }
       }
     
    -  if (this.config.requestKeywordDenylist) {
    -    // Scan request data for denied keywords
    -    for (const keyword of this.config.requestKeywordDenylist) {
    -      const match = Utils.objectContainsKeyValue(data, keyword.key, keyword.value);
    -      if (match) {
    -        throw new Parse.Error(
    -          Parse.Error.INVALID_KEY_NAME,
    -          `Prohibited keyword in request data: ${JSON.stringify(keyword)}.`
    -        );
    -      }
    -    }
    -  }
    +  this.checkProhibitedKeywords(data);
     
       // When the operation is complete, this.response may have several
       // fields.
    @@ -296,6 +285,7 @@ RestWrite.prototype.runBeforeSaveTrigger = function () {
               delete this.data.objectId;
             }
           }
    +      this.checkProhibitedKeywords(this.data);
         });
     };
     
    @@ -1651,5 +1641,20 @@ RestWrite.prototype._updateResponseWithData = function (response, data) {
       return response;
     };
     
    +RestWrite.prototype.checkProhibitedKeywords = function (data) {
    +  if (this.config.requestKeywordDenylist) {
    +    // Scan request data for denied keywords
    +    for (const keyword of this.config.requestKeywordDenylist) {
    +      const match = Utils.objectContainsKeyValue(data, keyword.key, keyword.value);
    +      if (match) {
    +        throw new Parse.Error(
    +          Parse.Error.INVALID_KEY_NAME,
    +          `Prohibited keyword in request data: ${JSON.stringify(keyword)}.`
    +        );
    +      }
    +    }
    +  }
    +};
    +
     export default RestWrite;
     module.exports = RestWrite;
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.