Skip to content

fix(core): keep prototype-named keys in getTransferState - #71387

Open
rootvector2 wants to merge 2 commits into
angular:mainfrom
rootvector2:transfer-state-debug-dictionary
Open

rootvector2 wants to merge 2 commits into
angular:mainfrom
rootvector2:transfer-state-debug-dictionary

Conversation

@rootvector2

Copy link
Copy Markdown
Contributor

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.dev application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

Follow-up to #70266. TransferState now keeps keys such as __proto__ in a null-prototype dictionary and round-trips them through toJson, but getTransferState in packages/core/src/render3/util/transfer_state_utils.ts (published as ng.傻getTransferState for DevTools) still copies the restored entries into a plain object literal. For a serialized state like {"__proto__":{"cached":true},"constructor":"state"} the copy assigns through the inherited __proto__ setter: the entry is missing from the returned object, the stored value becomes its prototype so cached shows up as an inherited property, and under Node's --disable-proto=throw the copy throws. The DevTools backend then merges each root's result into another plain object with Object.assign, which drops the entry a second time.

Issue Number: N/A

What is the new behavior?

getTransferState builds its result with the existing createDictionary helper, and the DevTools merge target is a null-prototype object, so prototype-named keys stay ordinary entries all the way to the Transfer State tab.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

The new spec in transfer_state_spec.ts fails on main (Object.keys is ['constructor'] and the prototype is the stored object) and passes with the change; //packages/core/test:test and the Chromium run of //devtools/projects/ng-devtools-backend/src/lib:client_event_subscribers_test are green locally.

`getTransferState` copies the restored transfer state into a plain object
literal. Keys come verbatim from the serialized state, and `TransferState`
already accepts keys such as `__proto__` and stores them in a null-prototype
dictionary. Copying such an entry into a plain object assigns through the
inherited `__proto__` setter instead: the entry disappears, the stored value
becomes the result's prototype so its properties leak in as inherited values,
and under `--disable-proto=throw` the copy throws.

Reuse the existing `createDictionary` helper for the accumulator so these keys
stay ordinary entries.
The transfer state backend merges the per-root `傻getTransferState` results
into a plain object literal with `Object.assign`. A key such as `__proto__`
assigns through the inherited setter, dropping the entry and reparenting the
merged object. Use a null-prototype object as the merge target so the key is
kept as an ordinary entry.
@pullapprove
pullapprove Bot requested review from dgp1130 and kirjs October 16, 2026 08:46
@angular-robot angular-robot Bot added area: core Issues related to the framework runtime area: devtools labels Oct 16, 2026
@ngbot ngbot Bot added this to the Backlog milestone Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: core Issues related to the framework runtime area: devtools

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant