diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 1af4d5c..b107518 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -76,6 +76,16 @@ jobs: with: node-version-file: ".nvmrc" registry-url: "https://registry.npmjs.org/" + # Do NOT run an immutable install here: this is a long-lived, + # auto-incrementing release branch. Its committed yarn.lock may lag the + # bumped package.json versions, and this job rewrites the lock anyway. + # An immutable install would fail before the version-bump/sync steps run. + install: "false" + + - name: Install dependencies + # Mutable install: installs deps for nx/build AND self-heals any lockfile + # drift left by a previous release before this run bumps the version. + run: yarn install --no-immutable - name: Update npm CLI for trusted publishing run: npm install -g npm@latest diff --git a/SECURITY.md b/SECURITY.md index ca1fc74..625fac1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -56,18 +56,20 @@ Key points: 3. Response timeline commitments - sets expectations 4. Safe harbor - encourages researchers to report without fear of legal action - ## Scope ### In scope (authorized testing targets) + - https://enclave.agentfront.dev (public demo / security testing sandbox) ### Out of scope + - Any other Frontegg/AgentFront environments, domains, APIs, or customer tenants not explicitly listed above - Attempts to access other users’ data, accounts, or tenants - Denial of Service (DoS), stress testing, or automated scanning that degrades availability ### Rules of engagement + - Use only test accounts/data you own or that we provide - Avoid privacy violations and data destruction - No persistence (no backdoors, no long-lived shells, no planting credentials) diff --git a/libs/core/package.json b/libs/core/package.json index bca3fe4..fd39ddd 100644 --- a/libs/core/package.json +++ b/libs/core/package.json @@ -48,6 +48,7 @@ "@babel/standalone": "^7.29.0", "@enclave-vm/ast": "2.14.0", "@enclave-vm/types": "2.14.0", + "@types/estree": "1.0.8", "acorn": "8.15.0", "acorn-walk": "8.3.4", "astring": "1.9.0", diff --git a/libs/core/src/__tests__/interpreter-transform-integration.spec.ts b/libs/core/src/__tests__/interpreter-transform-integration.spec.ts index 694dbbc..12ece16 100644 --- a/libs/core/src/__tests__/interpreter-transform-integration.spec.ts +++ b/libs/core/src/__tests__/interpreter-transform-integration.spec.ts @@ -21,7 +21,10 @@ import type { ExecutionContext, ToolHandler } from '../types'; /** The transform config required for the interpreter target. */ const INTERPRETER_TRANSFORM = { transformLoops: false } as const; -function makeContext(toolHandler?: ToolHandler, overrides: { maxToolCalls?: number; timeout?: number } = {}): ExecutionContext { +function makeContext( + toolHandler?: ToolHandler, + overrides: { maxToolCalls?: number; timeout?: number } = {}, +): ExecutionContext { return { config: { maxToolCalls: overrides.maxToolCalls ?? 20, timeout: overrides.timeout ?? 8000 }, stats: { duration: 0, toolCallCount: 0, iterationCount: 0, startTime: 0 }, @@ -74,7 +77,10 @@ describe('AgentScript transform + Interpreter (worker codecall path)', () => { }); it('blocks prototype-escape regardless of transform (secure by construction)', async () => { - const transformed = transformAgentScript("return ({}).constructor.constructor('return 1')();", INTERPRETER_TRANSFORM); + const transformed = transformAgentScript( + "return ({}).constructor.constructor('return 1')();", + INTERPRETER_TRANSFORM, + ); const res = await new InterpreterAdapter().execute(transformed, makeContext(handler)); expect(res.success).toBe(false); expect(res.error?.message).toMatch(/constructor/i); diff --git a/libs/core/src/adapters/__tests__/interpreter-adapter.spec.ts b/libs/core/src/adapters/__tests__/interpreter-adapter.spec.ts index 6ff5644..41f709f 100644 --- a/libs/core/src/adapters/__tests__/interpreter-adapter.spec.ts +++ b/libs/core/src/adapters/__tests__/interpreter-adapter.spec.ts @@ -2,7 +2,9 @@ import { InterpreterAdapter } from '../interpreter-adapter'; import type { ExecutionContext, ToolHandler } from '../../types'; /** Minimal ExecutionContext for the fields the adapter reads. */ -function makeContext(overrides: { maxToolCalls?: number; timeout?: number; toolHandler?: ToolHandler } = {}): ExecutionContext { +function makeContext( + overrides: { maxToolCalls?: number; timeout?: number; toolHandler?: ToolHandler } = {}, +): ExecutionContext { return { config: { maxToolCalls: overrides.maxToolCalls ?? 50, diff --git a/libs/core/src/adapters/interpreter-adapter.ts b/libs/core/src/adapters/interpreter-adapter.ts index 308cbb1..ab30d4d 100644 --- a/libs/core/src/adapters/interpreter-adapter.ts +++ b/libs/core/src/adapters/interpreter-adapter.ts @@ -67,8 +67,7 @@ export class InterpreterAdapter implements SandboxAdapter { // Wall-clock timeout drives the AbortSignal the interpreter checks per step. const timeout = context.config.timeout; - const timer = - timeout && timeout > 0 ? setTimeout(() => context.abortController.abort(), timeout) : undefined; + const timer = timeout && timeout > 0 ? setTimeout(() => context.abortController.abort(), timeout) : undefined; const interpreter = new Interpreter({ globals, diff --git a/libs/core/src/interpreter/interpreter.ts b/libs/core/src/interpreter/interpreter.ts index f2b840e..0186c47 100644 --- a/libs/core/src/interpreter/interpreter.ts +++ b/libs/core/src/interpreter/interpreter.ts @@ -283,7 +283,10 @@ export class Interpreter { } // ── Expressions ──────────────────────────────────────────────────────────-- - private async evalExpr(node: ESTree.Expression | ESTree.Pattern | ESTree.PrivateIdentifier, scope: Scope): Promise { + private async evalExpr( + node: ESTree.Expression | ESTree.Pattern | ESTree.PrivateIdentifier, + scope: Scope, + ): Promise { this.tick(); switch (node.type) { case 'Literal': @@ -467,7 +470,8 @@ export class Interpreter { // Cap string-amplifying ops whose allocation the step budget can't see. if (typeof m.object === 'string' && (m.key === 'repeat' || m.key === 'padStart' || m.key === 'padEnd')) { const n = Number(args[0]); - const produced = m.key === 'repeat' ? m.object.length * (n > 0 ? n : 0) : Math.max(m.object.length, n > 0 ? n : 0); + const produced = + m.key === 'repeat' ? m.object.length * (n > 0 ? n : 0) : Math.max(m.object.length, n > 0 ? n : 0); if (produced > MAX_STRING_OP_LENGTH) { throw new InterpreterError( `String '${m.key}' would produce ${produced} chars, exceeding the ${MAX_STRING_OP_LENGTH} limit`, diff --git a/libs/core/src/worker.ts b/libs/core/src/worker.ts index 80db2af..ad65052 100644 --- a/libs/core/src/worker.ts +++ b/libs/core/src/worker.ts @@ -18,10 +18,4 @@ export { InterpreterAdapter } from './adapters/interpreter-adapter'; export type { InterpreterAdapterOptions } from './adapters/interpreter-adapter'; export { Interpreter, InterpreterError, StepLimitError } from './interpreter/interpreter'; export type { InterpreterOptions } from './interpreter/interpreter'; -export type { - ExecutionContext, - ExecutionResult, - ExecutionError, - ExecutionStats, - ToolHandler, -} from './types'; +export type { ExecutionContext, ExecutionResult, ExecutionError, ExecutionStats, ToolHandler } from './types'; diff --git a/yarn.lock b/yarn.lock index 67261ca..ba2a634 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1707,18 +1707,6 @@ __metadata: languageName: node linkType: hard -"@enclave-vm/ast@npm:2.13.0": - version: 2.13.0 - resolution: "@enclave-vm/ast@npm:2.13.0" - dependencies: - "@types/estree": "npm:1.0.8" - acorn: "npm:8.15.0" - acorn-walk: "npm:8.3.4" - astring: "npm:1.9.0" - checksum: 10c0/bcdc0d2cb85ad857c1087f4f1f4356150e4c9dc7ba553e132f1ac8bafb0f74522f8f55bd5aa43cbb265dd03905c19238c5b698c2abe9f70f4b4a8c9036dc297f - languageName: node - linkType: hard - "@enclave-vm/ast@npm:2.14.0, @enclave-vm/ast@workspace:libs/ast": version: 0.0.0-use.local resolution: "@enclave-vm/ast@workspace:libs/ast" @@ -1731,20 +1719,7 @@ __metadata: languageName: unknown linkType: soft -"@enclave-vm/broker@npm:2.13.0": - version: 2.13.0 - resolution: "@enclave-vm/broker@npm:2.13.0" - dependencies: - "@enclave-vm/core": "npm:2.13.0" - "@enclave-vm/stream": "npm:2.13.0" - "@enclave-vm/types": "npm:2.13.0" - minimatch: "npm:^10.2.5" - zod: "npm:^4.3.6" - checksum: 10c0/1d27358a996ff7ea58df4b5f7eb5dc818eeebbeb583b1e2348fc2a531dc88550be993cde72e6a9072557bec243b59ecadf866fdbba1203d0bf44dfb52dd0940e - languageName: node - linkType: hard - -"@enclave-vm/broker@workspace:libs/broker": +"@enclave-vm/broker@npm:2.14.0, @enclave-vm/broker@workspace:libs/broker": version: 0.0.0-use.local resolution: "@enclave-vm/broker@workspace:libs/broker" dependencies: @@ -1767,16 +1742,6 @@ __metadata: languageName: unknown linkType: soft -"@enclave-vm/client@npm:2.13.0": - version: 2.13.0 - resolution: "@enclave-vm/client@npm:2.13.0" - dependencies: - "@enclave-vm/stream": "npm:2.13.0" - "@enclave-vm/types": "npm:2.13.0" - checksum: 10c0/82345a9963f5065bb0f48857284ec1a211ace1c9aeaa908cd42bafd1a65ce659f94b1b723693549cc935599dc26e2fb53533dd7607b89c1ba0a3cf0db5e5e4f0 - languageName: node - linkType: hard - "@enclave-vm/client@npm:2.14.0, @enclave-vm/client@workspace:libs/client": version: 0.0.0-use.local resolution: "@enclave-vm/client@workspace:libs/client" @@ -1786,29 +1751,6 @@ __metadata: languageName: unknown linkType: soft -"@enclave-vm/core@npm:2.13.0": - version: 2.13.0 - resolution: "@enclave-vm/core@npm:2.13.0" - dependencies: - "@babel/standalone": "npm:^7.29.0" - "@enclave-vm/ast": "npm:2.13.0" - "@enclave-vm/types": "npm:2.13.0" - acorn: "npm:8.15.0" - acorn-walk: "npm:8.3.4" - astring: "npm:1.9.0" - zod: "npm:^4.3.6" - peerDependencies: - "@huggingface/transformers": ^3.2.2 - vectoriadb: ^2.1.3 - peerDependenciesMeta: - "@huggingface/transformers": - optional: true - vectoriadb: - optional: true - checksum: 10c0/22b6c19c089e69a31baed9684c2e71d9393895866c0c7e9723c0489f6dcf77135d33c9ae9fdb408bea28b9a4b18c6d8d97ea62c4e9a88db57967e68848904986 - languageName: node - linkType: hard - "@enclave-vm/core@npm:2.14.0, @enclave-vm/core@workspace:libs/core": version: 0.0.0-use.local resolution: "@enclave-vm/core@workspace:libs/core" @@ -1816,6 +1758,7 @@ __metadata: "@babel/standalone": "npm:^7.29.0" "@enclave-vm/ast": "npm:2.14.0" "@enclave-vm/types": "npm:2.14.0" + "@types/estree": "npm:1.0.8" acorn: "npm:8.15.0" acorn-walk: "npm:8.3.4" astring: "npm:1.9.0" @@ -1905,15 +1848,6 @@ __metadata: languageName: unknown linkType: soft -"@enclave-vm/stream@npm:2.13.0": - version: 2.13.0 - resolution: "@enclave-vm/stream@npm:2.13.0" - dependencies: - "@enclave-vm/types": "npm:2.13.0" - checksum: 10c0/51e69c539c74bd082aeb58ecfda5eaeefe953337f0802042819ef5574bd2ed457e9873c48651f2a2f42bf2f8e1329c537684ce68feaaf51ac0c4e62f128ff5c2 - languageName: node - linkType: hard - "@enclave-vm/stream@npm:2.14.0, @enclave-vm/stream@workspace:libs/stream": version: 0.0.0-use.local resolution: "@enclave-vm/stream@workspace:libs/stream" @@ -1922,15 +1856,6 @@ __metadata: languageName: unknown linkType: soft -"@enclave-vm/types@npm:2.13.0": - version: 2.13.0 - resolution: "@enclave-vm/types@npm:2.13.0" - dependencies: - zod: "npm:^4.3.6" - checksum: 10c0/607822462b192e682712a847f9569501e1e833e33240306c77f83d0cc44b5e3d9bfabe7aa82337c798025d9fc4846943d3808252b096ce1de5fb1fed1560b666 - languageName: node - linkType: hard - "@enclave-vm/types@npm:2.14.0, @enclave-vm/types@workspace:libs/types": version: 0.0.0-use.local resolution: "@enclave-vm/types@workspace:libs/types" @@ -12817,10 +12742,10 @@ __metadata: version: 0.0.0-use.local resolution: "streaming-demo@workspace:apps/streaming-demo" dependencies: - "@enclave-vm/broker": "npm:2.13.0" - "@enclave-vm/client": "npm:2.13.0" - "@enclave-vm/core": "npm:2.13.0" - "@enclave-vm/types": "npm:2.13.0" + "@enclave-vm/broker": "npm:2.14.0" + "@enclave-vm/client": "npm:2.14.0" + "@enclave-vm/core": "npm:2.14.0" + "@enclave-vm/types": "npm:2.14.0" "@types/express": "npm:^4.17.21" "@types/node": "npm:^22.0.0" express: "npm:^4.21.0"