Files
webmail/lib/plugin-sandbox/permission-text.ts
T
Linus Rath e720be7e17 feat: let users install their own plugins, with admin review of the actual code
The admin page offered "User Plugin Uploads" and "Require Admin
Approval", but nothing in Settings used them: there was no upload, no
uninstall and no way to ask for approval, and the admin had no list of
requests. The approval code that existed pinned a hash of the code only
and showed the admin a manifest the browser sent, never the code.

Now:
- Settings > Plugins has an install button (when uploads are allowed),
  a remove button for the user's own plugins, and lists in plain
  language what each plugin may do, high-risk items first
- an install sends the ZIP to the server, which reads the manifest and
  code itself and pins the request to a hash of both
- the admin Plugins tab lists requests with the code and approves or
  denies them; a user may have 10 open requests
- a user plugin is checked again on every start, so a revoked approval
  or turning user plugins off takes effect
- the consent dialog and the plugin settings are translated, use the
  theme colours, and show the sites a plugin may send data to
2026-10-14 02:03:26 +02:00

37 lines
1.3 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',
'email:send',
'email:raw-send',
'email:blob-read',
'email:blob-write',
'email:render-takeover',
'http:fetch',
'http:post',
'filters:write',
]);
/** 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)));
}