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

Equal signs in query parameter values are not parsed correctly #774

Open
lydell opened this issue Apr 5, 2023 · 0 comments
Open

Equal signs in query parameter values are not parsed correctly #774

lydell opened this issue Apr 5, 2023 · 0 comments

Comments

@lydell
Copy link

lydell commented Apr 5, 2023

This is a valid URL:

http://example.com/?q=a=b

It has a query parameter called q with the value a=b.

ctx.request.query does not contain the q parameter at all though – it’s dropped. Because of the equals sign in the value.

A workaround is to escape that equals sign:

http://example.com/?q=a%3Db

Here’s how System.Web.HttpUtility.ParseQueryString, Python and Node.js/browsers parse it (they all handle unescaped equal signs in query paramter values):

❯ dotnet fsi

Microsoft (R) F# Interactive version 12.4.0.0 for F# 7.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> System.Web.HttpUtility.ParseQueryString("?q=a=b").Get("q");;
val it: string = "a=b"

❯ python3
Python 3.11.2 (main, Feb 16 2023, 02:55:59) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import urllib.parse
>>> urllib.parse.parse_qs("q=a=b")["q"]
['a=b']

❯ node
Welcome to Node.js v16.17.0.
Type ".help" for more information.
> new URLSearchParams("?q=a=b").get("q")
'a=b'

I believe this is the relevant section in the WHATWG URL spec:

https://url.spec.whatwg.org/#urlencoded-parsing

If bytes contains a 0x3D (=), then let name be the bytes from the start of bytes up to but excluding its first 0x3D (=), and let value be the bytes, if any, after the first 0x3D (=) up to the end of bytes. If 0x3D (=) is the first byte, then name will be the empty byte sequence. If it is the last, then value will be the empty byte sequence.

This code splits on = but only uses the value if the splitted list has length 2. It needs to either use the head as the key, and join the tail back up with =, or split just on the first equals sign:

/// Parse the data in the string to a dictionary, assuming k/v pairs are separated
/// by the ampersand character.
let parseData (s : string) =
let parseArr (d : string array) =
if d.Length = 2 then (d.[0], Some <| System.Net.WebUtility.UrlDecode(d.[1]))
else d.[0],None
s.Split('&')
|> Array.toList
|> List.filter (not << String.IsNullOrWhiteSpace)
|> List.map (fun (k : string) -> k.Split('=') |> parseArr)

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

No branches or pull requests

2 participants