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+FileAccesssubclassApproach B — .das_project: sandbox policy written in daslang
Topics covered:
CodeOfPolicies— compile-time language restrictionsMemory 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):
CodeOfPolicies— language-level restrictions passed tocompileDaScript()FileAccessvirtual overrides — fine-grained C++ control over modules, options, annotations
Approach B — .das_project file (Demos 5–6):
A
.das_projectfile — 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:
|
Forbids all |
|
Forbids module-scope |
|
Forbids heap allocations from globals |
|
Forbids |
|
Caps heap memory (0 = unlimited) |
|
Caps string heap memory |
|
Context stack size in bytes |
|
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:
|
Can this module be loaded at all? |
|
Can this module use |
|
Can this module be |
|
Can this |
|
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:
|
Resolve |
|
Whitelist which modules can load |
|
Control |
|
Whitelist |
|
Whitelist annotations |
|
Resolve |
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 combined —
CodeOfPoliciesplus 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++:
No restrictions — normal compilation succeeds
``no_unsafe`` policy — a script with
unsafeblocks is rejected; a safe script compiles fineMemory limits — stack and heap sizes are capped
Module restrictions —
SandboxFileAccessblocks modules not in the allow-list;canModuleBeUnsafe() = falseprevents transitive dependencies from using unsafe
Approach B — .das_project:
.das_project sandbox — the safe script compiles under the project’s policy callbacks
.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