node-convict icon indicating copy to clipboard operation
node-convict copied to clipboard

Prototype Pollution in convict via schema path (constructor.prototype.*)

Open 34selen opened this issue 3 months ago • 0 comments

Summary (CWE-1321 Prototype Pollution)

convict(schema) becomes globally polluted when the schema object contains a constructor.prototype.* path. During schema normalization/default propagation, the code walks into built-in properties and ends up writing to Object.prototype, which then reflects on all plain objects.

Steps to Reproduce (PoC)

Run in an isolated process; cleanup is included.

// poc.js
const convict = require('convict');

function clean() { try { delete Object.prototype.polluted; } catch {} }

clean();
try {
  convict({
    constructor: {
      prototype: { polluted: 'pwned!' }
    }
  });
} catch (_) {
  // schema format errors may be thrown; pollution can still occur earlier
}
console.log('[PoC1] polluted =', ({}).polluted); // "pwned!" → polluted
clean();

Expected / Actual

Expected: no write to Object.prototype from user-provided schema keys.

Actual: ({}).polluted === "pwned!".

Root Cause

During schema expansion / default injection (addDefaultValues) the implementation recurses through existing values of intermediate nodes. If a schema uses constructor → prototype, the recursion walks into Object function and its prototype, and the leaf write hits Object.prototype (global pollution). String-path filtering in set() doesn’t cover this initialization path.

34selen avatar Oct 04 '25 17:10 34selen