diff --git a/ts/test-both/util/url_test.ts b/ts/test-both/util/url_test.ts index bae6a684cb..e0727651c4 100644 --- a/ts/test-both/util/url_test.ts +++ b/ts/test-both/util/url_test.ts @@ -4,7 +4,11 @@ import { assert } from 'chai'; import { size } from '../../util/iterables'; -import { maybeParseUrl, setUrlSearchParams } from '../../util/url'; +import { + maybeParseUrl, + setUrlSearchParams, + urlPathFromComponents, +} from '../../util/url'; describe('URL utilities', () => { describe('maybeParseUrl', () => { @@ -84,4 +88,18 @@ describe('URL utilities', () => { assert.strictEqual(newUrl.search, '?foo=bar'); }); }); + + describe('urlPathFromComponents', () => { + it('returns / if no components are provided', () => { + assert.strictEqual(urlPathFromComponents([]), '/'); + }); + + it('joins components, percent-encoding them and removing empty components', () => { + const components = ['foo', '', '~', 'bar / baz qúx']; + assert.strictEqual( + urlPathFromComponents(components), + '/foo/~/bar%20%2F%20baz%20q%C3%BAx' + ); + }); + }); }); diff --git a/ts/textsecure/WebAPI.ts b/ts/textsecure/WebAPI.ts index 61d016fb3b..c0f8746518 100644 --- a/ts/textsecure/WebAPI.ts +++ b/ts/textsecure/WebAPI.ts @@ -54,7 +54,7 @@ import type { } from './Types.d'; import { handleStatusCode, translateError } from './Utils'; import * as log from '../logging/log'; -import { maybeParseUrl } from '../util/url'; +import { maybeParseUrl, urlPathFromComponents } from '../util/url'; // Note: this will break some code that expects to be able to use err.response when a // web request fails, because it will force it to text. But it is very useful for @@ -1780,7 +1780,7 @@ export function initialize({ await _ajax({ call: 'reportMessage', httpType: 'POST', - urlParameters: `/${senderUuid}/${serverGuid}`, + urlParameters: urlPathFromComponents([senderUuid, serverGuid]), responseType: 'bytes', }); } diff --git a/ts/util/url.ts b/ts/util/url.ts index b214501a31..c934128d9b 100644 --- a/ts/util/url.ts +++ b/ts/util/url.ts @@ -33,3 +33,9 @@ function cloneUrl(url: Readonly): URL { function stringifySearchParamValue(value: unknown): string { return value == null ? '' : String(value); } + +export function urlPathFromComponents( + components: ReadonlyArray +): string { + return `/${components.filter(Boolean).map(encodeURIComponent).join('/')}`; +}