diff --git a/js/magnetShared.js b/js/magnetShared.js index 20538e68..6e4400f5 100644 --- a/js/magnetShared.js +++ b/js/magnetShared.js @@ -223,7 +223,9 @@ export function normalizeMagnetInput(rawValue) { const params = []; for (const rawParam of rawParams) { - const [rawKey, rawVal = ""] = rawParam.split("=", 2); + const separatorIndex = rawParam.indexOf("="); + const rawKey = separatorIndex === -1 ? rawParam : rawParam.slice(0, separatorIndex); + const rawVal = separatorIndex === -1 ? "" : rawParam.slice(separatorIndex + 1); const key = rawKey.trim(); if (!key) { continue; diff --git a/tests/magnet-utils.test.mjs b/tests/magnet-utils.test.mjs index 1536229a..12b6c9c5 100644 --- a/tests/magnet-utils.test.mjs +++ b/tests/magnet-utils.test.mjs @@ -254,3 +254,50 @@ test("normalizeAndAugmentMagnet keeps unchanged xt/tr/ws/xs params singular", () "Inbound xs hint should remain singular" ); }); + +test("normalizeMagnetInput preserves parameter values containing additional equals signs", () => { + const infoHash = "0123456789abcdef0123456789abcdef01234567"; + const rawMagnet = + `magnet:?xt=urn:btih:${infoHash}` + + "&ws=https://cdn.example.com/video.mp4%3FX-Amz-Signature=abc123==%26X-Amz-Credential=test%3Dvalue" + + "&xs=https://cdn.example.com/video.torrent%3Ftoken=ZXlKaGJHY2lPaUpJVXpJMU5pSjkuZXlKcFpDST0=%26policy=a%3Db" + + "&dn=Episode=01=Directors=Cut" + + "&foo=bar=baz=="; + + const normalized = normalizeMagnetInput(rawMagnet); + const valuesByKey = Object.fromEntries( + normalized.params.map((param) => [param.lowerKey, param.value]) + ); + + assert.equal( + valuesByKey.ws, + "https://cdn.example.com/video.mp4%3FX-Amz-Signature=abc123==%26X-Amz-Credential=test%3Dvalue", + "ws should keep the full signed URL token" + ); + assert.equal( + valuesByKey.xs, + "https://cdn.example.com/video.torrent%3Ftoken=ZXlKaGJHY2lPaUpJVXpJMU5pSjkuZXlKcFpDST0=%26policy=a%3Db", + "xs should keep base64-like payloads and nested query params intact" + ); + assert.equal( + valuesByKey.dn, + "Episode=01=Directors=Cut", + "dn should preserve all '=' characters" + ); + assert.equal( + valuesByKey.foo, + "bar=baz==", + "Unknown params should preserve all '=' characters" + ); + + const rebuilt = buildMagnetUri( + normalized.normalizedScheme, + normalized.params, + normalized.fragment + ); + assert.equal( + rebuilt, + rawMagnet, + "buildMagnetUri should reproduce multi-equals values verbatim" + ); +});