logmasker

package module
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Sep 6, 2025 License: MIT Imports: 6 Imported by: 0

README

LogMasker Module

Go Reference

The LogMasker Module provides centralized log masking functionality for Modular applications. It acts as a decorator around the standard Logger interface to automatically redact sensitive information from log output based on configurable rules.

Features

  • Logger Decorator: Wraps any modular.Logger implementation with masking capabilities
  • Field-Based Masking: Define rules for specific field names (e.g., "password", "token")
  • Pattern-Based Masking: Use regex patterns to detect sensitive data (e.g., credit cards, SSNs)
  • MaskableValue Interface: Allow values to control their own masking behavior
  • Multiple Masking Strategies: Redact, partial mask, hash, or leave unchanged
  • Configurable Rules: Full YAML/JSON configuration support
  • Performance Optimized: Minimal overhead for production use
  • Framework Integration: Seamless integration with the Modular framework

Installation

Add the logmasker module to your project:

go get github.com/GoCodeAlone/modular/modules/logmasker

Configuration

The logmasker module can be configured using the following options:

logmasker:
  enabled: true                    # Enable/disable log masking
  defaultMaskStrategy: redact      # Default strategy: redact, partial, hash, none
  
  fieldRules:                      # Field-based masking rules
    - fieldName: password
      strategy: redact
    - fieldName: email  
      strategy: partial
      partialConfig:
        showFirst: 2
        showLast: 2
        maskChar: "*"
        minLength: 4
    - fieldName: token
      strategy: redact
    - fieldName: secret
      strategy: redact
    - fieldName: key
      strategy: redact
  
  patternRules:                    # Pattern-based masking rules
    - pattern: '\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b'  # Credit cards
      strategy: redact
    - pattern: '\b\d{3}-\d{2}-\d{4}\b'  # SSN format
      strategy: redact
  
  defaultPartialConfig:            # Default partial masking settings
    showFirst: 2
    showLast: 2  
    maskChar: "*"
    minLength: 4

Usage

Basic Usage

Register the module and use the masking logger service:

package main

import (
    "github.com/GoCodeAlone/modular"
    "github.com/GoCodeAlone/modular/modules/logmasker"
)

func main() {
    // Create application with your config and logger
    app := modular.NewApplication(configProvider, logger)
    
    // Register the logmasker module
    app.RegisterModule(logmasker.NewModule())
    
    // Initialize the application
    if err := app.Init(); err != nil {
        log.Fatal(err)
    }
    
    // Get the masking logger service
    var maskingLogger modular.Logger
    err := app.GetService("logmasker.logger", &maskingLogger)
    if err != nil {
        log.Fatal(err)
    }
    
    // Use the masking logger - sensitive data will be automatically masked
    maskingLogger.Info("User login", 
        "email", "user@example.com",     // Will be partially masked
        "password", "secret123",          // Will be redacted
        "sessionId", "abc-123-def")       // Will remain unchanged
    
    // Output: "User login" email="us*****.com" password="[REDACTED]" sessionId="abc-123-def"
}
MaskableValue Interface

Create values that control their own masking behavior:

// SensitiveToken implements MaskableValue
type SensitiveToken struct {
    Value string
    IsPublic bool
}

func (t *SensitiveToken) ShouldMask() bool {
    return !t.IsPublic
}

func (t *SensitiveToken) GetMaskedValue() any {
    return "[SENSITIVE-TOKEN]"
}

func (t *SensitiveToken) GetMaskStrategy() logmasker.MaskStrategy {
    return logmasker.MaskStrategyRedact
}

// Usage
token := &SensitiveToken{Value: "secret-token", IsPublic: false}
maskingLogger.Info("API call", "token", token)
// Output: "API call" token="[SENSITIVE-TOKEN]"
Custom Configuration

Override default masking behavior:

// Custom configuration in your config file
config := &logmasker.LogMaskerConfig{
    Enabled: true,
    DefaultMaskStrategy: logmasker.MaskStrategyPartial,
    FieldRules: []logmasker.FieldMaskingRule{
        {
            FieldName: "creditCard",
            Strategy:  logmasker.MaskStrategyHash,
        },
        {
            FieldName: "phone", 
            Strategy:  logmasker.MaskStrategyPartial,
            PartialConfig: &logmasker.PartialMaskConfig{
                ShowFirst: 3,
                ShowLast:  4,
                MaskChar:  "#",
                MinLength: 10,
            },
        },
    },
    PatternRules: []logmasker.PatternMaskingRule{
        {
            Pattern:  `\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b`, // Email regex
            Strategy: logmasker.MaskStrategyPartial,
            PartialConfig: &logmasker.PartialMaskConfig{
                ShowFirst: 2,
                ShowLast:  8, // Show domain
                MaskChar:  "*",
                MinLength: 6,
            },
        },
    },
}
Integration with Other Modules

The logmasker works seamlessly with other modules:

// In a module that needs masked logging
type MyModule struct {
    logger modular.Logger
}

func (m *MyModule) Init(app modular.Application) error {
    // Get the masking logger instead of the original logger
    return app.GetService("logmasker.logger", &m.logger)
}

func (m *MyModule) ProcessUser(user *User) {
    // All sensitive data will be automatically masked
    m.logger.Info("Processing user",
        "id", user.ID,
        "email", user.Email,           // Masked based on field rules
        "password", user.Password,     // Redacted
        "profile", user.Profile)       // Unchanged
}

Masking Strategies

Redact Strategy

Replaces the entire value with [REDACTED]:

password: "secret123" → "[REDACTED]"
Partial Strategy

Shows only specified characters, masking the rest:

email: "user@example.com" → "us**********com" (showFirst: 2, showLast: 3)
phone: "555-123-4567" → "555-***-4567" (showFirst: 3, showLast: 4)
Hash Strategy

Replaces value with a hash:

token: "abc123" → "[HASH:2c26b46b]"
None Strategy

Leaves the value unchanged (useful for overriding default behavior):

publicId: "user-123" → "user-123"

Field Rules vs Pattern Rules

  • Field Rules: Match exact field names in key-value logging pairs
  • Pattern Rules: Match regex patterns in string values regardless of field name

Field rules take precedence over pattern rules for the same value.

Performance Considerations

  • Lazy Compilation: Regex patterns are compiled once during module initialization
  • Early Exit: When masking is disabled, no processing overhead occurs
  • Efficient Matching: Field rules use map lookup, pattern matching is optimized
  • Memory Efficient: No unnecessary string copies for unmasked values

Error Handling

The module handles various error conditions gracefully:

  • Invalid Regex Patterns: Module initialization fails with descriptive error
  • Missing Logger Service: Module initialization fails if logger service unavailable
  • Configuration Errors: Reported during module initialization
  • Runtime Errors: Malformed log calls are passed through unchanged

Security Considerations

When using log masking in production:

  • Review Field Rules: Ensure all sensitive field names are covered
  • Test Pattern Rules: Validate regex patterns match expected sensitive data
  • Audit Log Output: Regularly review logs to ensure masking is working
  • Performance Impact: Monitor performance in high-throughput scenarios
  • Configuration Security: Ensure masking configuration itself doesn't contain secrets

Testing

Run the module tests:

cd modules/logmasker
go test ./... -v

The module includes comprehensive tests covering:

  • Field-based masking rules
  • Pattern-based masking rules
  • MaskableValue interface behavior
  • All masking strategies
  • Partial masking configuration
  • Module lifecycle and integration
  • Performance edge cases

Implementation Notes

  • The module wraps the original logger using the decorator pattern
  • MaskableValue interface allows for anytype-compatible value wrappers
  • Configuration supports full validation with default values
  • Regex patterns are pre-compiled for performance
  • The module integrates seamlessly with the framework's service system

Integration with Existing Logging

The logmasker module is designed to be a drop-in replacement for the standard logger:

// Before: Using standard logger
var logger modular.Logger
app.GetService("logger", &logger)

// After: Using masking logger  
var maskingLogger modular.Logger
app.GetService("logmasker.logger", &maskingLogger)

// Same interface, automatic masking
maskingLogger.Info("message", "key", "value")

This allows existing code to benefit from masking without modifications.

Documentation

Overview

Package logmasker provides centralized log masking functionality for the Modular framework.

This module wraps the Logger interface to provide configurable masking rules that can redact sensitive information from log output. It supports both field-based masking rules and value wrappers that can determine their own redaction behavior.

Features

The logmasker module offers the following capabilities:

  • Logger decorator that wraps any modular.Logger implementation
  • Configurable field-based masking rules
  • Regex pattern matching for sensitive data
  • MaskableValue interface for self-determining value masking
  • Multiple masking strategies (redact, partial mask, hash)
  • Performance optimized for production use

Configuration

The module can be configured through the LogMaskerConfig structure:

config := &LogMaskerConfig{
    Enabled: true,
    DefaultMaskStrategy: "redact",
    FieldRules: []FieldMaskingRule{
        {
            FieldName: "password",
            Strategy:  "redact",
        },
        {
            FieldName: "email",
            Strategy:  "partial",
            PartialConfig: &PartialMaskConfig{
                ShowFirst: 2,
                ShowLast:  2,
                MaskChar:  "*",
            },
        },
    },
    PatternRules: []PatternMaskingRule{
        {
            Pattern:  `\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b`,
            Strategy: "redact",
        },
    },
}

Usage Examples

Basic usage as a service wrapper:

// Get the original logger
var originalLogger modular.Logger
app.GetService("logger", &originalLogger)

// Get the masking logger service
var maskingLogger modular.Logger
app.GetService("logmasker.logger", &maskingLogger)

// Use the masking logger
maskingLogger.Info("User login", "email", "user@example.com", "password", "secret123")
// Output: "User login" email="us*****.com" password="[REDACTED]"

Index

Constants

View Source
const (
	// Module lifecycle events
	EventTypeModuleStarted = "com.modular.logmasker.started"
	EventTypeModuleStopped = "com.modular.logmasker.stopped"

	// Configuration events
	EventTypeConfigLoaded    = "com.modular.logmasker.config.loaded"
	EventTypeConfigValidated = "com.modular.logmasker.config.validated"
	EventTypeRulesUpdated    = "com.modular.logmasker.rules.updated"

	// Masking operation events
	EventTypeMaskingApplied = "com.modular.logmasker.masking.applied"
	EventTypeMaskingSkipped = "com.modular.logmasker.masking.skipped"
	EventTypeFieldMasked    = "com.modular.logmasker.field.masked"
	EventTypePatternMatched = "com.modular.logmasker.pattern.matched"
	EventTypeMaskingError   = "com.modular.logmasker.masking.error"
)

Event type constants for LogMasker module Following CloudEvents specification with reverse domain notation

View Source
const (
	// ServiceName is the name of the masking logger service.
	ServiceName = "logmasker.logger"

	// ModuleName is the name of the log masker module.
	ModuleName = "logmasker"
)

Variables

View Source
var ErrInvalidConfigType = errors.New("invalid config type for log masker")

ErrInvalidConfigType indicates the configuration type is incorrect for this module.

Functions

This section is empty.

Types

type FieldMaskingRule

type FieldMaskingRule struct {
	// FieldName is the exact field name to match (case-sensitive).
	FieldName string `yaml:"fieldName" json:"fieldName" desc:"Field name to mask"`

	// Strategy defines how to mask this field.
	Strategy MaskStrategy `yaml:"strategy" json:"strategy" desc:"Masking strategy to use"`

	// PartialConfig provides configuration for partial masking.
	PartialConfig *PartialMaskConfig `yaml:"partialConfig,omitempty" json:"partialConfig,omitempty" desc:"Configuration for partial masking"`
}

FieldMaskingRule defines masking rules for specific field names.

type LogMaskerConfig

type LogMaskerConfig struct {
	// Enabled controls whether log masking is active.
	Enabled bool `yaml:"enabled" json:"enabled" default:"true" desc:"Enable log masking"`

	// DefaultMaskStrategy is used when no specific rule matches.
	DefaultMaskStrategy MaskStrategy `yaml:"defaultMaskStrategy" json:"defaultMaskStrategy" default:"redact" desc:"Default masking strategy"`

	// FieldRules defines masking rules for specific field names.
	FieldRules []FieldMaskingRule `yaml:"fieldRules" json:"fieldRules" desc:"Field-based masking rules"`

	// PatternRules defines masking rules based on regex patterns.
	PatternRules []PatternMaskingRule `yaml:"patternRules" json:"patternRules" desc:"Pattern-based masking rules"`

	// DefaultPartialConfig provides default settings for partial masking.
	DefaultPartialConfig PartialMaskConfig `yaml:"defaultPartialConfig" json:"defaultPartialConfig" desc:"Default partial masking configuration"`
}

LogMaskerConfig defines the configuration for the log masking module.

type LogMaskerModule

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

LogMaskerModule implements the modular.Module interface to provide log masking functionality.

func NewModule

func NewModule() *LogMaskerModule

NewModule creates a new log masker module instance.

func (*LogMaskerModule) Dependencies

func (m *LogMaskerModule) Dependencies() []string

Dependencies returns the list of module dependencies.

func (*LogMaskerModule) GetRegisteredEventTypes

func (m *LogMaskerModule) GetRegisteredEventTypes() []string

GetRegisteredEventTypes implements the ObservableModule interface. Returns all event types that this logmasker module can emit.

func (*LogMaskerModule) Init

Init initializes the module.

func (*LogMaskerModule) Name

func (m *LogMaskerModule) Name() string

Name returns the module name.

func (*LogMaskerModule) ProvidesServices

func (m *LogMaskerModule) ProvidesServices() []modular.ServiceProvider

ProvidesServices declares what services this module provides.

func (*LogMaskerModule) RegisterConfig

func (m *LogMaskerModule) RegisterConfig(app modular.Application) error

RegisterConfig registers the module's configuration.

type MaskStrategy

type MaskStrategy string

MaskStrategy defines the type of masking to apply.

const (
	// MaskStrategyRedact replaces the entire value with "[REDACTED]".
	MaskStrategyRedact MaskStrategy = "redact"

	// MaskStrategyPartial shows only part of the value, masking the rest.
	MaskStrategyPartial MaskStrategy = "partial"

	// MaskStrategyHash replaces the value with a hash.
	MaskStrategyHash MaskStrategy = "hash"

	// MaskStrategyNone does not mask the value.
	MaskStrategyNone MaskStrategy = "none"
)

type MaskableValue

type MaskableValue interface {
	// ShouldMask returns true if this value should be masked in logs.
	ShouldMask() bool

	// GetMaskedValue returns the masked representation of this value.
	// If ShouldMask() returns false, this method may not be called.
	GetMaskedValue() any

	// GetMaskStrategy returns the preferred masking strategy for this value.
	// Can return an empty string to use the default strategy.
	GetMaskStrategy() MaskStrategy
}

MaskableValue is an interface that values can implement to control their own masking behavior. This allows for anytype-compatible value wrappers to determine if they should be redacted.

type MaskingLogger

type MaskingLogger struct {
	*modular.BaseLoggerDecorator
	// contains filtered or unexported fields
}

MaskingLogger implements modular.LoggerDecorator with masking capabilities. It extends BaseLoggerDecorator to leverage the framework's decorator infrastructure.

func (*MaskingLogger) Debug

func (l *MaskingLogger) Debug(msg string, args ...any)

Debug logs a debug message with masking applied to arguments.

func (*MaskingLogger) Error

func (l *MaskingLogger) Error(msg string, args ...any)

Error logs an error message with masking applied to arguments.

func (*MaskingLogger) Info

func (l *MaskingLogger) Info(msg string, args ...any)

Info logs an informational message with masking applied to arguments.

func (*MaskingLogger) Warn

func (l *MaskingLogger) Warn(msg string, args ...any)

Warn logs a warning message with masking applied to arguments.

type PartialMaskConfig

type PartialMaskConfig struct {
	// ShowFirst is the number of characters to show at the beginning.
	ShowFirst int `yaml:"showFirst" json:"showFirst" default:"0" desc:"Number of characters to show at start"`

	// ShowLast is the number of characters to show at the end.
	ShowLast int `yaml:"showLast" json:"showLast" default:"0" desc:"Number of characters to show at end"`

	// MaskChar is the character to use for masking.
	MaskChar string `yaml:"maskChar" json:"maskChar" default:"*" desc:"Character to use for masking"`

	// MinLength is the minimum length before applying partial masking.
	MinLength int `yaml:"minLength" json:"minLength" default:"4" desc:"Minimum length before applying partial masking"`
}

PartialMaskConfig defines how to partially mask a value.

type PatternMaskingRule

type PatternMaskingRule struct {
	// Pattern is the regular expression to match against string values.
	Pattern string `yaml:"pattern" json:"pattern" desc:"Regular expression pattern to match"`

	// Strategy defines how to mask values matching this pattern.
	Strategy MaskStrategy `yaml:"strategy" json:"strategy" desc:"Masking strategy to use"`

	// PartialConfig provides configuration for partial masking.
	PartialConfig *PartialMaskConfig `yaml:"partialConfig,omitempty" json:"partialConfig,omitempty" desc:"Configuration for partial masking"`
	// contains filtered or unexported fields
}

PatternMaskingRule defines masking rules based on regex patterns.

Jump to

Keyboard shortcuts

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