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

URLs with a "%" character encoded in a param don't work in SvelteKit. #3069

Closed
hperrin opened this issue Dec 17, 2021 · 9 comments
Closed

URLs with a "%" character encoded in a param don't work in SvelteKit. #3069

hperrin opened this issue Dec 17, 2021 · 9 comments
Labels
bug Something isn't working router
Milestone

Comments

@hperrin
Copy link

hperrin commented Dec 17, 2021

Describe the bug

SvelteKit's router can't navigate consistently to a URL with a '%' encoded in it (as in "%25" appears in the URL).

_navigate calls const info = this.parse(url);, which decodes the URL.
Then routes in the manifest calls decodeURIComponent on the already decoded component.

So this route:

const href = `/route/${encodeURIComponent('%test')}`;

If delivered to goto will break, but if you go to that URL directly, it will be handled.

As such, if you use this route instead:

const href = encodeURI(`/route/${encodeURIComponent('%test')}`);

goto can handle it just fine, but if you go to the URL directly, you end up with an encoded string in your page params.

Reproduction

I created a simple repo that reproduces this bug.

Basically, have a link with a "%" character in the URL in one of the params. If you single encode it, it won't work when you navigate in the app. If you double encode it, it won't work when you go directly to the page.

Logs

Uncaught (in promise) URIError: malformed URI sequence
    routes http://localhost:3000/.svelte-kit/dev/generated/manifest.js?t=1639767160433:15
    _load http://localhost:3000/.svelte-kit/dev/runtime/internal/start.js:982
    _get_navigation_result http://localhost:3000/.svelte-kit/dev/runtime/internal/start.js:790
    update http://localhost:3000/.svelte-kit/dev/runtime/internal/start.js:621
    handle_navigation http://localhost:3000/.svelte-kit/dev/runtime/internal/start.js:610
    _navigate http://localhost:3000/.svelte-kit/dev/runtime/internal/start.js:287
    init_listeners http://localhost:3000/.svelte-kit/dev/runtime/internal/start.js:170

System Info

System:
    OS: Linux 5.15 Manjaro Linux
    CPU: (12) x64 Intel(R) Core(TM) i5-10500H CPU @ 2.50GHz
    Memory: 5.64 GB / 15.45 GB
    Container: Yes
    Shell: 3.3.1 - /usr/bin/fish
  Binaries:
    Node: 14.18.1 - /usr/local/bin/node
    npm: 8.2.0 - /usr/local/bin/npm
  Browsers:
    Brave Browser: 96.1.33.106
    Firefox: 95.0.1
  npmPackages:
    @sveltejs/adapter-auto: next => 1.0.0-next.4 
    @sveltejs/kit: next => 1.0.0-next.202 
    svelte: ^3.44.0 => 3.44.3 

Severity

blocking all usage of SvelteKit

Additional Information

I'm working on an app that requires being able to use a percent sign in a query, so this breaks some portions of my app.

@hperrin
Copy link
Author

hperrin commented Dec 17, 2021

In case anyone else is running into this issue, I have a workaround for development mode only. (It absolutely should never be used in production!)

In your app.html file, just before %svelte.head%, place this:

    <script type="text/javascript">
      (function (root) {
        const _decodeURI = root.decodeURI;
        root.decodeURI = function (encodedURI) {
          const err = new Error();
          if (
            err.stack
              .split('\n')[1]
              .match(
                /^parse@.*[\/\\]\.svelte-kit[\/\\]dev[\/\\]runtime[\/\\]internal[\/\\]start\.js:.*$/
              )
          ) {
            return encodedURI;
          }
          return _decodeURI.call(this, encodedURI);
        }.bind(root);
      })(window);
    </script>

@hperrin
Copy link
Author

hperrin commented Dec 18, 2021

It also appears that returning a redirect in a load function will run encodeURI on the URL, meaning that there is no way to properly encode a URI component within it, since it will be double encoded.

@bluwy bluwy added p3-edge-case SvelteKit cannot be used in an uncommon way router labels Dec 19, 2021
@hperrin
Copy link
Author

hperrin commented Dec 20, 2021

There is also potentially no way to include a slash in a param, since that will be decoded by the decodeURI call before being routed. I haven't tested that case though.

@hperrin
Copy link
Author

hperrin commented Dec 25, 2021

Correct me if I'm wrong, but I believe the fix here is to just use the encoded URI in the manifest. Shouldn't that be a one line change?

@anamba
Copy link

anamba commented Jan 12, 2022

Well this might explain why my OpenID Connect implemention isn't working. The server keeps telling me "The requested redirect uri is malformed or doesn't match client redirect URI."

Sure enough, upon closer inspection, the redirect_uri got encoded twice somehow:

redirect_uri=http%253A%252F%252F ...

Edit: Argh... although I haven't figured out the issue yet, I am no longer sure that SvelteKit is the cause of this particular issue.

Edit 2: After ruling out many other things, SvelteKit is still a possible culprit. I am logging the URL to the console before redirecting, and I can paste that URL into the browser and it works. Right after that logging statement, that URL goes into a { status: 302, redirect: authorization_url } response and the url comes out mangled... And to be clear, it's not just the redirect_uri part of the URL, it's the whole thing (e.g. &scope=profile%2520openid%2520email).

@hperrin
Copy link
Author

hperrin commented Jan 19, 2022

Just to give you a workaround for development, @anamba, here's the updated code from my comment above to work with the latest SvelteKit. Keep in mind, this will not work in production.

    <script type="text/javascript">
      // TODO: remove this once https://github.com/sveltejs/kit/issues/3069 is resolved.
      (function (root) {
        const _decodeURI = root.decodeURI;
        root.decodeURI = function (encodedURI) {
          const err = new Error();
          if (
            err.stack
              .split('\n')[1]
              .match(
                /^parse@.*[\/\\]\.svelte-kit[\/\\]runtime[\/\\]client[\/\\]start\.js:.*$/
              )
          ) {
            return encodedURI;
          }
          return _decodeURI.call(this, encodedURI);
        }.bind(root);
      })(window);
    </script>

@Rich-Harris Rich-Harris added the bug Something isn't working label Apr 28, 2022
@Rich-Harris Rich-Harris added this to the 1.0 milestone Apr 28, 2022
@benmccann benmccann removed the p3-edge-case SvelteKit cannot be used in an uncommon way label Jul 19, 2022
@dciug
Copy link

dciug commented Aug 20, 2022

My current hack/workaround is this function:

export function escapeURI(uri) { return encodeURI(uri.replaceAll('%', '%25')); }

It results in URIs with %2525 but it gets the job done. You may also need to change tsconfig.json with "lib": ["ES2021", "ES2021.String"]

@benmccann
Copy link
Member

I can't reproduce this. @hperrin's project is fairly old at this point, but I created a new project and copied his files over and it seems to be working as expected.

I suspect this was fixed last month by https://github.com/sveltejs/kit/blob/master/packages/kit/CHANGELOG.md#100-next385 and @dciug is probably on a version older than that

@hperrin
Copy link
Author

hperrin commented Nov 22, 2022

I can't reproduce this. @hperrin's project is fairly old at this point, but I created a new project and copied his files over and it seems to be working as expected.

I suspect this was fixed last month by https://github.com/sveltejs/kit/blob/master/packages/kit/CHANGELOG.md#100-next385 and @dciug is probably on a version older than that

Yes. It looks like this is the fix. :D
d02f1f2#diff-d2344aa425f99c605f40fdf8ea76be2f063c21cafc0402e044b1a9a254f60e7dL13

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working router
Projects
None yet
Development

No branches or pull requests

6 participants