在 Makelore 构建并预检静态发布产物
This commit is contained in:
82
tests/unit/static-release-server.test.ts
Normal file
82
tests/unit/static-release-server.test.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
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('<main>ok</main>') },
|
||||
{ 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('<main>ok</main>');
|
||||
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('<main>ok</main>') },
|
||||
{ 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();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user