5.3.16. C++ Integration: Sandbox

This tutorial shows how to restrict what daslang scripts can do, which is essential when running untrusted code (user mods, plugin systems, online playgrounds). Two complementary approaches are shown:

  • Approach A — C++: CodeOfPolicies + FileAccess subclass

  • Approach B — .das_project: sandbox policy written in daslang

Topics covered:

  • CodeOfPolicies — compile-time language restrictions

  • Memory limits — heap and stack size caps

  • Custom FileAccess — restrict which modules scripts can require

  • .das_project — policy file with exported callback functions

5.3.16.1. Prerequisites

  • Tutorial 15 completed (tutorial_integration_cpp_custom_annotations).

5.3.16.2. Sandboxing approaches

daslang provides two complementary approaches:

Approach A — C++ code (Demos 1–4):

  1. CodeOfPolicies — language-level restrictions passed to compileDaScript()

  2. FileAccess virtual overrides — fine-grained C++ control over modules, options, annotations

Approach B — .das_project file (Demos 5–6):

  1. A .das_project file — a regular daslang script that exports callback functions. The host loads it, and the compiler calls the callbacks during compilation of user scripts.

Both can be combined — CodeOfPolicies is layered on top of either FileAccess subclass or .das_project callbacks.

5.3.16.3. CodeOfPolicies — language restrictions

Pass a CodeOfPolicies struct to compileDaScript():

CodeOfPolicies policies;
policies.no_unsafe = true;                   // forbid unsafe blocks
policies.no_global_variables = true;          // forbid module-scope var
policies.no_init = true;                      // forbid [init] functions
policies.max_heap_allocated = 1024 * 1024;    // 1 MB heap max
policies.max_string_heap_allocated = 256*1024; // 256 KB strings
policies.stack = 8 * 1024;                    // 8 KB stack

auto program = compileDaScript(script, fAccess, tout,
                               libGroup, policies);

Key policy flags:

no_unsafe

Forbids all unsafe blocks

no_global_variables

Forbids module-scope var

no_global_heap

Forbids heap allocations from globals

no_init

Forbids [init] functions

max_heap_allocated

Caps heap memory (0 = unlimited)

max_string_heap_allocated

Caps string heap memory

stack

Context stack size in bytes

threadlock_context

Adds context mutex for thread safety

When a policy is violated, compilation fails with error 40207.

5.3.16.4. Custom FileAccess — module restrictions

Subclass FsFileAccess and override virtual methods to control what scripts can access:

class SandboxFileAccess : public FsFileAccess {
    das_set<string> allowedModules;
public:
    SandboxFileAccess() {
        allowedModules.insert("$");      // built-in (always needed)
        allowedModules.insert("math");
        allowedModules.insert("strings");
    }

    bool isModuleAllowed(const string & mod,
                         const string &) const override {
        return allowedModules.count(mod) > 0;
    }

    bool canModuleBeUnsafe(const string &,
                           const string &) const override {
        return false;  // no module can use unsafe
    }

    bool isOptionAllowed(const string & opt,
                         const string &) const override {
        return opt != "persistent_heap";
    }
};

Available virtual overrides:

isModuleAllowed()

Can this module be loaded at all?

canModuleBeUnsafe()

Can this module use unsafe?

canBeRequired()

Can this module be require’d?

isOptionAllowed()

Can this options keyword be used?

isAnnotationAllowed()

Can this annotation be used?

All return true by default (no restrictions).

5.3.16.5. .das_project — policy in daslang

Instead of writing C++ code, you can define sandbox policies in a .das_project file — a regular .das script that exports callback functions. The compiler loads it, simulates it, and calls the exported functions during compilation of user scripts.

5.3.16.5.1. Loading a project file from C++

string projectPath = getDasRoot()
    + "/path/to/sandbox.das_project";
auto fAccess = make_smart<FsFileAccess>(
    projectPath, make_smart<FsFileAccess>());

CodeOfPolicies policies;
auto program = compileDaScript(scriptPath, fAccess, tout,
                               libGroup, policies);

The FsFileAccess(projectPath, fallbackAccess) constructor compiles and simulates the project file, then looks up exported callback functions by name.

5.3.16.5.2. Project file callbacks

options gen2
require strings
require daslib/strings_boost

typedef module_info = tuple<string; string; string> const
var DAS_PAK_ROOT = "./"

// REQUIRED — resolve every `require X` to a file path
[export]
def module_get(req, from : string) : module_info {
    // return (moduleName, fileName, importName)
}

// Whitelist which modules can be loaded
[export]
def module_allowed(mod, filename : string) : bool {
    if (mod == "$" || mod == "math" || mod == "strings") {
        return true
    }
    return false
}

// No module may use unsafe
[export]
def module_allowed_unsafe(mod, filename : string) : bool {
    return false
}

// Only safe options
[export]
def option_allowed(opt, from : string) : bool {
    return opt == "gen2" || opt == "indenting"
}

// Only safe annotations
[export]
def annotation_allowed(ann, from : string) : bool {
    return ann == "export" || ann == "private"
}

Available callbacks:

module_get

Resolve require paths (REQUIRED)

module_allowed

Whitelist which modules can load

module_allowed_unsafe

Control unsafe per module

option_allowed

Whitelist options directives

annotation_allowed

Whitelist annotations

include_get

Resolve include directives (optional)

DAS_PAK_ROOT is set by the runtime to the directory containing the .das_project file, useful for resolving relative paths.

5.3.16.5.3. C++ vs .das_project — when to use which

  • C++ subclass — when you need dynamic logic, access to host state, or tight integration with your engine’s asset pipeline

  • .das_project — when you want to ship policy as data alongside scripts, let level designers tweak restrictions, or prototype policies without recompiling the host

  • Both combinedCodeOfPolicies plus a .das_project; the C++ policies add a hard floor, the project file refines what’s allowed within that floor

5.3.16.6. Demo walkthrough

The tutorial runs six demos:

Approach A — C++:

  1. No restrictions — normal compilation succeeds

  2. ``no_unsafe`` policy — a script with unsafe blocks is rejected; a safe script compiles fine

  3. Memory limits — stack and heap sizes are capped

  4. Module restrictionsSandboxFileAccess blocks modules not in the allow-list; canModuleBeUnsafe() = false prevents transitive dependencies from using unsafe

Approach B — .das_project:

  1. .das_project sandbox — the safe script compiles under the project’s policy callbacks

  2. .das_project blocks violations — unsafe scripts and blocked modules are rejected by the project’s callbacks

5.3.16.7. Building and running

cmake --build build --config Release --target integration_cpp_16
bin\Release\integration_cpp_16.exe

Expected output:

=== Demo 1: No restrictions ===
--- Normal mode ---
=== Sandbox Tutorial ===
score = 42
sum(1..10) = 55
Hello from the sandbox!

=== Demo 2: Policies — no_unsafe ===
--- Safe script with no_unsafe ---
=== Sandbox Tutorial ===
...

--- Unsafe script with no_unsafe ---
  Compilation FAILED (expected in sandbox demo):
  error[40207]: unsafe function test
  ...

=== Demo 3: Memory limits ===
  Stack:       8192 bytes
  Max heap:    1048576 bytes
  Max strings: 262144 bytes
--- Memory-limited ---
=== Sandbox Tutorial ===
...

=== Demo 4: Module restrictions ===
--- Allowed script ---
=== Sandbox Tutorial ===
...

--- Blocked module script ---
  Compilation FAILED (expected in sandbox demo):
  ...

=== Demo 5: .das_project sandbox ===
--- Script under .das_project sandbox ---
=== Sandbox Tutorial ===
score = 42
sum(1..10) = 55
Hello from the sandbox!

=== Demo 6: .das_project blocks violations ===
--- Unsafe script under .das_project ---
  Compilation FAILED (expected in sandbox demo):
  error[40207]: unsafe function test
  ...

--- Blocked module under .das_project ---
  Compilation FAILED (expected in sandbox demo):
  ...

=== Available sandbox approaches ===
...

See also

Full source: 16_sandbox.cpp, 16_sandbox.das, 16_sandbox.das_project

Previous tutorial: tutorial_integration_cpp_custom_annotations

Next tutorial: tutorial_integration_cpp_coroutines