refactor(llm-security): parameterize buildSandboxedWorker with workerPath

This commit is contained in:
Kjell Tore Guttormsen 2026-04-18 10:37:10 +02:00
commit 112cb5af45
2 changed files with 70 additions and 4 deletions

View file

@ -18,7 +18,10 @@ import { fileURLToPath } from 'node:url';
import { dirname, resolve as resolvePath } from 'node:path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const WORKER_PATH = resolvePath(__dirname, 'vsix-fetch-worker.mjs');
const DEFAULT_VSIX_WORKER_PATH = resolvePath(__dirname, 'vsix-fetch-worker.mjs');
const DEFAULT_JETBRAINS_WORKER_PATH = resolvePath(__dirname, 'jetbrains-fetch-worker.mjs');
// Backward-compat alias — older internal refs may still import `WORKER_PATH`.
const WORKER_PATH = DEFAULT_VSIX_WORKER_PATH;
const WORKER_TIMEOUT_MS = 35_000; // fetch is 30s, give worker 5s of slack
const MAX_OUTPUT_BYTES = 1024 * 1024; // 1MB JSON cap (output is tiny in practice)
@ -76,10 +79,13 @@ export function buildBwrapArgs(allowedWritePath, innerArgs) {
*
* @param {string} tmpDir writable temp dir for the worker
* @param {string[]} workerArgs argv for the worker (after `node <worker>`)
* @param {string} [workerPath=DEFAULT_VSIX_WORKER_PATH] absolute path to the
* worker module. Defaults to the VSIX worker for backward compat; Step 12
* passes `DEFAULT_JETBRAINS_WORKER_PATH` for JetBrains plugin fetches.
* @returns {{cmd:string, args:string[], sandbox: 'sandbox-exec'|'bwrap'|null}}
*/
export function buildSandboxedWorker(tmpDir, workerArgs) {
const innerArgs = ['node', WORKER_PATH, ...workerArgs];
export function buildSandboxedWorker(tmpDir, workerArgs, workerPath = DEFAULT_VSIX_WORKER_PATH) {
const innerArgs = ['node', workerPath, ...workerArgs];
const profile = buildSandboxProfile(tmpDir);
if (profile) {
@ -105,8 +111,29 @@ export function buildSandboxedWorker(tmpDir, workerArgs) {
* @returns {Promise<{ok:boolean, sandbox:'sandbox-exec'|'bwrap'|null, payload:object}>}
*/
export function runVsixWorker(url, tmpDir, opts = {}) {
return runPluginWorker(
DEFAULT_VSIX_WORKER_PATH,
['--url', url, '--tmpdir', tmpDir],
tmpDir,
opts,
);
}
/**
* Generalized sandboxed-worker runner. Spawns any worker module, captures a
* single JSON line of stdout, enforces the same timeout / output cap as
* `runVsixWorker`. Step 12 uses this for the JetBrains worker it must NOT
* duplicate spawn/parse logic.
*
* @param {string} workerPath absolute path to the worker .mjs file
* @param {string[]} workerArgs argv for the worker
* @param {string} tmpDir writable temp dir for the worker
* @param {{allowFallback?: boolean}} [opts]
* @returns {Promise<{ok:boolean, sandbox:'sandbox-exec'|'bwrap'|null, payload:object}>}
*/
export function runPluginWorker(workerPath, workerArgs, tmpDir, opts = {}) {
const { allowFallback = true } = opts;
const { cmd, args, sandbox } = buildSandboxedWorker(tmpDir, ['--url', url, '--tmpdir', tmpDir]);
const { cmd, args, sandbox } = buildSandboxedWorker(tmpDir, workerArgs, workerPath);
if (!sandbox && !allowFallback) {
return Promise.reject(new Error('no OS sandbox available and fallback disabled'));
@ -162,8 +189,16 @@ export function runVsixWorker(url, tmpDir, opts = {}) {
});
}
export {
DEFAULT_VSIX_WORKER_PATH,
DEFAULT_JETBRAINS_WORKER_PATH,
};
export const __testing = {
WORKER_PATH,
DEFAULT_VSIX_WORKER_PATH,
DEFAULT_JETBRAINS_WORKER_PATH,
WORKER_TIMEOUT_MS,
MAX_OUTPUT_BYTES,
resolveWorkerPath: (name) => resolvePath(__dirname, name),
};