Skip to content

redact

A small, zero-dependency helper that strips credential-like content from free-form strings at the boundary between your process and untrusted observability surfaces — telemetry vendors, log aggregators, metric stores.

redact is the same sanitiser behind go-tool-base's telemetry and logging paths, extracted so any project can scrub secrets out of error messages, command arguments, and HTTP header values on their way out — without pulling in a framework.

import "gitlab.com/phpboyscout/go/redact"

The boundary-redaction idea

Error messages, command-line flags, and HTTP headers carry credentials by accident all the time: an HTTP client wraps a URL with an embedded token, a --api-key=sk-abc123 flag lands in os.Args, an OTLP export error quotes an Authorization header. The instant that content reaches a third party it is outside your control — replicated, indexed, and retained longer than you intended.

The defence is to redact at the boundary: run untrusted strings through String in-process, immediately before they are shipped to a surface where a leaked credential would be harmful. Redact on the way out — not on local, host-only debug logs that never leave the machine and may need the raw content.

The zero-dependency promise

The import graph is deliberately empty. The package is pure standard library (regexp and strings), so nothing external enters your build when you depend on redact — the only third-party module in go.mod is a test-only assertion library that never ships in your binary. A depfootprint_test.go guard fails the build if a framework, cloud SDK, or telemetry dependency ever creeps into the graph.

Every pattern is compiled once at package init and uses Go's RE2 engine (no backtracking), so matching is linear in the length of the input — there is no ReDoS exposure even on adversarial strings.

Quick start

package main

import (
    "fmt"

    "gitlab.com/phpboyscout/go/redact"
)

func main() {
    raw := "request to https://admin:hunter2@api.example.com/v1?api_key=sk-abc123XYZ failed"

    fmt.Println(redact.String(raw))
    // request to https://<redacted>@api.example.com/v1?api_key=*** failed
}

The URL userinfo, the api_key query parameter, and the sk- token are each masked, while the shape of the string — enough to debug with — survives.

The API

The public surface is four symbols. Nothing else is exported.

Symbol Purpose
func String(s string) string Redact a free-form string; idempotent, never panics, returns the input unchanged when nothing matches.
func Error(err error) string String(err.Error()), but nil-safe — returns "" for a nil error.
var SensitiveHeaderKeys []string The canonical list of HTTP header names whose values should be redacted before logging.
func IsSensitiveHeaderKey(name string) bool Reports whether a header name looks credential-bearing (case-insensitive).

Design

  • Zero dependencies. Pure standard library; a depfootprint_test.go guard enforces it.
  • Boundary redaction. Sanitise where data leaves the host, not everywhere.
  • Conservative by default. The opaque-token fallback requires ≥ 41 characters, so it never false-positives on UUIDs, MD5, or SHA-1 hashes.
  • Linear-time and safe. RE2 patterns, no backtracking; String is idempotent and provably non-panicking (fuzz-guaranteed).

Install

go get gitlab.com/phpboyscout/go/redact

Where to go next

The documentation follows the Diátaxis framework:

Not a silver bullet. Pattern catalogues never reach 100 % recall. A credential in a bespoke, non-standard format will slip through — so anywhere you handle such inputs, redact upstream too. The rule catalogue sets out exactly what is and is not caught.