privacy

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 15, 2025 License: Apache-2.0 Imports: 8 Imported by: 0

README

Privacy Package

A flexible data masking library for secure handling of sensitive information in Go applications.

Installation

go get github.com/dmitrymomot/gokit/privacy

Overview

The privacy package provides tools for masking sensitive data in Go applications. It offers a comprehensive set of strategies for data masking, including specialized implementations for common sensitive data types (emails, credit cards, phones) and a registry system for easy management of masking rules. The package is designed to be thread-safe, flexible, and easy to integrate into existing applications.

Features

  • Multiple masking strategies (redaction, partial visibility, tokenization, pseudonymization)
  • Type-specific maskers for common sensitive data types (emails, credit cards, phones)
  • Category-based registry for organized masking rules
  • Auto-detection of sensitive data patterns
  • Flexible configuration via functional options
  • Thread-safe implementation for concurrent usage
  • Comprehensive error handling with specific error types

Usage

Basic String Masking
import (
	"context"
	"fmt"
	"github.com/dmitrymomot/gokit/privacy"
)

func main() {
	// Create a string masker that shows first and last character
	masker, err := privacy.NewStringMasker(
		privacy.WithStrategy(privacy.StrategyPartialMask),
		privacy.WithReplacement('*'),
		privacy.WithVisibleChars(1, 1),
	)
	if err != nil {
		panic(err)
	}
	
	// Mask a sensitive string
	ctx := context.Background()
	result, err := masker.Mask(ctx, "SecretPassword")
	if err != nil {
		panic(err)
	}
	
	fmt.Println(result) // Output: S***********d
}
Email Masking
// Create an email masker with custom configuration
masker, err := privacy.NewEmailMasker(
	privacy.WithEmailReplacement('*'),      // Character for masking
	privacy.WithVisibleLocalChars(2),       // Show first 2 chars of local part
	privacy.WithVisibleDomainChars(0),      // Hide domain (except extension)
	privacy.WithShowDomainExt(true),        // Show domain extension
)
if err != nil {
	panic(err)
}

// Mask an email address
ctx := context.Background()
result, err := masker.Mask(ctx, "john.doe@example.com")
if err != nil {
	panic(err)
}

fmt.Println(result) // Output: jo******@example.com
Credit Card Masking
// Create a credit card masker (PCI-DSS compliant)
masker, err := privacy.NewCardMasker(
	privacy.WithCardReplacement('*'),       // Character for masking
	privacy.WithVisibleEndDigits(4),        // Show last 4 digits
	privacy.WithCardFormatting(true),       // Preserve formatting
)
if err != nil {
	panic(err)
}

// Mask a credit card number
ctx := context.Background()
result, err := masker.Mask(ctx, "4111 1111 1111 1111")
if err != nil {
	panic(err)
}

fmt.Println(result) // Output: **** **** **** 1111

// Also handles special formats like AMEX
amexResult, _ := masker.Mask(ctx, "3782 822463 10005")
fmt.Println(amexResult) // Output: **** ***** *0005
Using the Masking Registry
// Create a masking registry with default fallback masker
defaultMasker, _ := privacy.NewStringMasker(
	privacy.WithStrategy(privacy.StrategyRedact),
)
registry := privacy.NewMaskingRegistry(defaultMasker)

// Register specialized maskers for different data categories
emailMasker, _ := privacy.NewEmailMasker()
cardMasker, _ := privacy.NewCardMasker()
phoneMasker, _ := privacy.NewPhoneMasker()

registry.RegisterMasker(privacy.CategoryEmail, emailMasker)
registry.RegisterMasker(privacy.CategoryCreditCard, cardMasker)
registry.RegisterMasker(privacy.CategoryPhone, phoneMasker)

// Mask data by category
ctx := context.Background()
emailResult, _ := registry.MaskByCategory(ctx, privacy.CategoryEmail, "user@example.com")
cardResult, _ := registry.MaskByCategory(ctx, privacy.CategoryCreditCard, "4111 1111 1111 1111")
phoneResult, _ := registry.MaskByCategory(ctx, privacy.CategoryPhone, "(555) 123-4567")

fmt.Println(emailResult)  // Masked email output
fmt.Println(cardResult)   // Masked card output
fmt.Println(phoneResult)  // Masked phone output
Auto-Detection and Masking
import (
	"context"
	"fmt"
	"regexp"
	"github.com/dmitrymomot/gokit/privacy"
)

// Create auto-masking registry
registry := privacy.NewAutoMaskingRegistry(nil)

// Register maskers
emailMasker, _ := privacy.NewEmailMasker()
cardMasker, _ := privacy.NewCardMasker()
phoneMasker, _ := privacy.NewPhoneMasker()

registry.RegisterMasker(privacy.CategoryEmail, emailMasker)
registry.RegisterMasker(privacy.CategoryCreditCard, cardMasker)
registry.RegisterMasker(privacy.CategoryPhone, phoneMasker)

// Register detection rules using regex patterns
emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
registry.RegisterDetectionRule(privacy.CategoryEmail, func(data any) bool {
	str, ok := data.(string)
	return ok && emailRegex.MatchString(str)
})

// Define and register other detection rules...

// Auto-detect and mask data
ctx := context.Background()
email := "user@example.com"
card := "4111 1111 1111 1111"
phone := "+1 (555) 123-4567"

emailResult, _ := registry.AutoMask(ctx, email)
cardResult, _ := registry.AutoMask(ctx, card)
phoneResult, _ := registry.AutoMask(ctx, phone)

fmt.Println(emailResult)  // Auto-detected and masked as email
fmt.Println(cardResult)   // Auto-detected and masked as credit card
fmt.Println(phoneResult)  // Auto-detected and masked as phone number

Best Practices

  1. Security Considerations:

    • Always sanitize logs and outputs containing sensitive information
    • Choose appropriate masking strategies based on data sensitivity
    • Don't rely solely on masking for highly sensitive data (consider encryption)
    • Regularly review and update detection patterns for evolving data formats
  2. Performance Optimization:

    • Cache masker instances for reuse across requests
    • Consider using the registry pattern to organize maskers for complex applications
    • For high-volume applications, limit auto-detection to essential cases
    • Pre-register all required maskers during application initialization
  3. Error Handling:

    • Always check for errors when masking sensitive data
    • Use the predefined error types to handle specific failure cases
    • Log masking failures appropriately without revealing sensitive data
    • Implement graceful fallbacks when masking fails
  4. Configuration Best Practices:

    • Use default maskers for simple cases
    • Customize maskers based on your application's security requirements
    • Follow industry standards (e.g., PCI-DSS for credit cards)
    • Document which masking strategies are used for different data types

API Reference

Masker Interface
// Masker defines the interface for all masking operations
type Masker interface {
	Mask(ctx context.Context, data any) (any, error)
	CanMask(data any) bool
}
Masking Strategies
// Available masking strategies
const (
	StrategyRedact      MaskingStrategy = "redact"       // Complete replacement
	StrategyPartialMask MaskingStrategy = "partial"      // Show portions of data
	StrategyTokenize    MaskingStrategy = "tokenize"     // Replace with token
	StrategyPseudonymize MaskingStrategy = "pseudonymize" // Replace with pseudonym
	StrategyEncrypt     MaskingStrategy = "encrypt"      // Encrypt the data
	StrategyNoise       MaskingStrategy = "noise"        // Add statistical noise
)
Data Categories
// Predefined data categories
const (
	// General categories
	CategoryPII           DataCategory = "pii"
	CategoryFinancial     DataCategory = "financial"
	CategoryHealth        DataCategory = "health"
	CategoryCredentials   DataCategory = "credentials"
	CategoryLocation      DataCategory = "location"
	CategoryCommunication DataCategory = "communication"
	
	// Specific data categories
	CategoryCreditCard    DataCategory = "credit_card"
	CategoryEmail         DataCategory = "email"
	CategoryPhone         DataCategory = "phone"
	CategorySSN           DataCategory = "ssn"
	CategoryName          DataCategory = "name"
	CategoryAddress       DataCategory = "address"
	CategoryPassport      DataCategory = "passport"
	CategoryDriverLicense DataCategory = "driver_license"
)
Registry Interfaces
// MaskerRegistry manages multiple maskers by category
type MaskerRegistry interface {
	RegisterMasker(category DataCategory, masker Masker) error
	GetMasker(category DataCategory) (Masker, error)
	MaskByCategory(ctx context.Context, category DataCategory, data any) (any, error)
}

// AutoMaskingRegistry extends MaskingRegistry with detection capabilities
type AutoMaskingRegistry struct {
	*MaskingRegistry
	detectionRules map[DataCategory]DetectionRule
}

// DetectionRule is used to detect if data belongs to a specific category
type DetectionRule func(data any) bool
Error Types
// Common error types for precise error handling
var (
	// General masking errors
	ErrInvalidData         = errors.New("privacy: invalid data for masking")
	ErrInvalidMask         = errors.New("privacy: invalid mask configuration")
	ErrUnsupportedType     = errors.New("privacy: unsupported data type")
	ErrMaskingFailed       = errors.New("privacy: masking operation failed")
	
	// Registry errors
	ErrMaskerNotFound      = errors.New("privacy: masker not found for category")
	ErrInvalidMasker       = errors.New("privacy: invalid masker")
	ErrInvalidRule         = errors.New("privacy: invalid detection rule")
	ErrRuleNotFound        = errors.New("privacy: detection rule not found")
	ErrCategoryNotDetected = errors.New("privacy: data category could not be detected")
	
	// Configuration errors
	ErrInvalidRegex        = errors.New("privacy: invalid regex pattern")
	ErrNegativeCharCount   = errors.New("privacy: visible character count cannot be negative")
	ErrNegativeDigitCount  = errors.New("privacy: visible digit count cannot be negative")
	ErrTooManyVisibleDigits = errors.New("privacy: showing more than 4 digits is not allowed for security reasons")
	ErrNegativeMinLength   = errors.New("privacy: minimum length cannot be negative")
)
Available Maskers
// Create specialized maskers with their configuration options
func NewStringMasker(options ...Option) (*StringMasker, error)
func NewEmailMasker(options ...Option) (*EmailMasker, error)
func NewCardMasker(options ...Option) (*CardMasker, error)
func NewPhoneMasker(options ...Option) (*PhoneMasker, error)
Registry Creation
// Create new registries
func NewMaskingRegistry(defaultMasker Masker) *MaskingRegistry
func NewAutoMaskingRegistry(defaultMasker Masker) *AutoMaskingRegistry

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidData indicates that the provided data is invalid for masking.
	ErrInvalidData = errors.New("privacy: invalid data for masking")

	// ErrInvalidMask indicates that the masking configuration is invalid.
	ErrInvalidMask = errors.New("privacy: invalid mask configuration")

	// ErrUnsupportedType indicates the provided data type is not supported by the masker.
	ErrUnsupportedType = errors.New("privacy: unsupported data type")

	// ErrInvalidRegex indicates a regex pattern is invalid.
	ErrInvalidRegex = errors.New("privacy: invalid regex pattern")

	// ErrMaskingFailed indicates a general failure during the masking process.
	ErrMaskingFailed = errors.New("privacy: masking operation failed")

	// ErrMaskerNotFound indicates that no masker exists for the requested category.
	ErrMaskerNotFound = errors.New("privacy: masker not found for category")

	// ErrInvalidMasker indicates that the provided masker is invalid or nil.
	ErrInvalidMasker = errors.New("privacy: invalid masker")

	// ErrInvalidRule indicates that the provided detection rule is invalid or nil.
	ErrInvalidRule = errors.New("privacy: invalid detection rule")

	// ErrRuleNotFound indicates that no detection rule exists for the requested category.
	ErrRuleNotFound = errors.New("privacy: detection rule not found")

	// ErrCategoryNotDetected indicates that the data category could not be automatically detected.
	ErrCategoryNotDetected = errors.New("privacy: data category could not be detected")

	// ErrNegativeCharCount indicates that a visible character count cannot be negative.
	ErrNegativeCharCount = errors.New("privacy: visible character count cannot be negative")

	// ErrNegativeDigitCount indicates that a visible digit count cannot be negative.
	ErrNegativeDigitCount = errors.New("privacy: visible digit count cannot be negative")

	// ErrTooManyVisibleDigits indicates that showing too many digits is a security risk.
	ErrTooManyVisibleDigits = errors.New("privacy: showing more than 4 digits is not allowed for security reasons")

	// ErrNegativeMinLength indicates that a minimum length cannot be negative.
	ErrNegativeMinLength = errors.New("privacy: minimum length cannot be negative")

	// ErrPartialMaskRequiresVisibleChars indicates that partial masking requires visible characters.
	ErrPartialMaskRequiresVisibleChars = errors.New("privacy: partial masking requires at least some visible characters")

	// ErrInvalidEmailFormat indicates that the email format is invalid.
	ErrInvalidEmailFormat = errors.New("privacy: invalid email format")

	// ErrInvalidCardNumberLength indicates that a credit card number length is invalid.
	ErrInvalidCardNumberLength = errors.New("privacy: invalid credit card number length")

	// ErrNilMasker indicates that a masker cannot be nil.
	ErrNilMasker = errors.New("privacy: masker cannot be nil")

	// ErrNilDetectionRule indicates that a detection rule cannot be nil.
	ErrNilDetectionRule = errors.New("privacy: detection rule cannot be nil")
)

Predefined errors for the privacy package.

Functions

This section is empty.

Types

type AutoMaskingRegistry

type AutoMaskingRegistry struct {
	*MaskingRegistry
	// contains filtered or unexported fields
}

AutoMaskingRegistry extends MaskingRegistry with automatic category detection.

func NewAutoMaskingRegistry

func NewAutoMaskingRegistry(defaultMasker Masker) *AutoMaskingRegistry

NewAutoMaskingRegistry creates a new AutoMaskingRegistry with optional default masker.

func (*AutoMaskingRegistry) AutoMask

func (r *AutoMaskingRegistry) AutoMask(ctx context.Context, data any) (any, error)

AutoMask automatically detects the category of the data and applies the appropriate masker.

func (*AutoMaskingRegistry) DetectCategory

func (r *AutoMaskingRegistry) DetectCategory(data any) (DataCategory, bool)

DetectCategory attempts to detect the category of the given data.

func (*AutoMaskingRegistry) RegisterDetectionRule

func (r *AutoMaskingRegistry) RegisterDetectionRule(category DataCategory, rule DetectionRule) error

RegisterDetectionRule registers a detection rule for a specific data category.

func (*AutoMaskingRegistry) UnregisterDetectionRule

func (r *AutoMaskingRegistry) UnregisterDetectionRule(category DataCategory) error

UnregisterDetectionRule removes a detection rule for a specific data category.

type CardMasker

type CardMasker struct {
	// contains filtered or unexported fields
}

CardMasker implements a specialized masker for credit card numbers.

func NewCardMasker

func NewCardMasker(opts ...CardMaskerOption) (*CardMasker, error)

NewCardMasker creates a new CardMasker with the given options.

func (*CardMasker) CanMask

func (cm *CardMasker) CanMask(data any) bool

CanMask checks if the masker can mask the given data.

func (*CardMasker) Mask

func (cm *CardMasker) Mask(ctx context.Context, data any) (any, error)

Mask applies the masking strategy to the given credit card number.

type CardMaskerOption

type CardMaskerOption func(*CardMasker) error

CardMaskerOption is a function that configures a CardMasker.

func WithCardFormatting

func WithCardFormatting(format bool) CardMaskerOption

WithCardFormatting enables or disables formatting with spaces.

func WithCardReplacement

func WithCardReplacement(char rune) CardMaskerOption

WithCardReplacement sets the character used for masking card numbers.

func WithVisibleEndDigits

func WithVisibleEndDigits(count int) CardMaskerOption

WithVisibleEndDigits sets the number of digits to keep visible at the end.

type DataCategory

type DataCategory string

DataCategory represents the category of data for classification and policy enforcement.

const (
	// CategoryPII represents Personally Identifiable Information.
	CategoryPII DataCategory = "pii"

	// CategoryFinancial represents financial information like credit card numbers.
	CategoryFinancial DataCategory = "financial"

	// CategoryHealth represents health-related information (e.g., HIPAA in the US).
	CategoryHealth DataCategory = "health"

	// CategoryCredentials represents authentication credentials.
	CategoryCredentials DataCategory = "credentials"

	// CategoryLocation represents geolocation data.
	CategoryLocation DataCategory = "location"

	// CategoryCommunication represents communication data like emails, phone numbers.
	CategoryCommunication DataCategory = "communication"

	// CategoryCreditCard represents credit card information.
	CategoryCreditCard DataCategory = "credit_card"

	// CategoryEmail represents email addresses.
	CategoryEmail DataCategory = "email"

	// CategoryPhone represents phone numbers.
	CategoryPhone DataCategory = "phone"

	// CategorySSN represents Social Security Numbers.
	CategorySSN DataCategory = "ssn"

	// CategoryName represents personal names.
	CategoryName DataCategory = "name"

	// CategoryAddress represents physical addresses.
	CategoryAddress DataCategory = "address"

	// CategoryPassport represents passport numbers.
	CategoryPassport DataCategory = "passport"

	// CategoryDriverLicense represents driver's license numbers.
	CategoryDriverLicense DataCategory = "driver_license"
)

Predefined data categories.

type DetectionRule

type DetectionRule func(data any) bool

DetectionRule is a function that detects if a piece of data belongs to a specific category.

type EmailMasker

type EmailMasker struct {
	// contains filtered or unexported fields
}

EmailMasker implements a specialized masker for email addresses.

func NewEmailMasker

func NewEmailMasker(opts ...EmailMaskerOption) (*EmailMasker, error)

NewEmailMasker creates a new EmailMasker with the given options.

func (*EmailMasker) CanMask

func (em *EmailMasker) CanMask(data any) bool

CanMask checks if the masker can mask the given data.

func (*EmailMasker) Mask

func (em *EmailMasker) Mask(ctx context.Context, data any) (any, error)

Mask applies the masking strategy to the given email address.

type EmailMaskerOption

type EmailMaskerOption func(*EmailMasker) error

EmailMaskerOption is a function that configures an EmailMasker.

func WithEmailReplacement

func WithEmailReplacement(char rune) EmailMaskerOption

WithEmailReplacement sets the character used for masking email addresses.

func WithShowDomainExt

func WithShowDomainExt(show bool) EmailMaskerOption

WithShowDomainExt determines if the domain extension should be visible.

func WithVisibleDomainChars

func WithVisibleDomainChars(count int) EmailMaskerOption

WithVisibleDomainChars sets the number of characters to keep visible at the start of the domain.

func WithVisibleLocalChars

func WithVisibleLocalChars(count int) EmailMaskerOption

WithVisibleLocalChars sets the number of characters to keep visible at the start of the local part.

type Masker

type Masker interface {
	// Mask applies the masking strategy to the given data.
	// It returns the masked data or an error if masking fails.
	Mask(ctx context.Context, data any) (any, error)

	// CanMask checks if the masker can handle the given data type.
	// Returns true if the data can be masked, false otherwise.
	CanMask(data any) bool
}

Masker is the interface that all data masking implementations must implement.

type MaskerRegistry

type MaskerRegistry interface {
	// RegisterMasker adds a masker for a specific data category.
	RegisterMasker(category DataCategory, masker Masker) error

	// GetMasker retrieves a masker for a specific data category.
	GetMasker(category DataCategory) (Masker, error)

	// MaskByCategory masks data according to its category.
	MaskByCategory(ctx context.Context, category DataCategory, data any) (any, error)
}

MaskerRegistry manages a collection of maskers for different data types and categories.

type MaskingRegistry

type MaskingRegistry struct {
	// contains filtered or unexported fields
}

MaskingRegistry implements the Registry interface for managing maskers.

func NewMaskingRegistry

func NewMaskingRegistry(defaultMasker Masker) *MaskingRegistry

NewMaskingRegistry creates a new MaskingRegistry with optional default masker.

func (*MaskingRegistry) GetMasker

func (r *MaskingRegistry) GetMasker(category DataCategory) (Masker, error)

GetMasker retrieves a masker for a specific data category.

func (*MaskingRegistry) MaskByCategory

func (r *MaskingRegistry) MaskByCategory(ctx context.Context, category DataCategory, data any) (any, error)

MaskByCategory masks data according to its category.

func (*MaskingRegistry) RegisterMasker

func (r *MaskingRegistry) RegisterMasker(category DataCategory, masker Masker) error

RegisterMasker registers a masker for a specific data category.

func (*MaskingRegistry) UnregisterMasker

func (r *MaskingRegistry) UnregisterMasker(category DataCategory) error

UnregisterMasker removes a masker for a specific data category.

type MaskingStrategy

type MaskingStrategy string

MaskingStrategy is an identifier for different masking strategies.

const (
	// StrategyRedact completely redacts the value, replacing it with a fixed string.
	StrategyRedact MaskingStrategy = "redact"

	// StrategyPartialMask keeps parts of the data visible, masking the rest.
	StrategyPartialMask MaskingStrategy = "partial"

	// StrategyTokenize replaces the original value with a token that can be
	// mapped back to the original value in a secure environment.
	StrategyTokenize MaskingStrategy = "tokenize"

	// StrategyPseudonymize replaces the original value with a pseudonym
	// that is consistent for the same input value.
	StrategyPseudonymize MaskingStrategy = "pseudonymize"

	// StrategyEncrypt encrypts the data with a cryptographic algorithm.
	StrategyEncrypt MaskingStrategy = "encrypt"

	// StrategyNoise adds statistical noise to numerical data.
	StrategyNoise MaskingStrategy = "noise"
)

Predefined masking strategies.

type Option

type Option func(m any) error

Option is a functional option for configuring maskers.

type PhoneMasker

type PhoneMasker struct {
	// contains filtered or unexported fields
}

PhoneMasker implements a specialized masker for phone numbers.

func NewPhoneMasker

func NewPhoneMasker(opts ...PhoneMaskerOption) (*PhoneMasker, error)

NewPhoneMasker creates a new PhoneMasker with the given options.

func (*PhoneMasker) CanMask

func (pm *PhoneMasker) CanMask(data any) bool

CanMask checks if the masker can mask the given data.

func (*PhoneMasker) Mask

func (pm *PhoneMasker) Mask(ctx context.Context, data any) (any, error)

Mask applies the masking strategy to the given phone number.

type PhoneMaskerOption

type PhoneMaskerOption func(*PhoneMasker) error

PhoneMaskerOption is a function that configures a PhoneMasker.

func WithPhoneReplacement

func WithPhoneReplacement(char rune) PhoneMaskerOption

WithPhoneReplacement sets the character used for masking phone numbers.

func WithPreserveCountryCode

func WithPreserveCountryCode(preserve bool) PhoneMaskerOption

WithPreserveCountryCode determines if the country code should be preserved.

func WithPreserveFormat

func WithPreserveFormat(preserve bool) PhoneMaskerOption

WithPreserveFormat determines if the original formatting should be preserved.

func WithVisiblePhoneDigits

func WithVisiblePhoneDigits(count int) PhoneMaskerOption

WithVisiblePhoneDigits sets the number of digits to keep visible at the end.

type StringMasker

type StringMasker struct {
	// contains filtered or unexported fields
}

StringMasker implements the Masker interface for string data.

func NewStringMasker

func NewStringMasker(opts ...StringMaskerOption) (*StringMasker, error)

NewStringMasker creates a new StringMasker with the given options.

func (*StringMasker) CanMask

func (sm *StringMasker) CanMask(data any) bool

CanMask checks if the masker can mask the given data.

func (*StringMasker) Mask

func (sm *StringMasker) Mask(ctx context.Context, data any) (any, error)

Mask applies the masking strategy to the given string.

type StringMaskerOption

type StringMaskerOption func(*StringMasker) error

StringMaskerOption is a function that configures a StringMasker.

func WithMinLength

func WithMinLength(length int) StringMaskerOption

WithMinLength sets the minimum length of string that can be masked.

func WithReplacement

func WithReplacement(char rune) StringMaskerOption

WithReplacement sets the character used for masking.

func WithStrategy

func WithStrategy(strategy MaskingStrategy) StringMaskerOption

WithStrategy sets the masking strategy.

func WithVisibleChars

func WithVisibleChars(start, end int) StringMaskerOption

WithVisibleChars sets the number of characters to keep visible from the start and end of the string.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL