What
packages/javascript/src/task/JavaScriptTask.ts:37 is still a bare
const myInterpreter = new Interpreter(`${inputVariablesString} ${code}`);
myInterpreter.run();
run() executes to completion with no instruction budget, no deadline and no AbortSignal check, so while (true) {} in a javascript_code port wedges the thread it runs on — the main thread in the browser build, a worker in node. Six consecutive review cycles (first recorded 2026-08-03; grade held at B− since).
Why now
Two in-repo remedies now exist, both built for neighbouring problems and neither applied here:
step() is typed in this repo as of 7fea774. packages/javascript/src/task/interpreter.d.ts:103 declares step(): boolean — "Executes one step. Returns false when there are no more instructions." The vendored interpreter always had it; until this commit it was an implicit any under noImplicitAny, which was the standing excuse. The whole fix is a bounded loop:
const deadline = performance.now() + budgetMs;
let steps = 0;
while (myInterpreter.step()) {
if (++steps % 10_000 === 0) {
if (signal?.aborted) throw new TaskAbortedError(...);
if (performance.now() > deadline) throw new TaskInvalidInputError(...);
}
}
~10 lines in a 175-line file, with no dependency on the host runtime.
packages/tasks/src/util/BoundedRegex.server.ts:37-64 already runs untrusted work under an enforced node:vm timeout and converts an overrun into a TaskInvalidInputError rather than a hang, with the measurements in its docstring. That is the server-side shape if a vm is preferred over the step loop.
The centralised-limits precedent for the constant is packages/util/src/limits.ts (DEFAULT_LIMITS / SECURITY_LIMITS), which already carries the file/regex budgets packages/tasks added.
Why it matters
JavaScriptTask is registered by registerCommonTasks() in every entrypoint, so it is resolvable by type name from graph JSON the host did not author — the same threat model that drove 2cebe68e9 to take the three filesystem tasks out of the ambient registry. A JavaScriptTask node is the one remaining ambient task that accepts attacker-authored code, and it has no stopping condition but the process.
Acceptance
JavaScriptTask refuses a program that exceeds a configurable step budget or wall-clock deadline, with a typed error rather than a hang.
- The budget is honoured on both entrypoints (browser and node), which the
step() loop gets for free and a vm does not.
- A co-located test that runs
while(true){} and asserts the task fails within the budget. packages/javascript currently has no tsconfig.test.json and no co-located tests, so this is also the cheapest place to start one.
References
packages/javascript/src/task/JavaScriptTask.ts:22-48
packages/javascript/src/task/interpreter.d.ts:101-103 (added 7fea774)
packages/tasks/src/util/BoundedRegex.server.ts:18-64
Found during the 2026-08-31 review of packages/; standing open since 2026-08-03.
What
packages/javascript/src/task/JavaScriptTask.ts:37is still a barerun()executes to completion with no instruction budget, no deadline and noAbortSignalcheck, sowhile (true) {}in ajavascript_codeport wedges the thread it runs on — the main thread in the browser build, a worker in node. Six consecutive review cycles (first recorded 2026-08-03; grade held at B− since).Why now
Two in-repo remedies now exist, both built for neighbouring problems and neither applied here:
step()is typed in this repo as of7fea774.packages/javascript/src/task/interpreter.d.ts:103declaresstep(): boolean— "Executes one step. Returns false when there are no more instructions." The vendored interpreter always had it; until this commit it was an implicitanyundernoImplicitAny, which was the standing excuse. The whole fix is a bounded loop:~10 lines in a 175-line file, with no dependency on the host runtime.
packages/tasks/src/util/BoundedRegex.server.ts:37-64already runs untrusted work under an enforcednode:vmtimeout and converts an overrun into aTaskInvalidInputErrorrather than a hang, with the measurements in its docstring. That is the server-side shape if avmis preferred over the step loop.The centralised-limits precedent for the constant is
packages/util/src/limits.ts(DEFAULT_LIMITS/SECURITY_LIMITS), which already carries the file/regex budgetspackages/tasksadded.Why it matters
JavaScriptTaskis registered byregisterCommonTasks()in every entrypoint, so it is resolvable by type name from graph JSON the host did not author — the same threat model that drove2cebe68e9to take the three filesystem tasks out of the ambient registry. AJavaScriptTasknode is the one remaining ambient task that accepts attacker-authored code, and it has no stopping condition but the process.Acceptance
JavaScriptTaskrefuses a program that exceeds a configurable step budget or wall-clock deadline, with a typed error rather than a hang.step()loop gets for free and avmdoes not.while(true){}and asserts the task fails within the budget.packages/javascriptcurrently has notsconfig.test.jsonand no co-located tests, so this is also the cheapest place to start one.References
packages/javascript/src/task/JavaScriptTask.ts:22-48packages/javascript/src/task/interpreter.d.ts:101-103(added7fea774)packages/tasks/src/util/BoundedRegex.server.ts:18-64Found during the 2026-08-31 review of
packages/; standing open since 2026-08-03.