Non-literal Dynamic Import Detected

MEDIUM

__import__() or importlib.import_module() with a non-literal argument can import arbitrary modules when called with untrusted input.

Rule Information

Language
Python
Category
Python Core
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythondynamic-importimport-injection__import__importlibCWE-95OWASP-A03
CWE References

Interactive Playground

Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.

pathfinder scan --ruleset python/PYTHON-LANG-SEC-005 --project .
1
2
3
4
5
6
7
8
rule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

About This Rule

Understanding the vulnerability and how it is detected

Python's __import__() built-in and importlib.import_module() allow modules to be loaded by name at runtime. When the module name argument is derived from untrusted external input such as HTTP parameters, configuration files, or user-provided data, an attacker can import malicious modules, access sensitive built-in modules such as os or subprocess, or perform path traversal to load modules from unexpected filesystem locations.

Common vulnerable patterns include plugin systems that load user-specified modules by name, serialization systems that reconstruct classes using stored module paths, and configuration- driven dispatch tables that resolve handler names from external data.

The fix is always to validate the module name against an explicit allowlist of permitted module names before calling import_module(), ensuring no user-controlled value can cause an unexpected module to be loaded.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Arbitrary Module Execution

An attacker who controls the module name can import os, subprocess, socket, or any other standard library module and immediately call dangerous functions on it. Module-level code is executed on import, so importing a malicious module can trigger code execution before any function is called.

2

Plugin System Exploitation

Plugin systems using import_module() to load user-specified plugins are vulnerable to loading attacker-controlled modules from the Python path. If the attacker can place a file on the server or influence sys.path, they can execute arbitrary code through the plugin loader.

3

Pickle-like Class Reconstruction Attacks

Serialization formats that store class locations as module:classname strings and use import_module() to reconstruct them are vulnerable to the same class of attacks as pickle deserialization. An attacker who controls the stored class location can cause any callable to be invoked during deserialization.

4

Path Traversal to Sensitive Modules

Depending on sys.path configuration, an attacker may be able to use relative module names or dotted paths to traverse to modules outside the intended plugin directory, potentially loading internal utility modules that expose sensitive operations.

How to Fix

Recommended remediation steps

  • 1Always validate the module name against an explicit allowlist before calling import_module() or __import__().
  • 2Prefer a static import registry (dict mapping names to already-imported classes) over dynamic imports to avoid the risk entirely.
  • 3Restrict plugin directories and sys.path to prevent loading of modules outside the intended scope.
  • 4Use __import__() only with hardcoded string literals; use importlib.import_module() for dynamic imports with proper validation.
  • 5Log all dynamic module imports with the caller context to detect attempts to load unauthorized modules.

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule detects calls to __import__() and importlib.import_module() where the module name argument is not a string literal. It flags all such calls since the module name may be influenced by external input at runtime. Calls with hardcoded string literals are considered lower risk but are still flagged for review.

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

CWE Top 25
CWE-95 - Eval Injection in the MITRE CWE Top 25 Most Dangerous Software Weaknesses
OWASP Top 10
A03:2021 - Injection
NIST SP 800-53
SI-10: Information Input Validation
PCI DSS v4.0
Requirement 6.2.4 - Protect web-facing applications against injection attacks

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about Non-literal Dynamic Import Detected

Dynamic importing is only dangerous when the module name can be influenced by untrusted input. Plugin systems that load modules from a fixed list of developer-defined names are safe. The risk arises when an HTTP parameter, file path, or environment variable value reaches the module name argument without validation.
Both ultimately load Python modules, but importlib.import_module() is the recommended public API for dynamic imports since Python 3. __import__() is the lower-level built-in that import statements compile to. Both are equally dangerous when called with untrusted arguments. Prefer importlib.import_module() with an allowlist when dynamic loading is genuinely required.
Yes. Standard library modules such as os, subprocess, socket, shutil, and ctypes all provide capabilities that can be used for attacks. Importing os and calling os.system() or os.environ is a common technique for exploiting dynamic import vulnerabilities. An allowlist must not include any module with dangerous capabilities.
Yes, all non-literal dynamic imports are flagged. Package initialization code that uses importlib to load sub-modules can typically be rewritten with explicit static imports or a module registry pattern. Suppressed findings should document the trust boundary and explain why the module name cannot be attacker-controlled.
Serializers that reconstruct objects by dynamically importing their class module are vulnerable to this class of attack when the serialized data comes from untrusted sources. See PYTHON-LANG-SEC-040 through PYTHON-LANG-SEC-046 for deserialization-specific rules.
Maintain an allowlist of permitted plugin module names, ideally populated from a developer-controlled configuration file (not user input). Validate the requested plugin name against this list before calling import_module(). Consider using Python's entry_points mechanism (setuptools) for plugin discovery, which limits loading to installed packages.

New feature

Get these findings posted directly on your GitHub pull requests

The Non-literal Dynamic Import Detected rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works