util

package
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package util provides small, hardened safety primitives shared across the starpkg modules: bounded I/O, constant-time secret comparison, float-safe duration parsing, and panic recovery. Each centralizes a guard that domain modules previously re-implemented (often subtly wrong), so a fix lives in one place and new modules are safe by construction.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildChildEnv

func BuildChildEnv(hostEnv []string, allow []string, extra map[string]string) []string

BuildChildEnv builds the environment ("KEY=VALUE" slice) for a child process launched on behalf of a script. It inherits from the host ONLY the variables whose names are in allow — an explicit allowlist, NOT the host's full os.Environ(), which would leak every host secret (API keys, tokens) and LD_PRELOAD-style injection vectors into a script-spawned process — then applies extra (script-supplied additions/overrides). A nil/empty allow inherits nothing from the host. The result is sorted for determinism.

On Windows, environment variable names are case-insensitive, so an extra override reliably beats a case-variant host var ("PATH" vs "Path").

func CheckInputSize

func CheckInputSize(label string, n, max int) error

CheckInputSize returns an error if n exceeds max bytes; it is the in-memory counterpart of ReadAllLimited for input already held as a string or []byte. A max <= 0 means unlimited.

func DurationFromSeconds

func DurationFromSeconds(sec float64) time.Duration

DurationFromSeconds converts a fractional number of seconds to a time.Duration without the truncation bug of time.Duration(sec) * time.Second, which converts the float to an integer BEFORE multiplying and so silently drops any sub-second part (0.5 -> 0, an immediately-firing deadline). It multiplies in float space first, then clamps to the representable range instead of overflowing/wrapping; a NaN yields 0.

func OpContext

func OpContext(thread *starlark.Thread, timeout time.Duration) (context.Context, context.CancelFunc)

OpContext returns a context (and its cancel func) for a single host operation triggered from a script. It starts from the thread's run context — so the Machine's own cancellation/deadline (e.g. RunWithTimeout) propagates to the operation — and layers an additional host timeout when timeout > 0. The returned context is always cancellable and derived; a timeout <= 0 adds no extra deadline. The caller MUST call the returned cancel func (typically via defer) to release resources.

This closes the recurring gap where a blocking remote call (a DB query, an API request, a queue send) took no context and could hang the host until the vendor SDK's or the OS's TCP timeout.

func ReadAllLimited

func ReadAllLimited(r io.Reader, max int64) ([]byte, error)

ReadAllLimited reads all of r but fails if it would exceed max bytes, instead of buffering an unbounded amount — the OOM vector of a bare io.ReadAll on untrusted input (a request body, a fetched object, an attachment). A max <= 0 means unlimited.

func Recover

func Recover(errp *error, label string)

Recover, used in a deferred call, converts a panic into an error stored in *errp (only when *errp is still nil), prefixed with label. It lets a wrapper around a panicky third-party call return an error instead of crashing the host — the recurring "defer/recover around a 3rd-party parser/renderer" pattern. Use as: defer util.Recover(&err, "yaml decode").

func ResolveUnder

func ResolveUnder(root, userPath string) (string, error)

ResolveUnder resolves userPath against root and returns the confined path ONLY if it stays within root — a path jail. It confines "..", absolute paths, and symlinks (including dangling ones) in ANY path segment whose target escapes root, so a script-supplied path cannot reach an arbitrary host file (the traversal / arbitrary read-write class of bug). userPath is treated as relative to root: an absolute userPath is re-anchored under root.

The check is time-of-check: it validates and returns a path the caller opens later. It is safe when path components under root cannot be concurrently replaced by a hostile party; if the tree is attacker-writable between the call and the open, a TOCTOU race remains (open the returned path with O_NOFOLLOW, or use an OS-level jail, for that threat model).

func Retry

func Retry(ctx context.Context, attempts int, retryable func(error) bool, backoff func(attempt int) time.Duration, fn func() error) error

Retry runs fn up to attempts times, stopping early on success (nil error), on a non-retryable error (retryable returns false), or when ctx ends. It returns fn's last error, or ctx.Err() if the context ends first.

attempts < 1 is treated as 1: fn is ALWAYS invoked at least once. This is the point of the primitive — a retry count of 0 must not silently skip the call and report a fake success (the bug found where retry=0 returned an empty result with no error and no request made).

retryable may be nil (every error is retried). backoff may be nil (no delay); otherwise backoff(attempt) is waited between attempts, interruptible by ctx.

func SafeCall

func SafeCall[T any](label string, fn func() (T, error)) (result T, err error)

SafeCall runs fn and converts any panic into an error prefixed with label, so a panicky third-party call cannot crash the host. It returns fn's own result and error unchanged when fn does not panic.

func SecretEqual

func SecretEqual(a, b []byte) bool

SecretEqual reports whether a and b are equal, in constant time relative to their contents — use it to compare a caller-supplied token, password, or signature against a host secret so the comparison does not leak the secret via timing. A plain a == b (or bytes.Equal) short-circuits on the first differing byte and is timing-attackable. (Length inequality is not hidden, which matches crypto/subtle and is the standard trade-off.)

func SecretEqualString

func SecretEqualString(a, b string) bool

SecretEqualString is SecretEqual for strings.

func ToStarlark

func ToStarlark(v interface{}, lim DecodeLimits) (starlark.Value, error)

ToStarlark converts a Go value — typically the output of a decoder such as yaml/toml/json — into a Starlark value, bounded by lim. It centralizes the hardened "capwalk" every starpkg codec re-implemented (each subtly different):

  • caps nesting depth and total node count from lim;
  • materializes maps in deterministic (sorted-key) order;
  • tames a time.Time (RFC 3339, so sub-second precision is discarded) and any other fmt.Stringer (e.g. a TOML local date) to a string, so an opaque Go type never leaks into a script;
  • REJECTS a map whose distinct keys collide after stringification (e.g. int 1 and string "1") instead of silently dropping one — a real bug the hand-rolled decoders shared.

Types

type CappedWriter

type CappedWriter struct {
	// contains filtered or unexported fields
}

CappedWriter wraps an io.Writer and fails a Write that would push the total bytes written past its limit, so a bounded amount of output is produced from an otherwise-unbounded render or copy. A limit <= 0 means unlimited.

func NewCappedWriter

func NewCappedWriter(w io.Writer, limit int) *CappedWriter

NewCappedWriter returns a CappedWriter over w with the given byte limit (<= 0 means unlimited).

func (*CappedWriter) Write

func (c *CappedWriter) Write(p []byte) (int, error)

Write writes p to the underlying writer, or returns an error (writing nothing) if it would exceed the limit.

func (*CappedWriter) Written

func (c *CappedWriter) Written() int

Written reports how many bytes have been written through the CappedWriter.

type DecodeLimits

type DecodeLimits struct {
	MaxDepth int // maximum nesting depth
	MaxNodes int // maximum total number of values materialized
}

DecodeLimits bounds the work a ToStarlark conversion may do, fencing a huge or malicious decoded document. A zero field means that dimension is unbounded.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL