Skip to content
This repository was archived by the owner on Apr 5, 2026. It is now read-only.

fix: check all path segments for dangerous keys (bypass of CVE-2023-2…#43

Merged
mickhansen merged 2 commits intomickhansen:masterfrom
76embiid21:master
Feb 24, 2026
Merged

fix: check all path segments for dangerous keys (bypass of CVE-2023-2…#43
mickhansen merged 2 commits intomickhansen:masterfrom
76embiid21:master

Conversation

@76embiid21
Copy link
Copy Markdown
Contributor

@76embiid21 76embiid21 commented Feb 24, 2026

Description

The existing __proto__ guard introduced in commit 7d3aee1 (fix for CVE-2023-26132) only checks pieces[0], allowing a bypass when __proto__ appears at any position other than the first segment.

This PR extends the guard to check all path segments against dangerous keys (__proto__, constructor, prototype) in both set() and transform().

Important

This is a security fix. The bypass allows injecting properties into an object's prototype chain, which can lead to authorization bypass in downstream applications.

Vulnerability Details

Item Value
Affected versions 2.0.4 – 2.0.6
Severity CVSS 6.3 (Medium)
CWE CWE-1321 — Improperly Controlled Modification of Object Prototype Attributes
Affected functions dottie.set(), dottie.transform()

Root Cause

The current guard only inspects the first element of the path:

// set() — line ~75
if (pieces[0] === '__proto__') return;

// transform() — line ~145
if (pieces[0] === 'proto') break;

A path like 'a.__proto__.polluted' passes this check because pieces[0] is 'a', not '__proto__'.

Impact

  • Injects properties into a specific object's prototype chain (not global Object.prototype)
  • Injected properties are invisible to hasOwnProperty() and Object.keys()
  • Can lead to authorization bypass when server code checks properties without own-property guards (e.g., if (session.isAdmin))
  • current['__proto__'] = {} replaces an object's prototype, breaking inherited methods

Proof of Concept

<details> <summary>Click to expand PoC code</summary>
const dottie = require('dottie');

// 1. set() bypass
const obj = {};
dottie.set(obj, 'session.proto.isAdmin', true);

console.log(obj.session.isAdmin); // true
console.log(({}).isAdmin); // undefined (not global pollution)
console.log(obj.session.hasOwnProperty('isAdmin')); // false (invisible to hasOwnProperty)

// 2. transform() bypass
const flat = { 'user.proto.role': 'admin', 'user.name': 'guest' };
const result = dottie.transform(flat);

console.log(result.user.role); // 'admin'
console.log(({}).role); // undefined

Tested on Node.js v20/v22, dottie 2.0.6, Windows 11.

</details>

Changes

dottie.js

set() function (line ~75):

- if (pieces[0] === '__proto__') return;
+ var DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
+ if (pieces.some(function(p) { return DANGEROUS_KEYS.indexOf(p) !== -1; })) return;

transform() function (line ~145):

- if (pieces[0] === '__proto__') break;
+ var DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
+ if (pieces.some(function(p) { return DANGEROUS_KEYS.indexOf(p) !== -1; })) break;

New test files

  • test/set.proto-bypass.test.js — 7 test cases for set() bypass prevention
  • test/transform.proto-bypass.test.js — 7 test cases for transform() bypass prevention

Testing

All 61 tests pass, including 14 new test cases:

dottie.set - prototype pollution bypass prevention
  ✓ should block __proto__ at second position
  ✓ should block __proto__ at third position
  ✓ should still block __proto__ at first position (original CVE-2023-26132 fix)
  ✓ should block constructor at any position
  ✓ should block prototype at any position
  ✓ should allow normal nested paths
  ✓ should allow paths with similar-looking but safe key names

dottie.transform - prototype pollution bypass prevention
✓ should block proto at second position in keys
✓ should block proto at third position in keys
✓ should still block proto at first position (original fix)
✓ should block constructor-based pollution in transform keys
✓ should block prototype key in transform keys
✓ should transform normal dotted keys correctly

61 passing (14ms)

Checklist

  • Guard checks all path segments, not just pieces[0]
  • Covers __proto__, constructor, and prototype
  • Both set() and transform() are patched
  • New test cases added for bypass scenarios
  • Existing tests still pass
  • No breaking changes to legitimate usage

References

  • CVE-2023-26132 — Original prototype pollution vulnerability
  • 7d3aee1 — Original fix (first-segment-only guard)
  • CWE-1321 — Improperly Controlled Modification of Object Prototype Attributes
## Description

The existing __proto__ guard introduced in commit [7d3aee1](https://github.com/mickhansen/dottie.js/commit/7d3aee1c9c3c842720506e131de7e181e5c8db68) (fix for [CVE-2023-26132](https://www.cve.org/CVERecord?id=CVE-2023-26132)) only checks pieces[0], allowing a bypass when __proto__ appears at any position other than the first segment.

This PR extends the guard to check all path segments against dangerous keys (__proto__, constructor, prototype) in both set() and transform().

Important

This is a security fix. The bypass allows injecting properties into an object's prototype chain, which can lead to authorization bypass in downstream applications.

Vulnerability Details

Item Value
Affected versions 2.0.4 – 2.0.6
Severity CVSS 6.3 (Medium)
CWE [CWE-1321](https://cwe.mitre.org/data/definitions/1321.html) — Improperly Controlled Modification of Object Prototype Attributes
Affected functions dottie.set(), dottie.transform()

Root Cause

The current guard only inspects the first element of the path:

// set() — line ~75
if (pieces[0] === '__proto__') return;

// transform() — line ~145
if (pieces[0] === '__proto__') break;

A path like 'a.__proto__.polluted' passes this check because pieces[0] is 'a', not '__proto__'.

Impact

  • Injects properties into a specific object's prototype chain (not global Object.prototype)
  • Injected properties are invisible to hasOwnProperty() and Object.keys()
  • Can lead to authorization bypass when server code checks properties without own-property guards (e.g., if (session.isAdmin))
  • current['__proto__'] = {} replaces an object's prototype, breaking inherited methods

Proof of Concept

Click to expand PoC code
const dottie = require('dottie');

// 1. set() bypass
const obj = {};
dottie.set(obj, 'session.__proto__.isAdmin', true);

console.log(obj.session.isAdmin);                    // true
console.log(({}).isAdmin);                           // undefined (not global pollution)
console.log(obj.session.hasOwnProperty('isAdmin'));  // false     (invisible to hasOwnProperty)

// 2. transform() bypass
const flat = { 'user.__proto__.role': 'admin', 'user.name': 'guest' };
const result = dottie.transform(flat);

console.log(result.user.role);  // 'admin'
console.log(({}).role);         // undefined

Tested on Node.js v20/v22, dottie 2.0.6, Windows 11.

Changes

dottie.js

set() function (line ~75):

- if (pieces[0] === '__proto__') return;
+ var DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
+ if (pieces.some(function(p) { return DANGEROUS_KEYS.indexOf(p) !== -1; })) return;

transform() function (line ~145):

- if (pieces[0] === '__proto__') break;
+ var DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
+ if (pieces.some(function(p) { return DANGEROUS_KEYS.indexOf(p) !== -1; })) break;

New test files

  • test/set.proto-bypass.test.js — 7 test cases for set() bypass prevention
  • test/transform.proto-bypass.test.js — 7 test cases for transform() bypass prevention

Testing

All 61 tests pass, including 14 new test cases:

dottie.set - prototype pollution bypass prevention
  ✓ should block __proto__ at second position
  ✓ should block __proto__ at third position
  ✓ should still block __proto__ at first position (original CVE-2023-26132 fix)
  ✓ should block constructor at any position
  ✓ should block prototype at any position
  ✓ should allow normal nested paths
  ✓ should allow paths with similar-looking but safe key names

dottie.transform - prototype pollution bypass prevention
  ✓ should block __proto__ at second position in keys
  ✓ should block __proto__ at third position in keys
  ✓ should still block __proto__ at first position (original fix)
  ✓ should block constructor-based pollution in transform keys
  ✓ should block prototype key in transform keys
  ✓ should transform normal dotted keys correctly

61 passing (14ms)

Checklist

  • Guard checks all path segments, not just pieces[0]
  • Covers __proto__, constructor, and prototype
  • Both set() and transform() are patched
  • New test cases added for bypass scenarios
  • Existing tests still pass
  • No breaking changes to legitimate usage

References

…6132)

The previous fix (7d3aee1) only checked pieces[0] against '__proto__',
allowing bypass via paths like 'a.__proto__.polluted'.

This patch checks every segment against __proto__, constructor, and
prototype in both set() and transform().
Copy link
Copy Markdown
Owner

@mickhansen mickhansen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add unit tests to the PR

@mickhansen mickhansen merged commit 7e8fa13 into mickhansen:master Feb 24, 2026
1 check passed
@mickhansen
Copy link
Copy Markdown
Owner

Thank you for your contribution, released in dottie@2.0.7

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants