Documentation
¶
Overview ¶
Package runner executes the child process with secrets in its environment: exec, stream stdio, propagate exit code. Plaintext exists only in the child's env for its lifetime; Masker additionally scrubs the values from captured output.
Index ¶
Constants ¶
const MinMaskLen = 6
MinMaskLen is the shortest value the masker will rewrite. Below it, values are too likely to collide with ordinary output ("true", "8080") and would shred it; such secrets pass through unmasked by design.
Variables ¶
This section is empty.
Functions ¶
func Run ¶
Run executes argv with the given environment, wiring stdin through (nil reads as empty, for callers whose own stdin belongs to a protocol) and streaming the child's output to stdout/stderr (pass os.Stdout/os.Stderr for a direct wire, or Maskers to scrub captured output; the caller flushes those after Run returns), forwarding termination signals to the child. It returns the child's exit code (128+signal if the child was killed by a signal); a child that never started is a *StartError.
Types ¶
type Masker ¶ added in v0.6.0
type Masker struct {
// contains filtered or unexported fields
}
Masker is a Writer that rewrites occurrences of injected secret values in a byte stream with placeholders before forwarding to dst. It exists for captured output: anything a child process prints can end up in an agent's context window, a CI log, or a shell pipeline, and a server that echoes its connection string on boot would otherwise hand the secret to whatever is reading. Masking is accident-proofing, not a boundary: code that already holds the secret can always move it some other way.
Matching is exact-byte, streamed, leftmost-longest, and non-overlapping: each output position is replaced by the longest pattern that starts there, then scanning resumes after it. A value split across writes is still caught (the decision for a position is deferred until later bytes settle it), and Flush emits whatever is still pending at end-of-stream, so callers must call it after the child exits or trailing output is lost.
Each pattern runs as its own KMP automaton (a flat failure table per pattern), all advanced one step per input byte. KMP's failure links mean a byte is never re-scanned, so the cost is linear in the stream and a fixed factor of the pattern count, never quadratic in the pattern length: this matters because patterns can be kilobytes (a handful of secrets, each expanded into encodings, any of which may be a PEM). A byte that starts no pattern and extends no live match is emitted at once (plain passthrough); a byte is held only while some automaton is mid-match and could still complete a match covering it, so the hold is bounded by the longest live partial match (at most one pattern length), and is zero for output that touches no secret.
func NewMasker ¶ added in v0.6.0
NewMasker builds a masker over dst for the given secrets. Values shorter than MinMaskLen are skipped; duplicate values collapse into one pattern (named after the first env var alphabetically). With no usable patterns the masker degrades to a plain passthrough.
func NewMaskerFloor ¶ added in v0.16.0
NewMaskerFloor is NewMasker with an explicit minimum value length. A floor of 1 masks every non-empty value, for a consumer where keeping secrets out of the output outweighs occasionally shredding a short common string; the default floor trades that off the other way (it does not mangle ordinary short output). An empty value is always skipped, whatever the floor, since it would match at every position.
func (*Masker) Flush ¶ added in v0.6.0
Flush drains the pending queue at end of stream: no more bytes can arrive, so every pending byte is settled. It emits the longest recorded match at each front position, else one literal byte, then resets the matcher so a later Write starts clean. Callers must Flush after the child exits or trailing output is lost.
type StartError ¶ added in v0.11.0
type StartError struct{ Err error }
StartError reports that the child process never ran (the command was not found, not executable, or the spawn itself failed), as opposed to an error after a successful start. Callers map the distinction to exit codes.
func (*StartError) Error ¶ added in v0.11.0
func (e *StartError) Error() string
func (*StartError) Unwrap ¶ added in v0.11.0
func (e *StartError) Unwrap() error