mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-01 08:06:13 +00:00
fix: allow cleartext server URLs only on local hosts, name the mail domain as TOTP issuer and guard external links
This commit is contained in:
Binary file not shown.
@@ -12,6 +12,24 @@ import { secureFetch } from './client-cert';
|
|||||||
export const DISCOVERY_TIMEOUT_MS = 2500;
|
export const DISCOVERY_TIMEOUT_MS = 2500;
|
||||||
|
|
||||||
const SCHEME_RE = /^([a-z][a-z0-9+.-]*):\/\//i;
|
const SCHEME_RE = /^([a-z][a-z0-9+.-]*):\/\//i;
|
||||||
|
|
||||||
|
/** Loopback, the Android emulator's host alias, RFC 1918 ranges and `.local`. */
|
||||||
|
export function isLocalHost(url: string): boolean {
|
||||||
|
const m = /^[a-z][a-z0-9+.-]*:\/\/([^/?#:]+)/i.exec(url);
|
||||||
|
const host = (m ? m[1] : '').toLowerCase();
|
||||||
|
if (!host) return false;
|
||||||
|
if (host === 'localhost' || host === '127.0.0.1' || host === '::1' || host === '10.0.2.2') return true;
|
||||||
|
if (host.endsWith('.local') || host.endsWith('.localhost')) return true;
|
||||||
|
const ip = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(host);
|
||||||
|
if (!ip) return false;
|
||||||
|
const [a, b] = [Number(ip[1]), Number(ip[2])];
|
||||||
|
return a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True when a normalised server URL uses plain http. */
|
||||||
|
export function isCleartextUrl(url: string): boolean {
|
||||||
|
return /^http:\/\//i.test(url);
|
||||||
|
}
|
||||||
const EMAIL_RE = /^[^\s@]+@([^\s@]+\.[^\s@]+)$/;
|
const EMAIL_RE = /^[^\s@]+@([^\s@]+\.[^\s@]+)$/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,6 +50,12 @@ export function normalizeServerUrl(input: string): string | null {
|
|||||||
value = `https://${value}`;
|
value = `https://${value}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Basic credentials travel in every request. Cleartext is only acceptable
|
||||||
|
// for a server on the developer's own machine / LAN; on a public host it
|
||||||
|
// would fail opaquely on Android anyway (no cleartext traffic allowed) and
|
||||||
|
// leak the password on iOS.
|
||||||
|
if (/^http:\/\//i.test(value) && !isLocalHost(value)) return null;
|
||||||
|
|
||||||
// Someone pasting the endpoint they found in the webmail's settings gets the
|
// Someone pasting the endpoint they found in the webmail's settings gets the
|
||||||
// same result as someone typing the address they use in the browser.
|
// same result as someone typing the address they use in the browser.
|
||||||
value = value
|
value = value
|
||||||
|
|||||||
+6
-2
@@ -46,9 +46,13 @@ export interface TotpEnrolment {
|
|||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateTotpEnrolment(accountLabel: string): TotpEnrolment {
|
export function generateTotpEnrolment(accountLabel: string, issuerOverride?: string): TotpEnrolment {
|
||||||
const secret = base32Encode(randomBytes(SECRET_BYTES));
|
const secret = base32Encode(randomBytes(SECRET_BYTES));
|
||||||
const issuer = 'Stalwart';
|
// The issuer is what the authenticator app shows above the code; the
|
||||||
|
// account's mail domain identifies the service far better than the
|
||||||
|
// server software's name.
|
||||||
|
const at = accountLabel.lastIndexOf('@');
|
||||||
|
const issuer = issuerOverride || (at > 0 ? accountLabel.slice(at + 1) : 'Bulwark');
|
||||||
const label = accountLabel || 'account';
|
const label = accountLabel || 'account';
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
secret,
|
secret,
|
||||||
|
|||||||
Reference in New Issue
Block a user