pytest loads every plugin registered through installed third-party package entry points at startup, unless the PYTEST_DISABLE_PLUGIN_AUTOLOAD environment variable is set. That means installing a package can change how your tests run, with no line in your test code naming it. In CI, that is worth controlling.
Definition
A pytest plugin is Python code that hooks into test collection, execution and reporting. The pytest docs list where plugins come from: built in plugins, external plugins discovered through entry points in packaging metadata, and local conftest.py files. External plugins are the interesting category for supply chain reviews, because the discovery happens through installed packages.
At startup, pytest scans the command line for -p no:name to block plugins, then loads plugins registered through installed third-party entry points unless the disable variable is set. A plugin can then run code during import and during every hook.
Why it matters for security
Test runs usually have access to things production builds do not: test databases, API keys for sandboxes, source checkouts and sometimes cloud credentials in CI. A plugin pulled in as a dependency of a dependency gets all of that at import time.
| Scenario | Consequence |
|---|---|
| A new dev dependency brings a plugin | Behavior of the test run changes without a code change |
| A compromised plugin release | Code runs in every CI job that installs it |
| A plugin you no longer need stays installed | Unused code keeps running |
The controls the docs give you
The documentation describes three relevant mechanisms:
# Block one plugin by name for this run
pytest -p no:NAME
# Disable autoloading entirely, then load only what you name
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest -p xdist
# The same as a flag
pytest --disable-plugin-autoload -p NAME -p NAME2
You can also put these options in your configuration file so every contributor gets them, and use the PYTEST_ADDOPTS environment variable to apply a block only in specific environments such as a CI server. The flag form was added in pytest 8.4. The docs also warn against specifying the same plugin through multiple mechanisms when you control loading explicitly.
A practical approach
- List what is loaded. The header of a normal pytest run prints plugin names and versions. Save that output once and review it.
- Decide which plugins you expect: your coverage tool, parallelization, maybe a mocking helper.
- In CI, turn autoloading off and load only the approved list. A new plugin then fails loudly, or does nothing, until someone adds it to the list.
- Pin plugin versions in your lock file, and review upgrades like any other change.
- Keep secrets out of the test environment where you can. Use scoped, short lived credentials for tests that truly need them.
- Re-run the list after dependency updates. A diff in plugin names is a signal.
Trade-offs
Turning autoload off means you must name each plugin. That is an upkeep cost, and a good one: it forces a conversation when something new arrives. For small projects with a handful of well known plugins, a lock file plus a reviewed run header may be enough.
Try it yourself
Click through it at your own pace, or open it full screen. Look for the test tooling packages in the inventory and how they arrived.
Where Safeguard fits
Safeguard's SBOM and dependency scanning covers the Python packages behind your test tooling, and policies and gates can check dependency changes before merge. The free scan is at gold.safeguard.sh/scan.
Quick check
Does pytest load plugins from installed packages by itself?
Yes, plugins registered through entry points load at startup unless PYTEST_DISABLE_PLUGIN_AUTOLOAD is set.
Can I block a single plugin?
Yes, with pytest -p no:NAME, on the command line, in the config file or through PYTEST_ADDOPTS.
Sources
Last reviewed: 19 October 2026.