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

Using dompurify with SSR or Server Component fails with webpack error #46893

Closed
1 task done
karlhorky opened this issue Mar 7, 2023 · 18 comments · Fixed by #46990
Closed
1 task done

Using dompurify with SSR or Server Component fails with webpack error #46893

karlhorky opened this issue Mar 7, 2023 · 18 comments · Fixed by #46990
Labels
bug Issue was opened via the bug report template.

Comments

@karlhorky
Copy link
Contributor

karlhorky commented Mar 7, 2023

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
      Platform: linux
      Arch: x64
      Version: #22 SMP Tue Jan 10 18:39:00 UTC 2023
    Binaries:
      Node: 16.17.0
      npm: 8.15.0
      Yarn: 1.22.19
      pnpm: 7.1.0
    Relevant packages:
      next: 13.2.4-canary.5
      eslint-config-next: N/A
      react: 18.2.0
      react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true)

Link to the code that reproduces this issue

https://codesandbox.io/p/sandbox/angry-thunder-s7tvqk?file=%2Fapp%2Fpage.tsx

To Reproduce

  1. Open the CodeSandbox above, main code is this in app/page.tsx
    import DOMPurify from "dompurify";
    
    export default function Home() {
      return (
        <div
          dangerouslySetInnerHTML={{
            // 💥 Error: dompurify__WEBPACK_IMPORTED_MODULE_1___default(...).sanitize is not a function
            __html: DOMPurify.sanitize("<img src=x onerror=alert(1)//>"),
          }}
        />
      );
    }
  2. Observe the error message below
Error: dompurify__WEBPACK_IMPORTED_MODULE_1___default(...).sanitize is not a function

Screenshot 2023-03-07 at 18 24 45

This kind of works in the pages/ directory (the image appears in the browser), although still throws an error while rendering in SSR.

Describe the Bug

It seems that the dompurify library does not support Node.js usage out of the box.

One recommendation from Stack Overflow is to use isomorphic-dompurify (300k weekly downloads vs 3.5m for dompurify)

Expected Behavior

dompurify works out of the box with Next.js and Node.js

Related issues

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

@karlhorky karlhorky added the bug Issue was opened via the bug report template. label Mar 7, 2023
@karlhorky
Copy link
Contributor Author

karlhorky commented Mar 7, 2023

Looking into the isomorphic-dompurify library, it uses jsdom internally to construct a DOM and pass the window object to the DOMPurify function.

So it seems like something like this should work:

import DOMPurify from "dompurify";
import { JSDOM } from "jsdom";

export default function Home() {
  return (
    <div
      dangerouslySetInnerHTML={{
-       __html: DOMPurify.sanitize(
+       __html: DOMPurify((new JSDOM("<!DOCTYPE html>")).window).sanitize(
          "<img src=x onerror=alert(1)//>"
        ),
      }}
    />
  );
}

And indeed, then we get new error messages:

Failed to compile.
ModuleNotFoundError: Module not found: Error: Can't resolve 'canvas' in '/.../node_modules/jsdom/lib/jsdom'
> Build error occurred

This can be tracked down to here:

Where there is no concrete solution, but it seems like canvas is a required dependency, so would be good to install it.

Then after installing canvas, another error message - this one seems similar to what happened with #46493 :

error - ./node_modules/canvas/build/Release/canvas.node
Module parse failed: Unexpected character '' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)

Import trace for requested module:
./node_modules/canvas/build/Release/canvas.node
./node_modules/canvas/lib/bindings.js
./node_modules/canvas/index.js
./node_modules/jsdom/lib/jsdom/utils.js
./node_modules/jsdom/lib/jsdom/browser/Window.js
./node_modules/jsdom/lib/api.js
./app/page.tsx

So maybe it needs something to be marked as externals in the webpack config.... 🤔 eg. like this:

next.config.js

/** @type {import("next").NextConfig} */
module.exports = {
  reactStrictMode: true,
  experimental: {
    appDir: true,
  },
  webpack: (config) => {
    config.externals = [...config.externals, "canvas"];
    return config;
  },
};

Tried adding canvas first, and got some other errors:

warn  - ./node_modules/debug/src/node.js
Module not found: Can't resolve 'supports-color' in '/project/sandbox/node_modules/debug/src'

Import trace for requested module:
./node_modules/debug/src/node.js
./node_modules/debug/src/index.js
./node_modules/http-proxy-agent/dist/agent.js
./node_modules/http-proxy-agent/dist/index.js
./node_modules/jsdom/lib/jsdom/living/helpers/agent-factory.js
./node_modules/jsdom/lib/jsdom/browser/resources/resource-loader.js
./node_modules/jsdom/lib/api.js
./app/page.tsx

./node_modules/ws/lib/buffer-util.js
Module not found: Can't resolve 'bufferutil' in '/project/sandbox/node_modules/ws/lib'

Import trace for requested module:
./node_modules/ws/lib/buffer-util.js
./node_modules/ws/lib/receiver.js
./node_modules/ws/wrapper.mjs
./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
./node_modules/jsdom/lib/jsdom/browser/Window.js
./node_modules/jsdom/lib/api.js
./app/page.tsx

./node_modules/ws/lib/validation.js
Module not found: Can't resolve 'utf-8-validate' in '/project/sandbox/node_modules/ws/lib'

Import trace for requested module:
./node_modules/ws/lib/validation.js
./node_modules/ws/lib/receiver.js
./node_modules/ws/wrapper.mjs
./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
./node_modules/jsdom/lib/jsdom/browser/Window.js
./node_modules/jsdom/lib/api.js
./app/page.tsx
warn  - ./node_modules/debug/src/node.js
Module not found: Can't resolve 'supports-color' in '/project/sandbox/node_modules/debug/src'

Import trace for requested module:
./node_modules/debug/src/node.js
./node_modules/debug/src/index.js
./node_modules/http-proxy-agent/dist/agent.js
./node_modules/http-proxy-agent/dist/index.js
./node_modules/jsdom/lib/jsdom/living/helpers/agent-factory.js
./node_modules/jsdom/lib/jsdom/browser/resources/resource-loader.js
./node_modules/jsdom/lib/api.js
./app/page.tsx

./node_modules/ws/lib/buffer-util.js
Module not found: Can't resolve 'bufferutil' in '/project/sandbox/node_modules/ws/lib'

Import trace for requested module:
./node_modules/ws/lib/buffer-util.js
./node_modules/ws/lib/receiver.js
./node_modules/ws/wrapper.mjs
./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
./node_modules/jsdom/lib/jsdom/browser/Window.js
./node_modules/jsdom/lib/api.js
./app/page.tsx

./node_modules/ws/lib/validation.js
Module not found: Can't resolve 'utf-8-validate' in '/project/sandbox/node_modules/ws/lib'

Import trace for requested module:
./node_modules/ws/lib/validation.js
./node_modules/ws/lib/receiver.js
./node_modules/ws/wrapper.mjs
./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
./node_modules/jsdom/lib/jsdom/browser/Window.js
./node_modules/jsdom/lib/api.js
./app/page.tsx
wait  - compiling /_error (client and server)...
warn  - ./node_modules/debug/src/node.js
Module not found: Can't resolve 'supports-color' in '/project/sandbox/node_modules/debug/src'

Import trace for requested module:
./node_modules/debug/src/node.js
./node_modules/debug/src/index.js
./node_modules/http-proxy-agent/dist/agent.js
./node_modules/http-proxy-agent/dist/index.js
./node_modules/jsdom/lib/jsdom/living/helpers/agent-factory.js
./node_modules/jsdom/lib/jsdom/browser/resources/resource-loader.js
./node_modules/jsdom/lib/api.js
./app/page.tsx

./node_modules/ws/lib/buffer-util.js
Module not found: Can't resolve 'bufferutil' in '/project/sandbox/node_modules/ws/lib'

Import trace for requested module:
./node_modules/ws/lib/buffer-util.js
./node_modules/ws/lib/receiver.js
./node_modules/ws/wrapper.mjs
./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
./node_modules/jsdom/lib/jsdom/browser/Window.js
./node_modules/jsdom/lib/api.js
./app/page.tsx

./node_modules/ws/lib/validation.js
Module not found: Can't resolve 'utf-8-validate' in '/project/sandbox/node_modules/ws/lib'

Import trace for requested module:
./node_modules/ws/lib/validation.js
./node_modules/ws/lib/receiver.js
./node_modules/ws/wrapper.mjs
./node_modules/jsdom/lib/jsdom/living/websockets/WebSocket-impl.js
./node_modules/jsdom/lib/jsdom/browser/Window.js
./node_modules/jsdom/lib/api.js
./app/page.tsx

So maybe jsdom is required too? Eg. this config:

next.config.js

/** @type {import("next").NextConfig} */
module.exports = {
  reactStrictMode: true,
  experimental: {
    appDir: true,
  },
  webpack: (config) => {
    config.externals = [...config.externals, "canvas", "jsdom"];
    return config;
  },
};

After restarting the dev server and reloading the page, this works! (image is supposed to be broken)

Screenshot 2023-03-07 at 18 53 46

@karlhorky
Copy link
Contributor Author

karlhorky commented Mar 7, 2023

TLDR version: To use dompurify with Next.js and Node.js, install jsdom and canvas to construct a window object and pass to the DOMPurify() function:

CodeSandbox Demo: https://codesandbox.io/p/sandbox/lingering-river-j5fpi7?file=%2Fapp%2Fpage.tsx

Screenshot 2023-03-07 at 18 53 46

@karlhorky
Copy link
Contributor Author

@timneutkens @ijjk would you accept a PR for adding canvas and jsdom as externals to the webpack default externals opt-out list serverComponentsExternalPackages?

@karlhorky
Copy link
Contributor Author

cc @JesseKoldewijn, in case this approach is accepted by the team and you want to get another PR in for Next.js!

@JesseKoldewijn
Copy link
Contributor

What seems to be the package that throws up? Canvas or jsdom?

@JesseKoldewijn
Copy link
Contributor

As in, from what I understand after reading through this issue it seems like canvas is supposed to be a peer dep. of jsdom.

@JesseKoldewijn
Copy link
Contributor

Ill take a look into it in a sec. Currently omw home🤙

@JesseKoldewijn
Copy link
Contributor

Hey sorry, something came up regarding my grandpa that got incurable sick about 2 weeks ago (or rather we found out about it last week). I'll take a look tomorrow at work at this current issue and will keep y'all in the loop. Sorry for the delay.

@karlhorky
Copy link
Contributor Author

@JesseKoldewijn no worries, just when you get a chance. The packages canvas and jsdom should be added to the list, for the reasons described above (they cannot be bundled).

@JesseKoldewijn
Copy link
Contributor

has that already be done? or shall I do that for y'all

@karlhorky
Copy link
Contributor Author

Hasn't been done yet, nope! If you want to get another PR in, go ahead :)

@JesseKoldewijn
Copy link
Contributor

Will do! 👍

@kodiakhq kodiakhq bot closed this as completed in #46990 Mar 10, 2023
kodiakhq bot pushed a commit that referenced this issue Mar 10, 2023
#46990)

fixes #46893

### What?
fixing compile issue reguarding the dompurify package.

### Why?
To allow the usage of dompurify

### How?
Added jsdom and canvas to the external packages list which are used by dompurify and throw errors if not added to this list.
@karlhorky
Copy link
Contributor Author

Thanks for the PR and merge @JesseKoldewijn @ijjk ! 🙌 From a first look, seems like [email protected] is working:

CodeSandbox demo: https://codesandbox.io/p/sandbox/quirky-gagarin-speccj?file=%2Fapp%2Fpage.tsx

Screenshot 2023-03-12 at 11 22 30

@karlhorky
Copy link
Contributor Author

One thing that is kind of unusual to me is this extra wait - compiling /_error (client and server)... in the output (second to last line below), even though an error does not appear on the screen or in the logs (maybe this is filtered out somehow or hidden during client rendering?)

$ yarn dev
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
warn  - You have enabled experimental feature (appDir) in next.config.js.
warn  - Experimental features are not covered by semver, and may cause unexpected or broken application behavior. Use at your own risk.

info  - Thank you for testing `appDir` please leave your feedback at https://nextjs.link/app-feedback
event - compiled client and server successfully in 963 ms (262 modules)
wait  - compiling...
wait  - compiling /page (client and server)...
event - compiled client and server successfully in 1038 ms (451 modules)
wait  - compiling /_error (client and server)...
event - compiled client and server successfully in 151 ms (452 modules)

If I remove the DOMPurify(new JSDOM(..., then the error goes away...

@JesseKoldewijn
Copy link
Contributor

I've also seen that error compile pop up when using framer motion presence (the page transition component) so I'm not really sure where that _error compile wait comes from.

@karlhorky
Copy link
Contributor Author

ohh 🙈 It's the XSS payload <img src=x onerror=alert(1)//> lol

This can also be reproduced without DOMPurify, like this:

export default function Home() {
  return <img src="x" />;
}

The 404 will cause the error page to be rendered:

Screenshot 2023-03-12 at 11 39 45

@JesseKoldewijn
Copy link
Contributor

Ah gotcha

@github-actions
Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 12, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue was opened via the bug report template.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants