Skip to content

Regression when comparing equality using fast-deep-equal #1266

Description

@olemartinorg

🐛 Bug Report

immer@11.1.9 regresses the identity of draft.constructor after the CVE fix for constructor.prototype / __proto__ access. The security fix is understandable, but the implementation now returns a fresh wrapper on each access, which changes equality behavior for equal drafts inside a producer.

To Reproduce

Steps to reproduce the behavior:

  1. Install immer@11.1.9.
  2. Run the following test:
import assert from 'node:assert/strict';
import test from 'node:test';
import { produce } from 'immer';
import deepEqual from 'fast-deep-equal';

test('equal Immer drafts stay deep-equal', () => {
  produce({ a: { x: 1 }, b: { x: 1 } }, (draft) => {
    assert.equal(deepEqual(draft.a, draft.b), true);
    assert.equal(draft.a.constructor === draft.b.constructor, true);
  });
});
  1. Compare the result with immer@11.1.8.

Observed behavior

With immer@11.1.9, equal drafts no longer expose a stable constructor identity:

  • deepEqual(draft.a, draft.b) can fail where it previously passed
  • draft.a.constructor === draft.b.constructor is false

This is caused by the constructor proxy wrapper added in the CVE fix.

Expected behavior

Equal drafts should preserve stable observable behavior for normal property reads, including constructor, while still blocking prototype pollution via constructor.prototype and __proto__.

Environment

We only accept bug reports against the latest Immer version.

  • Introduced in immer version: 11.1.9 (also happens with 11.1.11)
  • I filed this report against the latest version of Immer
  • Occurs with setUseProxies(true)
  • Occurs with setUseProxies(false) (ES5 only)

Notes

I validated that:

  • 11.1.8 passes the repro
  • 11.1.9 fails the repro
  • a narrow fix that caches the constructor wrapper per underlying constructor restores the behavior while keeping the pollution guard in place

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions