How it shows up in Python
This class is not about a value reaching a dangerous call; it is about which file the interpreter runs. Python runs the code in a module the moment it is imported, so a file that an attacker can write is code the attacker can run. The usual ways it happens:
- A world-writable or group-writable directory on the import path. A plugin folder, a shared
/opttree, or a directory onPYTHONPATHthat another local user or service can write to. Dropping a file there, or editing one, injects code into every program that imports from it. - The current directory on the path. A tool run from a directory an attacker can write to may import a file they planted there, because the working directory sits early on the import path.
- Shadowing a standard-library name. A file called
json.pyorlogging.pynext to the program, or installed over a standard-library name, is imported instead of the real module.import jsonthen runs the attacker's file. - A plugin system that loads files from a writable location.
importlib,runpy, orcompile()/exec()reading a file whose contents an attacker could change.
The defence is ownership and permissions, not input filtering. Keep every directory on the import path owned by the deploying user and writable by no one else; keep world-writable directories off PYTHONPATH; run production tools from a directory only you can write to; and load plugins only from a location your deploy controls, ideally verifying them against a manifest you shipped. Running the application as an unprivileged user with a read-only code tree removes most of the opportunity.
Why it is hard to see
- The source file is clean. The vulnerability is the permissions on a directory, not a line of code, so a static analyzer reading the source has nothing to flag.
- It happens during startup. The malicious import often runs while the program is still initialising, before any request is handled, so request-scoped tooling never engages.
- It is a legitimate import.
import jsonis correct code; only the file it resolves to is wrong. Nothing about the call is suspicious on its own. - No network involved. This is a local attacker or a compromised neighbouring account, so an edge filter sees nothing.
What the es-python event looks like
With the foreign source on, es-python reports when the program loads code from a file another user on the machine can write. The event names the file: foreign-code: followed by the path, once per file per process so an import loop does not flood the list, and it carries the untrusted code load impact tag. The load operation is reported as run script, run pyc, import (foreign code) or compile, depending on how the file was reached. You can trust specific locations on purpose, such as a shared tools directory your team maintains, by listing their path prefixes in the agent's foreign-code allowlist; nothing is exempt automatically.
A second check, stdlib shadowing, fires when a module whose name belongs to the standard library is loaded from a file outside the standard-library tree, so import json running someone else's json.py is reported even when that file is one you own. Because loading foreign code is exactly what the agent exists to stop, a Raise rule here raises a RuntimeError and blocks the load, even during startup imports, rather than letting it run and only recording it.
How to fix and prevent it
- Make every directory on the import path, including the working directory for production tools, writable only by the deploying user.
- Keep the current directory and any world-writable directory off
PYTHONPATH. - Load plugins only from a location your deploy controls, and verify them against a manifest where you can.
- Give local modules names the standard library does not use; the shadow check points out a clash.
- Run the application as an unprivileged user with a read-only code tree.
- Turn on the foreign source in Report mode to see which files the program loads that another user could write, then raise on it once the list is clean.
Classification
Loading foreign-writable code is CWE-829, Inclusion of Functionality from Untrusted Control Sphere; shadowing a module via the import path is CWE-427, Uncontrolled Search Path Element. OWASP maps both to A08:2025 Software or Data Integrity Failures in the Top 10 for 2025.
Questions people ask
The questions that come up on forums and in reviews about this class, each answered in a few sentences.
What counts as untrusted code loading?
Importing or running a Python file that someone other than the program's owner could have written: a module in a directory another user can write to, a script fetched from a shared location, or a plugin folder with loose permissions. Whoever can edit that file runs code inside your program with its privileges. The flaw is in where the code comes from, not in what it contains today.
Is importing from a user-writable directory really a vulnerability?
Yes, because an import executes the module's top level, so a writable directory early on sys.path lets any local user run code in your process the next time it starts, and a file named like a standard-library module shadows the real one. Shared hosts, CI runners and containers that mount a host directory are where this happens in practice. Fix the permissions, keep sys.path to directories only the owner can write, and keep the working directory off the path.
How does es-python report untrusted code loading?
With the foreign source on, it reports a load from a file another user can write, named foreign-code: plus the path, once per file per process, with the untrusted code load impact tag and the operation (run script, run pyc, import (foreign code) or compile). A separate stdlib shadowing check fires when a standard-library module name is loaded from outside the standard library. A Raise rule blocks the load even during startup imports, and an allowlist of path prefixes trusts the locations you choose.