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

__staticRouterHydrationData html tag escape #10068

Merged
merged 4 commits into from Feb 14, 2023

Conversation

zheng-chuang
Copy link
Contributor

@zheng-chuang zheng-chuang commented Feb 8, 2023

fix bug when __staticRouterHydrationData contains </script>

@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented Feb 8, 2023

Hi @zheng-chuang,

Welcome, and thank you for contributing to React Router!

Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once.

You may review the CLA and sign it by adding your name to contributors.yml.

Once the CLA is signed, the CLA Signed label will be added to the pull request.

If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at hello@remix.run.

Thanks!

- The Remix team

@changeset-bot
Copy link

changeset-bot bot commented Feb 8, 2023

🦋 Changeset detected

Latest commit: 54f3fcd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
react-router-dom Patch
react-router Patch
react-router-dom-v5-compat Patch
react-router-native Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented Feb 8, 2023

Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳

@timdorr
Copy link
Member

timdorr commented Feb 8, 2023

I'm not sure what this is actually fixing. Can you explain why this change is needed?

@zheng-chuang
Copy link
Contributor Author

I'm not sure what this is actually fixing. Can you explain why this change is needed?

image

When ssr, if the data contains script tags, __staticRouterHydrationData will become like this

@timdorr
Copy link
Member

timdorr commented Feb 8, 2023

@brophdawg11 Any thoughts on this? This can probably be simplified to .replaceAll('<', '\\\\u003C').

@brophdawg11
Copy link
Contributor

I'm not sure we want to try to handle every potential edge case when it comes to serializing the context. The built-in serialization is very simple by design and provides an opt-out for these types of use cases via <StaticRouterProvider hydrate={false} />. That way you can serialize/deserialize any way you want and pass hydrationData to createBrowserRouter on the client side.

@zheng-chuang
Copy link
Contributor Author

zheng-chuang commented Feb 8, 2023

I'm not sure we want to try to handle every potential edge case when it comes to serializing the context. The built-in serialization is very simple by design and provides an opt-out for these types of use cases via <StaticRouterProvider hydrate={false} />. That way you can serialize/deserialize any way you want and pass hydrationData to createBrowserRouter on the client side.

Thanks, I'll close this PR. But I really hope you can make it easier with this library "serialize-javascript" which I just found.

@SyMind
Copy link

SyMind commented Feb 8, 2023

@brophdawg11

That way you can serialize/deserialize any way you want and pass hydrationData to createBrowserRouter on the client side.

I agree this, but I still think this PR is necessary.

React-router stringify the JSON string on server, parse JSON string in <script> tag on browser.

When </script> within JSON string in a <script> tag, error will occur. Because HTML parser will treat everything between <script> and </script> as part of the script.

In this case, the react router will fail to run! This is a bug!

This PR will not change the semantics of JSON string, but can effectively avoid the syntax conflict with HTML!

Some references for this problem in stackoverflow:

  1. https://stackoverflow.com/questions/22488830/script-within-a-javascript-string-in-a-script-tag
  2. https://stackoverflow.com/questions/66837/when-is-a-cdata-section-necessary-within-a-script-tag
  3. https://stackoverflow.com/questions/779959/is-it-necessary-to-escape-character-and-for-javascript-string

@SyMind
Copy link

SyMind commented Feb 8, 2023

@zheng-chuang Please reopen the PR.

@zheng-chuang zheng-chuang reopened this Feb 9, 2023
@zheng-chuang zheng-chuang force-pushed the fix/htmlescape branch 2 times, most recently from f8f7609 to 03aa55b Compare February 9, 2023 00:17
@brophdawg11
Copy link
Contributor

I don't think this is worth adding a 2kb gzipped dependency to react-router when this is solvable in user-land via any number of packages depending on your app needs (serialize-javascript, jsesc, and I'm sure many others).

I could be convinced that at the very least we should auto-escape HTML in the JSON string (<, >, ', ", &), so if you want to change this back to the simplest possible approach using .replace to allow safe serialization of <script> (or other html) tags inside your loaderData using the built-in hydration, I'll happily run it by the team.

Personally, if I needed to dynamically render HTML elements within my components, I would send up the data, not the markup:

function loader() {
  return json({
    scripts: [{ src: '/script.js' }],
  });
}

function component() {
  let data = useLoaderData();
  return data.scripts.map(s => <script src={s.src}></script>);
}

Or if I was loading pre-generated markup from something like a CMS, I'd just escape that in my loader if I knew it would contain HTML.

@brophdawg11
Copy link
Contributor

Also would you mind repointing this to dev since it has code changes?

@zheng-chuang
Copy link
Contributor Author

I don't think this is worth adding a 2kb gzipped dependency to react-router

But this runs on the server side and will not be packaged and sent to the browser.

@zheng-chuang zheng-chuang changed the base branch from main to dev February 10, 2023 00:52
@brophdawg11
Copy link
Contributor

It's not just for size reasons - react-router-dom has zero external dependencies at the moment:

  "dependencies": {
    "@remix-run/router": "1.3.2",
    "react-router": "6.8.1"
  },

@brophdawg11
Copy link
Contributor

@zheng-chuang I think you may need to rebase this onto dev as well - looks like it's picking up a few unrelated commits from being branched off main initially

@zheng-chuang zheng-chuang reopened this Feb 10, 2023
@zheng-chuang
Copy link
Contributor Author

zheng-chuang commented Feb 10, 2023

It's not just for size reasons - react-router-dom has zero external dependencies at the moment:

@brophdawg11 serialize-javascript has been removed. and branch is based on dev.

Comment on lines 1 to 2
// This code is copy form https://github.com/vercel/next.js
// License: https://github.com/vercel/next.js/blob/b2e9431bb46563264d450b1707dc0d9cdaf70d26/license.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// This code is copy form https://github.com/vercel/next.js
// License: https://github.com/vercel/next.js/blob/b2e9431bb46563264d450b1707dc0d9cdaf70d26/license.md
// This utility is based on https://github.com/zertosh/htmlescape
// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE

Since this file is already copied by Next.js, let's reference the original code as well.

@brophdawg11
Copy link
Contributor

Thanks @zheng-chuang!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants