import { describe, expect, it } from 'vitest';
import { createStaticArtifactSnapshot, startStaticReleaseServer } from '@electron/services/static-release-server';
function fixture() {
const source = [
{ path: 'index.html', bytes: Buffer.from('ok') },
{ path: 'assets/app.js', bytes: Buffer.from('globalThis.ok=true') },
];
return { snapshot: createStaticArtifactSnapshot(source), source };
}
describe('startStaticReleaseServer', () => {
it('serves only exact regular files under an unguessable root', async () => {
const { snapshot, source } = fixture();
source[0].bytes.fill(0);
const server = await startStaticReleaseServer(snapshot);
try {
const entry = new URL(server.entryUrl);
expect(entry.hostname).toBe('127.0.0.1');
expect(entry.pathname).toMatch(/^\/[a-f0-9]{48}\/index\.html$/);
const response = await fetch(server.entryUrl);
expect(await response.text()).toBe('ok');
expect(response.headers.get('cache-control')).toBe('no-store');
expect((await fetch(new URL('missing', server.entryUrl))).status).toBe(404);
expect((await fetch(new URL('.', server.entryUrl))).status).toBe(404);
expect((await fetch(server.entryUrl, { method: 'POST' })).status).toBe(405);
} finally {
await server.close();
}
await expect(fetch(server.entryUrl)).rejects.toThrow();
});
it('rejects traversal and backslashes while serving GET/HEAD from memory', async () => {
const server = await startStaticReleaseServer(fixture().snapshot);
try {
const entry = new URL(server.entryUrl);
const prefix = entry.pathname.slice(0, entry.pathname.lastIndexOf('/') + 1);
for (const path of [`${prefix}%2e%2e%2findex.html`, `${prefix}assets%5capp.js`]) {
const response = await fetch(`${entry.origin}${path}`);
expect(response.status).toBe(404);
}
const scriptUrl = `${entry.origin}${prefix}assets/app.js`;
expect((await fetch(scriptUrl)).headers.get('content-type')).toBe('text/javascript; charset=utf-8');
const head = await fetch(scriptUrl, { method: 'HEAD' });
expect(head.status).toBe(200);
expect(head.headers.get('content-length')).toBe(String(Buffer.byteLength('globalThis.ok=true')));
expect(await head.text()).toBe('');
} finally {
await server.close();
}
});
it('rejects forged, duplicate and unsafe snapshots', async () => {
await expect(startStaticReleaseServer({} as never)).rejects.toThrow('Main-owned');
for (const files of [
[{ path: '../index.html', bytes: Buffer.from('bad') }],
[{ path: 'index.html', bytes: Buffer.from('a') }, { path: 'INDEX.HTML', bytes: Buffer.from('b') }],
]) {
expect(() => createStaticArtifactSnapshot(files)).toThrow();
}
});
it.each([
['source.map', 'application/json; charset=utf-8'],
['sound.ogg', 'audio/ogg'],
['notes.txt', 'text/plain; charset=utf-8'],
])('serves %s with the production media type', async (path, expectedType) => {
const snapshot = createStaticArtifactSnapshot([
{ path: 'index.html', bytes: Buffer.from('ok') },
{ path, bytes: Buffer.from('data') },
]);
const server = await startStaticReleaseServer(snapshot);
try {
const entry = new URL(server.entryUrl);
const prefix = entry.pathname.slice(0, entry.pathname.lastIndexOf('/') + 1);
const response = await fetch(`${entry.origin}${prefix}${path}`);
expect(response.headers.get('content-type')).toBe(expectedType);
} finally {
await server.close();
}
});
});