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
10 changes: 10 additions & 0 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions libs/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@babel/standalone": "^7.29.0",
"@enclave-vm/ast": "2.13.0",
"@enclave-vm/types": "2.13.0",
"@types/estree": "1.0.8",
"acorn": "8.15.0",
"acorn-walk": "8.3.4",
"astring": "1.9.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion libs/core/src/adapters/__tests__/interpreter-adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions libs/core/src/adapters/interpreter-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 6 additions & 2 deletions libs/core/src/interpreter/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,10 @@ export class Interpreter {
}

// ── Expressions ──────────────────────────────────────────────────────────--
private async evalExpr(node: ESTree.Expression | ESTree.Pattern | ESTree.PrivateIdentifier, scope: Scope): Promise<unknown> {
private async evalExpr(
node: ESTree.Expression | ESTree.Pattern | ESTree.PrivateIdentifier,
scope: Scope,
): Promise<unknown> {
this.tick();
switch (node.type) {
case 'Literal':
Expand Down Expand Up @@ -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`,
Expand Down
8 changes: 1 addition & 7 deletions libs/core/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,7 @@ __metadata:
"@babel/standalone": "npm:^7.29.0"
"@enclave-vm/ast": "npm:2.13.0"
"@enclave-vm/types": "npm:2.13.0"
"@types/estree": "npm:1.0.8"
acorn: "npm:8.15.0"
acorn-walk: "npm:8.3.4"
astring: "npm:1.9.0"
Expand Down
Loading