mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-06-05 22:44:17 +00:00
Fix cache serialisation (#117)
* Fix cache serialisation * fix(cache): add integer overflow protection for serialization - [x] Add maxCacheEntrySize constant (64 MiB) to prevent memory overflow - [x] Validate byte slice size before adding marker byte - [x] Validate JSON-serialized data size before marker addition - [x] Add comprehensive overflow protection test cases
This commit is contained in:
+75
-4
@@ -21,6 +21,10 @@ const (
|
||||
CacheTypeJWK CacheType = "jwk"
|
||||
CacheTypeSession CacheType = "session"
|
||||
CacheTypeGeneral CacheType = "general"
|
||||
|
||||
// maxCacheEntrySize defines the maximum size for a single cache entry (64 MiB)
|
||||
// This prevents integer overflow when allocating memory for serialization
|
||||
maxCacheEntrySize = 64 * 1024 * 1024
|
||||
)
|
||||
|
||||
// UniversalCacheConfig provides configuration for the universal cache
|
||||
@@ -768,14 +772,81 @@ func (c *UniversalCache) Strategy() CacheStrategy {
|
||||
|
||||
// serialize converts a value to bytes for backend storage
|
||||
func (c *UniversalCache) serialize(value interface{}) ([]byte, error) {
|
||||
// Use JSON for serialization - simple and universal
|
||||
return json.Marshal(value)
|
||||
// If value is already a byte slice (e.g., pre-marshaled JSON from metadata_cache),
|
||||
// store it directly with a marker to prevent double-encoding.
|
||||
// This fixes the issue where []byte was being JSON-marshaled, causing Base64 encoding.
|
||||
if bytes, ok := value.([]byte); ok {
|
||||
// Validate size to prevent integer overflow
|
||||
if len(bytes) > maxCacheEntrySize {
|
||||
return nil, fmt.Errorf("cache entry size %d exceeds maximum allowed size %d", len(bytes), maxCacheEntrySize)
|
||||
}
|
||||
// Check for potential overflow when adding marker byte
|
||||
if len(bytes) == maxCacheEntrySize {
|
||||
return nil, fmt.Errorf("cache entry size would overflow when adding marker byte")
|
||||
}
|
||||
|
||||
// Prepend marker byte 0x00 to indicate raw bytes (not JSON-encoded)
|
||||
result := make([]byte, len(bytes)+1)
|
||||
result[0] = 0x00
|
||||
copy(result[1:], bytes)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// For all other types (maps, strings, etc.), use JSON encoding
|
||||
// Prepend marker byte 0x01 to indicate JSON-encoded data
|
||||
jsonData, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate size to prevent integer overflow
|
||||
if len(jsonData) > maxCacheEntrySize {
|
||||
return nil, fmt.Errorf("serialized cache entry size %d exceeds maximum allowed size %d", len(jsonData), maxCacheEntrySize)
|
||||
}
|
||||
// Check for potential overflow when adding marker byte
|
||||
if len(jsonData) == maxCacheEntrySize {
|
||||
return nil, fmt.Errorf("serialized cache entry size would overflow when adding marker byte")
|
||||
}
|
||||
|
||||
result := make([]byte, len(jsonData)+1)
|
||||
result[0] = 0x01
|
||||
copy(result[1:], jsonData)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// deserialize converts bytes from backend storage to a value
|
||||
func (c *UniversalCache) deserialize(data []byte, value interface{}) error {
|
||||
// Use JSON for deserialization
|
||||
return json.Unmarshal(data, value)
|
||||
if len(data) == 0 {
|
||||
return fmt.Errorf("cannot deserialize empty data")
|
||||
}
|
||||
|
||||
// Check for type marker (added by serialize)
|
||||
if data[0] == 0x00 {
|
||||
// Raw bytes - strip marker and return as-is
|
||||
rawBytes := data[1:]
|
||||
if ptr, ok := value.(*interface{}); ok {
|
||||
*ptr = rawBytes
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("cannot deserialize raw bytes into %T", value)
|
||||
}
|
||||
|
||||
if data[0] == 0x01 {
|
||||
// JSON-encoded - strip marker and unmarshal
|
||||
return json.Unmarshal(data[1:], value)
|
||||
}
|
||||
|
||||
// Legacy data without marker (for backward compatibility)
|
||||
// Try to unmarshal as JSON
|
||||
if err := json.Unmarshal(data, value); err != nil {
|
||||
// If unmarshal fails, treat as raw bytes
|
||||
if ptr, ok := value.(*interface{}); ok {
|
||||
*ptr = data
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// prefixKey adds a cache type prefix to the key for backend storage
|
||||
|
||||
Reference in New Issue
Block a user