You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: constructs/dynamicimports.md
+53Lines changed: 53 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,6 +9,7 @@ Dynamic imports are especially dangerous if you cannot validate upfront what is
9
9
Dynamic imports can be achieved using:
10
10
*`__import__`: This built-in function **SHOULD** never be used. It is an advanced, low-level function that is not needed in everyday Python programming.
11
11
*`importlib.import_module()`: If dynamic imports are unavoidable, this function is the preferred approach. However, its use must be validated upfront.
12
+
*`importlib.util.spec_from_file_location` in combination with `importlib.util.module_from_spec`
From a [security-by-design](https://nocomplexity.github.io/securitybydesign/) perspective, utilizing `importlib.util.spec_from_file_location()` alongside `module_from_spec()` to load plugins introduces severe architectural security risks. This mechanism operates at a low level, bypassing Python’s standard, sandboxed import restrictions and allowing the direct execution of arbitrary code from any accessible file path on the disk.
47
+
48
+
If the file paths or module specifications are influenced by untrusted input—such as user-controlled configuration files or unvalidated directory paths—an attacker can exploit this to achieve Remote Code Execution (RCE) by tricking the application into importing malicious scripts.
49
+
50
+
51
+
Rather than pointing directly to unpredictable locations on disk, modern Python architectures should leverage **namespace packages** or the standard **`importlib.metadata` entry points API**. By registering plugins via package metadata, the application shifts from a dangerous "pull-from-disk" model to a secure, declarative system where only explicitly installed distribution packages can be loaded.
52
+
53
+
54
+
55
+
43
56
## Mitigations
44
57
45
58
There is **always** a security risk when `importlib.import_module()` is used. No mitigation eliminates this risk entirely — it can only be reduced.
For new applications, the most robust and secure options are:
189
+
190
+
1.`importlib.metadata.entry_points()` when plugins are installed as Python packages. This leverages Python's packaging ecosystem and avoids arbitrary file loading.
191
+
2.`pkgutil.iter_modules()` + `importlib.import_module()` when plugins reside in a known package within your application. This provides automatic discovery while still using Python's standard import machinery.
192
+
3. Reserve `importlib.util.spec_from_file_location()` for cases where you genuinely need to load modules from arbitrary file paths (for example, user-provided scripts in a controlled environment). If you use it, ensure the plugin directory is trusted, validate or authenticate plugin files (for example, using signatures or hashes), and avoid passing user-controlled paths directly to the loader.
193
+
194
+
From a security perspective, `spec_from_file_location()` and related APIs (`module_from_spec()`, `exec_module()`) are **always** worth reviewing as this methods can bypass the normal import mechanism and can execute arbitrary Python files. By contrast, `import_module()`, `pkgutil.iter_modules()`, and `importlib.metadata.entry_points()` generally represent a lower-risk patterns because they work within Python's standard import and packaging systems.
195
+
196
+
:::{tip}
197
+
Never trust an import system in Python that has the ability to load arbitrary files!
-[Implicit Execution of Arbitrary Code via Automatic `tools.py` Loading ](https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-2g3w-cpc4-chr4)
0 commit comments