Safeguard
Developer Tools•

How do you ban risky modules in Node code with ESLint?

Use no-restricted-imports and no-eval to block child_process, eval and other risky APIs in a Node project, with a note on what the rules miss.

Safeguard Security Team
3 min read

Use the core ESLint rule no-restricted-imports to list modules your team must not import directly, with a message that points to the approved wrapper. Add no-eval for dynamic code. Both are built in, so they add no dependency, but neither covers dynamic imports or indirect paths.

What no-restricted-imports does

The ESLint documentation says the rule reports static import declarations that load disallowed modules. It does not check dynamic imports. You configure it with a list of names, or with paths for exact module names and patterns for gitignore style patterns. A message is appended to the error. The docs give an example for child_process:

"no-restricted-imports": ["error", {
  "paths": [{
    "name": "child_process",
    "message": "Do not import child_process directly."
  }],
  "patterns": [{
    "group": ["child_process/*"],
    "message": "Do not import child_process submodules directly."
  }]
}]

paths matches the exact module, and patterns covers subpaths.

Check the node: prefix yourself

Node code often writes node:child_process. The ESLint page does not say whether that form matches the example above, so test it. Add both spellings to paths to be safe, and write a tiny fixture file that imports each form and confirm the rule fires.

What no-eval covers

The no-eval rule flags calls to eval(), including indirect forms such as (0, eval)(...) and window.eval(...). The docs call eval potentially dangerous and note that using it on untrusted code can open a program to several injection attacks. The option allowIndirect defaults to false. The docs list limits: it flags any call named eval even when that is a local parameter, and it cannot see a renamed global such as const foo = window; foo.eval(...).

Pair it with a runtime flag

Lint is a review time control. Node.js also has --disallow-code-generation-from-strings, which makes eval and new Function throw. The docs say it does not affect the node:vm module. Use the flag as a second layer, and test your dependencies under it before enabling it in production.

Steps

  1. Decide your banned list. Common candidates are child_process, vm, and any module with a wrapper that adds validation.
  2. Write one wrapper per banned module, in a single folder.
  3. Add an override in your ESLint config that turns the rule off only for that folder.
  4. Add no-eval as an error.
  5. Add fixture tests for both import spellings.
  6. Add the lint step to required checks.

What this does not stop

A dependency can still call child_process internally, and lint will not see it. A dynamic import() or require() call with a computed name also slips past. For those, review what your dependencies do and keep scanning. The rule shapes your own code, not the code you install, which is why the next layer is understanding install scripts.

Quick check

Does no-restricted-imports catch dynamic import calls?

No. The ESLint docs say it checks static import declarations only.

Does the runtime flag affect node:vm?

No. The Node.js docs say --disallow-code-generation-from-strings does not affect node:vm.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.