mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 08:13:50 +00:00
128 lines
5.3 KiB
TypeScript
128 lines
5.3 KiB
TypeScript
// @vitest-environment node
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { NextRequest } from 'next/server';
|
|
|
|
// Endpoints answering anyone used to hand out what fingerprints an instance:
|
|
// exact versions and pending advisories, memory figures, the installed
|
|
// plugins and the hosts they reach, push relays, sidebar app URLs and every
|
|
// served domain.
|
|
|
|
const gate = vi.hoisted(() => ({ signedIn: false }));
|
|
vi.mock('@/lib/auth/instance-details', () => ({
|
|
canSeeInstanceDetails: async () => gate.signedIn,
|
|
signedInIdentity: async () => null,
|
|
}));
|
|
|
|
const config = vi.hoisted(() => ({ values: {} as Record<string, unknown> }));
|
|
const POLICY = {
|
|
restrictions: {},
|
|
features: {},
|
|
defaults: {},
|
|
themePolicy: { allowUserThemes: true },
|
|
forceEnabledPlugins: ['bulwark-gpg'],
|
|
approvedPlugins: ['quick-notes'],
|
|
forceEnabledThemes: [],
|
|
pushRelays: [{ label: 'Internal', url: 'https://relay.corp.example' }],
|
|
pushRelayUrl: 'https://relay.corp.example',
|
|
pushRelayUrlLocked: true,
|
|
defaultSidebarApps: [{ id: 'wiki', name: 'Wiki', url: 'https://wiki.corp.example' }],
|
|
};
|
|
vi.mock('@/lib/admin/config-manager', () => ({
|
|
configManager: {
|
|
ensureLoaded: async () => {},
|
|
get: (key: string, fallback: unknown) => (key in config.values ? config.values[key] : fallback),
|
|
getPolicy: () => POLICY,
|
|
},
|
|
}));
|
|
vi.mock('@/lib/logger', () => ({ logger: { error: vi.fn(), warn: vi.fn(), info: vi.fn(), debug: vi.fn() } }));
|
|
vi.mock('@/lib/admin/session', () => ({ requireAdminAuth: vi.fn(), getClientIP: () => '0.0.0.0' }));
|
|
vi.mock('@/lib/admin/audit', () => ({ auditLog: vi.fn() }));
|
|
|
|
const STATUS = {
|
|
schema: 1, current: '1.9.0', latest: '1.9.2', updateAvailable: true, severity: 'security',
|
|
url: 'https://example.org/release', advisory: 'Stored XSS in the reader, fixed in 1.9.2', checkedAt: '2026-09-26T00:00:00Z',
|
|
};
|
|
vi.mock('@/lib/version-check', () => ({
|
|
checkOnce: vi.fn(),
|
|
loadState: async () => ({ status: STATUS, lastCheckedAt: null, lastSuccessAt: null }),
|
|
freshStatus: (state: { status: unknown }) => state.status,
|
|
}));
|
|
vi.mock('@/lib/admin/plugin-registry', () => ({
|
|
getPluginRegistry: async () => ({ plugins: [] }),
|
|
getThemeRegistry: async () => ({ themes: [] }),
|
|
}));
|
|
vi.mock('@/lib/admin/plugin-dev', () => ({ listDevPlugins: async () => [] }));
|
|
|
|
import { GET as health } from '@/app/api/health/route';
|
|
import { GET as updateStatus } from '@/app/api/system/update-status/route';
|
|
import { GET as plugins } from '@/app/api/plugins/route';
|
|
import { GET as policy } from '@/app/api/admin/policy/route';
|
|
import { GET as connectorCapabilities } from '@/app/api/connector/capabilities/route';
|
|
|
|
const req = (path: string) => new NextRequest(`https://mail.example${path}`);
|
|
|
|
beforeEach(() => {
|
|
gate.signedIn = false;
|
|
config.values = {};
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllEnvs();
|
|
});
|
|
|
|
describe('endpoints for visitors who are not signed in', () => {
|
|
it('health answers the liveness probe but no diagnostics', async () => {
|
|
const body = await (await health(req('/api/health?detailed=true'))).json();
|
|
expect(body.status).toBe('healthy');
|
|
expect(body).not.toHaveProperty('version');
|
|
expect(body).not.toHaveProperty('memory');
|
|
expect(body).not.toHaveProperty('nodeVersion');
|
|
|
|
gate.signedIn = true;
|
|
expect(await (await health(req('/api/health?detailed=true'))).json()).toHaveProperty('memory');
|
|
});
|
|
|
|
it('update status follows loginShowVersion and never carries the advisory text', async () => {
|
|
const shown = await (await updateStatus(req('/api/system/update-status'))).json();
|
|
expect(shown.status).toMatchObject({ current: '1.9.0', severity: 'security', advisory: null });
|
|
|
|
config.values.loginShowVersion = false;
|
|
expect((await (await updateStatus(req('/api/system/update-status'))).json()).status).toBeNull();
|
|
|
|
gate.signedIn = true;
|
|
expect((await (await updateStatus(req('/api/system/update-status'))).json()).status).toEqual(STATUS);
|
|
});
|
|
|
|
it('the connector probe follows loginShowVersion and still lists its targets', async () => {
|
|
vi.stubEnv('NEXT_PUBLIC_APP_VERSION', '1.9.0');
|
|
expect((await (await connectorCapabilities()).json()).version).toBe('1.9.0');
|
|
|
|
config.values.loginShowVersion = false;
|
|
const hidden = await (await connectorCapabilities()).json();
|
|
expect(hidden.version).toBeNull();
|
|
expect(hidden.product).toBe('bulwark-webmail');
|
|
expect(hidden.targets.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('the plugin list needs a session', async () => {
|
|
expect((await plugins(req('/api/plugins'))).status).toBe(401);
|
|
gate.signedIn = true;
|
|
expect((await plugins(req('/api/plugins'))).status).toBe(200);
|
|
});
|
|
|
|
it('the policy keeps what the login page needs and drops the deployment map', async () => {
|
|
const res = await policy(req('/api/admin/policy'));
|
|
expect(res.headers.get('x-bulwark-policy-scope')).toBe('public');
|
|
const body = await res.json();
|
|
expect(body.themePolicy).toEqual(POLICY.themePolicy);
|
|
expect(body).toMatchObject({
|
|
forceEnabledPlugins: [], approvedPlugins: [], pushRelays: [], pushRelayUrl: '', defaultSidebarApps: [],
|
|
});
|
|
|
|
gate.signedIn = true;
|
|
const full = await policy(req('/api/admin/policy'));
|
|
expect(full.headers.get('x-bulwark-policy-scope')).toBeNull();
|
|
expect(await full.json()).toEqual({ ...POLICY, scopes: [] });
|
|
});
|
|
});
|