Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363import assert from "node:assert/strict";import { test } from "node:test";import { build } from "esbuild";import { Miniflare, convertV4MiniflareOptions } from "miniflare";
// CPU/subrequest enforcement belongs to Cloudflare's deployed runtime. Local// tests verify the actual loader configuration and use only finite scripts.test( "native Skill script runner isolates modules and bounds execution output", { timeout: 45_000 }, async (t) => { let worker; let cleanup; const dispose = () => (cleanup ??= worker?.dispose()); t.after(dispose); t.signal.addEventListener( "abort", () => { void dispose(); }, { once: true }, ); const bundle = await build({ entryPoints: ["tests/fixtures/skill-script-runner-worker.ts"], alias: { path: "node:path" }, target: "es2022", bundle: true, write: false, format: "esm", platform: "neutral", conditions: ["workerd", "worker", "browser"], mainFields: ["module", "main"], external: ["cloudflare:*", "node:*"], }); t.signal.throwIfAborted(); let outbound = 0; worker = new Miniflare( convertV4MiniflareOptions({ modules: true, script: bundle.outputFiles[0].text, compatibilityDate: "2026-09-04", compatibilityFlags: ["nodejs_compat"], workerLoaders: { LOADER: {} }, outboundService: () => { outbound++; return new Response("Must not be reachable"); }, }), ); const run = async (source, options = {}) => { t.signal.throwIfAborted(); const response = await worker.dispatchFetch("https://fixture.example", { method: "POST", body: JSON.stringify({ source, ...options }), signal: t.signal, }); const result = await response.json(); assert.ok( Buffer.byteLength(JSON.stringify(result.result)) <= 49152, "complete native result exceeds its reserved budget", ); return result; }; await t.test( "native JS and mjs modules return structured results and bounded console channels", async () => { const response = await run( `export default async function(input, ctx) { console.log('hello', input); console.warn('warning'); console.error('error'); return {input, version:ctx.skill.version, files:ctx.files}; }`, { input: { name: "owner" }, path: "scripts/run.mjs" }, ); assert.equal(response.result.ok, true); assert.deepEqual(response.result.result, { input: { name: "owner" }, version: "1.0.0", files: { "references/guide.txt": "Bundled reference" }, }); assert.equal(response.result.stdout, 'hello {"name":"owner"}\n'); assert.equal(response.result.stderr, "warning\nerror\n"); assert.equal(response.result.exitCode, 0); assert.equal(response.result.truncated, false); assert.deepEqual(response.loaded, [ { globalOutbound: "blocked", env: null, limits: { cpuMs: 1000, subRequests: 32 }, modules: ["executor.js", "skill-entry.js"], }, ]); const replacementTokens = await run(`export default () => '$& $$ $1';`); assert.equal(replacementTokens.result.result, "$& $$ $1"); }, ); await t.test( "unsupported source, syntax and runtime errors remain structured without raw exception text", async () => { for (const path of [ "scripts/run.py", "scripts/run.sh", "scripts/run.ts", ]) { const response = await run("not executable", { path }); assert.equal(response.result.error.code, "unsupported_runtime"); assert.deepEqual(response.loaded, []); } for (const source of [ "export default broken syntax SECRET_PRIVATE", "export default () => {throw new Error('SECRET_PRIVATE')}", "export const named = 1;", "import x from './missing.js'; export default () => x;", ]) { const response = await run(source); assert.equal(response.result.ok, false); assert.equal(response.result.exitCode, 1); assert.equal(response.result.error.code, "script_failed"); assert.ok( !JSON.stringify(response.result).includes("SECRET_PRIVATE"), ); } const input = await run("export default () => 1", { input: "็".repeat(12000), }); assert.equal(input.result.error.code, "invalid_input"); assert.deepEqual(input.loaded, []); const traversal = await run("export default () => 1", { path: "scripts/../secrets.js", }); assert.equal(traversal.result.error.code, "invalid_input"); }, ); await t.test( "user modules cannot access native lexical bridges, raw network, bindings or scratch output", async () => { const globals = await run( `import { env } from 'cloudflare:workers'; export default async (_, ctx) => {let outputDenied=false; try {await ctx.output.writeFile('out','data')} catch {outputDenied=true}; return {host:typeof __host,logs:typeof __logs,envKeys:Object.keys(env),outputDenied};}`, ); assert.deepEqual(globals.result.result, { host: "undefined", logs: "undefined", envKeys: [], outputDenied: true, }); const network = await run( `export default async () => await (await fetch('https://outside.example')).text()`, ); assert.equal(network.result.error.code, "script_failed"); assert.equal(outbound, 0); const socket = await run( `import { connect } from 'cloudflare:sockets'; export default async () => { const socket = connect('outside.invalid:443'); try { await socket.opened; return 'opened'; } finally { await socket.close(); } }`, ); assert.equal(socket.result.error.code, "script_failed"); assert.equal( outbound, 0, "raw fetch and TCP cannot leave the isolated worker", ); const unavailable = await run( `export default async (_,ctx) => ctx.tools.any_ungranted_tool({})`, ); assert.equal(unavailable.result.ok, false); assert.equal(unavailable.calls, 0); }, ); await t.test( "only supplied guarded host tools and approved workspace paths are reachable", async () => { const permitted = await run( `export default async (_,ctx) => {const before=await ctx.workspace.readFile('approved.txt'); await ctx.workspace.writeFile('approved.txt','updated'); return [before,await ctx.tools.echo({value:'echoed'})];}`, { workspace: "read-write", tools: true }, ); assert.equal(permitted.result.ok, true); assert.deepEqual(permitted.result.result, [ "Workspace contents", "echoed", ]); assert.equal(permitted.files["approved.txt"], "updated"); const denied = await run( `export default async (_,ctx) => ctx.workspace.writeFile('approved.txt','denied')`, { workspace: "read" }, ); assert.equal(denied.result.ok, false); assert.equal(denied.calls, 0); const hidden = await run( `export default async (_,ctx) => ctx.workspace.readFile('private.txt')`, { workspace: "read" }, ); assert.equal(hidden.result.ok, false); assert.equal(hidden.calls, 0); const malformed = await run( `export default async (_,ctx) => ctx.tools.echo({value:'a',approvedOnce:true})`, { tools: true }, ); assert.equal(malformed.result.ok, false); assert.equal(malformed.calls, 0); }, ); await t.test( "host arguments are validated before native proxy serialization", async () => { const invalid = [ `ctx.tools.echo({value:'x'.repeat(1000000)})`, `ctx.tools.call('echo', {value:'x'.repeat(32768)})`, `ctx.tools.echo({get value(){ return 'small'; }})`, `ctx.tools.echo({toJSON(){return {value:'small'}}})`, `ctx.tools.echo(Object.assign(Object.create({toJSON(){return {value:'small'}}}), {value:'small'}))`, `ctx.tools.echo({value:undefined})`, `ctx.tools.echo(['small'])`, `ctx.tools.call({toString(){return 'echo'}}, {value:'small'})`, `ctx.tools.constructor({value:'small'})`, `ctx.tools.call('__proto__', {value:'small'})`, `ctx.workspace.readFile({toJSON(){return 'approved.txt'}})`, `ctx.workspace.readFile('x'.repeat(241))`, `ctx.workspace.writeFile('approved.txt','x'.repeat(16385))`, `ctx.workspace.writeFile('approved.txt','\\u0001'.repeat(6000))`, `ctx.workspace.listFiles({toJSON(){return '.'}})`, `ctx.workspace.glob({toJSON(){return '*'}})`, `ctx.workspace.stat({toJSON(){return 'approved.txt'}})`, ]; for (const expression of invalid) { const response = await run( `export default async (_,ctx) => ${expression}`, { tools: true, workspace: "read-write" }, ); assert.equal(response.result.ok, false, expression); assert.equal(response.hostCallbacks, 0, expression); } const normal = await run( `export default async (_,ctx) => [await ctx.tools.call('echo',{value:'first'}),await ctx.tools.echo({value:'second'}),await ctx.workspace.stat('approved.txt')]`, { tools: true, workspace: "read" }, ); assert.equal(normal.result.ok, true); assert.equal(normal.hostCallbacks, 3); assert.deepEqual(normal.result.result, [ "first", "second", { type: "file", size: 18 }, ]); }, ); await t.test( "native host serialization cannot be replaced through sandbox globals or prototypes", async () => { for (const mutation of [ `JSON.stringify=()=> '[{"name":"echo","input":{"value":"bypass"}}]'`, `JSON.parse=()=> ({value:'bypass'})`, `Object.defineProperty(globalThis,'JSON',{get(){return {stringify:()=> '[]'}}})`, `Object.prototype.toJSON=()=> ({value:'bypass'})`, `Array.prototype.toJSON=()=> [{name:'echo',input:{value:'bypass'}}]`, `Object.setPrototypeOf(Array.prototype,{toJSON(){return []}})`, `Object.prototype.hasOwnProperty=()=>true`, `Function.prototype.call=()=> ({value:'bypass'})`, `Object.defineProperty(Uint8Array,Symbol.hasInstance,{value:()=>true})`, `ArrayBuffer.isView=()=>true`, `globalThis.String=()=> 'echo'`, ]) { const response = await run( `export default async (_,ctx) => {${mutation}; return ctx.tools.echo({value:'allowed'})}`, { tools: true }, ); assert.equal(response.result.ok, false, mutation); assert.equal(response.hostCallbacks, 0, mutation); } const regex = await run( `export default async (_,ctx) => {RegExp.prototype.test=()=>true; String.prototype.split=()=>['approved.txt']; return ctx.workspace.readFile('../private.txt')}`, { workspace: "read" }, ); assert.equal(regex.result.ok, false); assert.equal(regex.hostCallbacks, 0); const iterator = await run( `export default async (_,ctx) => {Array.prototype[Symbol.iterator]=function*(){yield {name:'echo',input:{value:'bypass'}}}; return ctx.tools.echo({value:'allowed'})}`, { tools: true }, ); assert.equal(iterator.result.ok, true); assert.equal(iterator.result.result, "allowed"); assert.equal( iterator.hostCallbacks, 1, "only the sanitized argument reaches the host", ); }, ); await t.test( "escaped Unicode logs and oversized values are bounded before returning through RPC", async () => { const response = await run( `export default () => {console.log(('๐\\u0000"\\\\').repeat(10000)); console.error(('็\\n').repeat(10000)); return '๐'.repeat(20000);}`, ); assert.equal(response.result.ok, false); assert.equal(response.result.error.code, "output_limit"); assert.equal(response.result.result, null); assert.equal(response.result.truncated, true); assert.ok(response.result.stdout.isWellFormed()); assert.ok(response.result.stderr.isWellFormed()); assert.ok( Buffer.byteLength(JSON.stringify(response.result.stdout)) <= 8192, ); assert.ok( Buffer.byteLength(JSON.stringify(response.result.stderr)) <= 8192, ); const cyclic = await run( `export default () => {const x={}; x.self=x; return x;}`, ); assert.equal(cyclic.result.error.code, "script_failed"); const getter = await run( `export default () => {let n=0; return {get value(){return ++n}};}`, ); assert.deepEqual( getter.result.result, { value: 1 }, "serialize a user getter only once", ); }, ); await t.test( "output bounds survive user changes to global helpers", async () => { const response = await run( `export default () => { String.prototype.slice=()=> 'x'.repeat(100000); Math.ceil=()=>999999; JSON.stringify=()=> 'x'.repeat(100000); console.log('๐'.repeat(10000)); return {safe:true}; }`, ); assert.equal(response.result.ok, true); assert.deepEqual(response.result.result, { safe: true }); assert.equal(response.result.truncated, true); assert.ok(response.result.stdout.isWellFormed()); assert.ok( Buffer.byteLength(JSON.stringify(response.result.stdout)) <= 8192, ); }, ); await t.test( "cancellation returns promptly and caller revocation prevents late host side effects", async () => { const preabort = await run("export default () => 1", { preabort: true, }); assert.equal(preabort.result.error.code, "cancelled"); assert.deepEqual(preabort.loaded, []); const response = await run( `export default async (_,ctx) => {await new Promise(r=>setTimeout(r,150)); return ctx.tools.echo({value:'late'});}`, { tools: true, cancelAfter: 20, waitAfter: 250 }, ); assert.equal(response.result.error.code, "cancelled"); assert.ok(response.elapsed < 1000); assert.equal(response.calls, 0); assert.equal( response.attempts, 1, "late native script callback actually reached the revoked host guard", ); }, ); await t.test( "host execution deadline bounds a nonsettling native script", async () => { const response = await run( `export default async () => new Promise(() => {})`, ); assert.equal(response.result.error.code, "timeout"); assert.ok(response.elapsed >= 19000 && response.elapsed < 25000); assert.equal(response.calls, 0); }, ); },);