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

fix: parse boolean values in URL query parameters #9722

Closed
wants to merge 3 commits into from
Closed

Conversation

glowcloud
Copy link
Contributor

Refs #9674

@heldersepu
Copy link
Contributor

heldersepu added a commit to heldersepu/swagger-ui that referenced this pull request Mar 23, 2024
@heldersepu
Copy link
Contributor

This is such an easy fix ... Why does it takes so long to merge?

@char0n char0n self-assigned this Apr 10, 2024
@char0n
Copy link
Member

char0n commented Apr 10, 2024

Hi @heldersepu,

Any reason why is that not enabled by default?

Please have a look at GHSA-qrmm-w75w-3wpx

Long story short, it's a security concern, and there must be an explicit decision to enable the query config. We've explicitly decided to enable it on deployments we control like petstore.

@char0n
Copy link
Member

char0n commented Apr 10, 2024

This is such an easy fix ... Why does it takes so long to merge?

Please be patient. This is huge and complex software and it's just one piece in the entire Swagger ecosystem. We expedite features and bug-fixes with the speed that matches our resources.

@char0n
Copy link
Member

char0n commented Apr 10, 2024

I'm suggesting to rewrite the functions into following form:

export const parseSearch = () => {
  const searchParams = new URLSearchParams(win.location.search)

  if (searchParams.size === 0) return {}

  for (const [key, value] of searchParams) {
    searchParams.set(key, value  === 'true' ? true : value === 'false' ? false : value)
  }

  return Object.fromEntries(searchParams)
}

export const serializeSearch = (searchMap) => {
  const searchParams = new URLSearchParams(Object.entries(searchMap))
  return String(searchParams)
}

Explanation: instead of using custom search params parsing, the functions now use standardized WHATWG spec compliant parser.

@char0n
Copy link
Member

char0n commented Apr 10, 2024

But still IMHO we shouldn't be doing this in this function as this function was designed as general purpose search parameters parser. This should be done only depending on the context. Why only handle boolean if there is also a number and null ?

for (const [key, value] of searchParams) {
    searchParams.set(key, value  === 'true' ? true : value === 'false' ? false : value)
}

@glowcloud
Copy link
Contributor Author

I'm suggesting to rewrite the functions into following form:

export const parseSearch = () => {
  const searchParams = new URLSearchParams(win.location.search)

  if (searchParams.size === 0) return {}

  for (const [key, value] of searchParams) {
    searchParams.set(key, value  === 'true' ? true : value === 'false' ? false : value)
  }

  return Object.fromEntries(searchParams)
}

export const serializeSearch = (searchMap) => {
  const searchParams = new URLSearchParams(Object.entries(searchMap))
  return String(searchParams)
}

Explanation: instead of using custom search params parsing, the functions now use standardized WHATWG spec compliant parser.

I see that two unit tests fail with this: the one I added in this PR and:

  it("encode url components", () => {
        expect(serializeSearch({foo: "foo bar"})).toEqual("foo=foo%20bar")
      })

Will check out how to modify these to make it work.

@char0n
Copy link
Member

char0n commented Apr 10, 2024

I see that two unit tests fail with this: the one I added in this PR and:

Yes because the encoding in the test is no longer compatible with WHATWG spec and how url search params SHOULD be encoded apart from how path segment is encoded. To say it simply - how the path and search params are encoded diverged in WHATWG spec.

@glowcloud
Copy link
Contributor Author

glowcloud commented Apr 10, 2024

But still IMHO we shouldn't be doing this in this function as this function was designed as general purpose search parameters parser. This should be done only depending on the context. Why only handle boolean if there is also a number and null ?

for (const [key, value] of searchParams) {
    searchParams.set(key, value  === 'true' ? true : value === 'false' ? false : value)
}

Yes, I think we should also handle these. For number it can seem like there's no issue there, for example with defaultModelExpandDepth but in our prop types we expect a number so we get this error (only in dev):

Warning: Failed prop type: Invalid prop `expandDepth` of type `string` supplied to `Model`, expected `number`.

The setting will work properly in this case, I'm guessing because of type coercion, here when I set it to 1:
Screenshot 2024-04-10 at 16 12 55

@char0n
Copy link
Member

char0n commented Apr 10, 2024

But still IMHO we shouldn't be doing this in this function as this function was designed as general purpose search parameters parser. This should be done only depending on the context. Why only handle boolean if there is also a number and null ?

What I mean by this is that we shouldn't actually do it at all (inside the parseSearch function). I'll reiterate that the parseSearch is a general purpose function and is used in multiple places in the codebase. And depending where it's used, we apply coercion differently. Converting string to number is very complex task; well not the conversion but rather detection what possibly is a number.

@char0n
Copy link
Member

char0n commented Apr 10, 2024

We can do this as two PRs. First PR is aligning those two functions with WHATWG spec. The seconds one would actually do the context dependent coercion somewhere in

let queryConfig = opts.queryConfigEnabled ? parseSearch() : {}

@glowcloud
Copy link
Contributor Author

Reverted the changes to parseSearch and added new functions for conversion.

I went through the config options and hopefully didn't miss anything. I skipped the options that allow functions.

Also tested it manually and an example for these options:

?deepLinking=true&defaultModelExpandDepth=5&syntaxHighlight={"theme":"monokai"}&supportedSubmitMethods=["get"]
Screenshot 2024-04-11 at 14 11 57 Screenshot 2024-04-11 at 14 12 34

We can see that:

  • try it out is not enabled for the POST request, is enabled for the GET request
  • syntax highlight theme is set to monokai
  • model is expanded according to defaultModelExpandDepth

Deep linking also works, example URL:

http://localhost:3200/?deepLinking=true&defaultModelExpandDepth=5&syntaxHighlight={%22theme%22:%22monokai%22}&supportedSubmitMethods=[%22get%22]#/pet/updatePet

Let me know if it's intended to support arrays and objects in the URL as well.

@glowcloud
Copy link
Contributor Author

Superseded by #9829

@glowcloud glowcloud closed this Apr 16, 2024
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.

3 participants