Skip to content

Commit

Permalink
feat_: SensitiveString type
Browse files Browse the repository at this point in the history
  • Loading branch information
igor-sirotin committed Dec 10, 2024
1 parent 8b95c81 commit 8b507f8
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 0 deletions.
54 changes: 54 additions & 0 deletions internal/security/sensitive_string.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package security

import (
"encoding/json"
)

const redactionPlaceholder = "***"

// SensitiveString is a type for handling sensitive information securely.
// This helps to achieve the following goals:
// 1. Prevent accidental logging of sensitive information.
// 2. Provide controlled visibility (e.g., redacted output for String() or MarshalJSON()).
// 3. Enable controlled access to the sensitive value when needed.
type SensitiveString struct {
value string
}

// NewSensitiveString creates a new SensitiveString
func NewSensitiveString(value string) *SensitiveString {
return &SensitiveString{value: value}
}

// SetValue updates the sensitive string value.
func (s *SensitiveString) SetValue(value string) {
s.value = value
}

// String provides a redacted version of the sensitive string
func (s *SensitiveString) String() string {
if s.value == "" {
return ""
}
return redactionPlaceholder
}

// MarshalJSON ensures that sensitive strings are redacted when marshaled to JSON
func (s *SensitiveString) MarshalJSON() ([]byte, error) {
return json.Marshal(s.String())
}

// UnmarshalJSON implements unmarshalling a sensitive string from JSON
func (s *SensitiveString) UnmarshalJSON(data []byte) error {
var value string
if err := json.Unmarshal(data, &value); err != nil {
return err
}
s.value = value
return nil
}

// Reveal exposes the sensitive value (use with caution)
func (s *SensitiveString) Reveal() string {
return s.value
}
51 changes: 51 additions & 0 deletions internal/security/sensitive_string_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package security

import (
"encoding/json"
"testing"

"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/require"
)

func TestNewSensitiveString(t *testing.T) {
secretValue := gofakeit.LetterN(10)
s := NewSensitiveString(secretValue)
require.Equal(t, secretValue, s.Reveal())
}

func TestStringRedaction(t *testing.T) {
secretValue := gofakeit.LetterN(10)
s := NewSensitiveString(secretValue)
require.Equal(t, redactionPlaceholder, s.String())
}

func TestEmptyStringRedaction(t *testing.T) {
s := NewSensitiveString("")
require.Equal(t, "", s.String())
}

func TestMarshalJSON(t *testing.T) {
secretValue := gofakeit.LetterN(10)
s := NewSensitiveString(secretValue)
data, err := json.Marshal(s)
require.NoError(t, err)
require.JSONEq(t, `"`+redactionPlaceholder+`"`, string(data))
}

func TestUnmarshalJSON(t *testing.T) {
secretValue := gofakeit.LetterN(10)
data := `"` + secretValue + `"`
var s SensitiveString
err := json.Unmarshal([]byte(data), &s)
require.NoError(t, err)
require.Equal(t, secretValue, s.Reveal())
}

func TestSetValue(t *testing.T) {
initialValue := gofakeit.LetterN(10)
newValue := gofakeit.LetterN(10)
s := NewSensitiveString(initialValue)
s.SetValue(newValue)
require.Equal(t, newValue, s.Reveal())
}

0 comments on commit 8b507f8

Please sign in to comment.