Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
reviews:
profile: chill
high_level_summary: true
base_branches:
- main
- release/*
auto_review:
base_branches:
- "main"
- "release/.*"

path_instructions:
- path: "apps/demo/**"
Expand Down
12 changes: 6 additions & 6 deletions libs/broker/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@enclave-vm/broker",
"version": "2.8.0",
"version": "2.10.0",
"description": "Tool broker and session management for the EnclaveJS streaming runtime",
"author": "AgentFront <info@agentfront.dev>",
"homepage": "https://github.com/agentfront/enclave",
Expand Down Expand Up @@ -35,10 +35,10 @@
}
},
"dependencies": {
"@enclave-vm/types": "2.8.0",
"@enclave-vm/stream": "2.8.0",
"@enclave-vm/core": "2.8.0",
"minimatch": "^10.0.1",
"zod": "^4.1.13"
"@enclave-vm/types": "2.10.0",
"@enclave-vm/stream": "2.10.0",
"@enclave-vm/core": "2.10.0",
"minimatch": "^10.1.1",
"zod": "^4.3.6"
}
}
10 changes: 5 additions & 5 deletions libs/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@enclave-vm/core",
"version": "2.8.0",
"version": "2.10.0",
"description": "Sandbox runtime for secure JavaScript code execution",
"author": "AgentFront <info@agentfront.dev>",
"homepage": "https://github.com/agentfront/enclave",
Expand Down Expand Up @@ -38,13 +38,13 @@
}
},
"dependencies": {
"@babel/standalone": "^7.28.6",
"@enclave-vm/types": "2.8.0",
"@enclave-vm/ast": "2.8.0",
"@babel/standalone": "^7.29.0",
"@enclave-vm/types": "2.10.0",
"@enclave-vm/ast": "2.10.0",
"acorn": "8.15.0",
"acorn-walk": "8.3.4",
"astring": "1.9.0",
"zod": "^4.1.13"
"zod": "^4.3.6"
},
"peerDependencies": {
"@huggingface/transformers": "^3.2.2",
Expand Down
22 changes: 21 additions & 1 deletion libs/core/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@
"tags": ["scope:publishable", "type:lib", "versioning:independent"],
"implicitDependencies": ["ast"],
"targets": {
"build-worker-script": {
"executor": "@nx/esbuild:esbuild",
"dependsOn": ["^build"],
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "libs/core/dist/src/adapters/worker-pool",
"main": "libs/core/src/adapters/worker-pool/worker-script.ts",
"tsConfig": "libs/core/tsconfig.lib.json",
"format": ["cjs"],
"declaration": false,
"bundle": true,
"thirdParty": false,
"platform": "node",
"esbuildOptions": {
"outExtension": { ".js": ".js" },
"external": ["@enclave-vm/types", "@enclave-vm/ast", "acorn", "acorn-walk", "astring"]
}
}
},
"test-perf": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
Expand All @@ -16,9 +35,10 @@
},
"build-cjs": {
"executor": "@nx/esbuild:esbuild",
"dependsOn": ["^build"],
"dependsOn": ["^build", "build-worker-script"],
"outputs": ["{options.outputPath}"],
"options": {
"deleteOutputPath": false,
"outputPath": "libs/core/dist",
"main": "libs/core/src/index.ts",
"tsConfig": "libs/core/tsconfig.lib.json",
Expand Down
9 changes: 3 additions & 6 deletions libs/core/src/__tests__/worker-pool-adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,12 +376,7 @@ describe('WorkerPoolAdapter', () => {
});

describe('Custom Globals', () => {
// NOTE: Custom globals test is skipped because the AST guard rejects unknown identifiers
// even in PERMISSIVE mode. The worker pool adapter correctly serializes globals to the
// worker, but the Enclave's AST validation blocks access to unregistered globals.
// To use custom globals, they must be registered with the AST guard's allowed globals list.

it.skip('should inject custom globals', async () => {
it('should inject custom globals', async () => {
const enclave = createWorkerEnclave({
securityLevel: 'PERMISSIVE',
globals: {
Expand All @@ -398,6 +393,8 @@ describe('WorkerPoolAdapter', () => {

expect(result.success).toBe(true);
expect(result.value).toEqual({ value: 42 });

enclave.dispose();
});
});

Expand Down
12 changes: 6 additions & 6 deletions libs/core/src/adapters/vm-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1115,12 +1115,12 @@ export class VmAdapter implements SandboxAdapter {
this.context = baseSandbox;

// Wrap code in async IIFE to handle top-level await
const wrappedCode = `
(async () => {
${code}
return typeof __ag_main === 'function' ? await __ag_main() : undefined;
})();
`;
// Use string concatenation instead of template literals to avoid code injection patterns
const wrappedCode =
'(async () => {\n' +
code +
'\nreturn typeof __ag_main === "function" ? await __ag_main() : undefined;\n' +
'})();';

// Compile script
const script = new vm.Script(wrappedCode, {
Expand Down
8 changes: 7 additions & 1 deletion libs/core/src/adapters/worker-pool/worker-pool-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export class WorkerPoolAdapter implements SandboxAdapter {
private disposed = false;
private initialized = false;

// Bound event handler for proper cleanup
private readonly boundMemoryExceededHandler = this.handleMemoryExceeded.bind(this);

// Metrics
private _totalExecutions = 0;
private _successfulExecutions = 0;
Expand All @@ -71,7 +74,7 @@ export class WorkerPoolAdapter implements SandboxAdapter {
this.rateLimiter = createRateLimiter(this.config.maxMessagesPerSecond);

// Set up memory monitor events
this.memoryMonitor.on('memoryExceeded', this.handleMemoryExceeded.bind(this));
this.memoryMonitor.on('memoryExceeded', this.boundMemoryExceededHandler);
}

/**
Expand Down Expand Up @@ -153,6 +156,9 @@ export class WorkerPoolAdapter implements SandboxAdapter {
}

this.disposed = true;

// Remove event listener before stopping to prevent memory leaks
this.memoryMonitor.off('memoryExceeded', this.boundMemoryExceededHandler);
this.memoryMonitor.stop();
this.executionQueue.clear();

Expand Down
12 changes: 6 additions & 6 deletions libs/core/src/adapters/worker-pool/worker-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,12 +333,12 @@ async function handleExecute(msg: ExecuteMessage): Promise<void> {

// Wrap code in async IIFE to support top-level await
// Must call __ag_main() if defined, as the enclave transforms code to wrap in async function __ag_main()
const wrappedCode = `
(async () => {
${msg.code}
return typeof __ag_main === 'function' ? await __ag_main() : undefined;
})();
`;
// Use string concatenation for clarity; security is enforced by AST validation upstream
const wrappedCode =
'(async () => {\n' +
msg.code +
'\nreturn typeof __ag_main === "function" ? await __ag_main() : undefined;\n' +
'})();';

// Compile and run with timeout
const script = new vm.Script(wrappedCode, {
Expand Down
8 changes: 6 additions & 2 deletions libs/core/src/enclave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ export class Enclave {
private readonly referenceConfig?: ReferenceConfig;
private readonly scoringGate?: ScoringGate;
private readonly doubleVmConfig: DoubleVmConfig;
private readonly customGlobalNames: string[];
private adapter?: SandboxAdapter;

constructor(options: CreateEnclaveOptions = {}) {
Expand Down Expand Up @@ -291,12 +292,13 @@ export class Enclave {

// Create validator with custom globals
// Extract custom global names from the final globals (includes Babel if preset='babel')
const customGlobalNames = Object.keys(initialGlobals);
// Store for use in transformation (so custom globals aren't transformed to __safe_ prefix)
this.customGlobalNames = Object.keys(initialGlobals);

// For each custom global, we need to whitelist both:
// 1. The original name (customValue)
// 2. The transformed name (__safe_customValue)
const customAllowedGlobals = customGlobalNames.flatMap((name) => [name, `__safe_${name}`]);
const customAllowedGlobals = this.customGlobalNames.flatMap((name) => [name, `__safe_${name}`]);

// Create validator based on selected preset
// Note: presetName is defined earlier (before config) for Babel global injection
Expand Down Expand Up @@ -388,6 +390,8 @@ export class Enclave {
wrapInMain: needsWrapping,
transformCallTool: true,
transformLoops: true,
// Pass custom global names so they aren't transformed to __safe_ prefix
additionalIdentifiers: this.customGlobalNames,
});
}

Expand Down
2 changes: 1 addition & 1 deletion libs/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@
"@enclave-vm/client": "2.8.0"
},
"peerDependencies": {
"react": ">=17.0.0"
"react": ">=18.0.0"
}
}
2 changes: 1 addition & 1 deletion libs/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"@enclave-vm/core": "2.8.0"
},
"devDependencies": {
"ws": "^8.0.0"
"ws": "^8.19.0"
},
"engines": {
"node": ">=18.0.0"
Expand Down
2 changes: 1 addition & 1 deletion libs/types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@
}
},
"dependencies": {
"zod": "^4.1.13"
"zod": "^4.3.6"
}
}
43 changes: 21 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,27 @@
},
"private": true,
"devDependencies": {
"@mintlify/cli": "^4.0.0",
"@eslint/js": "^9.8.0",
"@nx/esbuild": "22.3.3",
"@nx/eslint": "22.3.3",
"esbuild": "^0.25.0",
"@nx/eslint-plugin": "22.3.3",
"@nx/jest": "22.3.3",
"@nx/js": "22.3.3",
"@nx/node": "22.3.3",
"@nx/webpack": "22.3.3",
"@nx/workspace": "22.3.3",
"@swc-node/register": "~1.9.1",
"@swc/core": "~1.5.7",
"@swc/helpers": "~0.5.11",
"@swc/jest": "~0.2.38",
"@testing-library/dom": "^10.0.0",
"@eslint/js": "^9.39.2",
"@nx/esbuild": "22.4.4",
"@nx/eslint": "22.4.4",
"esbuild": "^0.27.2",
"@nx/eslint-plugin": "22.4.4",
"@nx/jest": "22.4.4",
"@nx/js": "22.4.4",
"@nx/node": "22.4.4",
"@nx/webpack": "22.4.4",
"@nx/workspace": "22.4.4",
"@swc-node/register": "~1.11.1",
"@swc/core": "~1.15.11",
"@swc/helpers": "~0.5.18",
"@swc/jest": "~0.2.39",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/jest": "^30.0.0",
"@types/node": "^24.0.0",
"@types/react": "^19.2.8",
"@types/react": "^19.2.10",
"@types/react-dom": "^19.2.3",
"@types/supertest": "^6.0.3",
"eslint": "^9.39.2",
Expand All @@ -59,16 +58,16 @@
"jest-util": "^30.2.0",
"jsonc-eslint-parser": "^2.4.2",
"lint-staged": "^16.2.7",
"nx": "22.3.3",
"nx": "22.4.4",
"prettier": "^3.7.4",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"supertest": "^7.2.2",
"ts-jest": "^29.4.6",
"ts-node": "10.9.2",
"tslib": "^2.8.1",
"typescript": "~5.9.3",
"typescript-eslint": "^8.52.0"
"typescript-eslint": "^8.54.0"
},
"workspaces": [
"apps/*",
Expand Down
Loading
Loading