Lodash isMatchWith Custom Validation on Missing Keys
Lodash's _.isMatchWith enables developers to perform
deep comparisons between a target object and a source object using
custom validation logic. When evaluating missing object keys, it
provides a safe, flexible mechanism to distinguish between an explicitly
undefined property value and a completely omitted key. This
article details how _.isMatchWith invokes custom comparator
functions, passes object contextual arguments, and enables secure
assertion patterns to prevent false-positive matches on missing
properties.
The Mechanics of
_.isMatchWith
The _.isMatchWith(object, source, [customizer]) method
iterates over the own enumerable string-keyed properties of the
source object and determines if the target
object contains equivalent property values. Unlike the
standard _.isMatch, which uses Lodash's internal equality
algorithm (a variant of SameValueZero),
_.isMatchWith delegates individual comparisons to a
user-defined customizer function.
For each property encountered on source, the
customizer is invoked with five arguments:
customizer(objValue, srcValue, key, object, source)If the customizer returns undefined, the
method falls back to the default internal comparison. If it returns a
boolean, that return value strictly determines whether the specific
field matches.
The Challenge of
Missing Keys and undefined
In JavaScript, accessing a non-existent property on an object
evaluates to undefined. This creates a security and
correctness risk when verifying schemas: a target object that completely
lacks a key might inadvertently satisfy a rule if the source object
matches on an undefined value or a permissive
validator.
Standard equality functions cannot distinguish between:
{ role: undefined }(Key exists, explicitly set toundefined){}(Key is missing entirely)
If validation logic relies solely on
objValue === srcValue or merely checks the type of
objValue, an omitted property might be treated as a valid
undefined input, potentially bypassing required-field
constraints.
Securely Validating Key Existence
To securely validate missing keys, the customizer
function must leverage the full signature provided by
_.isMatchWith—specifically using the fourth argument,
object, to confirm property presence before validating the
value.
By employing
Object.prototype.hasOwnProperty.call(object, key) or the
in operator, developers can assert whether the key
physically exists on the target structure.
const _ = require('lodash');
function secureValidator(objValue, srcValue, key, object) {
// Check if the property actually exists on the target object
const hasKey = Object.prototype.hasOwnProperty.call(object, key);
// If the target object is missing the key, explicitly fail validation
if (!hasKey) {
return false;
}
// If custom logic is required for validation (e.g., custom predicate)
if (typeof srcValue === 'function') {
return srcValue(objValue);
}
// Fall back to default Lodash comparison for existing keys
return undefined;
}
const source = {
requiredField: (val) => typeof val === 'string',
};
const incompleteTarget = {};
const validTarget = { requiredField: "configured" };
console.log(_.isMatchWith(incompleteTarget, source, secureValidator)); // false
console.log(_.isMatchWith(validTarget, source, secureValidator)); // trueProtection Against Prototype Pollution
When implementing custom validation against dynamic payloads, relying
on Object.prototype.hasOwnProperty.call rather than
invoking object.hasOwnProperty directly safeguards against
prototype pollution vulnerabilities. If an untrusted payload contains a
malicious hasOwnProperty override, the decoupled method
call ensures reliable execution without throwing runtime exceptions or
executing injected prototype methods.
By verifying the presence of the key on the parent
object reference inside the customizer and explicitly
returning false upon omission, _.isMatchWith
prevents unintended fallback evaluations and enforces strict schema
compliance.