Skip to main content
interlace
Plugin: node-securityRules

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

AspectDetails
SeverityWarning (architecture)
Auto-Fix❌ No (requires architecture change)
CategorySecurity
ESLint MCP✅ Optimized for ESLint MCP integration
Best ForBundler optimization, static analysis

Rule Details

Dynamic require() calls prevent static analysis and break tree-shaking in bundlers.

Why This Matters

IssueImpactSolution
📦 Bundle sizeCan't tree-shakeStatic imports
🔍 Static analysisTools can't analyze depsLiteral paths
🔒 SecurityArbitrary module loadingExplicit 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'
  }
}

Further Reading

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 tracked

Mitigation: 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 tracked

Mitigation: Apply the same rule to imported modules. Use module boundaries and explicit exports.

⚙️ Options

OptionTypeDefaultDescription
allowContextsstring[][]Allow dynamic requires in specific contexts.
allowPatternsstring[][]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:

CodeWhy 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
.oxlintrc.json
{  "jsPlugins": ["oxlint-plugin-node-security"],  "rules": {    "node-security/no-dynamic-require": "error"  }}
vite.config.ts
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.