How Lodash isNative Bypasses Overridden Functions
Lodash’s _.isNative utility function determines whether
a target function is a genuine host-environment built-in rather than a
userland polyfill or an overridden implementation. This article examines
the internal mechanisms Lodash uses to achieve this, including isolating
pristine prototype methods, dynamically generating engine-tailored
regular expressions, and detecting hidden metadata flags from
third-party polyfills to ensure functions are evaluated securely without
executing untrusted code.
The Problem with Naive Function Verification
In JavaScript, functions can be monkey-patched or assigned custom properties. A naive check for a native function often looks like this:
function isNative(fn) {
return fn.toString().includes('[native code]');
}This approach has critical vulnerabilities:
- Method Overriding: If a function object defines its
own
toStringmethod (e.g.,fn.toString = () => "function () { [native code] }"); it can easily deceive the caller. - Engine Discrepancies: Different JavaScript engines format native function representations differently (for instance, differences between V8, SpiderMonkey, and JavaScriptCore).
Bypassing
Instance-Level toString Hijacking
To prevent a function from forging its own string representation,
Lodash does not call the method on the target instance directly.
Instead, it captures and uses the original, unmutated
Function.prototype.toString method:
const fnToString = Function.prototype.toString;
// Invocation
fnToString.call(value);By explicitly invoking the native
Function.prototype.toString with .call(value),
Lodash completely bypasses any custom .toString properties
an attacker or library may have placed directly onto the function
instance.
Dynamic Regular Expression Generation
Different JavaScript environments output variations of native functions. Rather than relying on a hardcoded string or static pattern, Lodash builds a dynamic regular expression tailored to the host environment at load time.
Lodash generates this expression by converting a known native
function—Object.prototype.hasOwnProperty—to a string and
escaping special characters:
const reIsNative = RegExp(
'^' +
fnToString.call(Object.prototype.hasOwnProperty)
.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
.replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') +
'$'
);This dynamic pattern captures the precise format used by the current
engine while replacing the function name with a wildcard pattern
(.*?). When an arbitrary function is passed into
_.isNative, its string representation via
Function.prototype.toString.call(value) must match this
engine-specific template exactly.
Detecting Polyfill Masking
Modern polyfill libraries like core-js often attempt to
hide their presence by spoofing Function.prototype.toString
to return [native code]. To prevent false positives, Lodash
inspects the function for internal masking flags.
When core-js replaces or wraps a native method, it
typically leaves a metadata fingerprint on the global environment or
function instance:
const coreJsData = root['__core-js_shared__'];
const maskFunctionKey = coreJsData ? coreJsData.keys && coreJsData.keys.IE_PROTO : '';
function isMasked(func) {
return !!maskFunctionKey && (maskFunctionKey in func);
}Lodash checks whether a function contains these masking markers. If a function matches the native regular expression but has been flagged by a polyfill framework, Lodash rejects it as non-native.
Handling Host Objects and Proxies
In addition to string inspection, _.isNative
incorporates basic type validation:
- It asserts that the input is a valid object or function
(
typeof value === 'function'or non-null object for host methods in older environments). - It safely catches errors that might be thrown when inspecting non-standard host objects (such as those in legacy Internet Explorer or specific embedded runtimes).
While ES2015 Proxy objects cannot be completely
distinguished from native functions if explicitly configured to mimic
them at the prototype level, Lodash's multi-layered strategy protects
against common override techniques, monkey-patching, and polyfill
impersonation.