Skip to content

Conversation

@jonastemplestein
Copy link
Contributor

@jonastemplestein jonastemplestein commented Dec 6, 2025

Implements a sandboxed TypeScript execution system using Effect-TS:

  • Core types: ParentContext, ExecutionResult, CompiledModule, SandboxConfig
  • Error types: ValidationError, TranspilationError, ExecutionError, TimeoutError
  • Services: Transpiler, CodeValidator, SandboxExecutor, TypeScriptSandbox
  • Implementations:
    • Acorn-based code validator (static analysis for security)
    • Sucrase transpiler (fast dev transpilation)
    • Bun native transpiler (production, Bun-only)
    • Unsafe executor (eval-based, no isolation)
    • Bun Worker executor (true process isolation)
  • Pre-composed layers: DevFastLayer, DevSafeLayer, BunProductionLayer
  • Comprehensive test suite (20 tests)

User code contract:
export default async (ctx) => {
const result = await ctx.callbacks.fetchData("key")
return { value: ctx.data.multiplier * 2, fetched: result }
}


Note

Introduce a modular TypeScript sandbox (code-mode/) with Bun/Sucrase transpilation, acorn-based security validation, TS compiler type-checking, eval-based execution with timeouts, and comprehensive tests, plus docs and minimal deps.

  • Code Mode (new src/code-mode/):
    • Services & Orchestration: CodeMode composite (compile/run) coordinating TypeCheckerTranspilerValidatorExecutor; default CodeModeLive layer.
    • Implementations:
      • Transpiler: Bun.Transpiler (if available) with Sucrase fallback.
      • Validator: acorn + AST walk; blocks imports, eval/new Function, constructor/proto chains, undeclared globals.
      • TypeChecker: TypeScript compiler API with preamble support; surfaces diagnostics with adjusted line numbers.
      • Executor: eval-based execution with injected ctx and timeout handling.
    • Types & Errors: types.ts (ExecutionResult, CompiledModule, configs) and rich error classes (ValidationError, TypeCheckError, ExecutionError, TimeoutError, SecurityViolation).
    • Tests: Extensive code-mode.test.ts covering valid flows, TS features, security blocks, timeouts, compile-once reuse, and type-checking scenarios.
  • Docs:
    • src/code-mode/README.md usage/architecture.
    • Integration plan in docs/todo/code-mode-integration-plan.md for MiniAgent/OpenTUI.
  • Dependencies:
    • Add acorn, acorn-walk, sucrase (and lockfile updates).

Written by Cursor Bugbot for commit 500a090. This will update automatically on new commits. Configure here.

@jonastemplestein jonastemplestein force-pushed the claude/bun-typescript-sandbox-01MLNNGF4P4Qxvwufdrz2X4o branch from 4b49c65 to c26b292 Compare December 6, 2025 17:36
loader: "ts",
target: "browser", // Use browser target for clean output
trimUnusedImports: true
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Bun transpiler output incompatible with executor wrappers

The BunTranspilerLive with target: "browser" preserves ES module syntax (export default), but the executors wrap transpiled code inside functions where export statements are syntactically invalid. This causes a syntax error at runtime. The Sucrase transpiler correctly converts ES modules to CommonJS (exports.default = ...) using transforms: ["typescript", "imports"], but the Bun transpiler lacks equivalent configuration. BunProductionLayer and BunFastLayer would fail to execute any user code using exports.

Additional Locations (2)

Fix in Cursor Fix in Web

return await exported(ctx);
}
return exported;
\`);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Template literal interpolation breaks with backticks in user code

The user's transpiled JavaScript is interpolated into a template literal using ${javascript}. If the user code contains backticks (template literals like `hello ${name}`), the backtick will terminate the outer template literal prematurely, causing syntax errors or potentially allowing code injection. Valid user code that uses template literals will break when executed. The javascript string needs to be escaped or the injection mechanism should use string concatenation or JSON.stringify instead of template literal interpolation.

Additional Locations (1)

Fix in Cursor Fix in Web

@jonastemplestein jonastemplestein force-pushed the claude/bun-typescript-sandbox-01MLNNGF4P4Qxvwufdrz2X4o branch from a2c922a to c1b104f Compare December 6, 2025 19:40
]
const propName = node.property?.type === "Identifier"
? node.property.name
: (node.property?.type === "Literal" ? node.property.value : null)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Template literal property access bypasses security validation

The MemberExpression handler only checks for Identifier and Literal property types when detecting dangerous properties like constructor or __proto__. Template literals without expressions (e.g., obj[`constructor`]) are parsed as TemplateLiteral AST nodes, not Literal nodes. This causes propName to be null, bypassing the security check entirely. An attacker could write [][`constructor`][`constructor`]("return process.env")() to access the Function constructor and execute arbitrary code, defeating the sandbox security for the unsafe executor.

Fix in Cursor Fix in Web

target: "browser",
trimUnusedImports: true
})
return transpiler.transformSync(typescript)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Bun transpiler keeps ESM syntax breaking executor

The Bun transpiler with target: "browser" preserves ESM syntax (export default), but the executor wraps code inside a function and expects CommonJS-style exports (module.exports). When running in Bun, the transpiled code like export default (ctx) => ... will cause a SyntaxError because export statements are invalid inside a function body. The tests pass only because Vitest runs in Node where Sucrase is used, which converts ESM to CommonJS. Production Bun execution would fail.

Additional Locations (1)

Fix in Cursor Fix in Web

const module = { exports: {} };
const exports = module.exports;
${javascript}
const exported = module.exports.default || module.exports;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Falsy default exports return incorrect value

The expression module.exports.default || module.exports uses the || operator, which returns module.exports when module.exports.default is a falsy value like 0, false, "", or null. This causes the executor to return the entire module object instead of the intended exported value. Using nullish coalescing (??) or an explicit property existence check would correctly handle falsy exports.

Fix in Cursor Fix in Web

claude and others added 8 commits December 7, 2025 22:38
Implements a sandboxed TypeScript execution system using Effect-TS:

- Core types: ParentContext, ExecutionResult, CompiledModule, SandboxConfig
- Error types: ValidationError, TranspilationError, ExecutionError, TimeoutError
- Services: Transpiler, CodeValidator, SandboxExecutor, TypeScriptSandbox
- Implementations:
  - Acorn-based code validator (static analysis for security)
  - Sucrase transpiler (fast dev transpilation)
  - Bun native transpiler (production, Bun-only)
  - Unsafe executor (eval-based, no isolation)
  - Bun Worker executor (true process isolation)
- Pre-composed layers: DevFastLayer, DevSafeLayer, BunProductionLayer
- Comprehensive test suite (20 tests)

User code contract:
  export default async (ctx) => {
    const result = await ctx.callbacks.fetchData("key")
    return { value: ctx.data.multiplier * 2, fetched: result }
  }
- Block .constructor, __proto__, and prototype manipulation properties
- Add Effect.async cleanup functions to prevent resource leaks on interruption
- Add safeResume wrapper to prevent double-calling resume callbacks
- Add comprehensive security bypass tests for constructor chain attacks
- Document static analysis limitations for dynamic computed properties
- Use Layer.mergeAll for idiomatic layer composition
- Add Effect.fn tracing to compile/run methods for observability
- Enhance errors with SandboxErrorTypeId for runtime type guards
- Add cause tracking via Schema.Defect for error chains
- Add computed message getters with location info
- Add TypeScript feature tests: generics, enums, classes, unions
- Test invalid TypeScript produces useful TranspilationError
- executor-unsafe: Replace Effect.async + Promise.race with Effect.gen
  + Effect.try + Effect.tryPromise + Effect.timeoutFail (93 → 50 lines)
- executor-bun-worker: Extract cleanup/cleanupAll helpers to reduce duplication
- errors: Remove _message + override getter pattern, use message directly
  (matches simpler pattern in src/errors.ts)
- Remove TypeId boilerplate and isSandboxError predicate (unused)

Net reduction: ~100 lines while improving readability
Effect.timeoutFail doesn't work correctly when the underlying Promise
never resolves - the interrupt signal isn't observed. Promise.race at
the JavaScript level properly handles this case.
- Rename src/sandbox → src/code-mode
- Remove bun worker executor (unnecessary complexity)
- Remove multiple layer compositions (single CodeModeLive)
- Add comprehensive README.md explaining architecture
- Transpiler falls back to sucrase for Node/vitest compatibility

The validator exists for SECURITY, not type-checking:
- Blocks imports/require (no filesystem access)
- Blocks eval/Function constructor (no sandbox escape)
- Blocks prototype chain attacks (__proto__, .constructor)
- Enforces globals allowlist (no process, Bun access)
Add TypeChecker service using TypeScript compiler API for optional type
checking before transpilation. Key changes:

- New TypeChecker service with configurable compilerOptions and preamble
- Preamble allows injecting ctx type definitions checked but not transpiled
- Line numbers in errors exclude preamble lines for accurate reporting
- TypeCheckError with diagnostics array (message, line, column, code)
- Simplified ctx structure: flat object instead of ctx.callbacks/ctx.data
- Type checking disabled by default for backwards compatibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Plan for integrating TypeScript sandbox with actor architecture:
- New event types (CodeBlockStart, CodeExecutionResult, TypeCheckResult)
- OpenTUI Code component with Tree-Sitter syntax highlighting
- Feed reducer extension for code block rendering
- Execution service wrapping CodeMode

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@jonastemplestein jonastemplestein force-pushed the claude/bun-typescript-sandbox-01MLNNGF4P4Qxvwufdrz2X4o branch from c1afbe0 to 500a090 Compare December 7, 2025 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants