Terminal is a command ACL and argument sanitizer. It filters which commands can be spawned and validates arguments before execution. It is not a sandbox, container, or process isolation layer.
- Allow/Deny Pattern Matching - rejects commands not matching the allow list
- Argument Sanitization - blocks shell metacharacters, null bytes, path traversal
- Workspace Validation - enforces
cwdis within allowed directories - Environment Filtering - strips sensitive environment variables
- Timeout Enforcement - kills runaway processes via native
AbortSignal - Privilege Dropping - runs child processes as a different
uid/gid - Windows Hardening - hides console windows, disables argument quoting
Terminal leverages native Node.js child_process.spawn() options for additional hardening:
- Native
AbortSignal- Uses Node.js built-insignaloption. WhenTerminal.kill()is called or a timeout fires, Node handles the kill signal natively without race conditions uid/gid- Drops privileges at spawn time viasetuid/setgid. The child runs as a different user/group than the parentkillSignal- Configurable default signal (default:SIGTERM). Change toSIGKILLfor immediate terminationwindowsHide- Prevents console window pop-ups on WindowswindowsVerbatimArguments- Disables Windows argument quoting/escaping, preventing injection via shell parsing
- Does NOT inspect file contents -
python3 /workspace/delete.pypasses all checks - Does NOT sandbox the filesystem - allowed commands can read/write anywhere the OS permits
- Does NOT isolate network access - allowed commands can open sockets freely
- Does NOT prevent privilege escalation by default - allowed commands can call
sudoif the OS allows. Useuid/gidconfig to drop privileges at spawn time - Does NOT inspect interpreter scripts -
node -epayloads without metacharacters pass through
- Shell Injection -
echo; rm -rf /in arguments is blocked bystrictArgs - Command Chaining -
||and&&in arguments is blocked bystrictArgs - Path Traversal -
../../../etc/passwdin arguments is blocked by path traversal check - Variable Expansion -
$(whoami)in arguments is blocked bystrictArgs - Null Byte Injection -
\x00in arguments is blocked
- Script File Execution - any allowed interpreter (
python3,node,deno) executing a malicious script file - Allowed Command Abuse -
gitwith malicious alias config,tarextracting to arbitrary paths - Environment Variable Abuse -
LD_PRELOADinjection,PATHhijacking (ifPATHis allowed) - Symlink Escapes - symlinks inside workspaces pointing outside allowed directories
- Resource Exhaustion - fork bombs, memory exhaustion, disk filling (requires cgroups)
Terminal filters the command string. The OS must enforce the actual execution boundary.
# Create a restricted user
useradd -m -s /bin/bash terminal-runner
chown -R terminal-runner:terminal-runner /workspace
su terminal-runner -c "deno run app.ts"# Make sensitive files unreadable
chmod 700 /etc/secrets
chmod 600 ~/.ssh/id_rsa
# Make workspace read-only where possible
chmod -R u+w /workspace/input
chmod -R u-w /workspace/output # Write output to a separate dir# Create a minimal root filesystem
mkdir -p /jail/{bin,lib,lib64,workspace}
cp /bin/bash /bin/ls /jail/bin/
chroot /jail /bin/bash# Isolate filesystem, network, and process namespace
unshare --mount --net --pid --fork --map-root-user /bin/bash// Deno example using --allow-net, --allow-read flags
// Node.js example using --experimental-sea or external seccomp tools
deno run --allow-read=/workspace --allow-write=/workspace app.ts# Limit CPU and memory
cgcreate -g cpu,memory:/terminal-limit
cgset -r cpu.cfs_quota_us=50000 terminal-limit # 50% CPU
cgset -r memory.limit_in_bytes=512M terminal-limit
cgexec -g cpu,memory:terminal-limit deno run app.tsFROM denoland/deno:latest
WORKDIR /workspace
COPY . .
RUN chmod -R o-w /etc /usr /bin
USER 1000:1000
CMD ["deno", "run", "app.ts"]docker run --rm \
--read-only \
--tmpfs /tmp:noexec,nosuid,size=100m \
-v $(pwd)/workspace:/workspace:ro \
-u 1000:1000 \
terminal-app# AppArmor profile restricting file access
cat > /etc/apparmor.d/terminal-runner << 'EOF'
#include <tunables/global>
profile terminal-runner {
#include <abstractions/base>
/workspace/** r,
/workspace/output/** w,
/bin/** ix,
/usr/bin/** ix,
deny /etc/passwd r,
deny /home/** w,
}
EOF
aa-enforce terminal-runnerFor real security, stack all layers:
Layer 8: Terminal ACL (command allow/deny, argument sanitization)
Layer 7: Unprivileged user (no root access)
Layer 6: Filesystem permissions (chmod, chown)
Layer 5: chroot / bind mounts (directory isolation)
Layer 4: Linux namespaces (unshare)
Layer 3: seccomp-bpf (syscall filtering)
Layer 2: cgroups (resource limits)
Layer 1: Container / VM (process + network isolation)
Terminal alone is Layer 8. Never rely on it as your only defense.
- Configuration - Configure Terminal settings
- Interpreter Usage - Risks of file-based execution
- API Reference - Security-related APIs