fix(fmt): stop share-URI query values from being decoded twice or dropped (#10027)

`Utils.ParseQueryString` already unescapes every value, and
`BaseFmt.GetQueryDecoded` unescaped it again. A value that still held a
valid percent sequence after the first pass decayed on the second: an
obfuscation password of `ob%41fs` is exported as `ob%2541fs` and imported
back as `obAfs`. Only well-formed sequences are affected, which is why the
damage is silent - `100%` and `66%ff` survive untouched.

The same function also split each pair on every `=`, and skipped the pair
unless exactly two halves came out. RFC 3986 lists `=` among the
sub-delimiters a query value may carry, so only the first one separates the
key from the value, and `HttpUtility.ParseQueryString` reads a query string
the same way. Splitting on all of them discarded a syntactically valid
pair: `?ech=AAj+DQAEAAAAAA==` was lost entirely, and so was a `plugin`
value in the non-canonical SIP002 spelling, since those are `;` separated
`key=value` lists.

v2rayN percent-encodes both on export, so its own links were never
affected; what changes is that the parser now follows the grammar instead
of discarding a pair it cannot split in two.

Splitting on the first `=` only, and reading the value the parser already
decoded, fixes both. `ParseQueryString` keeps decoding because
`ConfigHandler` reads its result directly.

`GetQueryDecoded` and `GetQueryValue` are now equivalent; they are left
separate to keep this change small, and can be collapsed if you prefer.
This commit is contained in:
Miheichev Aleksandr Sergeevich
2026-08-28 06:38:02 +00:00
committed by GitHub
parent f332caf507
commit 2a65e5ac80
3 changed files with 82 additions and 2 deletions
+3 -1
View File
@@ -200,7 +200,9 @@ public class Utils
var parts = query[1..].Split('&', StringSplitOptions.RemoveEmptyEntries);
foreach (var part in parts)
{
var keyValue = part.Split('=');
// Split on the FIRST '=' only: RFC 3986 lists '=' among the sub-delimiters a query
// value may carry, so everything after the first one belongs to the value.
var keyValue = part.Split('=', 2);
if (keyValue.Length != 2)
{
continue;