mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 16:16:49 +00:00
The shell now takes a message's signed attributes where it took a digest, so a page can't get a login or TLS digest signed. A quiet option lets a plugin answer from what's allowed, for anything the user didn't ask for. An admin's install no longer covers the certificates on someone's own computer: a managed plugin gets `keystore` when that person says yes, also when an update adds it, and it's forgotten once everyone has signed out. Changed test assertions: the fixture plugin needs a grant now, as managed alone is no longer enough, and the bridge gets an options argument.
70 lines
2.9 KiB
TypeScript
70 lines
2.9 KiB
TypeScript
// How permissions are shown to people: the consent dialog and the plugin list
|
|
// in Settings. The text itself lives in the locale files under
|
|
// `plugins.permissions.<key>.{title,body}`.
|
|
|
|
import { ALL_PERMISSIONS, IMPLICIT_PERMISSIONS } from '../plugin-types';
|
|
|
|
/**
|
|
* Permissions that let a plugin read or send mail content, change what is
|
|
* sent or shown, run with full access, or move data off the device. The
|
|
* consent dialog lists them first and marks them.
|
|
*/
|
|
export const HIGH_RISK_PERMISSIONS: ReadonlySet<string> = new Set([
|
|
'crypto:full',
|
|
// Signs and decrypts in the user's name with their certificates.
|
|
'keystore',
|
|
// email.get reads any message in the account, not only the open one.
|
|
'email:read',
|
|
'email:send',
|
|
'email:raw-send',
|
|
// Sends mail in the user's name; the host asks once per address.
|
|
'email:forward',
|
|
'email:blob-read',
|
|
'email:blob-write',
|
|
'email:render-takeover',
|
|
'http:fetch',
|
|
'http:post',
|
|
'filters:write',
|
|
'http:proxy',
|
|
// Reads everything the user writes, as they write it, and may send it on.
|
|
'compose:check',
|
|
// A plugin can point the reply-to of a sender address elsewhere, so
|
|
// answers to the user's mail go to someone else.
|
|
'identity:write',
|
|
// The out-of-office reply goes out to everyone who writes, in the user's
|
|
// name, with whatever text the plugin set.
|
|
'vacation:write',
|
|
// Answering an invitation sends a reply to the organiser in the user's name.
|
|
'calendar:respond',
|
|
// Changes how the app behaves for the user (lib/settings-schema.ts).
|
|
'settings:change',
|
|
]);
|
|
|
|
/**
|
|
* What a manifest asks for, as an install grants it (checkPluginManifest): optional
|
|
* permissions count, minus what this version doesn't offer and what the tier can't have.
|
|
* Everything that shows permissions from a manifest goes through this.
|
|
*/
|
|
export function declaredPermissions(manifest: unknown): string[] {
|
|
const m = (manifest ?? {}) as { permissions?: unknown; optionalPermissions?: unknown; tier?: unknown };
|
|
const strings = (value: unknown) => (Array.isArray(value) ? value.filter((p): p is string => typeof p === 'string') : []);
|
|
const known = new Set<string>(ALL_PERMISSIONS);
|
|
return [...new Set([...strings(m.permissions), ...strings(m.optionalPermissions)])]
|
|
.filter((p) => known.has(p))
|
|
.filter((p) => p !== 'keystore' || m.tier === 'privileged');
|
|
}
|
|
|
|
/** Locale key segment for a permission: `email:read` → `email_read`. */
|
|
export function permissionKey(perm: string): string {
|
|
return perm.replace(/[:-]/g, '_');
|
|
}
|
|
|
|
/** Permissions worth showing: known, not implicit, high-risk first. */
|
|
export function visiblePermissions(perms: readonly string[]): string[] {
|
|
const known = new Set<string>(ALL_PERMISSIONS);
|
|
const implicit = new Set<string>(IMPLICIT_PERMISSIONS);
|
|
return [...new Set(perms)]
|
|
.filter((p) => known.has(p) && !implicit.has(p))
|
|
.sort((a, b) => Number(HIGH_RISK_PERMISSIONS.has(b)) - Number(HIGH_RISK_PERMISSIONS.has(a)));
|
|
}
|