Help us shape the future of Go! Take the 2026 Go Developer Survey

redact

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package redact masks what this server prints for a person and must not survive being pasted somewhere else, and the secrets a job log can carry.

`status` and `doctor` print for a human, and the bug form asks for their output. An instance's host names an organization, a project path names its work, and a username or an address names a person. GitLab's error text and a request URL carry all of them by routes nobody chose. They are masked here, in one place, where the text is produced — not in a Writer, which could see an address split across two writes and miss it — so a print added later is safe without its author knowing the rule.

Index

Constants

View Source
const (
	// TokenPattern is every token shape GitLab documents, from the token
	// prefix table at https://docs.gitlab.com/security/tokens/ (read
	// 2026-09-26): personal, project, group and impersonation tokens
	// (glpat-), OAuth application secrets (gloas-), deploy tokens
	// (gldt-), runner authentication tokens (glrt-, glrtr-), CI/CD job
	// tokens (glcbt-), trigger tokens (glptt-), feed tokens (glft-),
	// incoming mail tokens (glimt-), agent for Kubernetes tokens
	// (glagent-), workspace tokens (glwt-), SCIM tokens (glsoat-),
	// feature flag client tokens (glffct-) and the session cookie
	// (_gitlab_session=). The legacy runner registration token prefix
	// GR1348941 is on GitLab's runner pages rather than that table.
	//
	// The body takes dots, because a routable token carries dotted
	// segments after the random part; a trailing sentence period is left
	// alone. An instance can change the personal access token prefix,
	// and a token with a custom prefix has no shape to find.
	TokenPattern = `\b(?:glpat|gloas|gldt|glrtr|glrt|glcbt|glptt|glft|glimt|glagent|glwt|glsoat|glffct)-[0-9A-Za-z_\-]{20,}(?:\.[0-9A-Za-z_\-]+)*` +
		`|\bGR1348941[0-9A-Za-z_\-]{20,}` +
		`|_gitlab_session=[0-9A-Za-z]+`

	// AddressPattern is an email address in free text. The local part
	// takes "…", so an address already masked here is matched whole and
	// masked to the same thing again rather than half of it surviving.
	AddressPattern = `[A-Za-z0-9._%+\-=…]+@[A-Za-z0-9.\-…]+\.[A-Za-z]{2,}`

	// ClientIDPattern is an OAuth application id as GitLab shows it: 64
	// lowercase hex characters. A SHA-1 commit id is 40 and is left
	// alone; a SHA-256 one would be masked, which is the safe direction.
	ClientIDPattern = `\b[0-9a-f]{64}\b`

	// URLPattern is an http or https URL in free text.
	URLPattern = `https?://[^\s"'<>()\[\]{}]+`
)

The shapes this package masks, as pattern text so the maintainer tooling under scripts/ matches the same ones.

View Source
const (
	KindToken    = "token"
	KindHost     = "host"
	KindPath     = "path"
	KindQuery    = "query"
	KindUser     = "user"
	KindEmail    = "email"
	KindClientID = "client-id"
	// KindID is a numeric id; it is matched as a whole number, even
	// inside a longer name.
	KindID = "id"
)

Kinds of value, as a Masker's placeholders name them.

Variables

This section is empty.

Functions

func Addresses

func Addresses(s string) string

Addresses masks every address in s with Email.

func ClientID

func ClientID(s string) string

ClientID replaces every OAuth application id shape with <client-id>.

func Clip

func Clip(s string, n int) string

Clip masks s with Text and then truncates it with Truncate. In that order: truncating first can cut a value short of its shape, and the rest of it then survives.

func Email

func Email(addr string) string

Email keeps enough of an address for its owner to recognize it and not enough for anyone else to use it. The domain goes too: for somebody else's address it names their organization.

func ID

func ID(id string) string

ID truncates an identifier to a correlation key that cannot be looked up or pasted into a URL: its first six characters. The logging rule (§9.2) allows that much and no more.

func IsCertificateError

func IsCertificateError(err error) bool

IsCertificateError reports a certificate the client refused: an unknown authority, a name that does not match, one expired or unfit.

func MaskSecrets

func MaskSecrets(text string) (string, int)

MaskSecrets replaces every secret shape in a job log with [MASKED <kind>] and returns the masked text and how many it replaced. The count goes into the result, so a reader knows the log was altered and where to look.

func NetError

func NetError(err error) string

NetError renders a transport error without the host names, addresses and URLs net, net/http and crypto/x509 put in it, which logs may not carry (§9.2): a lookup names the host it looked up, a dial the address, a request the URL, and a certificate for the wrong host names both hosts.

It is an allowlist. Only shapes whose text is known to be fixed are rendered, each in words of its own; anything else becomes a fixed phrase, because an error nobody has read may carry any of those.

func Text

func Text(s string) string

Text masks everything in s that has a shape: tokens, URLs, addresses and application ids. It is for text this server did not write, such as an error, whose values are not known in advance. Tokens go first, so nothing after them can split one and leave half behind.

func Tokens

func Tokens(s string) string

Tokens replaces every GitLab token shape with <token>.

func Truncate

func Truncate(s string, n int) string

Truncate cuts s to at most n bytes, on a rune boundary, marking the cut with "…". It masks nothing.

func URL

func URL(raw string) string

URL masks one URL: the host unless public, a namespace or project path, an API resource named by path, any user information, the query and the fragment. What GitLab uses for itself — /api/v4, /oauth — is kept, because which endpoint failed is most of a diagnosis.

func URLs

func URLs(s string) string

URLs masks every URL in s with URL.

Types

type Masker

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

A Masker replaces what identifies an instance, a project or a person with stable placeholders, {host 1}, {user 1}, {path 2}, for the reports people paste into a bug form. The same value always gets the same placeholder, so a report still reads as a story, and Footer says how many of each were hidden.

Shapes are found as Text finds them. Values that have no shape — the username `doctor` read from /user, a project path the person typed — are registered with Known and matched as whole words, case insensitively.

func NewMasker

func NewMasker() *Masker

NewMasker returns a masker with nothing registered.

func (*Masker) Count

func (m *Masker) Count() int

Count is how many distinct values have been masked.

func (*Masker) Footer

func (m *Masker) Footer() string

Footer says what was hidden, one count per kind in name order, or that nothing was.

func (*Masker) Known

func (m *Masker) Known(kind, value string)

Known registers a value of a kind to mask wherever it appears. An empty value is ignored, as is gitlab.com as a host.

func (*Masker) Text

func (m *Masker) Text(s string) string

Text masks s. Shapes first, as Text does, then registered values in the text between the placeholders already written, so a username that happens to be "host" cannot rewrite {host 1}.

Jump to

Keyboard shortcuts

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