Package redact
provides a variety of string redactor implementations. The available redactors include: simple
, substring
, blackout
, middle
, regex
, url
, and chain
.
go get -u github.com/kristinjeanna/redact
Each redactor implements the redact.Redactor
interface:
// Redactor is the common interface implemented by all redactors.
type Redactor interface {
// Redact redacts the input string and returns the result.
Redact(s string) (string, error)
}
The simple
redactor is a redactor that simply replaces an entire
string with a specified replacement string.
package main
import (
"fmt"
"log"
"github.com/kristinjeanna/redact/simple"
)
func main() {
redactor := simple.New("[redacted]")
input := "this string contains sensitive information"
result, err := redactor.Redact(input)
if err != nil {
log.Fatalf("an error occurred while redacting: %s", err)
}
fmt.Println(result)
// Output: [redacted]
}
The substring
redactor is a redactor that replaces all occurrences of the
substring in the specified string.
package main
import (
"fmt"
"log"
"github.com/kristinjeanna/redact/substring"
)
func main() {
redactor := substring.New("contains sensitive", "XXXXX")
input := "this string contains sensitive information"
result, err := redactor.Redact(input)
if err != nil {
log.Fatalf("an error occurred while redacting: %s", err)
}
fmt.Println(result)
// Output: this string XXXXX information
}
The blackout
redactor strikes out each non-whitespace character of an input
string with a specified replacement string.
The following example uses a single Unicode full block character (U+2588) to redact the input string:
package main
import (
"fmt"
"log"
"github.com/kristinjeanna/redact/blackout"
)
func main() {
redactor := blackout.New("█")
input := "this string contains sensitive information"
result, err := redactor.Redact(input)
if err != nil {
log.Fatalf("an error occurred while redacting: %s", err)
}
fmt.Println(result)
// Output: ████ ██████ ████████ █████████ ███████████
}
The middle
redactor is a redactor that replaces the middle contents of a string with a replacement string, leaving a prefix of unredacted characters and a suffix of unredacted characters if the input string is long enough. For shorter input strings, the redactor uses only a prefix or suffix or just the replacement string itself.
The following example redacts the middle portion of the string:
package main
import (
"fmt"
"log"
"github.com/kristinjeanna/redact/middle"
)
func main() {
redactor := middle.New()
input := "this string contains sensitive information"
result, err := redactor.Redact(input)
if err != nil {
log.Fatalf("an error occurred while redacting: %s", err)
}
fmt.Println(result)
// Output: thi[redacted]ion
}
The prefix and suffix default to 3 characters but this is configurable when
creating a redactor via the middle.NewFromOptions
function. Additionally,
a mode option can enable only the prefix or suffix of the input string to
remain unredacted.
package main
import (
"fmt"
"log"
m "github.com/kristinjeanna/redact/middle"
)
func main() {
redactor, err := m.NewFromOptions(
m.WithMode(PrefixOnlyMode),
m.WithReplacementText("XXXXX"),
m.WithPrefixLength(8),
)
if err != nil {
log.Fatalf("an error occurred while creating redactor: %s", err)
}
input := "this string contains sensitive information"
result, err := redactor.Redact(input)
if err != nil {
log.Fatalf("an error occurred while redacting: %s", err)
}
fmt.Println(result)
// Output: this strXXXXX
}
The regex
reactor is a redactor that performs redaction according to a slice of
redactor/regex pairs. For each match of a regex, the corresponding redactor is
executed on that match.
New pairs can be created via the NewPair()
and the
NewPairUsingSimple()
functions. NewPairUsingSimple()
is a convenience
method to create a pair using a SimpleRedactor
which associates a regex
with a replacement string. This function is also the means by which regular
expression capture groups can be used since the
(*regexp.Regexp).ReplaceAllString()
is employed by the Redact()
function
for pairs having a SimpleRedactor
. When a pair possesses a redactor other
than SimpleRedactor
, the Redact()
function invokes the redactor on
successively modified versions of the input string. Capture group may not
always work, especially in the cases where the replacement text matches against
the regex. The Redact()
method will return an error in such cases to prevent
an infinite loop.
The following example redacts the letters "i" and "s" from the input string:
package main
import (
"fmt"
"log"
"github.com/kristinjeanna/redact/regex"
)
func main() {
// a regex.Pair holds the regex and the replacement string for matches
pair, err := regex.NewPairUsingSimple("X", "[is]")
if err != nil {
log.Fatalf("an error occurred while create regex replacement pair: %s", err)
}
redactor, err := regex.New([]Pair{*pair})
if err != nil {
log.Fatalf("an error occurred while creating redactor: %s", err)
}
input = "this string contains sensitive information"
result, err := redactor.Redact(input)
if err != nil {
log.Fatalf("an error occurred while redacting: %s", err)
}
fmt.Println(result)
// Output: thXX XtrXng contaXnX XenXXtXve XnformatXon
}
The following shows an example of using capture groups:
package main
import (
"fmt"
"log"
"github.com/kristinjeanna/redact/regex"
)
func main() {
// a regex.Pair holds the regex and the replacement redactor for matches
pair, err := NewPairUsingSimple("${1}XXXX", "(b[aA][rRzZ])")
if err != nil {
log.Fatalf("an error occurred while creating the regex pair: %s", err)
}
// the redactor is constructed with a slice of Pairs
redactor, err := New([]Pair{*pair})
if err != nil {
log.Fatalf("an error occurred while creating redactor: %s", err)
}
sampleString := "foo bar baz"
result, err := redactor.Redact(sampleString)
if err != nil {
log.Fatalf("an error occurred while redacting: %s", err)
}
fmt.Println(result)
// Output: foo barXXXX bazXXXX
}
The url
redactor enables redacting a password and, optionally a
username, from a URL string.
The following example redacts the password from a MySQL connection string:
package main
import (
"fmt"
"log"
"github.com/kristinjeanna/redact/url"
)
func main() {
redactor := url.New("REDACTED", nil)
input := "mysql://user:password@localhost:3306"
result, err := redactor.Redact(input)
if err != nil {
log.Fatalf("an error occurred while redacting: %s", err)
}
fmt.Println(result)
// Output: mysql://user:REDACTED@localhost:3306
}
The chain
redactor consists of a slice of redactors that each redact an
input string in the order that they appear in the slice.
The following example chains a substring
redactor with a
regex
redactor:
package main
import (
"fmt"
"log"
"github.com/kristinjeanna/redact"
"github.com/kristinjeanna/redact/chain"
"github.com/kristinjeanna/redact/regex"
"github.com/kristinjeanna/redact/substring"
)
func main() {
substringRedactor := substring.New("contains", "HIDES")
regexPair, err := regex.NewPairUsingSimple(" [redacted] ", `(?i)\ss\w*\s`)
if err != nil {
log.Fatalf("an error occurred while creating regex pair: %s", err)
}
regexRedactor, err := regex.New([]regex.Pair{*regexPair})
if err != nil {
log.Fatalf("an error occurred while creating regex redactor: %s", err)
}
chainRedactor := chain.New([]redact.Redactor{substringRedactor, regexRedactor})
input := "this string contains sensitive information"
result, err := chainRedactor.Redact(input)
if err != nil {
log.Fatalf("an error occurred while redacting: %s", err)
}
fmt.Println(result)
// Output: this [redacted] HIDES [redacted] information
}