fix: replace YAML-based DeepCopy with recursive deep copy to fix #973 (#2675)

Environment.DeepCopy() used a YAML marshal/unmarshal round-trip to copy
values. When SOPS/KMS-encrypted secret files contained values with special
characters (colons, quotes, braces such as ~masked:ab#7i7!;{'".), the
YAML round-trip could mangle or silently drop adjacent keys, producing
the "map has no entry for key" error reported in #973.

Replace the YAML-based DeepCopy with maputil.DeepCopyMap(), a proper
recursive deep copy that:
- Preserves original Go types (string "true" stays string, not bool)
- Never loses data due to special characters in values
- Normalises map[any]any keys to strings (matching CastKeysToStrings)

Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
yxxhero
2026-06-29 20:40:18 +08:00
committed by GitHub
parent bbf925f154
commit ad10814842
4 changed files with 225 additions and 43 deletions
+38
View File
@@ -39,6 +39,44 @@ func CastKeysToStrings(s any) (map[string]any, error) {
return new, nil
}
// DeepCopyMap creates a deep copy of a map[string]any, recursively copying all
// nested maps and slices. Unlike YAML marshal/unmarshal round-trips, it preserves
// the original Go types of all values (e.g. the string "true" stays a string,
// not a bool) and never loses data due to special characters in values.
// It also normalises map[any]any keys to strings, matching CastKeysToStrings.
func DeepCopyMap(m map[string]any) map[string]any {
if m == nil {
return nil
}
result := make(map[string]any, len(m))
for k, v := range m {
result[k] = deepCopyValue(v)
}
return result
}
func deepCopyValue(v any) any {
switch val := v.(type) {
case map[string]any:
return DeepCopyMap(val)
case map[any]any:
copied := make(map[string]any, len(val))
for k, item := range val {
copied[fmt.Sprintf("%v", k)] = deepCopyValue(item)
}
return copied
case []any:
copied := make([]any, len(val))
for i, item := range val {
copied[i] = deepCopyValue(item)
}
return copied
default:
// Primitives (string, int, bool, nil, float, etc.) are copied by value.
return val
}
}
func RecursivelyStringifyMapKey(v any) (any, error) {
var castedV any
switch typedV := v.(type) {