Open all Signal links in app

This commit is contained in:
Evan Hahn 2022-02-02 12:29:01 -06:00 committed by GitHub
parent 07968ea42b
commit 60d348e7cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 90 additions and 7 deletions

View File

@ -82,6 +82,7 @@ import {
parseSgnlHref, parseSgnlHref,
parseCaptchaHref, parseCaptchaHref,
parseSignalHttpsLink, parseSignalHttpsLink,
rewriteSignalHrefsIfNecessary,
} from '../ts/util/sgnlHref'; } from '../ts/util/sgnlHref';
import { toggleMaximizedBrowserWindow } from '../ts/util/toggleMaximizedBrowserWindow'; import { toggleMaximizedBrowserWindow } from '../ts/util/toggleMaximizedBrowserWindow';
import { import {
@ -334,13 +335,15 @@ function prepareUrl(
}).href; }).href;
} }
async function handleUrl(event: Electron.Event, target: string) { async function handleUrl(event: Electron.Event, rawTarget: string) {
event.preventDefault(); event.preventDefault();
const parsedUrl = maybeParseUrl(target); const parsedUrl = maybeParseUrl(rawTarget);
if (!parsedUrl) { if (!parsedUrl) {
return; return;
} }
const target = rewriteSignalHrefsIfNecessary(rawTarget);
const { protocol, hostname } = parsedUrl; const { protocol, hostname } = parsedUrl;
const isDevServer = const isDevServer =
process.env.SIGNAL_ENABLE_HTTP && hostname === 'localhost'; process.env.SIGNAL_ENABLE_HTTP && hostname === 'localhost';

View File

@ -1,4 +1,4 @@
// Copyright 2020-2021 Signal Messenger, LLC // Copyright 2020-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai'; import { assert } from 'chai';
@ -13,6 +13,7 @@ import {
parseCaptchaHref, parseCaptchaHref,
parseE164FromSignalDotMeHash, parseE164FromSignalDotMeHash,
parseSignalHttpsLink, parseSignalHttpsLink,
rewriteSignalHrefsIfNecessary,
} from '../../util/sgnlHref'; } from '../../util/sgnlHref';
function shouldNeverBeCalled() { function shouldNeverBeCalled() {
@ -380,4 +381,62 @@ describe('sgnlHref', () => {
); );
}); });
}); });
describe('rewriteSignalHrefsIfNecessary', () => {
it('rewrites http://signal.group hrefs, making them use HTTPS', () => {
assert.strictEqual(
rewriteSignalHrefsIfNecessary('http://signal.group/#abc123'),
'https://signal.group/#abc123'
);
});
it('rewrites http://signal.art hrefs, making them use HTTPS', () => {
assert.strictEqual(
rewriteSignalHrefsIfNecessary(
'http://signal.art/addstickers/#pack_id=abc123'
),
'https://signal.art/addstickers/#pack_id=abc123'
);
});
it('rewrites http://signal.me hrefs, making them use HTTPS', () => {
assert.strictEqual(
rewriteSignalHrefsIfNecessary('http://signal.me/#p/+18885551234'),
'https://signal.me/#p/+18885551234'
);
});
it('removes auth if present', () => {
assert.strictEqual(
rewriteSignalHrefsIfNecessary(
'http://user:pass@signal.group/ab?c=d#ef'
),
'https://signal.group/ab?c=d#ef'
);
assert.strictEqual(
rewriteSignalHrefsIfNecessary(
'https://user:pass@signal.group/ab?c=d#ef'
),
'https://signal.group/ab?c=d#ef'
);
});
it('does nothing to other hrefs', () => {
[
// Normal URLs
'http://example.com',
// Already HTTPS
'https://signal.art/addstickers/#pack_id=abc123',
// Different port
'http://signal.group:1234/abc?d=e#fg',
// Different subdomain
'http://subdomain.signal.group/#abcdef',
// Different protocol
'ftp://signal.group/#abc123',
'ftp://user:pass@signal.group/#abc123',
].forEach(href => {
assert.strictEqual(rewriteSignalHrefsIfNecessary(href), href);
});
});
});
}); });

View File

@ -1,10 +1,11 @@
// Copyright 2020-2021 Signal Messenger, LLC // Copyright 2020-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import type { LoggerType } from '../types/Logging'; import type { LoggerType } from '../types/Logging';
import { maybeParseUrl } from './url'; import { maybeParseUrl } from './url';
import { isValidE164 } from './isValidE164'; import { isValidE164 } from './isValidE164';
const SIGNAL_HOSTS = new Set(['signal.group', 'signal.art', 'signal.me']);
const SIGNAL_DOT_ME_HASH_PREFIX = 'p/'; const SIGNAL_DOT_ME_HASH_PREFIX = 'p/';
function parseUrl(value: string | URL, logger: LoggerType): undefined | URL { function parseUrl(value: string | URL, logger: LoggerType): undefined | URL {
@ -44,9 +45,7 @@ export function isSignalHttpsLink(
!url.password && !url.password &&
!url.port && !url.port &&
url.protocol === 'https:' && url.protocol === 'https:' &&
(url.host === 'signal.group' || SIGNAL_HOSTS.has(url.host)
url.host === 'signal.art' ||
url.host === 'signal.me')
); );
} }
@ -143,3 +142,25 @@ export function parseE164FromSignalDotMeHash(hash: string): undefined | string {
const maybeE164 = hash.slice(SIGNAL_DOT_ME_HASH_PREFIX.length); const maybeE164 = hash.slice(SIGNAL_DOT_ME_HASH_PREFIX.length);
return isValidE164(maybeE164, true) ? maybeE164 : undefined; return isValidE164(maybeE164, true) ? maybeE164 : undefined;
} }
/**
* Converts `http://signal.group/#abc` to `https://signal.group/#abc`. Does the same for
* other Signal hosts, like signal.me. Does nothing to other URLs. Expects a valid href.
*/
export function rewriteSignalHrefsIfNecessary(href: string): string {
const resultUrl = new URL(href);
const isHttp = resultUrl.protocol === 'http:';
const isHttpOrHttps = isHttp || resultUrl.protocol === 'https:';
if (SIGNAL_HOSTS.has(resultUrl.host) && isHttpOrHttps) {
if (isHttp) {
resultUrl.protocol = 'https:';
}
resultUrl.username = '';
resultUrl.password = '';
return resultUrl.href;
}
return href;
}