hygiene

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package hygiene provides an always-on test-environment isolation floor for Go suites: a throwaway HOME, an isolated git config and identity, transport lockdown, credential stripping, and cleanup-restoring chdir.

Usage

The composite entry point is Isolate. One call at the top of a test (or of a helper every test in the package funnels through) binds the whole floor:

func TestSomething(t *testing.T) {
	hygiene.Isolate(t)
	// HOME is a throwaway dir, git reads an empty global config with a
	// throwaway identity, only the file:// transport is allowed, and every
	// ambient credential variable is gone.
}

Each piece is also exported on its own -- ThrowawayHome, IsolateGitConfig, LockdownTransports, StripCredentials -- for suites that need one guarantee without the others. Chdir is separate on purpose: it is a per-test tool, not part of the floor.

Contract

Every helper takes a testing.TB, calls TB.Helper so failures point at the caller, and undoes itself through TB.Cleanup. Nothing here is global or process-wide: the isolation lives exactly as long as the test (or subtest) whose TB was passed in.

Parallelism

Every environment mutation goes through TB.Setenv, which panics when the test has called T.Parallel. That is intended and is not worked around: a parallel test cannot own a process-wide variable like HOME, so a suite that wants this floor cannot run its tests in parallel with each other. The panic makes the conflict immediate and obvious instead of letting one test's HOME leak into another's.

No socket guard

Unlike the Python plugin, this package ships no network guard. Go has no equivalent of sys.addaudithook, so there is no in-process interception point that could refuse a dial without patching the runtime. Network isolation for Go suites is owned by the sandbox runner (the bubblewrap wrapper that runs the suite with no network namespace), not by this package. Do not add a half-guard here that only covers net.Dial: a partial guard reads as a guarantee and is worse than none.

Index

Constants

This section is empty.

Variables

View Source
var CredentialVars = []string{
	"SSH_AUTH_SOCK",
	"GIT_ASKPASS",
	"GITHUB_TOKEN",
	"GH_TOKEN",
	"GITHUB_API_TOKEN",
	"NPM_TOKEN",
	"NODE_AUTH_TOKEN",
	"PYPI_TOKEN",
	"TWINE_PASSWORD",
	"TWINE_USERNAME",
	"CARGO_REGISTRY_TOKEN",
	"AWS_ACCESS_KEY_ID",
	"AWS_SECRET_ACCESS_KEY",
	"AWS_SESSION_TOKEN",
	"CLOUDFLARE_API_TOKEN",
	"CF_PAGES_API_TOKEN",
	"ANTHROPIC_API_KEY",
	"OPENAI_API_KEY",
}

CredentialVars is the closed list of ambient credential vectors that StripCredentials removes from the environment. A test that genuinely needs one sets a FAKE value itself with TB.Setenv.

The list mirrors the Python plugin's CREDENTIAL_VARS, plus GIT_ASKPASS. That one variable is the two floors' single deliberate divergence: the Python floor pins it to /bin/false, while this package removes it here. Both close the same door -- git cannot obtain a credential either way -- and a cross-language test holds the rest of the two lists identical.

Functions

func Chdir

func Chdir(t testing.TB, dir string)

Chdir moves the process working directory to dir for the duration of t and restores the previous one when t finishes.

A failed restore FAILS the test rather than being logged and swallowed: the process is then sitting in the wrong directory, and every later test in the binary would run against it. A test that reports the damage is the only honest outcome.

The process working directory is global state, so this -- like the rest of the package -- is incompatible with T.Parallel. PWD is updated alongside the real working directory so that child processes inheriting the environment agree with the parent about where they are.

func Isolate

func Isolate(t testing.TB, opts ...Option)

Isolate binds the full environment floor for the duration of t: the preserved toolchain caches (if any) are pinned first, then HOME and the four XDG base directories are repointed at a throwaway directory, git's global and system config are emptied, the git identity is replaced, transports are locked down to file:// with git's ssh and proxy helpers pinned to a command that always fails, and every ambient credential variable is removed.

The throwaway home is not returned; call ThrowawayHome (which is memoized per TB and returns the same directory Isolate created) when the path is needed.

func IsolateGitConfig

func IsolateGitConfig(t testing.TB)

IsolateGitConfig cuts git off from the developer's configuration and identity: GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM point at empty files inside the throwaway home (which it allocates through ThrowawayHome if the test has not already), the author and committer identity become a throwaway one, and GIT_TERMINAL_PROMPT is 0 so no git invocation can ever block a test on a password prompt.

The config files are empty rather than carrying the identity, because the GIT_AUTHOR_* / GIT_COMMITTER_* variables below already supply it and outrank any config file -- a git invocation that ignores the config path entirely still cannot commit as the developer.

core.hooksPath is deliberately NOT set. It overrides repo-local hooks too, which would silently disable a suite's own pre-push-hook tests; an empty global config already prevents the developer's hooks from firing.

func LockdownTransports

func LockdownTransports(t testing.TB)

LockdownTransports restricts git to the local file transport for the duration of t. Any ssh://, https:// or git:// URL a test reaches for -- a real remote, a real fetch, a real push -- fails at the protocol check instead of touching the network.

GIT_SSH_COMMAND and GIT_PROXY_COMMAND are pinned to /bin/false as a second, independent layer. The protocol list is the first line and would be enough on its own, but a test (or a tool under test) that sets GIT_ALLOW_PROTOCOL itself would lift it -- and then the developer's real ssh, with their real key and their real proxy, is what git would run. Pinning both helpers means that path dies at an executable that only ever fails.

func StripCredentials

func StripCredentials(t testing.TB)

StripCredentials removes every variable in CredentialVars from the environment for the duration of t. The original values are restored when t finishes.

func ThrowawayHome

func ThrowawayHome(t testing.TB) string

ThrowawayHome repoints HOME, USERPROFILE and the four XDG base directories (XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_CACHE_HOME, XDG_STATE_HOME) at a fresh temporary directory owned by t, and returns that directory. Every variable is restored when t finishes, and the directory is removed with the rest of t's TempDir tree.

The XDG directories are repointed as well as HOME because a great many tools read them FIRST: a suite that moved only HOME would still let a tool read the developer's real ~/.config (gh's hosts.yml lives there) and write into their real caches.

The call is memoized per TB: asking twice within the same test returns the same directory rather than moving HOME again. Each subtest gets its own TB and therefore its own home.

Types

type KnownVar

type KnownVar int

KnownVar names one toolchain cache variable that a suite may opt into preserving across the HOME repoint. The enum is closed: a caller cannot ask for an arbitrary variable name, so a credential vector can never become preservable by typo.

const (
	GoPath KnownVar = iota
	GoModCache
	GoCache
	PythonUserBase
	CargoHome
	RustupHome
	NpmCache
	UvCache
	PipCache
	GradleUserHome
)

The closed preserve enum. Every entry names a cache or package location that holds build artifacts, not secrets, and that would otherwise send a toolchain into a cold rebuild (or hide an already-installed module) once HOME moves.

func (KnownVar) Env

func (v KnownVar) Env() string

Env returns the environment variable this enum member pins.

func (KnownVar) String

func (v KnownVar) String() string

String returns the enum member's Go name, or a marker for an out-of-range value.

type Option

type Option func(*options)

Option customizes Isolate. The only option is Preserve; there is deliberately no option that turns a floor piece off.

func Preserve

func Preserve(v ...KnownVar) Option

Preserve opts the named toolchain caches into surviving the HOME repoint. Each one is pinned to its current value, or to its default location under the real home when it is unset, before HOME changes.

Jump to

Keyboard shortcuts

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