The vulnerability lies in Preact's handling of VNodes (Virtual DOM elements). The core issue is a type confusion vulnerability caused by the use of loose equality checks (== and !=) instead of strict equality checks (=== and !==) when validating VNodes. The advisory states that a regression softened the protection against constructing VNodes from arbitrary JSON. The patch restores these strict equality checks.
By analyzing the commits that patch the vulnerability, I identified the exact locations where these weak checks were replaced. The patch 37c3e030ab19337f10d32cd49d1c3d14e102d890 modifies three key functions:
isValidElement in src/create-element.js: This function is responsible for checking if an object is a valid Preact element. The change from == to === ensures that only objects with a constructor that is strictly undefined (as set by Preact's createElement function) are considered valid VNodes.
diff in src/diff/index.js: This is a central function in Preact's rendering process. It contained a check newVNode.constructor != UNDEFINED to reject invalid VNodes. Changing this to !== prevents crafted objects from bypassing this guard.
constructNewChildrenArray in src/diff/children.js: This function, used during the diffing of child elements, also contained a loose equality check on the constructor property. The fix to use === prevents an attacker-controlled object from being treated as a cloneable VNode.
An attacker could exploit this by providing a JSON payload with a crafted object where a string was expected. If the application does not sanitize this input, Preact would incorrectly interpret this object as a VNode due to the loose equality checks, leading to potential HTML injection and cross-site scripting (XSS). The identified functions are the exact points in the code where this flawed validation occurred.
isValidElementsrc/create-element.js
diffsrc/diff/index.js
constructNewChildrenArraysrc/diff/children.js
| Package Name | Ecosystem | Vulnerable Versions | First Patched Version |
|---|---|---|---|
| preact | npm | >= 10.26.5, < 10.26.10 | 10.26.10 |
| preact | npm | >= 10.27.0, < 10.27.3 | 10.27.3 |
| preact | npm | >= 10.28.0, < 10.28.2 | 10.28.2 |