- @xenova/transformers 2.17.2 in a Web Worker; Opus-MT en↔es ONNX q8 packs self-hosted on the relay VPS (/models + CORS); ONNX Runtime 1.14 WASM self-hosted in public/ort/ (37MB) loaded from ../ort/. - Hard network guard: only model GET/HEAD (no query) and /ort/ assets allowed. Message text travels only via postMessage; 9 tests prove fetch is never called with the text and every other request is blocked. - TranslateMessage in DMs + crew chat; Settings auto-translate off by default; memory-only translations; per-model download progress; 12 UI strings in all 24 locales. - Both model directions verified from the hosted files; tsc, 138 tests, build and smoke green.
50 lines
1.7 KiB
TypeScript
50 lines
1.7 KiB
TypeScript
/**
|
|
* Network guard for the translation worker.
|
|
*
|
|
* The worker is allowed to fetch exactly two kinds of URLs:
|
|
* 1. the self-hosted model files (GET/HEAD only), and
|
|
* 2. same-origin ONNX runtime assets (.wasm/.mjs/.js).
|
|
*
|
|
* Everything else throws before the request leaves the worker. Message text is
|
|
* never put into a URL or a request body; it only travels through
|
|
* `postMessage` between the UI and the worker.
|
|
*/
|
|
export interface TranslationGuardOptions {
|
|
modelOrigin: string;
|
|
modelPathPrefix: string;
|
|
appOrigin: string;
|
|
/** Only the self-hosted ONNX runtime (e.g. `/ort/`) may be fetched app-side. */
|
|
appAssetPathPrefix: string;
|
|
}
|
|
|
|
const APP_ASSET = /\.(?:wasm|mjs|js)$/i;
|
|
|
|
export function createGuardedFetch(
|
|
realFetch: typeof fetch,
|
|
options: TranslationGuardOptions,
|
|
): typeof fetch {
|
|
return (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
const request = input instanceof Request ? input : null;
|
|
const raw = typeof input === 'string'
|
|
? input
|
|
: input instanceof URL
|
|
? input.href
|
|
: input.url;
|
|
const url = new URL(raw, request?.url ?? options.appOrigin);
|
|
const method = (init?.method ?? request?.method ?? 'GET').toUpperCase();
|
|
const read = method === 'GET' || method === 'HEAD';
|
|
const isModel = url.origin === options.modelOrigin
|
|
&& url.pathname.startsWith(options.modelPathPrefix)
|
|
&& !url.search
|
|
&& read;
|
|
const isAppAsset = url.origin === options.appOrigin
|
|
&& url.pathname.startsWith(options.appAssetPathPrefix)
|
|
&& APP_ASSET.test(url.pathname)
|
|
&& read;
|
|
|
|
if (!isModel && !isAppAsset) {
|
|
throw new Error('blocked network request');
|
|
}
|
|
return realFetch(input as RequestInfo, init);
|
|
}) as typeof fetch;
|
|
}
|