Files
gods-eye-view/src/tooling/allowedHosts.test.mjs
T
Sameh KhamisandClaude Opus 5.5 e6f1dd70a1 fix(server): apply the Host check before the app's own routes
Vite installs its Host check after the middleware plugins add in
configureServer, so the provider and /api routes answered any Host,
including a DNS-rebinding name. hostCheckPlugin runs first on the dev and
preview servers and applies Vite's rule with the same allowed hosts: IP
addresses, localhost and *.localhost, the configured server host, and the
listed names. A test drives a real Vite server with an unlisted Host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 20:15:54 -07:00

154 lines
4.5 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { request as httpRequest } from 'node:http';
import { fileURLToPath } from 'node:url';
import {
DEFAULT_ALLOWED_HOSTS,
hostCheckPlugin,
isAllowedHost,
resolveAllowedHosts,
} from '../../build/allowedHosts.js';
test('uses the restricted local host allowlist by default', () => {
assert.deepEqual(resolveAllowedHosts(), DEFAULT_ALLOWED_HOSTS);
assert.equal(resolveAllowedHosts().includes(true), false);
});
test('adds explicitly configured LAN hostnames without widening the allowlist', () => {
assert.deepEqual(
resolveAllowedHosts('globe.lan, globe.internal ,globe.lan'),
['localhost', '127.0.0.1', 'globe.lan', 'globe.internal'],
);
});
test('ignores empty configured host entries', () => {
assert.deepEqual(resolveAllowedHosts(' , , globe.lan, '), [
'localhost',
'127.0.0.1',
'globe.lan',
]);
});
test('rejects suffix and wildcard entries so every LAN hostname is explicit', () => {
assert.deepEqual(resolveAllowedHosts('.local,*.example,globe.lan'), [
'localhost',
'127.0.0.1',
'globe.lan',
]);
});
test('a Host is allowed by the same rule Vite applies', () => {
const allowed = ['localhost', '127.0.0.1', 'globe.lan', '.corp.example'];
for (const host of [
'localhost:4173',
'LOCALHOST:4173',
'app.localhost',
'127.0.0.1:4173',
'192.168.1.20:4173',
'[::1]:4173',
'globe.lan:4173',
'corp.example',
'a.corp.example:8080',
'office:4173',
])
assert.equal(isAllowedHost(host, allowed, ['office']), true, host);
for (const host of [
undefined,
'',
'evil.example:4173',
'globe.lan.evil.example',
'localhost.evil.example',
'mybox.local:4173',
'[not-an-ip]:4173',
])
assert.equal(isAllowedHost(host, allowed, ['office']), false, String(host));
assert.equal(isAllowedHost('anything.example', true), true);
});
test('the Host check is installed first on the dev and preview servers', () => {
const plugin = hostCheckPlugin();
assert.equal(plugin.enforce, 'pre');
for (const hook of ['configureServer', 'configurePreviewServer']) {
let middleware;
plugin[hook]({
config: {
server: { allowedHosts: ['localhost'], host: 'localhost' },
preview: { allowedHosts: ['localhost'], host: undefined },
},
middlewares: { use: (fn) => (middleware = fn) },
});
const respond = (host) => {
const res = {
status: null,
writeHead(status) {
this.status = status;
},
end() {},
};
let passed = false;
middleware({ headers: { host } }, res, () => (passed = true));
return passed ? 'next' : res.status;
};
assert.equal(respond('localhost:4173'), 'next', hook);
assert.equal(respond('evil.example:4173'), 403, hook);
}
});
test('an unlisted Host cannot reach a route a plugin mounts', async (t) => {
const { createServer } = await import('vite');
const { createBrowserViteConfig } = await import('../../build/vite.js');
const provider = {
name: 'fixture-provider',
configureServer(server) {
server.middlewares.use('/api/fixture', (req, res) => {
res.end('provider answered');
});
},
};
const base = createBrowserViteConfig({ plugins: [provider] });
const vite = await createServer({
...base,
root: fileURLToPath(new URL('../../', import.meta.url)),
configFile: false,
envFile: false,
publicDir: false,
logLevel: 'silent',
// Only this test's plugins: the Host check and the fixture provider.
plugins: base.plugins.filter((plugin) =>
['host-check', 'fixture-provider'].includes(plugin?.name),
),
optimizeDeps: { noDiscovery: true, include: [] },
server: {
...base.server,
host: '127.0.0.1',
port: 0,
hmr: false,
watch: null,
},
});
await vite.listen();
t.after(() => vite.close());
const { port } = vite.httpServer.address();
const get = (host) =>
new Promise((resolve, reject) => {
const request = httpRequest(
{
host: '127.0.0.1',
port,
path: '/api/fixture',
headers: { Host: host },
},
(response) => {
response.resume();
resolve(response.statusCode);
},
);
request.on('error', reject);
request.end();
});
assert.equal(await get(`localhost:${port}`), 200);
assert.equal(await get(`127.0.0.1:${port}`), 200);
assert.equal(await get(`evil.example:${port}`), 403);
assert.equal(await get(`mybox.local:${port}`), 403);
});