feat: hide the version from the connector probe when the login page hides it #896

This commit is contained in:
Linus Rath
2026-10-17 19:18:21 +02:00
parent 03031fcb13
commit 8583c9efef
2 changed files with 22 additions and 3 deletions
+5 -2
View File
@@ -15,7 +15,8 @@ import { TARGET_NAMES } from '@/lib/connector/registry';
* here, and the answer is narrower than the login page it sits next to. It
* says nothing about accounts, domains or the mail server - only the product,
* its version, the branded app name, and the target names, which are a
* compile-time constant.
* compile-time constant. The version follows loginShowVersion, like the login
* page (#896); the connector only needs the target names to warn.
*
* `appName` can be a customer's own branding, so the whole endpoint is gated
* on `connectorEnabled` for deployments that would rather disclose nothing.
@@ -36,7 +37,9 @@ export async function GET() {
product: 'bulwark-webmail',
appName:
configManager.get<string>('appName', '') || process.env.NEXT_PUBLIC_APP_NAME || 'Bulwark',
version: process.env.NEXT_PUBLIC_APP_VERSION || null,
version: configManager.get<boolean>('loginShowVersion', true)
? process.env.NEXT_PUBLIC_APP_VERSION || null
: null,
connectorPath: '/connector',
targets: TARGET_NAMES,
},
+17 -1
View File
@@ -1,5 +1,5 @@
// @vitest-environment node
import { beforeEach, describe, expect, it, vi } from 'vitest';
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:
@@ -56,6 +56,7 @@ 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}`);
@@ -64,6 +65,10 @@ beforeEach(() => {
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();
@@ -87,6 +92,17 @@ describe('endpoints for visitors who are not signed in', () => {
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;