Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare v3 release #35

Merged
merged 7 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,32 @@
[![Build Status](https://github.com/rsjethani/secret/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/rsjethani/secret/actions)
[![Go Report Card](https://goreportcard.com/badge/github.com/rsjethani/secret)](https://goreportcard.com/report/github.com/rsjethani/secret)

# secret v2

### What secret is?
- It provides simple Go types like [secret.Text](https://pkg.go.dev/github.com/rsjethani/secret/v2#Text) to encapsulate your secret. Example:
# Installation
```
go get github.com/rsjethani/secret/v3
```

# What secret is?
It provides simple Go types like `Text` to securely store secrets. For example:
```go
type Login struct {
User string
Password secret.Text
}
```
- The encapsulated secret remains inaccessible to operations like printing, logging, JSON serializtion etc. A (customizable) redact hint like `*****` is returned instead.
- The only way to access the actual secret value is by asking explicitly via the `.Value()` method.
- See [godev reference](https://pkg.go.dev/github.com/rsjethani/secret/v2#pkg-examples) for usage examples.
The encapsulated secret remains inaccessible to operations like printing, logging, JSON serialization etc. A (customizable) redact hint like `*****` is returned instead. The only way to access the actual secret value is by asking explicitly via the `.Secret()` method. See package documentation more reference and examples.

### What secret is not?
# What secret is not?
- It is not a secret management service or your local password manager.
- It is not a Go client to facilitate communication with secret managers like Hashicorp Vault, AWS secret Manager etc. Checkout [teller](https://github.com/spectralops/teller) if that is what you are looking for.

### Installation
```
go get github.com/rsjethani/secret/v2
```
NOTE: v1 is deprecated now.
# Versions

### v3
Current version, same functionality as v2 but much cleaner API.

### v2
Perfectly usable but no longer maintained.

### v1
Deprecated.
27 changes: 10 additions & 17 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,22 @@ import (
"github.com/rsjethani/secret/v2"
)

func ExampleText() {
s := secret.Text{}
fmt.Println(s, s.Value())

// Output: *****
}

func ExampleNew() {
s := secret.New("$ecre!")
fmt.Println(s, s.Value())
fmt.Println(s, s.Secret())

// Output: ***** $ecre!
}

func ExampleRedactHint() {
s := secret.New("$ecre!", secret.RedactHint(secret.FiveX))
fmt.Println(s, s.Value())
func ExampleRedactAs() {
s := secret.New("$ecre!", secret.RedactAs(secret.FiveX))
fmt.Println(s, s.Secret())

s = secret.New("$ecre!", secret.RedactHint(secret.Redacted))
fmt.Println(s, s.Value())
s = secret.New("$ecre!", secret.RedactAs(secret.Redacted))
fmt.Println(s, s.Secret())

s = secret.New("$ecre!", secret.RedactHint("my redact hint"))
fmt.Println(s, s.Value())
s = secret.New("$ecre!", secret.RedactAs("my redact hint"))
fmt.Println(s, s.Secret())

// Output:
// XXXXX $ecre!
Expand Down Expand Up @@ -68,7 +61,7 @@ func ExampleText_UnmarshalText() {
}

fmt.Printf("%+v\n", login)
fmt.Println(login.Password.Value())
fmt.Println(login.Password.Secret())

// Output:
// {User:John Password:*****}
Expand All @@ -77,7 +70,7 @@ func ExampleText_UnmarshalText() {

func ExampleEqual() {
tx1 := secret.New("hello")
tx2 := secret.New("hello", secret.RedactHint(secret.Redacted))
tx2 := secret.New("hello", secret.RedactAs(secret.Redacted))
tx3 := secret.New("world")
fmt.Println(secret.Equal(tx1, tx2))
fmt.Println(secret.Equal(tx1, tx3))
Expand Down
54 changes: 26 additions & 28 deletions secret.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
// Package secret provides types to guard your secret values from leaking into logs, std* etc.
//
// The objective is to disallow writing/serializing of secret values to std*, logs, JSON string
// etc. but provide access to the secret when requested explicitly.
package secret

// Text provides a way to safely store your secret value and a corresponding redact hint. This
// redact hint is what is used in operations like printing and serializing.
// Text provides a way to safely store your secret string until you actually need it. Operations
// like printing and serializing see a proxy/redact string there by avoiding leaking the secret.
// Once created, the instance is readonly except for the [Text.UnmarshalText] operation, but that
// too only modifies the local copy. Hence the type is concurrent safe.
type Text struct {
secret *string
redact *string
}

// New returns [Text] for the secret with [FiveStar] as the default redact hint. Provide options
// like [RedactHint] to modify default behavior.
// New returns [Text] for the secret with [FiveStar] as the default redact string. Provide options
// like [RedactAs] to modify default behavior.
func New(secret string, options ...func(*Text)) Text {
tx := Text{
secret: new(string),
Expand All @@ -29,36 +27,36 @@ func New(secret string, options ...func(*Text)) Text {
return tx
}

// Some common redact hints.
// RedactAs is a functional option to set r as the redact string for [Text]. You can use one of
// the common redact strings provided with this package like [FiveX] or provide your own.
func RedactAs(r string) func(*Text) {
return func(t *Text) {
*t.redact = r
}
}

// Some common redact strings.
const (
FiveX string = "XXXXX"
FiveStar string = "*****"
Redacted string = "[REDACTED]"
)

// RedactHint is a functional option to set r as the redact hint for the [Text]. You can use one of
// the common redact hints provided with this package like [FiveX] or provide your own string.
func RedactHint(r string) func(*Text) {
return func(t *Text) {
*t.redact = r
}
}

// String implements the [fmt.Stringer] interface and returns only the redact hint. This prevents the
// secret value from being printed to std*, logs etc.
// String implements the [fmt.Stringer] interface and returns only the redact string. This prevents
// the actual secret string from being sent to std*, logs etc.
func (tx Text) String() string {
if tx.redact == nil {
return FiveStar
if tx.redact != nil {
return *tx.redact
}
return *tx.redact
return FiveStar
}

// Value gives you access to the actual secret value stored inside Text.
func (tx Text) Value() string {
if tx.secret == nil {
return ""
// Secret returns the actual secret string stored inside [Text].
func (tx Text) Secret() string {
if tx.secret != nil {
return *tx.secret
}
return *tx.secret
return ""
}

// MarshalText implements [encoding.TextMarshaler]. It marshals redact string into bytes rather than
Expand All @@ -74,7 +72,7 @@ func (tx *Text) UnmarshalText(b []byte) error {

// If the original redact is not nil then use it otherwise fallback to default.
if tx.redact != nil {
*tx = New(s, RedactHint(*tx.redact))
*tx = New(s, RedactAs(*tx.redact))
} else {
*tx = New(s)
}
Expand Down
Loading