Skip to content

ihopenre-eng/logmask

Repository files navigation

logmask

CI Go Reference Go Report Card

Keep secrets out of your logs.

logmask brings the masking model proven by GitHub Actions (::add-mask::) and Jenkins credential masking to Go applications as an embeddable library: register every secret value your application knows, typically at configuration load time, and logmask replaces those values, plus their URL-encoded and base64 variants, anywhere they appear in log output.

go get github.com/ihopenre-eng/logmask

Requires Go 1.22 or newer. The only dependency is sirupsen/logrus, and only for the logrusmask adapter package.

Why not "detect sensitive information"?

Generic detection of secrets in free-form text is a data-loss-prevention problem: it cannot be solved without false positives. Applications, however, usually know their secrets. They parsed them out of a config file, an environment variable, or a secrets manager. logmask exploits that: exact, cheap, zero-false-positive replacement for known values, plus a small opt-in set of structural detectors for credential shapes that are unambiguous.

Usage

Core

m := logmask.New(logmask.WithDetectors(logmask.AllDetectors()...))

// Register secrets as you resolve them from config / env / secret stores.
m.Add(cfg.GitToken, cfg.RegistryPassword)

m.Mask(`pushing to https://ci:s3cr3t@git.example.com/repo.git`)
// pushing to https://***@git.example.com/repo.git

One Masker is meant to be created once and shared. Mask is safe for concurrent use, including concurrently with Add, so secrets discovered mid-run (a token fetched lazily, a password read from a vault) can be registered at any point.

logrus

logger := logrus.StandardLogger()
logger.AddHook(logrusmask.New(m))

// Every entry is masked before formatting, at every level:
logger.Errorf("clone failed for %s", remoteURL) // credentials in remoteURL: masked

Child-process output

logmask.Writer is a line-buffered io.Writer for streams that bypass your logger, such as the stdout/stderr of subprocesses:

cmd.Stdout = logmask.NewWriter(os.Stdout, m)
cmd.Stderr = logmask.NewWriter(os.Stderr, m)

A secret split across multiple Write calls is still masked as long as it does not span a newline.

Detectors

Detectors cover credential shapes the process never handled as discrete values. All are opt-in (WithDetectors) and scoped to near-zero false-positive patterns:

Detector Redacts
URLUserinfo() scheme://user:pass@host, scheme://token@host (whole userinfo)
AuthPairs() Authorization: Bearer …, PRIVATE-TOKEN: …, api_key=…, password: …, and similar
KnownTokens() GitHub/GitLab/Slack/npm/PyPI/Stripe tokens, AWS access key IDs, JWTs (prefix-identified)

Explicit non-goals: entropy-based detection and PII heuristics. If you need those, you want a DLP scanner, not a logging library.

Performance

Mask is lock-free on the hot path: registered values (longest first) are compiled into a single-pass strings.Replacer that is swapped atomically whenever Add registers something new, so the core model costs no regexp evaluation at all. Detectors are opt-in, and each one guards its precompiled regexp behind a literal substring pre-filter, so lines that cannot contain a match never pay regexp cost.

With 50 registered secrets on a typical 95-byte log line (i5-1335U, go test -bench=. -benchmem):

BenchmarkMask-12                  5504928    213.7 ns/op    216 B/op    3 allocs/op
BenchmarkMaskWithDetectors-12     1397176   1633   ns/op    216 B/op    3 allocs/op

What logmask does not protect against

Masking is a defense in depth measure, not a substitute for not logging secrets. It cannot help with a secret that was transformed before it reached the log line (hashed, chunked across a newline, re-encoded in a form logmask does not register), and it does not touch data already written to disk or shipped to a log aggregator. Treat it as the last line of defense.

License

MIT

About

Keep secrets out of your Go logs - registry-based masking, GitHub Actions ::add-mask:: model

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages