mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 08:13:50 +00:00
Several endpoints answered anyone with what fingerprints a deployment: `/api/health?detailed=true` (versions, memory), the update status (exact version and the text of a pending security advisory, even with `loginShowVersion` off), `/api/plugins` (installed plugins and the hosts they may reach), the admin policy (push relays, sidebar app URLs, plugin lists) and `/api/config` (every domain each server serves). Visitors without a session now get: - the plain liveness answer from `/api/health`; - the update status only where the login page shows the version, and never the advisory text; - a 401 from `/api/plugins`, which the client only syncs after signing in; - the policy without plugin lists, push relays and sidebar apps, marked `X-Bulwark-Policy-Scope: public`; the client fetches the full policy once it signs in; - server domain lists only when servers are picked by domain. A mailbox session, a remembered session cookie or the admin session counts as signed in. Deployments without a session secret cannot tell sessions apart and keep the old answers.
34 lines
1.5 KiB
TypeScript
34 lines
1.5 KiB
TypeScript
import { cookies } from 'next/headers';
|
|
import type { NextRequest } from 'next/server';
|
|
import { MAX_ACCOUNT_SLOTS } from '@/lib/account-utils';
|
|
import { decryptSession } from '@/lib/auth/crypto';
|
|
import { hasSessionSecret } from '@/lib/auth/session-secret';
|
|
import { sessionCookieName } from '@/lib/auth/session-cookie';
|
|
import { verifyAdminSession } from '@/lib/admin/session';
|
|
import { ADMIN_SESSION_COOKIE } from '@/lib/admin/types';
|
|
import { getStalwartCredentials } from '@/lib/stalwart/credentials';
|
|
|
|
/**
|
|
* Whether a request may see details that fingerprint this instance: exact
|
|
* versions, pending security advisories, installed plugins and the hosts
|
|
* they talk to, push relays, sidebar app URLs, memory figures.
|
|
*
|
|
* Yes for anyone signed in here - a mailbox session (the per-slot auth
|
|
* context, or the remembered session cookie before the context is restored
|
|
* on a new browser session) or the admin dashboard. Without a session
|
|
* secret no session can be told apart server-side, so those deployments
|
|
* keep answering everyone as before.
|
|
*/
|
|
export async function canSeeInstanceDetails(request: NextRequest): Promise<boolean> {
|
|
if (!hasSessionSecret()) return true;
|
|
if (await getStalwartCredentials(request)) return true;
|
|
|
|
const jar = await cookies();
|
|
for (let slot = 0; slot < MAX_ACCOUNT_SLOTS; slot++) {
|
|
const session = jar.get(sessionCookieName(slot))?.value;
|
|
if (session && decryptSession(session)) return true;
|
|
}
|
|
const admin = jar.get(ADMIN_SESSION_COOKIE)?.value;
|
|
return !!admin && verifyAdminSession(admin) !== null;
|
|
}
|