# How to Protect Against Python Supply Chain Attacks with uv


Malicious packages uploaded to PyPI are usually caught and yanked within hours or days. A [dependency cooldown](https://pydevtools.com/handbook/explanation/what-is-a-dependency-cooldown.md) tells [uv](https://pydevtools.com/handbook/reference/uv.md) to ignore packages published within a recent window, so compromised versions get removed before they ever reach your environment.

## Set a dependency cooldown

The `exclude-newer` setting accepts durations and timestamps. For security hardening, durations are the right choice because they create a rolling window that moves forward with the current date.

### On the command line

```bash
uv lock --exclude-newer "7 days"
uv sync --exclude-newer "7 days"
```

### In project configuration

Add the setting to `pyproject.toml` or `uv.toml` so it applies to every `uv lock` and `uv sync` in the project:

```toml
# pyproject.toml
[tool.uv]
exclude-newer = "7 days"
```

```toml
# uv.toml
exclude-newer = "7 days"
```

### In user-level configuration

Apply the cooldown to all projects on a machine by adding it to the user-level `uv.toml`:

```toml
# ~/.config/uv/uv.toml
exclude-newer = "7 days"
```
```toml
# %APPDATA%\uv\uv.toml
exclude-newer = "7 days"
```
### Accepted duration formats

| Format | Example | Equivalent |
|--------|---------|------------|
| Friendly duration | `"24 hours"`, `"1 week"`, `"30 days"` | Rolling window from now |
| ISO 8601 duration | `"PT24H"`, `"P7D"`, `"P30D"` | Rolling window from now |
| RFC 3339 timestamp | `"2026-03-20T00:00:00Z"` | Fixed point in time |

Durations are resolved to a fixed number of seconds assuming 24-hour days (DST is ignored). Calendar units like months and years are not allowed.

## Let an urgent patch through the cooldown

When a security fix lands inside the cooldown window, `exclude-newer-package` overrides the global setting for a single package. Set it to `false` to exempt that package entirely, or to a timestamp or duration to give it a different window:

```toml
[tool.uv]
exclude-newer = "7 days"
exclude-newer-package = { my-package = false }   # install my-package regardless of age
```

```bash
uv lock --exclude-newer "7 days" --exclude-newer-package my-package=false
```

Per-package values take precedence over the global cooldown, so the rest of the dependency tree keeps its seven-day buffer while the exempted package resolves to its newest release.

## Choose a cooldown period

The right window depends on how fast a project needs new releases versus how much risk it can tolerate.

7 days is a practical default. Most malicious uploads are detected within this window, and most projects can wait a week for new dependency versions. The [litellm supply chain attack](/blog/litellm-supply-chain-attack-and-securing-python-dependencies/) in March 2026 was discovered and yanked within hours, so a 7-day buffer would have been more than enough.

30 days suits production deployments and internal tooling where stability matters more than access to recent releases.

## Caveats

Silent exclusion. When `exclude-newer` filters out a package version, uv does not mention it in error messages. If resolution fails because the only matching version was published too recently, the error will say no compatible version exists. Keep this in mind when debugging resolution failures.

Index compatibility. The cooldown relies on the `upload-time` field defined in [PEP 700](https://peps.python.org/pep-0700/). PyPI supports this field. Custom or private indexes may not, in which case `exclude-newer` has no effect for packages hosted there.

> [!TIP]
> A cooldown protects the moment when new versions enter the dependency tree. A [lockfile](https://pydevtools.com/handbook/how-to/how-to-use-a-uv-lockfile-for-reproducible-python-environments.md) pins the exact versions that were resolved. Use both together.

## Block known malware at install time

A cooldown buys time for malicious uploads to be detected. uv's malware check acts on the detections themselves: with the check enabled, every command that syncs the environment (`uv add`, `uv sync`, and others) looks up the locked packages against the MAL advisories in the [OSV](https://osv.dev/) database, sourced from the OpenSSF [malicious-packages](https://github.com/ossf/malicious-packages) project. If a locked package matches an advisory, uv stops the sync before the package is installed, so none of its code runs.

Enable the check with the `UV_MALWARE_CHECK` environment variable:

```bash
export UV_MALWARE_CHECK=1
```
```powershell
$env:UV_MALWARE_CHECK = "1"
```
With the variable set, each sync reports the lookup:

```console
$ uv sync
Resolved 2 packages in 2ms
warning: Malware checks are experimental and may change without warning. Pass `--preview-features malware-check` to disable this warning.
Checked 1 package in 0.39ms
```

The check only blocks malware that has already been identified and recorded as an advisory. The gap between a malicious upload and its advisory is the window the cooldown covers, so run both.

> [!NOTE]
> Malware checks require uv 0.11.16 or later. Run `uv self version` to check, and `uv self update` to upgrade.

## Learn more

- [uv: A Complete Guide](https://pydevtools.com/handbook/explanation/uv-complete-guide.md) covers what uv does, how fast it is, the core workflows, and recent releases.
- [`--exclude-newer` for reproducible environments](https://pydevtools.com/handbook/how-to/how-to-use-exclude-newer-for-reproducible-python-environments.md)
- [How to Scan Python Dependencies for Vulnerabilities](https://pydevtools.com/handbook/how-to/how-to-scan-python-dependencies-for-vulnerabilities.md) covers `uv audit`, the after-the-fact companion to these install-time defenses.
- [LiteLLM supply chain attack and securing Python dependencies](/blog/litellm-supply-chain-attack-and-securing-python-dependencies/)
- [uv documentation on dependency cooldowns](https://docs.astral.sh/uv/concepts/resolution/#dependency-cooldowns)
- [Vulnerability and malware checks in uv](https://astral.sh/blog/uv-audit) is Astral's announcement of the malware check and `uv audit`.
