internal

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 22, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package internal provides audit logging functionality.

Package internal provides shared internal types and utilities.

Package internal provides variable expansion with security limits.

Package internal provides file I/O utilities.

Package internal provides internal interfaces for dependency injection.

Package internal provides JSON flattening utilities.

Package internal provides key validation utilities.

Package internal provides security limits for the env package.

Package internal provides internal struct marshaling utilities.

Package internal provides internal line parsing utilities.

Package internal provides path resolution utilities for dot-notation key access.

Package internal provides path validation utilities for secure file access.

Package internal provides shared resource pools for the env package.

Package internal provides secure io.Reader implementations with security limits.

Package internal provides sensitive key detection and masking utilities.

Package internal provides input validation for environment variable keys and values.

Package internal provides YAML flattening utilities.

Package internal provides YAML lexing utilities.

Package internal provides YAML parsing utilities.

Index

Constants

View Source
const (
	DefaultBufferSize    = 100
	DefaultFlushInterval = 5 * time.Second
)

Default values for BufferedHandler.

View Source
const (
	// DefaultMaxFileSize is the maximum allowed file size (2 MB).
	DefaultMaxFileSize int64 = 2 * 1024 * 1024

	// DefaultMaxLineLength is the maximum allowed line length.
	DefaultMaxLineLength int = 1024

	// DefaultMaxKeyLength is the maximum allowed key length.
	DefaultMaxKeyLength int = 64

	// DefaultMaxValueLength is the maximum allowed value length.
	DefaultMaxValueLength int = 4096

	// DefaultMaxVariables is the maximum number of variables per file.
	DefaultMaxVariables int = 500

	// DefaultMaxExpansionDepth is the maximum variable expansion depth.
	DefaultMaxExpansionDepth int = 5
)

Default security limits for high-security configurations. These values are intentionally conservative to prevent various attacks.

View Source
const (
	// HardMaxFileSize is the absolute maximum file size (100 MB).
	HardMaxFileSize int64 = 100 * 1024 * 1024

	// HardMaxLineLength is the absolute maximum line length.
	HardMaxLineLength int = 64 * 1024

	// HardMaxKeyLength is the absolute maximum key length.
	HardMaxKeyLength int = 1024

	// HardMaxValueLength is the absolute maximum value length.
	HardMaxValueLength int = 1024 * 1024

	// HardMaxVariables is the absolute maximum variables per file.
	HardMaxVariables int = 10000

	// HardMaxExpansionDepth is the absolute maximum expansion depth.
	HardMaxExpansionDepth int = 20
)

Hard security limits that cannot be exceeded. These are absolute maximums for safety and are used across all packages.

View Source
const (
	// MaxPooledBuilderSize is the maximum capacity for pooled strings.Builder objects.
	// Builders larger than this are discarded instead of pooled.
	MaxPooledBuilderSize = 16 * 1024 // 16KB

	// MaxPooledScannerBufferSize is the maximum capacity for pooled scanner buffers.
	// Buffers larger than this are discarded instead of pooled.
	MaxPooledScannerBufferSize = 256 * 1024 // 256KB

	// MaxPooledByteSliceSize is the maximum capacity for pooled byte slices.
	// Slices larger than this are discarded instead of pooled.
	MaxPooledByteSliceSize = 4096 // 4KB

	// MaxPooledMapSize is the maximum size for pooled map objects.
	// Maps with more entries than this are discarded instead of pooled.
	MaxPooledMapSize = 128
)

Pool size limits for sync.Pool objects. Objects larger than these limits are not returned to pools to prevent memory bloat from holding onto large allocations.

Variables

View Source
var (
	// ErrFileTooLarge indicates the file exceeds the maximum allowed size.
	ErrFileTooLarge = fmt.Errorf("file exceeds maximum size limit")

	// ErrLineTooLong indicates a line exceeds the maximum allowed length.
	ErrLineTooLong = fmt.Errorf("line exceeds maximum length limit")

	// ErrInvalidValue indicates a value is invalid.
	ErrInvalidValue = errors.New("invalid value content")

	// ErrSecurityViolation indicates a security policy violation.
	ErrSecurityViolation = errors.New("security policy violation")
)

Sentinel errors used by internal packages.

View Source
var DefaultPathValidator = NewPathValidator(PathValidatorConfig{})

DefaultPathValidator is the default path validator instance. It uses the default key masking function.

View Source
var ErrNonASCII = errors.New("string contains non-ASCII characters")

ErrNonASCII is returned when a string contains non-ASCII characters in functions that require ASCII-only input.

View Source
var Patterns = []string{

	"PASSWORD",
	"SECRET",
	"TOKEN",
	"AUTH",
	"CREDENTIAL",
	"PASSPHRASE",
	"SESSION",
	"COOKIE",

	"API_KEY",
	"APIKEY",
	"ACCESS_KEY",
	"SECRET_KEY",
	"PRIVATE_KEY",
	"PUBLIC_KEY",

	"PRIVATE",
	"ENCRYPTION_KEY",
	"ENCRYPT_KEY",
	"DECRYPT_KEY",
	"SIGNING_KEY",
	"SIGN_KEY",
	"VERIFY_KEY",

	"SSN",
	"SOCIAL_SECURITY",
	"CREDIT_CARD",
	"CARD_NUMBER",
	"CVV",
	"CVC",
	"CCV",
	"PAN",

	"MNEMONIC",
	"SEED",
	"RECOVERY",
	"WALLET",
	"PRIVATE_ADDRESS",

	"CONNECTION_STRING",
	"CONN_STRING",
	"DATABASE_URL",
	"DB_PASSWORD",

	"AWS_SECRET",
	"AZURE_KEY",
	"GCP_KEY",
	"SERVICE_ACCOUNT",
}

Patterns defines patterns that indicate sensitive data. This is the single source of truth for sensitive key detection. Keys containing these patterns (case-insensitive) will be masked in logs and errors.

Functions

func ClearInternCache

func ClearInternCache()

ClearInternCache clears the key interning cache. This is useful for long-running applications that want to release memory held by cached keys that are no longer needed. This function is safe for concurrent use.

func DefaultMaskKey added in v1.1.0

func DefaultMaskKey(key string) string

DefaultMaskKey masks a key name for safe logging and error reporting. Shows only the first 2 characters followed by "***" for keys longer than 3 characters. This is the default masking function used by validators and path validators.

func DetectCycle

func DetectCycle(vars map[string]string) (string, bool)

DetectCycle checks if expanding the given variables would cause a cycle. Returns the key that causes the cycle and true if a cycle is detected.

func EscapeValue

func EscapeValue(value string) string

EscapeValue escapes a value for .env file format. This function uses pooled builder for efficiency.

func ExtractNumericIndex

func ExtractNumericIndex(path string) (basePath string, index int, ok bool)

ExtractNumericIndex extracts a numeric index from the end of a path. Returns: (basePath, index, true) if path ends with numeric index Returns: ("", -1, false) otherwise

Examples:

"servers.0" -> ("servers", 0, true)
"service.cors.origins.0" -> ("service.cors.origins", 0, true)
"database.host" -> ("", -1, false)

func FlattenJSON

func FlattenJSON(data []byte, cfg JSONFlattenConfig) (map[string]string, error)

FlattenJSON converts nested JSON data to a flat map of string key-value pairs. Keys are converted to uppercase with the configured delimiter.

func FlattenYAML

func FlattenYAML(value *Value, cfg YAMLFlattenConfig) (map[string]string, error)

FlattenYAML converts a YAML Value tree to a flat map of string key-value pairs. Keys are converted to uppercase with the configured delimiter.

func GetBuilder

func GetBuilder() *strings.Builder

GetBuilder retrieves a strings.Builder from the shared pool. Returns a fallback builder if the pool returns an unexpected type.

func GetByteSlice

func GetByteSlice() *[]byte

GetByteSlice retrieves a byte slice from the pool. Returns a fallback slice if the pool returns an unexpected type.

func HashKey

func HashKey(key string, numShards int) uint32

HashKey returns a hash value for the given key using an optimized algorithm. For short keys (<=8 chars), uses a simple multiplicative hash. For longer keys, uses FNV-1a with sampling for better performance. The numShards parameter determines the range of the returned hash (0 to numShards-1).

Performance optimizations: - Uses branchless bit manipulation for keys 1-4 chars (most common case) - Avoids conditional branches in the hot path - Single optimization point for numShards==8 at function exit

func InternKey

func InternKey(key string) string

InternKey returns an interned copy of the key string if available, or stores and returns the input key. This reduces allocations when the same keys are parsed repeatedly. This implementation uses sharded caches with thread-safe access for better concurrency performance.

Optimization: Uses sync.Mutex instead of sync.RWMutex because: 1. The cache is small (128 entries per shard), making lookup very fast 2. RWMutex has higher overhead for the common case of cache hit + small cache 3. Simpler lock management improves cache locality

SECURITY: This function maintains strict consistency between the cache map and order slice to prevent memory leaks and ensure correct FIFO eviction.

func IsASCII added in v1.1.0

func IsASCII(s string) bool

IsASCII checks if a string contains only ASCII characters (bytes < 0x80). This is a fast path function used for input validation.

func IsKey

func IsKey(key string) bool

IsKey determines if a key likely contains sensitive data. Uses byte-level comparison to avoid allocations from strings.ToUpper.

func IsValidJSONKey

func IsValidJSONKey(key string) bool

IsValidJSONKey checks if a key matches the JSON key pattern ^[A-Za-z0-9_@\-.]+$ This is faster than using regex for the common case. Allowed characters: letters, digits, underscore, at-sign, hyphen, dot. Note: Square brackets are NOT allowed to prevent key name confusion and ambiguity with array index notation.

func IsYamlNumber

func IsYamlNumber(s string) bool

IsYamlNumber checks if a string is a valid YAML number.

func IsYamlNumberBytes

func IsYamlNumberBytes(s []byte) bool

IsYamlNumberBytes checks if a byte slice is a valid YAML number.

func KeysToUpper

func KeysToUpper(m map[string]string) map[string]bool

KeysToUpper converts map keys to uppercase for comparison. This function is optimized to minimize allocations. The caller owns the returned map and does not need to return it to a pool.

func KeysToUpperPooled

func KeysToUpperPooled(m map[string]string) map[string]bool

KeysToUpperPooled converts map keys to uppercase using a pooled map. The returned map MUST be returned to the pool using PutKeysToUpperMap after use. This reduces allocations when the map is only needed temporarily.

Example:

upperKeys := KeysToUpperPooled(vars)
defer PutKeysToUpperMap(upperKeys)
// use upperKeys...

func LockMemory

func LockMemory(data []byte) error

LockMemory locks the specified memory region using mlock. This prevents the memory from being swapped to disk.

func MarshalEnv

func MarshalEnv(m map[string]string, sorted bool) (string, error)

MarshalEnv converts a map to .env file format.

func MarshalEnvAs

func MarshalEnvAs(m map[string]string, format MarshalFormat, sorted bool) (string, error)

MarshalEnvAs converts a map to the specified format.

func MaskInString

func MaskInString(s string) string

MaskInString masks potentially sensitive content in a string.

func MaskKey

func MaskKey(key string) string

MaskKey masks a key name for logging purposes.

func MaskValue

func MaskValue(key, value string) string

MaskValue masks a value based on its sensitivity. This is a utility function for general value masking.

func MemLockSupported

func MemLockSupported() bool

MemLockSupported returns true on Unix systems.

func ParseDoubleQuoted

func ParseDoubleQuoted(value string) (string, error)

ParseDoubleQuoted handles double-quoted values with escape sequences.

func ParseDoubleQuotedBytes

func ParseDoubleQuotedBytes(value []byte) (string, error)

ParseDoubleQuotedBytes handles double-quoted values from a byte slice with escape sequences.

func ParseSingleQuoted

func ParseSingleQuoted(value string) (string, error)

ParseSingleQuoted handles single-quoted values (no escape processing).

func ParseSingleQuotedBytes

func ParseSingleQuotedBytes(value []byte) (string, error)

ParseSingleQuotedBytes handles single-quoted values from a byte slice (no escape processing).

func PutBuilder

func PutBuilder(sb *strings.Builder)

PutBuilder returns a strings.Builder to the shared pool. Builders with capacity exceeding MaxPooledBuilderSize are discarded to prevent memory bloat. The builder is reset before returning to pool for hygiene and to allow GC to reclaim the previous string data.

SECURITY LIMITATION: strings.Builder does not expose its internal buffer, so we cannot directly clear the contents before pooling. However, the data will be overwritten when the builder is reused. For environments requiring strict data clearing, consider not using the pool for sensitive operations. The Go standard library guarantees that Reset() only sets len to 0, preserving the underlying buffer capacity.

func PutByteSlice

func PutByteSlice(buf *[]byte)

PutByteSlice returns a byte slice to the pool. Slices with capacity exceeding MaxPooledByteSliceSize are discarded.

SECURITY NOTE: This function clears the slice contents before pooling to prevent sensitive data from persisting in pooled buffers. This is important because byte slices may contain parsed environment variable values. The capacity check is performed after clearing to prevent memory bloat.

func PutKeysToUpperMap

func PutKeysToUpperMap(m map[string]bool)

PutKeysToUpperMap returns a pooled map obtained from KeysToUpperPooled. It is safe to call with nil.

func ResolvePath

func ResolvePath(path string) []string

ResolvePath converts a dot-notation path to candidate storage keys. It is used to look up nested values that were flattened during JSON/YAML parsing.

For dot-notation paths (containing "."):

  • Returns only the converted uppercase storage keys
  • "database.host" -> ["DATABASE_HOST"]
  • "servers.0.host" -> ["SERVERS_0_HOST", "SERVERS[0]_HOST"]

For simple keys (no dots):

  • Returns both the original key and uppercase version
  • "HOST" -> ["HOST", "HOST"] (same key)
  • "host" -> ["host", "HOST"] (try original first, then uppercase)

Note: For simple keys, callers should use direct Get() with the key and its uppercase version to avoid this allocation entirely.

SECURITY: Paths exceeding HardMaxKeyLength are rejected to prevent memory exhaustion attacks.

func SanitizeForLog

func SanitizeForLog(s string) string

SanitizeForLog removes potentially sensitive information from a string. It scans for patterns that might indicate sensitive data and masks them.

Performance: Uses single-pass scanning with strings.Builder to avoid O(n*m) complexity from multiple string scans and allocations.

func Struct

func Struct(v interface{}, prefix string) (map[string]string, error)

Struct converts a struct to environment variables. Struct fields can be tagged with `env:"KEY"` to specify the env variable name. Nested structs are flattened with underscore-separated keys.

func StructInto

func StructInto(data map[string]string, val reflect.Value, prefix string) error

StructInto populates a struct from environment variables. Struct fields can be tagged with `env:"KEY"` to specify the env variable name. Optional `envDefault:"value"` sets a default if the key is not found. Also supports inline format: `env:"KEY,envDefault:VALUE"`.

func ToUpperASCII

func ToUpperASCII(s string) string

ToUpperASCII converts an ASCII string to uppercase. This is faster than strings.ToUpper for ASCII-only strings. Uses single-pass algorithm: convert while detecting lowercase. Returns the uppercase string (shares backing array if already uppercase).

SECURITY WARNING: This function is designed for ASCII-only input. Non-ASCII bytes (>= 0x80) are passed through unchanged without validation. Callers must validate input if ASCII-only keys are required for security. For environment variable keys, this is acceptable because: 1. Environment variable names are conventionally ASCII 2. Key validation elsewhere rejects non-ASCII keys 3. Visual spoofing attacks with Unicode are mitigated by key pattern validation

For use cases requiring strict ASCII validation, use ToUpperASCIISafe instead.

func ToUpperASCIISafe added in v1.1.0

func ToUpperASCIISafe(s string) (string, error)

ToUpperASCIISafe converts an ASCII string to uppercase with strict ASCII validation. Returns ErrNonASCII if the input contains any bytes >= 0x80. This is the safe version of ToUpperASCII for use when input validation is required.

Performance: This function has minimal overhead compared to ToUpperASCII (single additional bounds check per character that was already being performed).

func TrimSpace

func TrimSpace(s string) string

TrimSpace trims leading and trailing whitespace from a string. This is an optimized version that returns the original string if no trimming is needed, avoiding allocation in the common case where values are already trimmed.

func TryParseYamlValue

func TryParseYamlValue(value string) (string, bool)

TryParseYamlValue attempts to parse YAML-style values.

func TryParseYamlValueBytes

func TryParseYamlValueBytes(value []byte) (string, bool)

TryParseYamlValueBytes attempts to parse YAML-style values from a byte slice.

func UnlockMemory

func UnlockMemory(data []byte)

UnlockMemory unlocks the specified memory region using munlock.

func ValidateFilePath added in v1.1.0

func ValidateFilePath(filename string) error

ValidateFilePath validates a file path using the default validator. This is a convenience function for backward compatibility.

func WriteFile

func WriteFile(filename string, buf *bytes.Buffer) (err error)

WriteFile writes content to a file using an atomic write pattern. It writes to a temp file first, then renames for atomicity.

Types

type Action

type Action string

Action represents the type of action being audited.

const (
	ActionLoad       Action = "load"
	ActionParse      Action = "parse"
	ActionGet        Action = "get"
	ActionSet        Action = "set"
	ActionDelete     Action = "delete"
	ActionValidate   Action = "validate"
	ActionExpand     Action = "expand"
	ActionSecurity   Action = "security"
	ActionError      Action = "error"
	ActionFileAccess Action = "file_access"
)

Audit action constants.

type AuditLogger added in v1.1.0

type AuditLogger interface {
	LogError(action Action, key, errMsg string) error
}

AuditLogger records audit events.

type Auditor

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

Auditor provides audit logging functionality.

func NewAuditor

func NewAuditor(handler Handler, isSensitive IsSensitiveFunc, masker MaskerFunc, enabled bool) *Auditor

NewAuditor creates a new Auditor with the specified handler.

func (*Auditor) Close

func (a *Auditor) Close() error

Close closes the underlying handler.

func (*Auditor) IsEnabled

func (a *Auditor) IsEnabled() bool

IsEnabled returns whether audit logging is enabled.

func (*Auditor) Log

func (a *Auditor) Log(action Action, key, reason string, success bool) error

Log records an audit event.

func (*Auditor) LogError

func (a *Auditor) LogError(action Action, key, errMsg string) error

LogError records an error event.

func (*Auditor) LogSecurity

func (a *Auditor) LogSecurity(key, reason string) error

LogSecurity records a security-related event.

func (*Auditor) LogWithDuration

func (a *Auditor) LogWithDuration(action Action, key, reason string, success bool, duration time.Duration) error

LogWithDuration records an audit event with timing information.

func (*Auditor) LogWithFile

func (a *Auditor) LogWithFile(action Action, key, file, reason string, success bool) error

LogWithFile records an audit event with file information.

func (*Auditor) SetEnabled

func (a *Auditor) SetEnabled(enabled bool)

SetEnabled enables or disables audit logging.

type BufferedHandler

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

BufferedHandler buffers audit events and writes them in batches. This significantly reduces I/O overhead for high-frequency operations.

Features:

  • Batched writes reduce system call overhead
  • Time-based auto-flush ensures events are written promptly
  • Thread-safe for concurrent use
  • Graceful shutdown on Close()

Example:

underlying := NewJSONHandler(file)
buffered := NewBufferedHandler(BufferedHandlerConfig{
    Handler:       underlying,
    BufferSize:    100,
    FlushInterval: 5 * time.Second,
})
defer buffered.Close()

func NewBufferedHandler

func NewBufferedHandler(cfg BufferedHandlerConfig) *BufferedHandler

NewBufferedHandler creates a new BufferedHandler with the given configuration.

func (*BufferedHandler) BufferLen

func (h *BufferedHandler) BufferLen() int

BufferLen returns the current number of events in the buffer.

func (*BufferedHandler) Close

func (h *BufferedHandler) Close() error

Close flushes any remaining events and stops the background flush goroutine.

func (*BufferedHandler) Flush

func (h *BufferedHandler) Flush() error

Flush writes all buffered events to the underlying handler. It clears the buffer after successful write. This method is safe for concurrent use.

func (*BufferedHandler) IsFull

func (h *BufferedHandler) IsFull() bool

IsFull returns true if the buffer is at capacity.

func (*BufferedHandler) Log

func (h *BufferedHandler) Log(event Event) error

Log adds an event to the buffer. If the buffer is full, it triggers an automatic flush.

func (*BufferedHandler) RequestFlush

func (h *BufferedHandler) RequestFlush()

RequestFlush signals that a flush should be performed soon. This is useful for triggering a flush from another goroutine without waiting for the flush to complete.

type BufferedHandlerConfig

type BufferedHandlerConfig struct {
	// Handler is the underlying handler to write to (required)
	Handler Handler

	// BufferSize is the maximum number of events to buffer before auto-flush
	// Default: 100
	BufferSize int

	// FlushInterval is the maximum time to wait before auto-flush
	// Set to 0 to disable time-based auto-flush
	// Default: 5 seconds
	FlushInterval time.Duration

	// OnError is called when an error occurs during flush
	// If nil, errors are silently ignored
	OnError func(error)
}

BufferedHandlerConfig holds configuration for BufferedHandler.

type ChannelHandler

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

ChannelHandler sends audit events to a channel.

Channel Ownership: This handler does NOT own the channel. The caller is responsible for closing the channel when done. The handler's Close() method does nothing because closing a send-only channel (chan<-) would panic if the caller hasn't finished receiving.

Blocking Behavior: This handler blocks if the channel buffer is full. Use a buffered channel if non-blocking behavior is required.

Example:

ch := make(chan Event, 100)
handler := NewChannelHandler(ch)
// Start consumer goroutine
go func() {
    for event := range ch {
        process(event)
    }
}()
// ... use handler ...
handler.Close() // Does NOT close ch
close(ch)       // Caller must close the channel to signal EOF to receiver

func NewChannelHandler

func NewChannelHandler(ch chan<- Event) *ChannelHandler

NewChannelHandler creates a new ChannelHandler that sends events to the provided channel. The caller retains ownership of the channel and must close it when finished to signal EOF to receivers.

func (*ChannelHandler) Close

func (h *ChannelHandler) Close() error

Close implements Handler. NOTE: This method does NOT close the underlying channel because the handler does not own it. The caller must close the channel separately.

func (*ChannelHandler) Log

func (h *ChannelHandler) Log(event Event) error

Log sends an audit event to the channel. This method blocks if the channel is full.

type CloseableChannelHandler

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

CloseableChannelHandler sends audit events to a channel and owns the channel lifecycle. Unlike ChannelHandler, this handler creates its own channel and closes it when Close() is called.

This is useful when you want the handler to manage the complete lifecycle of the channel, ensuring receivers are properly signaled when the handler is closed.

Example:

handler := NewCloseableChannelHandler(100)
// Get the channel for receiving
ch := handler.Channel()
// Start consumer goroutine
go func() {
    for event := range ch {
        process(event)
    }
    fmt.Println("Channel closed, consumer exiting")
}()
// ... use handler ...
handler.Close() // Closes the channel, consumer goroutine exits gracefully

func NewCloseableChannelHandler

func NewCloseableChannelHandler(bufferSize int) *CloseableChannelHandler

NewCloseableChannelHandler creates a new CloseableChannelHandler with a buffered channel of the specified size. The handler owns the channel and will close it when Close() is called.

func (*CloseableChannelHandler) Channel

func (h *CloseableChannelHandler) Channel() <-chan Event

Channel returns the underlying channel for receiving events. The returned channel will be closed when Close() is called.

func (*CloseableChannelHandler) Close

func (h *CloseableChannelHandler) Close() error

Close implements Handler. Closes the underlying channel, signaling to any receivers that no more events will be sent. Safe to call multiple times.

func (*CloseableChannelHandler) IsClosed

func (h *CloseableChannelHandler) IsClosed() bool

IsClosed returns true if the handler has been closed.

func (*CloseableChannelHandler) Log

func (h *CloseableChannelHandler) Log(event Event) (err error)

Log sends an audit event to the channel. Returns an error if the handler has been closed. This method blocks if the channel is full. Uses recover to safely handle the race between Log and Close.

type Dependencies added in v1.1.0

type Dependencies interface {
	KeyValidator
	ValueValidator
	VariableExpander
	AuditLogger
}

Dependencies combines all parser dependencies.

type Event

type Event struct {
	Timestamp time.Time `json:"timestamp"`
	Action    Action    `json:"action"`
	Key       string    `json:"key,omitempty"`
	File      string    `json:"file,omitempty"`
	Reason    string    `json:"reason,omitempty"`
	Success   bool      `json:"success"`
	Masked    bool      `json:"masked,omitempty"`
	Details   string    `json:"details,omitempty"`
	Duration  int64     `json:"duration_ns,omitempty"`
}

Event represents a single audit log entry.

type Expander

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

Expander handles variable expansion with security limits.

func NewExpander

func NewExpander(cfg ExpanderConfig) *Expander

NewExpander creates a new Expander with the specified configuration.

func (*Expander) Expand

func (e *Expander) Expand(s string) (string, error)

Expand performs variable expansion on the input string.

func (*Expander) ExpandAllInMap added in v1.1.0

func (e *Expander) ExpandAllInMap(vars map[string]string) (map[string]string, error)

ExpandAllInMap expands all variables in a map using this expander. This is the primary method for batch expansion, used by parsers.

The method: 1. Checks if any values need expansion (fast path for no variables) 2. Detects cycles to prevent infinite loops 3. Expands all values recursively

Returns the original map if no expansion is needed, avoiding allocations.

type ExpanderConfig

type ExpanderConfig struct {
	MaxDepth   int
	Lookup     func(string) (string, bool)
	Mode       Mode
	KeyPattern *regexp.Regexp // For key validation
}

ExpanderConfig holds configuration for creating a new Expander.

type ExpansionError

type ExpansionError struct {
	Key   string // The key being expanded
	Depth int    // The current expansion depth
	Limit int    // The maximum allowed depth
	Chain string // The expansion chain (sanitized)
}

ExpansionError provides detailed information about variable expansion failures.

func (*ExpansionError) Error

func (e *ExpansionError) Error() string

Error implements the error interface. SECURITY: Key names are masked to prevent sensitive information leakage. Only the first 2 characters of the key are shown followed by "***".

type FileError

type FileError struct {
	Path  string // The file path
	Op    string // The operation that failed (open, read, stat)
	Err   error  // The underlying error
	Size  int64  // File size if relevant
	Limit int64  // The limit that was exceeded if relevant
}

FileError provides detailed information about file-related errors.

func (*FileError) Error

func (e *FileError) Error() string

Error implements the error interface.

func (*FileError) Unwrap

func (e *FileError) Unwrap() error

Unwrap returns the underlying error for errors.Is() and errors.As().

type Handler

type Handler interface {
	Log(event Event) error
	Close() error
}

Handler defines the interface for audit log handlers.

func DefaultHandler

func DefaultHandler() Handler

DefaultHandler returns the default audit handler (writes to stderr).

type IsSensitiveFunc

type IsSensitiveFunc func(key string) bool

IsSensitiveFunc is a function type that determines if a key is sensitive.

type JSONError

type JSONError struct {
	Path    string // JSON path where error occurred
	Message string
	Err     error
}

JSONError represents a JSON parsing error.

func (*JSONError) Error

func (e *JSONError) Error() string

Error implements the error interface.

func (*JSONError) Unwrap

func (e *JSONError) Unwrap() error

Unwrap returns the underlying error for errors.Is() and errors.As().

type JSONFlattenConfig

type JSONFlattenConfig struct {
	// KeyDelimiter is the delimiter for nested keys (default: "_").
	KeyDelimiter string
	// ArrayIndexFormat controls how array indices are formatted.
	// "underscore": KEY_0, KEY_1, etc.
	ArrayIndexFormat string
	// NullAsEmpty converts null values to empty strings (default: true).
	NullAsEmpty bool
	// NumberAsString converts numbers to strings (default: true).
	NumberAsString bool
	// BoolAsString converts booleans to strings (default: true).
	BoolAsString bool
	// MaxDepth limits the maximum nesting depth to prevent stack overflow.
	MaxDepth int
}

JSONFlattenConfig holds configuration for JSON flattening.

type JSONHandler

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

JSONHandler writes audit events as JSON to an io.Writer.

func NewJSONHandler

func NewJSONHandler(w io.Writer) *JSONHandler

NewJSONHandler creates a new JSONHandler.

func (*JSONHandler) Close

func (h *JSONHandler) Close() error

Close implements Handler.

func (*JSONHandler) Log

func (h *JSONHandler) Log(event Event) error

Log writes an audit event as JSON.

type KeyValidator added in v1.1.0

type KeyValidator interface {
	ValidateKey(key string) error
}

KeyValidator validates environment variable keys.

type Lexer

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

Lexer tokenizes YAML input.

func NewYAMLLexer

func NewYAMLLexer(input string) *Lexer

NewYAMLLexer creates a new YAML lexer.

func (*Lexer) NextToken

func (l *Lexer) NextToken() (Token, error)

NextToken returns the next token from the input.

func (*Lexer) Tokenize

func (l *Lexer) Tokenize() ([]Token, error)

Tokenize returns all tokens from the input. Pre-allocates token slice based on input length for efficiency.

type LineAuditLogger added in v1.1.0

type LineAuditLogger = AuditLogger

Type aliases for backward compatibility with existing internal code.

type LineExpander added in v1.1.0

type LineExpander = VariableExpander

Type aliases for backward compatibility with existing internal code.

type LineKeyValidator added in v1.1.0

type LineKeyValidator = KeyValidator

Type aliases for backward compatibility with existing internal code.

type LineParser

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

LineParser handles parsing of individual lines.

func NewLineParser

func NewLineParser(cfg LineParserConfig, keyValidator LineKeyValidator, auditor LineAuditLogger, expander LineExpander) *LineParser

NewLineParser creates a new LineParser. The validators, auditor, and expander parameters accept interfaces rather than concrete types, allowing for better testability and flexibility. For the keyValidator parameter, you can pass the same object as valueValidator if it implements both interfaces.

func (*LineParser) ExpandAll

func (p *LineParser) ExpandAll(vars map[string]string) (map[string]string, error)

ExpandAll expands all variables in the map. Returns the original map if no expansion is needed, avoiding unnecessary allocations. This method delegates to Expander.ExpandAllInMap for the actual expansion logic.

func (*LineParser) ParseLine

func (p *LineParser) ParseLine(line string) (string, string, error)

ParseLine parses a single line and returns the key and value.

func (*LineParser) ParseLineBytes

func (p *LineParser) ParseLineBytes(line []byte) (string, string, error)

ParseLineBytes parses a single line from a byte slice and returns the key and value. This version avoids string allocation by working directly with bytes.

func (*LineParser) ParseValue

func (p *LineParser) ParseValue(value string) (string, error)

ParseValue parses a value handling quotes and escapes.

func (*LineParser) ParseValueBytes

func (p *LineParser) ParseValueBytes(value []byte) (string, error)

ParseValueBytes parses a value from a byte slice handling quotes and escapes. This version avoids string allocation until the final result.

func (*LineParser) SetValueValidator added in v1.1.0

func (p *LineParser) SetValueValidator(v LineValueValidator)

SetValueValidator sets a separate value validator if needed. This is useful when key and value validation are handled by different objects.

type LineParserConfig

type LineParserConfig struct {
	AllowExportPrefix bool
	AllowYamlSyntax   bool
	OverwriteExisting bool
	MaxVariables      int
	ExpandVariables   bool
}

LineParserConfig holds the configuration needed for parsing.

type LineParserDependencies added in v1.1.0

type LineParserDependencies = Dependencies

Type aliases for backward compatibility with existing internal code.

type LineValueValidator added in v1.1.0

type LineValueValidator = ValueValidator

Type aliases for backward compatibility with existing internal code.

type LogHandler

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

LogHandler writes audit events using the standard log package.

func NewLogHandler

func NewLogHandler(logger *log.Logger) *LogHandler

NewLogHandler creates a new LogHandler.

func (*LogHandler) Close

func (h *LogHandler) Close() error

Close implements Handler.

func (*LogHandler) Log

func (h *LogHandler) Log(event Event) error

Log writes an audit event using the logger.

type MarshalError

type MarshalError struct {
	Field   string
	Message string
}

MarshalError represents a marshaling/unmarshaling error.

func (*MarshalError) Error

func (e *MarshalError) Error() string

Error implements the error interface.

type MarshalFormat

type MarshalFormat int

MarshalFormat represents the output format for marshaling.

const (
	// FormatEnv outputs in .env file format.
	FormatEnv MarshalFormat = iota
	// FormatJSON outputs in JSON format.
	FormatJSON
	// FormatYAML outputs in YAML format.
	FormatYAML
)

type MaskerFunc

type MaskerFunc func(key, value string) string

MaskerFunc is a function type that masks a key-value pair.

type Mode

type Mode int

Mode controls how variables are expanded.

const (
	// ModeNone disables variable expansion.
	ModeNone Mode = iota
	// ModeEnv expands $VAR and ${VAR} syntax.
	ModeEnv
	// ModeAll expands $VAR, ${VAR}, and ${VAR:-default} syntax.
	ModeAll
)

type NopHandler

type NopHandler struct{}

NopHandler discards all audit events.

func NewNopHandler

func NewNopHandler() *NopHandler

NewNopHandler creates a new NopHandler.

func (*NopHandler) Close

func (h *NopHandler) Close() error

Close does nothing.

func (*NopHandler) Log

func (h *NopHandler) Log(event Event) error

Log does nothing.

type ParseError

type ParseError struct {
	File    string // The file being parsed (if applicable)
	Line    int    // The line number where the error occurred
	Content string // Sanitized content (sensitive data masked)
	Err     error  // The underlying error
}

ParseError provides detailed information about parsing failures.

func (*ParseError) Error

func (e *ParseError) Error() string

Error implements the error interface.

func (*ParseError) Unwrap

func (e *ParseError) Unwrap() error

Unwrap returns the underlying error for errors.Is() and errors.As().

type Parser

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

Parser parses YAML tokens into a Value tree.

func NewYAMLParser

func NewYAMLParser(tokens []Token, maxDepth int) *Parser

NewYAMLParser creates a new YAML parser.

func (*Parser) Parse

func (p *Parser) Parse() (*Value, error)

Parse parses the tokens and returns the root value.

type PathValidator added in v1.1.0

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

PathValidator validates file paths for security. It checks for path traversal attempts, absolute paths, and other dangerous patterns.

func NewPathValidator added in v1.1.0

func NewPathValidator(cfg PathValidatorConfig) *PathValidator

NewPathValidator creates a new PathValidator with the specified configuration.

func (*PathValidator) Validate added in v1.1.0

func (v *PathValidator) Validate(filename string) error

Validate validates a file path for security. It checks for path traversal attempts and other potentially dangerous patterns.

Security checks performed:

  • Empty filename
  • Null bytes (could bypass extension checks)
  • URL encoding (could bypass path checks)
  • UNC paths (Windows network paths)
  • Unix absolute paths
  • Windows drive letters
  • Path traversal (..)
  • Windows reserved device names
  • Symlink escape attacks

type PathValidatorConfig added in v1.1.0

type PathValidatorConfig struct {
	// MaskKey is an optional function to mask sensitive key names in errors.
	// If nil, a default masking function is used.
	MaskKey func(string) string
}

PathValidatorConfig holds configuration for path validation.

type SecureReader

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

SecureReader wraps an io.Reader with security limits. It enforces maximum size and line length constraints.

func NewSecureReader

func NewSecureReader(r io.Reader, maxSize int64, maxLineLen int) *SecureReader

NewSecureReader creates a new SecureReader with the specified limits.

func (*SecureReader) Read

func (r *SecureReader) Read(p []byte) (n int, err error)

Read implements io.Reader with security checks.

type SecurityError

type SecurityError struct {
	Action  string // The action that was blocked
	Reason  string // The security reason for blocking
	Key     string // The key involved (if applicable, sanitized)
	Details string // Additional sanitized details
}

SecurityError provides detailed information about security violations.

func (*SecurityError) Error

func (e *SecurityError) Error() string

Error implements the error interface.

func (*SecurityError) Is

func (e *SecurityError) Is(target error) bool

Is implements errors.Is for SecurityError. This allows errors.Is(err, ErrSecurityViolation) to match SecurityError.

type Token

type Token struct {
	Type     TokenType
	Value    string
	Line     int
	Column   int
	Indent   int // Indentation level (number of spaces / 2)
	IsQuoted bool
}

Token represents a YAML token.

type TokenType

type TokenType int

TokenType represents the type of a YAML token.

const (
	// TokenEOF marks the end of input.
	TokenEOF TokenType = iota
	// TokenNewline represents a newline character.
	TokenNewline
	// TokenDocumentStart represents a document separator (---).
	TokenDocumentStart
	// TokenKey represents a key in a key-value pair.
	TokenKey
	// TokenValue represents a scalar value.
	TokenValue
	// TokenDash represents the start of an array item.
	TokenDash
	// TokenColon represents the colon separator.
	TokenColon
	// TokenComment represents a comment.
	TokenComment
	// TokenIndent represents increased indentation.
	TokenIndent
	// TokenDedent represents decreased indentation.
	TokenDedent
)

type ValidationError

type ValidationError struct {
	Field   string // The field that failed validation
	Value   string // Sanitized value (sensitive data masked)
	Rule    string // The validation rule that was violated
	Message string // Human-readable explanation
}

ValidationError provides detailed information about validation failures.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Error implements the error interface.

func (*ValidationError) Is

func (e *ValidationError) Is(target error) bool

Is implements errors.Is for ValidationError. This allows errors.Is(err, ErrInvalidConfig) to match ValidationError.

type Validator

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

Validator provides input validation for environment variable keys and values.

func NewValidator

func NewValidator(cfg ValidatorConfig) *Validator

NewValidator creates a new Validator with the specified configuration.

func (*Validator) IsSensitive

func (v *Validator) IsSensitive(key string) bool

IsSensitive returns true if the key appears to be sensitive.

func (*Validator) MaskValue

func (v *Validator) MaskValue(key, value string) string

MaskValue masks a value for logging purposes.

func (*Validator) ShouldMask

func (v *Validator) ShouldMask(key string) bool

ShouldMask returns true if the key's value should be masked in logs.

func (*Validator) ValidateKey

func (v *Validator) ValidateKey(key string) error

ValidateKey validates an environment variable key. Returns an error if the key is invalid or forbidden.

func (*Validator) ValidateRequired

func (v *Validator) ValidateRequired(keys map[string]bool) error

ValidateRequired checks that all required keys are present. Returns an error listing any missing required keys.

func (*Validator) ValidateValue

func (v *Validator) ValidateValue(value string) error

ValidateValue validates an environment variable value. Returns an error if the value contains invalid content.

type ValidatorConfig

type ValidatorConfig struct {
	KeyPattern     *regexp.Regexp
	AllowedKeys    []string
	ForbiddenKeys  []string
	RequiredKeys   []string
	MaxKeyLength   int
	MaxValueLength int
	ValidateUTF8   bool              // Validate that values are valid UTF-8
	IsSensitive    func(string) bool // Injected from root package
	MaskKey        func(string) string
	MaskSensitive  func(string) string
}

ValidatorConfig holds configuration for creating a new Validator.

type Value

type Value struct {
	Type   ValueType
	Scalar string
	Map    map[string]*Value
	Array  []*Value
	Line   int
	Column int
}

Value represents a YAML value.

func NewArrayValue

func NewArrayValue(line, col int) *Value

NewArrayValue creates a new array value with pre-allocated capacity.

func NewMapValue

func NewMapValue(line, col int) *Value

NewMapValue creates a new map value with pre-allocated capacity.

func NewScalarValue

func NewScalarValue(s string, line, col int) *Value

NewScalarValue creates a new scalar value.

func ParseYAML

func ParseYAML(data []byte, maxDepth int) (*Value, error)

ParseYAML parses YAML input and returns a Value tree.

type ValueType

type ValueType int

ValueType represents the type of a YAML value.

const (
	// ValueTypeScalar represents a scalar value (string, number, bool, null).
	ValueTypeScalar ValueType = iota
	// ValueTypeMap represents a map/object.
	ValueTypeMap
	// ValueTypeArray represents an array/list.
	ValueTypeArray
)

type ValueValidator added in v1.1.0

type ValueValidator interface {
	ValidateValue(value string) error
}

ValueValidator validates environment variable values.

type VariableExpander added in v1.1.0

type VariableExpander interface {
	Expand(s string) (string, error)
}

VariableExpander performs variable expansion.

type YAMLError

type YAMLError struct {
	Path    string // YAML path where error occurred
	Line    int    // Line number where error occurred
	Column  int    // Column number where error occurred
	Message string
	Err     error
}

YAMLError represents a YAML parsing error.

func (*YAMLError) Error

func (e *YAMLError) Error() string

Error implements the error interface.

func (*YAMLError) Unwrap

func (e *YAMLError) Unwrap() error

Unwrap returns the underlying error for errors.Is() and errors.As().

type YAMLFlattenConfig

type YAMLFlattenConfig struct {
	// KeyDelimiter is the delimiter for nested keys (default: "_").
	KeyDelimiter string
	// ArrayIndexFormat controls how array indices are formatted.
	// "underscore": KEY_0, KEY_1, etc.
	// "bracket": KEY[0], KEY[1], etc.
	ArrayIndexFormat string
	// NullAsEmpty converts null/~ values to empty strings (default: true).
	NullAsEmpty bool
	// NumberAsString converts numbers to strings (default: true).
	NumberAsString bool
	// BoolAsString converts booleans to strings (default: true).
	BoolAsString bool
	// MaxDepth limits the maximum nesting depth to prevent stack overflow.
	MaxDepth int
}

YAMLFlattenConfig holds configuration for YAML flattening.

Jump to

Keyboard shortcuts

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