Advertisement

← vibesdk

worker/services/sandbox/request-handler.test.ts

4.9 KB Download file
import { afterEach, describe, expect, it, vi } from 'vitest';

const validatePortToken = vi.fn();
const containerFetch = vi.fn();
const sandboxFetch = vi.fn();

vi.mock('@cloudflare/sandbox', () => ({
	getSandbox: () => ({
		validatePortToken,
		containerFetch,
		fetch: sandboxFetch,
	}),
	Sandbox: class {},
}));

// Imported after the mock so the module under test picks up the mocked getSandbox.
const { proxyToSandbox } = await import('./request-handler');

const env = { Sandbox: {} } as unknown as Parameters<typeof proxyToSandbox>[1];

function req(hostname: string, init?: RequestInit): Request {
	return new Request(`https://${hostname}/`, init);
}

// port-sandboxId-token.domain — token is exactly 16 chars.
const TOKEN = 'abcdef0123456789';

afterEach(() => {
	vi.clearAllMocks();
});

describe('proxyToSandbox', () => {
	it('returns null for a hostname that is not a sandbox route', async () => {
		const res = await proxyToSandbox(req('example.com'), env);
		expect(res).toBeNull();
		expect(validatePortToken).not.toHaveBeenCalled();
	});

	it('proxies through when the port token is valid', async () => {
		validatePortToken.mockResolvedValue(true);
		containerFetch.mockResolvedValue(new Response('ok'));

		const res = await proxyToSandbox(req(`8001-mysandbox-${TOKEN}.preview.example.dev`), env);

		expect(validatePortToken).toHaveBeenCalledWith(8001, TOKEN);
		expect(containerFetch).toHaveBeenCalledTimes(1);
		expect(res?.status).toBe(200);
	});

	it('returns 404 for a syntactically valid but unissued token (never reaches container)', async () => {
		validatePortToken.mockResolvedValue(false);

		const res = await proxyToSandbox(req(`8001-mysandbox-${TOKEN}.preview.example.dev`), env);

		expect(res?.status).toBe(404);
		expect(containerFetch).not.toHaveBeenCalled();
		expect(sandboxFetch).not.toHaveBeenCalled();
	});

	it('blocks control-plane port 3000 with 404 even with a token, before any token check', async () => {
		const res = await proxyToSandbox(req(`3000-mysandbox-${TOKEN}.preview.example.dev`), env);

		expect(res?.status).toBe(404);
		expect(validatePortToken).not.toHaveBeenCalled();
		expect(containerFetch).not.toHaveBeenCalled();
	});

	it('strips Cookie/Authorization but forwards safe headers to the container', async () => {
		validatePortToken.mockResolvedValue(true);
		containerFetch.mockResolvedValue(new Response('ok'));

		await proxyToSandbox(
			req(`8001-mysandbox-${TOKEN}.preview.example.dev`, {
				headers: {
					Cookie: 'session=secret',
					Authorization: 'Bearer secret',
					'X-Csrf-Token': 'secret',
					'X-Api-Key': 'secret',
					Accept: 'text/html',
					'Content-Type': 'application/json',
				},
			}),
			env,
		);

		const proxied = containerFetch.mock.calls[0][0] as Request;
		expect(proxied.headers.get('Cookie')).toBeNull();
		expect(proxied.headers.get('Authorization')).toBeNull();
		expect(proxied.headers.get('X-Csrf-Token')).toBeNull();
		expect(proxied.headers.get('X-Api-Key')).toBeNull();
		expect(proxied.headers.get('Accept')).toBe('text/html');
		expect(proxied.headers.get('Content-Type')).toBe('application/json');
	});

	it('adds proxy X-* headers on the container request', async () => {
		validatePortToken.mockResolvedValue(true);
		containerFetch.mockResolvedValue(new Response('ok'));

		await proxyToSandbox(req(`8001-mysandbox-${TOKEN}.preview.example.dev`), env);

		const proxied = containerFetch.mock.calls[0][0] as Request;
		expect(proxied.headers.get('X-Sandbox-Name')).toBe('mysandbox');
		expect(proxied.headers.get('X-Forwarded-Host')).toBe(
			`8001-mysandbox-${TOKEN}.preview.example.dev`,
		);
		expect(proxied.headers.get('X-Forwarded-Proto')).toBe('https');
	});

	it('forwards WebSocket handshake headers but strips Cookie on a valid upgrade', async () => {
		validatePortToken.mockResolvedValue(true);
		sandboxFetch.mockResolvedValue(new Response('ok'));

		await proxyToSandbox(
			req(`8001-mysandbox-${TOKEN}.preview.example.dev`, {
				headers: {
					Upgrade: 'websocket',
					Connection: 'Upgrade',
					'Sec-WebSocket-Version': '13',
					Cookie: 'session=secret',
					Authorization: 'Bearer secret',
				},
			}),
			env,
		);

		const forwarded = sandboxFetch.mock.calls[0][0] as Request;
		expect(forwarded.headers.get('Upgrade')).toBe('websocket');
		expect(forwarded.headers.get('Sec-WebSocket-Version')).toBe('13');
		expect(forwarded.headers.get('Cookie')).toBeNull();
		expect(forwarded.headers.get('Authorization')).toBeNull();
	});

	it('rejects a WebSocket upgrade with a bad token (no sandbox.fetch)', async () => {
		validatePortToken.mockResolvedValue(false);

		const res = await proxyToSandbox(
			req(`8001-mysandbox-${TOKEN}.preview.example.dev`, {
				headers: { Upgrade: 'websocket' },
			}),
			env,
		);

		expect(res?.status).toBe(404);
		expect(sandboxFetch).not.toHaveBeenCalled();
	});
});