require.extensions / Module._extensions
The handler fires on every require() call matching the registered extension
- Lives in
require.extensions / Module._extensions in any loaded module- Attacker needs
- Supply-chain foothold
- Lifecycle
- InstallStartupyour codeModule loadRuntimeREPL
Runs on import or require, for every module the app loads.
How it runs
Deprecated since Node 0.10 but still functional. require.extensions (and its internal twin Module._extensions) registers custom handlers for file extensions. When require() hits a file with a registered extension, it calls your handler instead of the default loader.
require.extensions['.txt'] = (module, filename) => {
const fs = require('fs');
// arbitrary code runs here for every require('./anything.txt')
module.exports = fs.readFileSync(filename, 'utf8');
};
On its own this is a feature. It's how ts-node and @babel/register work. The problem is that registration is global and first-come-first-served. Any dependency buried three levels deep can register a handler for .json and intercept every require('./config.json') in your app, or wrap the .node loader with instrumentation. Registration happens when the dependency is required. Nothing notifies you, nothing checks permissions, and there's no way to scope a handler to a single package.
Defend
Audit dependencies that register extension handlers; prefer ESM loaders with explicit scoping; monitor for unexpected handler registration.