From babcf44891e6c680b1eca029c66e509c7e40fc30 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Wed, 15 Jul 2026 02:49:34 +0200 Subject: [PATCH] fix(frontend): decode URL-safe base64 when parsing an imported share link Base64.decode called window.atob directly, which rejects the base64url alphabet (- and _) and unpadded input. But the panel's own share-link emitter uses Base64.encode(x, true) (URL-safe, unpadded), and real SIP002 links do too, so importing a Shadowsocks link whose method:password encodes with a - or _ threw, fell back to the raw undecoded string, and produced a wrong method and garbage password (the vmess parser shared the same limitation). Normalize base64url and re-pad before atob so decode round-trips every emitted link. --- frontend/src/test/outbound-link-parser.test.ts | 13 +++++++++++++ frontend/src/utils/index.ts | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/frontend/src/test/outbound-link-parser.test.ts b/frontend/src/test/outbound-link-parser.test.ts index ef1fe455b..3ab512b87 100644 --- a/frontend/src/test/outbound-link-parser.test.ts +++ b/frontend/src/test/outbound-link-parser.test.ts @@ -250,6 +250,19 @@ describe('parseShadowsocksLink', () => { expect(settings.servers[0].method).toBe('aes-256-gcm'); expect(settings.servers[0].password).toBe('legacypw'); }); + + it('decodes URL-safe base64 userinfo (as the emitter writes it)', () => { + // genShadowsocksLink emits Base64.encode(method:password, true) — URL-safe, + // so a password whose encoding contains - or _ must still round-trip. + const method = 'aes-256-gcm'; + const password = '>>>'; + const userinfo = Base64.encode(`${method}:${password}`, true); + expect(userinfo).toMatch(/[-_]/); + const out = parseShadowsocksLink(`ss://${userinfo}@1.2.3.4:8388#urlsafe`); + const settings = out?.settings as { servers: Array<{ method: string; password: string }> }; + expect(settings.servers[0].method).toBe(method); + expect(settings.servers[0].password).toBe(password); + }); }); describe('parseHysteria2Link', () => { diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts index c58ce781c..845c3e109 100644 --- a/frontend/src/utils/index.ts +++ b/frontend/src/utils/index.ts @@ -641,8 +641,10 @@ export class Base64 { } static decode(content: string = ''): string { + const normalized = content.replace(/-/g, '+').replace(/_/g, '/'); + const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '='); return new TextDecoder().decode( - Uint8Array.from(window.atob(content), (c) => c.charCodeAt(0)), + Uint8Array.from(window.atob(padded), (c) => c.charCodeAt(0)), ); } }