Skip to content

A string redaction/sanitization package for Golang that provides a variety of string redaction mechanisms.

License

Notifications You must be signed in to change notification settings

kristinjeanna/redact

Repository files navigation

github.com/kristinjeanna/redact

GitHub license Last commit Build and test Latest tag Go Report Card codecov Go Reference

Package redact provides a variety of string redactor implementations. The available redactors include: simple, substring, blackout, middle, regex, url, and chain.

Table of Contents

Install

go get -u github.com/kristinjeanna/redact

Overview of redactors

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)
}

simple

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]
}

substring

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
}

blackout

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: ████ ██████ ████████ █████████ ███████████
}

middle

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
}

regex

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
}

url

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
}

chain

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
}

About

A string redaction/sanitization package for Golang that provides a variety of string redaction mechanisms.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages