Data scrubbing options for protecting sensitive data https://godoc.org/maze.io/x/scrub
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
2.6 KiB

// Package scrub offers data scrubbing options for protecting sensitive data.
package scrub
import (
"regexp"
"strings"
)
var (
// Replacement string.
Replacement = `*redacted*`
// ReplaceChar is used for equal length replacement.
ReplaceChar = '*'
)
// Common patterns
const (
reBase64 = `[0-9A-Za-z./+=,$-]`
)
// Scrubber redacts sensitive data.
type Scrubber interface {
// Scrub a string.
Scrub(string) string
}
// Scrubbers are zero or more Scrubber that act as a single scrubber.
type Scrubbers []Scrubber
// Scrub with all scrubbers, the first scrubber to alter the input will return the scrubbed output.
func (scrubbers Scrubbers) Scrub(s string) string {
for _, scrubber := range scrubbers {
if v := scrubber.Scrub(s); v != s {
return v
}
}
return s
}
// All registered scrubbers in safe evaluation order.
var All = Scrubbers{
Command,
CryptHash,
PEMDHParameters,
PEMPrivateKey,
EntropyScrubber{
Whitespace: []rune(DefaultWhitespace),
Threshold: DefaultEntropyThreshold,
},
}
// re is shorthand to compile a regular expression
func re(pattern string) *regexp.Regexp {
return regexp.MustCompile(pattern)
}
// ScrubEqualLength redacts match from in, keeping the same length.
func scrubEqualLength(in, match string) string {
var (
l = len(match)
replace = []byte(strings.Repeat(string(ReplaceChar), l))
)
copy(replace, Replacement)
// We also keep newline characters from the original string.
for i, c := range []byte(match) {
if c == '\n' || c == '\r' || c == '\t' {
replace[i] = c
if i+1 < l {
copy(replace[i+1:], Replacement)
}
}
}
// Replace output.
return strings.Replace(in, match, string(replace), 1)
}
/*
// Reader acts as an io.Reader.
func Reader(r io.Reader, scrubbers Scrubbers) io.Reader {
if len(scrubbers) == 0 {
return r
}
return reader{r, scrubbers}
}
type reader struct {
r io.Reader
s Scrubbers
}
// Read from the underlying Reader, then scrub the output buffer.
func (r reader) Read(p []byte) (n int, err error) {
if n, err = r.r.Read(p); n > 0 {
log.Printf("read %d: %q", n, p[:n])
s := string(p[:n])
if v := r.s.Scrub(s); v != s {
m := copy(p, s)
for i := m; m < n; i++ {
p[i] = byte(ReplaceChar)
}
}
}
return
}
*/
type regexpScrubber struct {
pattern *regexp.Regexp
equalLength bool
}
func (r regexpScrubber) Scrub(s string) string {
matches := r.pattern.FindStringSubmatch(s)
if len(matches) > 0 {
for _, match := range matches[1:] {
if r.equalLength {
s = scrubEqualLength(s, match)
} else {
s = strings.Replace(s, match, Replacement, 1)
}
}
}
return s
}