Networking & HTTP
Bashkit’s HTTP builtins — curl, wget, and http — are the only way a script
can reach the network, and they are default-deny. With no configuration, every
outbound request is blocked. You opt in host by host with a NetworkAllowlist.
There is no DNS rebinding window, no automatic redirect following across hosts, and no access to private or cloud-metadata IP ranges. Networking is a sandbox boundary, not a convenience.
<rect x="190" y="44" width="138" height="44" rx="4" fill="#fff" stroke="#d4a43a" stroke-width="1.5"/>
<text x="259" y="64" text-anchor="middle">allowlist</text>
<text x="259" y="80" text-anchor="middle" fill="#404040" font-size="11">+ private-IP block</text>
<rect x="380" y="44" width="138" height="44" rx="4" fill="#fff" stroke="#0a1636" stroke-opacity="0.3"/>
<text x="449" y="64" text-anchor="middle">sign (opt-in)</text>
<text x="449" y="80" text-anchor="middle" fill="#404040" font-size="11">bot-auth</text>
<rect x="570" y="44" width="128" height="44" rx="4" fill="#0a1636"/>
<text x="634" y="70" text-anchor="middle" fill="#ffffff">send →</text>
<g stroke="#0a1636" stroke-opacity="0.5" fill="none">
<path d="M138 66 H190" marker-end="url(#ar)"/>
<path d="M328 66 H380" marker-end="url(#ar)"/>
<path d="M518 66 H570" marker-end="url(#ar)"/>
</g>
<text x="259" y="118" text-anchor="middle" fill="#404040" font-size="11">blocked → request fails</text>
Allowing hosts
use bashkit::{Bash, NetworkAllowlist};
let allowlist = NetworkAllowlist::new()
.allow("https://api.example.com") // entire host
.allow("https://cdn.example.com/assets/"); // path prefix
let mut bash = Bash::builder().network(allowlist).build();
bash.exec("curl https://api.example.com/v1/users").await?;
Pattern matching
A request matches an allowlist entry when:
- Scheme matches exactly —
httpsis nothttp. - Host matches exactly — no wildcards, no implicit subdomains.
- Port matches — defaults applied (443 for https, 80 for http).
- Path is a prefix — the entry’s path must be a prefix of the request path.
This is literal-string matching by design: there is no DNS resolution at check time, which closes the DNS-spoofing and rebinding classes of attack (TM-NET-001/002).
Built-in SSRF protection
Even for an allowed host, requests that resolve to private or reserved IP ranges
are refused at connect time (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12,
192.168.0.0/16, 169.254.0.0/16 including cloud metadata, CGNAT, and the IPv6
equivalents). This blocks SSRF via DNS rebinding even if an allowlisted hostname
later resolves to an internal address (TM-NET-002/004/008). Override only when
you fully control the environment:
let allowlist = NetworkAllowlist::new()
.allow("http://localhost:8080")
.block_private_ips(false); // dangerous — testing only
NetworkAllowlist::allow_all() disables host checks entirely. Use it only for
fully trusted scripts.
CLI
The CLI keeps network access off unless you ask for it:
# Blocked by default
bashkit -c 'curl https://example.com'
# Unrestricted outbound (trusted scripts only)
bashkit --http-allow-all -c 'curl https://example.com'
Per-host allowlisting is a library-level concern (NetworkAllowlist); the CLI
exposes the coarse --http-allow-all switch for trusted use.
Observing and rewriting requests
HTTP requests flow through the same hooks pipeline as the rest of the interpreter, so a host can observe, rewrite, or cancel an outbound request before it leaves — useful for logging, header injection, or policy enforcement.
See also
- Credential injection — attach secrets to outbound requests without exposing them to the script.
- Request signing — cryptographic bot identity for signed outbound requests.
- Security — the full sandbox boundary model.