The Configuration Aggregation Problem
In modern cloud-native architectures, configurations rarely exist as a single monolithic JSON file. Instead, applications assemble their active state through multiple layered sources:
When you merge these distinct files, subtle differences in merge semantics can cause catastrophic outages or critical security vulnerabilities.
The Threat: Prototype Pollution via Unsafe Deep Merge
Prototype Pollution is one of the most pervasive vulnerabilities in dynamic JavaScript runtimes. When a recursive merge function blindly copies keys without filtering object meta-properties, an attacker can overwrite `Object.prototype`.
Consider this malicious JSON payload:
{
"__proto__": {
"isAdmin": true,
"role": "super-admin"
}
}If your deep merge function recursively descends into `target[key]`:
// VULNERABLE CODE - DO NOT USE!
function unsafeDeepMerge(target, source) {
for (let key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
if (!target[key]) target[key] = {};
unsafeDeepMerge(target[key], source[key]); // target['__proto__'] modifies Object.prototype!
} else {
target[key] = source[key];
}
}
return target;
}When this runs, **every plain object in the entire application** now evaluates `{}.isAdmin === true`. Authentication gates fail open across the entire server.
The Bulletproof Defense
Always explicitly filter dangerous keys at the loop boundary:
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
export function safeDeepMerge(target: any, source: any) {
const output = { ...target };
for (const key of Object.keys(source)) {
if (FORBIDDEN_KEYS.has(key)) {
// Drop dangerous prototype keys immediately
continue;
}
const targetVal = output[key];
const sourceVal = source[key];
if (
isPlainObject(targetVal) &&
isPlainObject(sourceVal)
) {
output[key] = safeDeepMerge(targetVal, sourceVal);
} else {
output[key] = sourceVal;
}
}
return output;
}Array Collision Strategies: Replace vs. Concat vs. Union
Unlike plain objects where key conflicts have intuitive overwrite rules, arrays present an architectural choice:
Strategy A: Replace (Default in Kubernetes & Helm)
If `base.json` has `"tags": ["web", "frontend"]` and `override.json` has `"tags": ["prod"]`, the resulting array is `["prod"]`. This allows explicit overrides, but makes additive extensions difficult.
Strategy B: Concatenate
The arrays are merged directly: `["web", "frontend", "prod"]`. If duplicate values exist, both remain.
Strategy C: Set Union (Deduplicated)
The elements are combined into a mathematical set: `Array.from(new Set([...base, ...override]))`. This works cleanly for scalar items, but requires deep equality checks for arrays containing nested objects.
The DataTools Approach
The DataTools JSON Merge utility implements: