no-dynamic-require
Forbid require() calls with non-literal arguments
Forbid require() calls with non-literal arguments. This rule is part of eslint-plugin-node-security.
Quick Summary
| Aspect | Details |
|---|---|
| Severity | Warning (architecture) |
| Auto-Fix | ❌ No (requires architecture change) |
| Category | Security |
| ESLint MCP | ✅ Optimized for ESLint MCP integration |
| Best For | Bundler optimization, static analysis |
Rule Details
Dynamic require() calls prevent static analysis and break tree-shaking in bundlers.
Why This Matters
| Issue | Impact | Solution |
|---|---|---|
| 📦 Bundle size | Can't tree-shake | Static imports |
| 🔍 Static analysis | Tools can't analyze deps | Literal paths |
| 🔒 Security | Arbitrary module loading | Explicit imports |
Examples
❌ Incorrect
const moduleName = getModuleName();
const mod = require(moduleName); // Dynamic
const plugin = require(`./plugins/${name}`); // Template literal
const handler = require(path.join(__dirname, name)); // Computed✅ Correct
// Static requires
const mod = require('./module');
// Dynamic import (when truly needed)
const mod = await import(`./plugins/${name}`);
// Explicit mapping
const plugins = {
a: require('./plugins/a'),
b: require('./plugins/b'),
};
const plugin = plugins[name];Configuration Examples
Basic Usage
{
rules: {
'node-security/no-dynamic-require': 'warn'
}
}Related Rules
no-commonjs- Prevent CommonJS usageno-unsafe-dynamic-require- Security-focused variant
Further Reading
- Webpack Dynamic Imports - Code splitting guide
Known False Negatives
The following patterns are not detected due to static analysis limitations:
Dynamic Variable References
Why: Static analysis cannot trace values stored in variables or passed through function parameters.
// ❌ NOT DETECTED - Value from variable
const value = externalSource();
processValue(value); // Variable origin not trackedMitigation: Implement runtime validation and review code manually. Consider using TypeScript branded types for validated inputs.
Wrapped or Aliased Functions
Why: Custom wrapper functions or aliased methods are not recognized by the rule.
// ❌ NOT DETECTED - Custom wrapper
function myWrapper(data) {
return internalApi(data); // Wrapper not analyzed
}
myWrapper(unsafeInput);Mitigation: Apply this rule's principles to wrapper function implementations. Avoid aliasing security-sensitive functions.
Cross-Module Data Flow
Why: ESLint rules analyze one file at a time. Values imported from other modules cannot be traced.
// ❌ NOT DETECTED - Value from import
import { getValue } from './helpers';
processValue(getValue()); // Cross-file not trackedMitigation: Apply the same rule to imported modules. Use module boundaries and explicit exports.
⚙️ Options
| Option | Type | Default | Description |
|---|---|---|---|
allowContexts | string[] | [] | Allow dynamic requires in specific contexts. |
allowPatterns | string[] | [] | Regex patterns matched against the source text of the require argument; a match suppresses the report. |
Not a finding
The question is whether a specifier can change, not whether it is spelled as a literal. Resolution runs through ESLint's scope analysis, so all of these are silent:
| Code | Why it is silent |
|---|---|
require(`b`) | A template with no interpolation is a literal. |
const d = 'debounce'; require(`lodash/${d}`) | Every interpolated part resolves to a constant. |
require(__dirname + '/utils') | __dirname is where the module sits on disk, fixed at load. |
require.resolve('eslint/package.json') | A lookup against the dependency tree. |
If it fires, the specifier reaches a name this file cannot resolve — a parameter, an import, a variable written more than once. That is genuinely unknowable here, which is exactly the case the rule exists for.
Use this rule
npm install -D oxlint-plugin-node-security{ "jsPlugins": ["oxlint-plugin-node-security"], "rules": { "node-security/no-dynamic-require": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-node-security'], rules: { 'node-security/no-dynamic-require': 'error', }, },});oxlint-plugin-node-security re-exports eslint-plugin-node-security/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.