Documentation
¶
Overview ¶
Package network is the gobash secure HTTP layer. It defines the Doer interface every network-touching built-in talks to, the allow-list machinery, the typed errors host code can match with errors.As, and the default SecureFetch implementation backed by net/http.
Phase 9 surface ¶
Five things land in Phase 9:
- The Doer interface and Response value type (§9.3).
- Config + AllowedURLEntry + RequestTransform + DNSLookupResult (§9.1).
- The allow-list compiler and per-redirect re-validation logic (§9.2).
- NewSecureFetch (§9.3) — an http.Client-equivalent that enforces Config and defaults to DENY-ALL when no allowed prefixes are supplied.
- The five typed errors (§9.4): NetworkAccessDeniedError, TooManyRedirectsError, RedirectNotAllowedError, MethodNotAllowedError, ResponseTooLargeError.
Phase 9 does NOT add any built-ins; curl / html-to-markdown ship in Phase 10. The package is consumed today by gobash.(*Bash) (which resolves a Doer for every Exec) and by gobash/command.Context.Fetch (which Phase 10 commands will read).
Security defaults ¶
Network is OFF unless BashOptions.Network or BashOptions.Fetch is explicitly configured. When Network is set with no AllowedURLPrefixes and DangerouslyAllowFullAccess=false, every request fails with NetworkAccessDeniedError — there is no implicit allow-all path. DenyPrivateRanges layers on top of the allow-list: even an allowed URL is denied if its resolved IPs are private/loopback/link-local.
Go configuration compatibility ¶
Header transforms accumulate across all matching allow-list entries in configuration order; later transforms override earlier values for the same header name. They are recomputed for each redirect destination. AllowedMethods distinguishes nil (GET/HEAD defaults) from an empty slice (deny every method), except in DangerouslyAllowFullAccess mode.
Unlike optional fields in just-bash, the existing scalar Config fields cannot distinguish omission from an explicit zero. For compatibility, MaxRedirects=0 still means 20 (use -1 to return the first redirect response), and Timeout=0 and MaxResponseSize=0 still select their package defaults. DenyPrivateRanges defaults to false, not just-bash's production default of true; production callers should explicitly enable it. Changing these field types or silently reinterpreting existing configurations would break the Go API or its behavior.
Reference (read-only) ¶
vercel-labs/just-bash, src/network/. The Go port preserves the TS matcher semantics: origin normalization, %2f / %5c rejection, and per-redirect re-validation.
Index ¶
Constants ¶
const ( DefaultMaxRedirects = 20 DefaultTimeout = 30 * time.Second DefaultMaxResponseSize = 10 * 1024 * 1024 // 10 MiB )
Default values applied by NewSecureFetch when Config fields are zero. Exported so tests and host code can refer to the same constants without re-deriving them.
Variables ¶
var DefaultAllowedMethods = []string{"GET", "HEAD"}
DefaultAllowedMethods is the methods set applied when Config.AllowedMethods is nil. GET and HEAD are the safest defaults matching the just-bash TS table.
Functions ¶
This section is empty.
Types ¶
type AllowedURLEntry ¶
type AllowedURLEntry struct {
// URL is the prefix to allow. Must include scheme and host. The
// path component is treated as a literal prefix — entry URL
// "https://api.example.com/v1/" matches request URLs whose path
// starts with "/v1/" on that origin. %2f / %5c in the path are
// rejected at compile time because they create ambiguity with
// real / and \ characters.
URL string
// Transform is the list of header rewrites applied when this entry
// matches the outgoing request. Multiple entries with overlapping
// origins all contribute their headers, last-write-wins per header
// name.
Transform []RequestTransform
}
AllowedURLEntry is one allow-list slot. URL provides the scheme://host[:port][/path] prefix; Transform lists header rewrites applied on every matching request hop.
The spec
type Config ¶
type Config struct {
// AllowedURLPrefixes whitelists specific origins (scheme+host[+port])
// and optionally a path prefix and per-entry header transforms. An
// incoming URL matches when its origin equals the entry's origin
// AND its path begins with the entry's path. Each entry is
// re-validated on every redirect hop.
AllowedURLPrefixes []AllowedURLEntry
// AllowedMethods restricts which HTTP methods the Doer will issue.
// Nil means DefaultAllowedMethods (GET, HEAD). An explicitly empty
// slice denies every method (unless DangerouslyAllowFullAccess is set).
// Method names are case-insensitive.
AllowedMethods []string
// DangerouslyAllowFullAccess turns SecureFetch into a pass-through.
// All URLs and methods are permitted. DenyPrivateRanges is still
// honored. Intended for development sandboxes — do not enable in
// production hosts.
DangerouslyAllowFullAccess bool
// MaxRedirects caps the redirect chain. The initial request is hop
// zero; the limit is the number of additional Location hops the
// client will follow. Zero means DefaultMaxRedirects (20). A
// negative value disables redirect following entirely (the first
// 3xx returns to the caller).
MaxRedirects int
// Timeout is the per-request deadline (initial + redirects). Zero
// means DefaultTimeout (30s). A negative value disables the
// per-request timeout (caller's ctx is still honored).
Timeout time.Duration
// MaxResponseSize caps the response body in bytes. Zero means
// DefaultMaxResponseSize (10 MiB). A negative value disables the
// cap; SecureFetch will still read the whole body into memory.
MaxResponseSize int64
// DenyPrivateRanges resolves every connection target via DNSResolve
// (or net.DefaultResolver if nil) and rejects any address in a
// private, loopback, link-local, or multicast range. Combine with
// DangerouslyAllowFullAccess to permit arbitrary public hosts
// while still blocking SSRF into the host's local network.
DenyPrivateRanges bool
// DNSResolve is an injection point for tests and custom resolvers.
// When nil, SecureFetch falls back to net.DefaultResolver.LookupIP.
// When non-nil, it is the sole source of address records used for
// the DenyPrivateRanges check.
DNSResolve func(ctx context.Context, host string) ([]DNSLookupResult, error)
}
Config configures SecureFetch. The zero Config denies every URL — to enable any traffic at all, set AllowedURLPrefixes or DangerouslyAllowFullAccess.
The spec
type DNSLookupResult ¶
type DNSLookupResult struct {
// Address is the textual representation (192.0.2.1 or 2001:db8::1).
Address string
// Family is 4 (AF_INET) or 6 (AF_INET6). Unknown families cause
// the address to be skipped during private-range checks.
Family int
}
DNSLookupResult is one resolved address from Config.DNSResolve. Family is the AF_* constant: 4 for AF_INET, 6 for AF_INET6.
The spec
type Doer ¶
Doer is the gobash analog of an http.Client. Every network-touching built-in (Phase 10's curl, future fetch / wget) talks to a Doer, never directly to net/http. Hosts may supply their own implementation via BashOptions.Fetch to mock network traffic in tests or to route through a custom transport (proxy, mTLS, etc.).
Implementations MUST respect ctx cancellation and MUST NOT mutate the supplied req. The returned Response is owned by the caller.
func NewSecureFetch ¶
NewSecureFetch returns a Doer that enforces cfg. Passing nil produces a fully-locked-down Doer that denies every URL — this is the policy applied when BashOptions.Network is nil and the host did not supply BashOptions.Fetch either.
The returned Doer is safe for concurrent use by multiple goroutines.
The spec
type MethodNotAllowedError ¶
MethodNotAllowedError is returned when the request's method is not in Config.AllowedMethods (and DangerouslyAllowFullAccess is false).
The spec
func (*MethodNotAllowedError) Error ¶
func (e *MethodNotAllowedError) Error() string
type NetworkAccessDeniedError ¶
NetworkAccessDeniedError is returned when a URL does not match any allow-list entry (and DangerouslyAllowFullAccess is not set), or when an otherwise-allowed URL resolves only to private addresses while DenyPrivateRanges is enabled.
The spec
func (*NetworkAccessDeniedError) Error ¶
func (e *NetworkAccessDeniedError) Error() string
type RedirectNotAllowedError ¶
RedirectNotAllowedError is returned when a redirect target fails allow-list re-validation. The two URLs are the source and the rejected destination, matching the TS error shape.
The spec
func (*RedirectNotAllowedError) Error ¶
func (e *RedirectNotAllowedError) Error() string
type RequestTransform ¶
type RequestTransform struct {
// Headers is the name → value overlay. Header names are
// canonicalized via http.CanonicalHeaderKey before application.
Headers map[string]string
}
RequestTransform is a header overlay applied to a matching request. Headers replace any existing header of the same name.
The spec
type Response ¶
type Response struct {
// Status is the numeric HTTP status code (200, 404, etc.).
Status int
// StatusText is the HTTP status line text ("OK", "Not Found", ...).
StatusText string
// Headers is the response header set. Multi-value headers retain
// every value. Callers MUST NOT mutate this map.
Headers http.Header
// Body is the response body bytes, capped at Config.MaxResponseSize.
// If the server attempted to send more, Doer.Do returns
// *ResponseTooLargeError and Response is nil.
Body []byte
// URL is the final URL after any redirect chain. For a non-redirected
// response it equals the request URL string.
URL string
}
Response is the value type returned by Doer.Do. It is a snapshot: Body has already been read (subject to MaxResponseSize), so the caller does not need to Close anything. URL records the final URL after redirect resolution so commands like curl -w can report it.
type ResponseTooLargeError ¶
type ResponseTooLargeError struct {
Max int64
}
ResponseTooLargeError is returned when a response body exceeds Config.MaxResponseSize bytes. The body is not buffered into Response.Body when this error fires.
The spec
func (*ResponseTooLargeError) Error ¶
func (e *ResponseTooLargeError) Error() string
type TooManyRedirectsError ¶
type TooManyRedirectsError struct {
Max int
}
TooManyRedirectsError is returned when a request would exceed Config.MaxRedirects redirect hops.
The spec
func (*TooManyRedirectsError) Error ¶
func (e *TooManyRedirectsError) Error() string