Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose properties on Node prototype to support DOMPurify anti-clobbering #1165

Closed
luxaritas opened this issue Nov 20, 2023 · 9 comments
Closed
Labels
enhancement New feature or request

Comments

@luxaritas
Copy link

Is your feature request related to a problem? Please describe.
I am currently working on happy-dom support in DOMPurify. In order to protect against DOM clobbering, it uses the Node prototype in order to access the following:

  • cloneNode
  • nextSibling
  • childNodes
  • parentNode

In order to do this, all of these properties need to be either functions or getters (not raw properties) and cannot be overridden in child classes of Node.

Describe the solution you'd like

  • Move full implementation of cloneNode into Node
  • Implement parentNode as a getter
  • Add unit tests to ensure the implementation of these functions maintains consistent
  • Audit potential opportunities for DOM clobbering of node/node subclass properties. While it appears happy-dom has protections for this already, this would probably be a good opportunity to do a full audit of instances where properties are dynamically set and add unit tests specifically to protect against vulnerabilities of this kind.
  • While not necessary for DOMPurify, it may be desirable for other ecosystem users to avoid overrides for all methods of Node (and subclasses), so that they can be referenced via prototype when specifically intending to avoid DOM clobbering.

Describe alternatives you've considered

  • Removing prototype lookupGetter usage in DOMPurify: This would be unsafe and against the point of the library.
  • Specifically for parentNode, fall back to property when getter is not available. This however would weaken security guarantees in DOMPurify.
  • No DOMPurify support

Additional context
I appreciate that this could require some nontrivial architecture changes. However, DOMPurify is an important security library in the ecosystem, and I believe support would be highly valuable.

Additional discussion is available in cure53/DOMPurify#878

@mcous
Copy link

mcous commented Apr 6, 2024

This issue appears to be the cause of testing-library/svelte-testing-library#319 and sveltejs/svelte#10358.

The upcoming Svelte v5 relies on calling various methods through Node.prototype as well as Element.prototype. For example, this minimal snippet, based on Svelte's initial mounting code, doesn't work in Happy DOM:

import { Window } from "happy-dom";

const window = new Window({ url: "http://localhost:3000" });
const document = window.document;
const cloneNode = window.Node.prototype.cloneNode;

const element = document.createElement("div");
const clone1 = element.cloneNode();
const clone2 = cloneNode.call(element);

console.log("clone1", clone1.tagName); // clone1 DIV
console.log("clone2", clone2.tagName); // clone2 null

From a very brief perusal of the Happy DOM code, this looks like a pretty significant change in how it sets up nodes, elements, etc. But it also means Svelte 5 isn't really usable with Happy DOM.

I'm happy to help out in any way possible here, if that's at all helpful!

@luxaritas
Copy link
Author

Hey @capricorn86 - sorry for the ping. Given the work in #1393, I'm wondering if you had any thoughts about the other items here. While I don't have much bandwidth right now due to upcoming travel, I'm happy to try and help where I can, but it would probably be best to start with hearing your thoughts on how to approach this.

@capricorn86
Copy link
Owner

Hi @luxaritas!

The other items in the list should already be working if called on the prototype as they are not overridden by any child class. However, it would be good to add some unit tests to make sure that something isn't introduced in the future.

@capricorn86
Copy link
Owner

There might be some other methods/properties that are overridden not covered by the list. In that case we should make sure that they aren't and calling them on Node.prototype.* will work.

@luxaritas
Copy link
Author

IIRC the issues for the others was

In order to do this, all of these properties need to be either functions or getters (not raw properties)

@luxaritas
Copy link
Author

luxaritas commented Apr 9, 2024

(The linked dompurify PR has some discussion around that issue - I think it also requires the internal property managed by the getter to be named by a symbol in order to maintain the security guarantees)

@capricorn86
Copy link
Owner

IIRC the issues for the others was

In order to do this, all of these properties need to be either functions or getters (not raw properties)

The other properties on Node are getters and setters. They are no raw properties anymore. This was fixed in v13.1.0.

E.g. in order to call Node.prototype.parentNode, you would have to something like Object.getOwnPropertyDescriptor(Node.prototype, 'parentNode').get.call(element), which should be the same as in the browser.

@luxaritas
Copy link
Author

Ah! Seems I'm behind on recent changes. I'll work on rerunning the dompurify tests soon to see if there's anything else on this front that needs addressing. Thank you for the information (and previous work)!

@capricorn86
Copy link
Owner

Thank you @luxaritas! 🙂 Let's close this ticket for now then. Feel free to re-open this ticket or a new one if you find something.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants