mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 08:13:50 +00:00
109 lines
3.6 KiB
TypeScript
109 lines
3.6 KiB
TypeScript
/**
|
|
* The browser-side switches of the "no third-party traffic" mode (#903).
|
|
*
|
|
* Small on purpose: this module ships in every client chunk and in Lite. The
|
|
* registry with the admin-facing descriptions is lib/admin/outgoing-hosts-registry.ts.
|
|
* Every helper that opens an outside host asks noThirdPartyTraffic(), which is
|
|
* true while the mode is on and while the config isn't known yet.
|
|
*/
|
|
|
|
import { noThirdPartyTraffic } from './third-party-mode';
|
|
|
|
/** Exact host names of the built-in outside hosts (the registry repeats them; a test keeps both equal). */
|
|
export const BUILT_IN_HOSTNAMES: readonly string[] = [
|
|
'telemetry.bulwarkmail.org',
|
|
'version.telemetry.bulwarkmail.org',
|
|
'notifications.relay.bulwarkmail.org',
|
|
'extensions.bulwarkmail.org',
|
|
'connector.bulwarkmail.org',
|
|
'icons.duckduckgo.com',
|
|
'api.mymemory.translated.net',
|
|
'www.google.com',
|
|
'randomuser.me',
|
|
'img.freepik.com',
|
|
];
|
|
|
|
function hostnameOf(url: string): string | null {
|
|
try {
|
|
return new URL(url).hostname.toLowerCase();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** Whether a URL points at one of the built-in outside hosts. */
|
|
export function isBuiltInHost(url: string): boolean {
|
|
const host = hostnameOf(url);
|
|
return !!host && BUILT_IN_HOSTNAMES.includes(host);
|
|
}
|
|
|
|
/** A push relay may be used: always, except the built-in one in the mode. */
|
|
export function relayAllowed(url: string): boolean {
|
|
return !(noThirdPartyTraffic() && isBuiltInHost(url));
|
|
}
|
|
|
|
/** Sender icons through /api/favicon. */
|
|
export function senderFaviconsAllowed(): boolean {
|
|
return !noThirdPartyTraffic();
|
|
}
|
|
|
|
/** The Google Maps link on an event's address. */
|
|
export function mapsLinksAllowed(): boolean {
|
|
return !noThirdPartyTraffic();
|
|
}
|
|
|
|
/** OpenPGP WKD lookups at the recipient's domain. */
|
|
export function wkdAllowed(): boolean {
|
|
return !noThirdPartyTraffic();
|
|
}
|
|
|
|
/** Sample photos of the mock server. */
|
|
export function sampleAvatarsAllowed(): boolean {
|
|
return !noThirdPartyTraffic();
|
|
}
|
|
|
|
/**
|
|
* An avatar URL may be shown: a plugin's (Gravatar, Libravatar...) or a
|
|
* contact card's photo link. In the mode only when it stays on this origin or
|
|
* is inline data, so those avatars fall back to initials instead of reaching
|
|
* out (or tripping the CSP).
|
|
*/
|
|
export function avatarUrlAllowed(url: string | null | undefined, origin?: string): boolean {
|
|
if (!url) return false;
|
|
if (!noThirdPartyTraffic()) return true;
|
|
if (/^(data|blob):/i.test(url)) return true;
|
|
const base = origin ?? (typeof window !== 'undefined' ? window.location.origin : '');
|
|
try {
|
|
return !!base && new URL(url, base).origin === new URL(base).origin;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export interface NetworkDeclaringPlugin {
|
|
permissions?: readonly string[];
|
|
httpOrigins?: readonly string[];
|
|
frameOrigins?: readonly string[];
|
|
}
|
|
|
|
/**
|
|
* A plugin declares network access when it may fetch outside hosts
|
|
* (`http:fetch` with httpOrigins), use the credential proxy (`http:proxy`) or
|
|
* embed outside frames (frameOrigins). Same-origin `http:post` doesn't count.
|
|
*/
|
|
export function pluginDeclaresNetwork(plugin: NetworkDeclaringPlugin): boolean {
|
|
const permissions = plugin.permissions ?? [];
|
|
return permissions.includes('http:fetch')
|
|
|| permissions.includes('http:proxy')
|
|
|| (plugin.httpOrigins?.length ?? 0) > 0
|
|
|| (plugin.frameOrigins?.length ?? 0) > 0;
|
|
}
|
|
|
|
/** Error code a refused plugin carries (shown translated in Settings > Plugins). */
|
|
export const PLUGIN_REFUSED_ERROR = 'third-party-off';
|
|
|
|
/** Whether a plugin may run in this browser. */
|
|
export function pluginAllowed(plugin: NetworkDeclaringPlugin): boolean {
|
|
return !(noThirdPartyTraffic() && pluginDeclaresNetwork(plugin));
|
|
}
|