Documentation
¶
Overview ¶
Package redaction removes detected secrets from JSON documents before they leave the machine.
Detection and rewriting are deliberately split. Secrets are detected against the document rendered as indented JSON, because the patterns that catch generic credentials rely on seeing a key next to its value. The rewrite then happens structurally, on the decoded value tree, so the output cannot be malformed JSON. A convergence loop re-scans after every rewrite, which makes idempotency a property of the algorithm rather than of the placeholder format.
Index ¶
Constants ¶
const DefaultMaxPasses = 4
DefaultMaxPasses bounds the convergence loop. A successful redaction costs two passes: one that finds the secrets and one that confirms none are left.
Redaction is deliberately unbounded in size and time. A session that cannot be scanned cannot be stored, and failing an attestation over a large-but-honest transcript is worse than taking a while over it. Callers that need a bound can impose one through the context they pass to Redact.
Variables ¶
var ( // ErrNotConverged is returned when repeated passes keep detecting secrets, // which in practice means a placeholder is itself matching a rule. ErrNotConverged = errors.New("secret redaction did not converge") // ErrInvalidJSON is returned when the document is not a single JSON object. ErrInvalidJSON = errors.New("document is not a JSON object") // ErrDuplicateKey is returned when an object repeats a key. Decoding keeps // only the last value for a repeated key, so a secret in an earlier one would // never be scanned; redaction refuses the document rather than pass it // through unexamined. ErrDuplicateKey = errors.New("document contains a duplicate object key") )
Functions ¶
func DefaultPlaceholder ¶
DefaultPlaceholder is the replacement text for a redacted secret. It is deterministic, so redacting the same document twice yields the same digest, and it names the rule so a reviewer can tell what kind of credential was present without being able to recover it.
func IsDefaultPlaceholder ¶
IsDefaultPlaceholder reports whether s is a placeholder DefaultPlaceholder could have produced.
Types ¶
type Finding ¶
type Finding struct {
// RuleID names the rule that matched. It ends up in the placeholder, so it
// must never contain secret material.
RuleID string
// Secret is the matched credential, verbatim as it appeared in the scanned
// text.
Secret string
}
Finding is a located secret. It is deliberately decoupled from any particular scanning engine's types.
type Option ¶
type Option func(*Redactor)
Option customises a Redactor.
func WithPathFilter ¶
func WithPathFilter(f PathFilter) Option
WithPathFilter restricts which string leaves may be rewritten. The default allows every leaf.
func WithPlaceholder ¶
WithPlaceholder overrides the replacement text derived from a rule id.
The two functions are supplied together on purpose. Some rules match a position rather than a value, so they report a placeholder as though it were a secret; recognising our own output is what stops redaction from rewriting it and keeps redacting an already-redacted document a no-op. A format without a matching recogniser would silently lose that.
type PathFilter ¶
PathFilter reports whether the string leaf at the given path may be rewritten. Paths look like "/data/raw_session/main/0/content": a leading slash, object keys and array indices separated by slashes.
type Redactor ¶
type Redactor struct {
// contains filtered or unexported fields
}
Redactor rewrites JSON documents, replacing detected secrets with placeholders. It is safe for concurrent use as long as the Scanner is.
type Report ¶
type Report struct {
// Replacements is the total number of substitutions performed.
Replacements int
// ByRule counts substitutions per rule id.
ByRule map[string]int
// Unlocated counts, per rule id, secrets the scanner reported but which
// could not be attributed to any eligible leaf. That happens when the match
// lies in a path the filter protects, or when it spans the JSON punctuation
// between two adjacent leaves.
Unlocated map[string]int
// Passes is the number of detection passes performed.
Passes int
}
Report summarises a Redact call. It never contains secret material, so it is safe to log and to surface as material annotations.
type Scanner ¶
Scanner detects secrets in a text fragment.
func DefaultScanner ¶
DefaultScanner returns the process-wide betterleaks-backed scanner. Constructing a detector compiles several hundred regexes and builds a keyword trie, so it is built once, lazily, and only for attestations that need it.