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
- Decide your banned list. Common candidates are
child_process,vm, and any module with a wrapper that adds validation. - Write one wrapper per banned module, in a single folder.
- Add an override in your ESLint config that turns the rule off only for that folder.
- Add
no-evalas an error. - Add fixture tests for both import spellings.
- 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.