Repository navigation
Validate values received from the remote environment - #740
Open
robin-drexler wants to merge 1 commit into
Open
robin-drexler wants to merge 1 commit into
robin-drexler wants to merge 1 commit into
Conversation
The remote environment is untrusted, but receivers stored text, attribute, and property values without checking their shape, and the Preact/React host renderers handed them straight to the framework. Preact treats any object whose `constructor` resolves to `undefined` as a virtual node; such objects survive `postMessage`, so a remote could render arbitrary markup (including `dangerouslySetInnerHTML`) on the host via text nodes, attributes, or properties. - Add validation helpers to `@remote-dom/core/receivers` and apply them in `RemoteReceiver` and `SignalRemoteReceiver` on insert and update: text and attributes are normalized to strings (nullish attributes are removed), property values are rejected if they contain a framework-node shape, event listeners must be functions, `__proto__` is rejected as a name, and element names must be strings. - Preact and React renderers ignore non-string attributes and never take `children`, `ref`, `key`, or `dangerouslySetInnerHTML` from remote data. - `updateRemoteElementAttribute()` sends the same coerced string it stores. - Add tests exercising each route with smuggled payloads end to end.
Contributor
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Non-fork copy of #738 (by @greatscotty) so CI runs against the main repo. Same commit (
6385dd4), no changes.What this changes
The remote environment is untrusted, but the host receivers stored text, attribute, and property values without checking their shape, and the Preact/React host renderers handed them straight to the framework. Preact treats any object whose
constructorresolves toundefinedas one of its own virtual nodes. Objects like that can’t be written as JSON but do survivepostMessage(and the thread deserializer also lets a literal__proto__key through, which has the same effect), so a remote could send, where the host expects a string, an object that Preact turns into real DOM – includingdangerouslySetInnerHTML.A fix that only coerced text nodes would have left attributes and properties open; this closes all of the routes at the receiver, which is the host’s trust boundary.
@remote-dom/core@remote-dom/core/receivers:normalizeRemoteText,normalizeRemoteAttribute(s),assertSafeRemoteProperty/assertSafeRemoteProperties,assertRemotePropertyName,assertRemoteEventListener(s),assertRemoteElementName,normalizeRemotePropertyUpdate.RemoteReceiverapplies them on insert and onupdateText/updateProperty: text and attributes are normalized to strings (objects are described viaObject.prototype.toString, never by calling the remote’stoString; nullish attribute updates remove the attribute), property values are rejected if any object in them hasconstructor === undefined, event listeners must be functions,__proto__is rejected as a name, element names must be strings.updateRemoteElementAttribute()now sends the same coerced string it stores locally (RemoteElement.updateRemoteAttribute()previously sent the raw argument).@remote-dom/signalsSignalRemoteReceiverapplies the same validation.@remote-dom/preact,@remote-dom/reactusePropsForRemoteElementonly passes string attributes through, and never takeschildren,ref,key,dangerouslySetInnerHTML(or__proto__) from remote properties, attributes, or slot names.Tests
New tests send smuggled payloads (both the own-
constructorand__proto__variants, passed through a simulated structured-clone + deserializer) end to end through the real receiver and renderer into jsdom:packages/preact/source/tests/untrusted-values.test.tsx– text (initial + update), attributes (initial + update), properties (initial, update, nested), spread props, ref override. Before this change 16/20 fail with attacker markup in the DOM; after, all pass.packages/react/source/tests/untrusted-values.test.tsx– React’s$$typeofcheck prevents markup injection, but the host tree crashed (“Objects are not valid as a React child”); now renders plain text.packages/core/source/tests/receivers-untrusted-values.test.ts, additions toSignalRemoteReceiver.test.tsandelements.test.ts– helper and receiver-level coverage, including__proto__names, Map/Set/cyclic values, and vnode shapes hidden behind a redirected prototype.Full suite, type-check and prettier pass.
Notes
remote-uibranch is in a separate PR.