Security Model
Security Model
Section titled “Security Model”Philosophy
Section titled “Philosophy”Nox is built on a single axiom:
The code running inside the sandbox is assumed to be potentially malicious or incorrect at all times.
Whether the code was written by a third-party plugin developer, submitted by an end user, or generated by an AI model, we need verification at every layer. Nox does exactly that.
The Three Pillars of Nox Security
Section titled “The Three Pillars of Nox Security”Pillar 1: AST-Level Sandboxing
Section titled “Pillar 1: AST-Level Sandboxing”Nox does not compile .nox scripts to JVM bytecode.
By compiling to a custom bytecode executed by a custom VM, we guarantee that the script can only perform actions explicitly coded into the VM’s instruction handlers.
| Threat | Mitigation |
|---|---|
| JVM Reflection | Impossible. The script has no concept of Kotlin/JVM classes, methods, or the Class object. There is no opcode for reflection. |
| Arbitrary Native Calls | Impossible. The only way to call Kotlin code is through the registered FFI system, which is type-checked and permission-gated. |
| Memory Corruption | Impossible. The VM accesses pre-allocated arrays (pMem, rMem) with bounds-checked indices. There are no raw pointers. |
| Class Loading | Impossible. The VM has no class loader mechanism. It cannot instantiate arbitrary Kotlin/JVM objects. |
Note: All this works on the assumption that the plugins are well-intentioned and free from flaws. If a plugin is malicious, it can try to exploit the VM itself. However, the VM is written in Kotlin and is subject to the same security measures as any other JVM application. However flimsy those may be.
The guarantee: If an operation is not explicitly implemented as a VM opcode or a registered library function, it simply does not exist within the sandbox.
Pillar 2: Capability-Based Access Control
Section titled “Pillar 2: Capability-Based Access Control”No code inside the sandbox has any implicit permissions. Every interaction with the outside world must go through an explicit capability request.
┌──────────────────────────────────────────────────┐│ SANDBOX BOUNDARY ││ ││ .nox script ││ ┌──────────────────────┐ ││ │ File.read("/data") │ ││ └──────────┬───────────┘ ││ │ ││ ▼ ││ ┌──────────────────────┐ ││ │ Permission Bridge │── File.Read("/data") ││ │ (inside VM) │ ││ └──────────┬───────────┘ │ │└──────────────┼─────────────────────┼─────────────┘ │ │ ▼ ▼ ┌───────────────────────────────────────┐ │ SUPERVISOR │ │ ┌─────────────────────────────┐ │ │ │ Policy Engine / User GUI │ │ │ │ │ │ │ │ ✓ Granted / ✗ Denied │ │ │ └─────────────────────────────┘ │ └───────────────────────────────────────┘The Permission Bridge Protocol
Section titled “The Permission Bridge Protocol”When the VM encounters a library call tagged as requiring permission (e.g., File.read, Http.get):
- The VM pauses execution of the current instruction
- Constructs a typed
PermissionRequest. For example,PermissionRequest.File.Read("/data/file.txt") - Calls
RuntimeContext.requestPermission(request)and the Sandbox’s coroutine suspends - The Host pattern-matches on the request type and evaluates its policy. For example:
- Auto-grant policies for pre-approved operations
- User prompts via GUI for sensitive requests
- Blanket deny for prohibited categories
- Returns a typed
PermissionResponseand the response flows back to the Sandbox - The Sandbox inspects the response:
Granted: The operation proceeds (with constraints if provided)Denied: ASecurityExceptionis thrown inside the sandbox, carrying the denialreason
Why This Works
Section titled “Why This Works”- Granularity: Permissions are checked per-operation, not per-program. A program can read one file but be denied another.
- Context-Aware: The Host receives a typed request object with all details (path, URL, etc.) and can make informed decisions.
- Constraint-Capable: The Host can attach typed constraints to a grant (e.g.,
FileGrant(maxBytes = 1_048_576)). - Non-Bypassable: The permission check lives inside the VM instruction handler. There is no way to call
File.readwithout going through the bridge.
Pillar 3: Resource Exhaustion Prevention
Section titled “Pillar 3: Resource Exhaustion Prevention”Even without accessing external resources, malicious code can attack the host through resource exhaustion. Nox employs multiple “Watchdogs” to prevent this.
| Watchdog | Threat Mitigated | Mechanism |
|---|---|---|
| Instruction Counter | Infinite loops (while(true){}) | The VM increments a counter every iteration of the execution loop. Exceeding the limit (e.g., 500,000 ops) triggers QuotaExceededException. |
| Execution Timeout | Hung or stalled scripts | The Host tracks wall-clock time per Sandbox. Exceeding the maximum duration (e.g., 60s) forces termination. |
| Memory Cap | Memory bombs (allocating huge strings/arrays) | The VM monitors the size of objects entering the system (e.g., File.read results, JSON parses). Exceeding the threshold (e.g., 100MB) triggers MemoryLimitException. |
| Recursion Limit | Stack overflow attacks | The VM’s internal call stack is a fixed-size array (~1024 frames). Exceeding it prevents a StackOverflowError on the host JVM. |
| Register File Size | Variable explosion | The pre-allocated pMem/rMem arrays have a fixed size (~65k slots). Scripts cannot allocate beyond this. |
Defense in Depth
Section titled “Defense in Depth”Security in Nox is layered. A failure in one layer is caught by the next:
Layer 1: Static Analysis (Compile Time) │ Type mismatches, undeclared variables, schema violations │ caught before any code runs. │ ▼Layer 2: VM Sandbox (Runtime) │ Only explicitly implemented operations exist. │ No reflection, no raw memory, no class loading. │ ▼Layer 3: Permission Bridge (Runtime) │ Every external operation requires explicit approval. │ Per-operation, context-aware, non-bypassable. │ ▼Layer 4: Resource Watchdogs (Runtime) │ Hard limits on CPU, memory, recursion, and time. │ Enforced by the VM loop itself. │ ▼Layer 5: Sandbox Isolation (Architecture) Sandboxes are single-use coroutines. No state persists between invocations. Failure in one Sandbox cannot affect others.Security Annotations in Program Files
Section titled “Security Annotations in Program Files”Program authors can declare expected permissions in the file header. These are informational hints and they do not grant permissions but help the Host understand what a program will request.
@tool:name "data_fetcher"@tool:description "Fetches and processes remote data."@tool:permission "http.get"@tool:permission "file.write"Threat Model Summary
Section titled “Threat Model Summary”| Attack Vector | Status | Defense |
|---|---|---|
| Arbitrary code execution on host | ✗ Blocked | Custom bytecode VM, no JVM bytecode generation |
| File system access without permission | ✗ Blocked | Permission Bridge on all File.* calls |
| Network access without permission | ✗ Blocked | Permission Bridge on all Http.* calls |
| Secret/env variable leakage | ✗ Blocked | Permission Bridge on all Env.* calls |
| System fingerprinting via OS properties | ✗ Blocked | Env.SystemInfo gated behind Permission Bridge |
| Infinite loop / CPU exhaustion | ✗ Blocked | Instruction counter watchdog |
| Memory bomb / OOM | ✗ Blocked | Memory cap watchdog |
| Stack overflow | ✗ Blocked | Fixed-size call stack |
| Cross-sandbox data leakage | ✗ Blocked | Single-use Sandboxes, no shared state |
| JVM reflection / class loading | ✗ Blocked | No opcodes for reflection; concept doesn’t exist in NSL |
| Plugin escape (FFI) | ✗ Blocked | All FFI calls wrapped in exception containment |