Tide

Security

Sandbox model, restrictions, and what's locked down

Overview

Tide runs workflow code in a locked-down JavaScript sandbox. The runtime blocks code generation, removes dangerous globals, freezes prototypes, and restricts imports. This prevents workflows from escaping the sandbox, accessing host resources, or modifying the runtime itself.

Code Generation

Both eval() and the Function constructor are disabled:

eval("1 + 1");           // Error: eval() is disabled in Tide for security.
new Function("return 1"); // Error: Function constructor is disabled in Tide for security.
Function("return 1");     // Error: Function constructor is disabled in Tide for security.

All constructor escape routes are blocked:

  • (() => {}).constructor("code") -- Arrow function constructor
  • (async () => {}).constructor("code") -- Async function constructor
  • (function*(){}).constructor("code") -- Generator function constructor
  • (async function*(){}).constructor("code") -- Async generator function constructor

Removed Globals

The following globals are removed (set to undefined and frozen):

GlobalWhy
DenoRuntime internals -- would allow file/network/process access
processNode.js global -- would allow env/argv/exit access
WebAssemblyWASM execution -- could bypass sandbox restrictions
SharedArrayBufferCross-thread primitives -- removed for isolation
AtomicsCross-thread primitives -- removed for isolation
WebSocketNetwork access
XMLHttpRequestNetwork access
WorkerThreading -- would allow parallel execution outside sandbox
navigatorBrowser API -- not applicable
locationBrowser API -- not applicable
windowBrowser API -- not applicable
selfBrowser API -- not applicable
setTimeoutUse sleep() instead -- setTimeout is non-deterministic
importScriptsDynamic script loading

Frozen Prototypes

All core JavaScript prototypes are frozen with Object.freeze():

  • Object.prototype, Array.prototype, String.prototype, Number.prototype, Boolean.prototype
  • RegExp.prototype, Error.prototype (and all error subtypes)
  • Map.prototype, Set.prototype, WeakMap.prototype, WeakSet.prototype
  • Promise.prototype, Symbol.prototype, ArrayBuffer.prototype, DataView.prototype
  • JSON, Math, Reflect
  • All typed array prototypes (Int8Array, Uint8Array, Float64Array, etc.)

This prevents prototype pollution attacks:

Object.prototype.polluted = true;  // TypeError: Cannot add property polluted, object is not extensible

Import Restrictions

Only local file imports are allowed. The module loader checks the URL scheme and rejects anything that isn't file://:

import { helper } from "./utils.ts";     // OK -- local file
import "https://deno.land/std/path/mod.ts"; // Error: Importing from 'https://' is not allowed

Error message: Importing from 'https://' is not allowed. Only local file imports are permitted.

File System Sandbox

All file operations (readFile, writeFile, readFileBytes, writeFileBytes, removeFile, listFiles) operate on the in-memory VFS, never the host filesystem. There is no API to access the real filesystem.

What IS Available

Despite the restrictions, workflows have access to:

  • All standard JavaScript built-ins (String, Array, Object, Map, Set, Promise, etc.)
  • console.log() and console.error() (journaled for deterministic replay)
  • Date.now() and new Date() (frozen timestamp)
  • File operations on the virtual filesystem
  • sleep() for timed delays
  • step() for transactional operations
  • S schema builder for input validation
  • TypeScript support (transpiled on the fly)
  • JSON and regular expressions
  • Math (except Math.random -- works but produces the same value each time since prototypes are frozen)

Sandbox Initialization Order

  1. Runtime snapshot is loaded (includes console, readFile, writeFile, etc.)
  2. Frozen time is initialized (Date.now overridden, performance.now returns 0)
  3. Sandbox is initialized (eval disabled, globals removed, prototypes frozen)
  4. User module is loaded and evaluated

The sandbox is initialized AFTER frozen time but BEFORE user code runs, ensuring user code never has access to an unsandboxed environment.

Security Test Example

Tide includes a security test (examples/security-test/main.ts) that verifies all 10 protections:

  1. eval() is blocked
  2. new Function() is blocked
  3. Function() (without new) is blocked
  4. Arrow function constructor escape is blocked
  5. Async function constructor escape is blocked
  6. Deno global is undefined
  7. WebAssembly is undefined
  8. Prototype pollution is blocked
  9. Normal operations (readFile/writeFile) still work
  10. Steps still work

Run it: tide run examples/security-test/main.ts

On this page