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

Do not escape query included in url launch parameter #1774

Merged
merged 6 commits into from
Oct 13, 2023

Conversation

manics
Copy link
Member

@manics manics commented Oct 11, 2023

I'm not sure if this test is correct, but I think it illustrates the bug in
jupyterhub/nbgitpuller#329
https://discourse.jupyter.org/t/nb-on-binder-with-parameters-now-resulting-in-404/21860

 FAIL  js/packages/binderhub-client/lib/index.test.js
  ● Get full redirect URL and deal with query and encoded query (with pathType=url)

    expect(received).toBe(expected) // Object.is equality

    Expected: "https://hub.test-binder.org/user/something/endpoint?a=1/2&b=3%3F%2F&token=token"
    Received: "https://hub.test-binder.org/user/something/endpoint%3Fa=1/2&b=3%3F%2F?token=token"

      179 |       )
      180 |       .toString(),
    > 181 |   ).toBe(
          |     ^
      182 |     "https://hub.test-binder.org/user/something/endpoint?a=1/2&b=3%3F%2F&token=token",
      183 |   );
      184 | });

      at Object.toBe (js/packages/binderhub-client/lib/index.test.js:181:5)

@manics manics force-pushed the binderhub-client-js-query-url branch 3 times, most recently from 0aff7c5 to 312e0d9 Compare October 11, 2023 20:35
@yuvipanda
Copy link
Collaborator

@manics I opened a PR to this PR fixing this: manics#8. Well, almost fixing it - you'll see it fails currently because the unencoded / in path does get encoded when we use URL. I'm not sure unencoded / are allowed in queryparams, but clearly our prior implementation did allow them.

So our two options are:

  1. Determine that encoding any / in query params is ok
  2. Use another method that strictly fully preserves path

I think (1) is ok, and we can sort of validate this by looking at the two reported URLs that had problems, and perhaps also look at our analytics event streams for paths with unencoded / in them. What do you think?

@manics
Copy link
Member Author

manics commented Oct 12, 2023

First of all was my test definitely correct? I wasn't completely sure whether the path (including query) argument of getFullRedirectURL was meant to be encoded or not. If / is meant to be encoded in a query then I think let's assume it is for now since browsers often auto encode/decode some things even something doesn't quite follow the spec.

The main problem is the leading ? being encoded which it looks like you've fixed!

Edit: Feel free to rebase and force push if you want, otherwise I'll do it later

manics and others added 4 commits October 12, 2023 19:49
Previously, if 'path' contained query parameters, they
were not treated correctly. Here they do get treated
correctly, although new constructor mechanics mean that the
serverUrl we get back from binderhub can't have query parameters
now.
@manics manics force-pushed the binderhub-client-js-query-url branch from 0f2f23a to b93f4e0 Compare October 12, 2023 18:53
@manics manics marked this pull request as ready for review October 12, 2023 19:03
@manics
Copy link
Member Author

manics commented Oct 12, 2023

Rebased

@manics manics added the bug label Oct 12, 2023
@consideRatio
Copy link
Member

@manics could you update the title of this PR as well to reflect its more than just test related now?

@manics manics changed the title Add failing test for js client URL with query Do not escape query included in url launch parameter Oct 12, 2023
@manics
Copy link
Member Author

manics commented Oct 12, 2023

Done!

@yuvipanda
Copy link
Collaborator

I added an additional test specifically using nbgitpuller, and showing that URL encoding seems to be done now just the right amount, rather than double or none.

Comment on lines +121 to +124
// Ensure there is a trailing / in serverUrl
if (!url.pathname.endsWith("/")) {
url.pathname += "/";
}

This comment was marked as outdated.

This comment was marked as outdated.

Copy link
Member

@consideRatio consideRatio Oct 13, 2023

Choose a reason for hiding this comment

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

Ah!

I think there is a breaking change here.

-url.pathname = url.pathname + "/doc/tree/" + encodeURI(path);
+url = new URL("doc/tree/" + encodeURI(path), url);

These are different I think, to verify that:

console.log("old new behavior presented below")

let nonRoot1 = new URL("https://developer.mozilla.org/test")
nonRoot1.pathname = nonRoot1.pathname.replace(/\/$/, "");
nonRoot1.pathname = nonRoot1.pathname + "/doc/tree/"
console.log(nonRoot1.href)

let root1 = new URL("https://developer.mozilla.org")
root1.pathname = root1.pathname.replace(/\/$/, "");
root1.pathname = root1.pathname + "/doc/tree/"
console.log(root1.href)

console.log("new behavior comes below")

let nonRoot2 = new URL("https://developer.mozilla.org/test")
let newNonRoot2 = new URL("doc/tree", nonRoot2)
console.log(newNonRoot2.href)

let root2 = new URL("https://developer.mozilla.org")
let newRoot2 = new URL("doc/tree", root2)
console.log(newRoot2.href)

image

Copy link
Member Author

Choose a reason for hiding this comment

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

The trailing / in the pathname is required (/test/ not /test), it affects how the parts are joined.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, preventing this behavior is why we enforce path ends with / even if it is not passed in. So baseUrl will never not have trailing slash. As @manics says, it affects how parts are joined.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@consideRatio I added another test case to demonstrate why I think this is fine.

Copy link
Member

Choose a reason for hiding this comment

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

Hmmm but what if baseUrl is a jupyterhub with a prefix?

Note that now, but not before, the nonRoot examples doesnt have the /test prefix in the path?

Copy link
Member

Choose a reason for hiding this comment

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

If you both think there is no loss of path issue, this is good even if i dont understand the details.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmmm but what if baseUrl is a jupyterhub with a prefix?

The code appends a trailing / if there isn't one, so this should behave correctly

let nonRoot2 = new URL("https://developer.mozilla.org/test")
if (!nonRoot2.pathname.endsWith("/")) {
    nonRoot2.pathname += "/";
}
let newNonRoot2 = new URL("doc/tree", nonRoot2)
console.log(newNonRoot2.href)
https://developer.mozilla.org/test/doc/tree

Copy link
Member

Choose a reason for hiding this comment

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

Ah, so the href can be the same for a URL object, but end up resulting in a different URL when used to construct another. I think i got it and this LGTM

Copy link
Member

@consideRatio consideRatio left a comment

Choose a reason for hiding this comment

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

This looks good to me from what I can tell, and with the added tests, we should be in better shape than we already are I think.

I'm not sure about the expected behavior and such =/

Copy link
Member

@consideRatio consideRatio left a comment

Choose a reason for hiding this comment

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

See https://github.com/jupyterhub/binderhub/pull/1774/files#r1358088687, I think this isn't correct yet because there is a behavior change unrelated to escaping.

@yuvipanda
Copy link
Collaborator

ok, I tested this locally. You can first see me try an nbgitpuller link on the main branch, and it fails. Then I switch to this branch, and it succeeds (once if hard refresh the page to get new JS).

Screen.Recording.2023-10-14.at.12.57.24.AM.mov

Thank you for working on this @manics and thanks for the review, @consideRatio. I am hoping that my cleanup + automated testing efforts will allow us to build more of these things without breaking.

@yuvipanda yuvipanda merged commit 451eba4 into jupyterhub:main Oct 13, 2023
12 of 15 checks passed
@yuvipanda yuvipanda added the code:js-binderhub-client js changes to binderhub-client label Oct 13, 2023
@manics manics deleted the binderhub-client-js-query-url branch October 13, 2023 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug code:js-binderhub-client js changes to binderhub-client
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants