Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Security

Supply-chain and CI security are increasingly important for scientific Python projects; new attacks are targeting smaller packages than ever before thanks to the ease with which exploits can be found and utilized with AI. The first six months of 2026 had 4.5x the malicious package volume of all of 2025[1].

Most of these attacks strung together smaller vulnerabilities into something exploitable, often in CI like GitHub Actions. Once in, the attacks upload malicious packages that spread the attack via PyPI or NPM.

This page has recommendations for keeping your repository and its automation secure. This will never be complete, but even a few small steps can make your code much more secure.

GitHub Actions

SEC001 GitHub Actions workflows are a common source of security issues, due to how commonly it is used, and its original design being focused on ease of use and convenience.

Common security problems:

Zizmor

zizmor is a static analysis tool that audits your workflows for common problems, including many of the ones above. You can run it as a pre-commit hook or as a GitHub Action:

pre-commit
GitHub Actions
- repo: https://github.com/zizmorcore/zizmor-pre-commit
  rev: "v1.26.1"
  hooks:
    - id: zizmor

You can silence individual findings with # zizmor: ignore[rule] comments, or collect them in a zizmor.yml config file.

Cooldowns

Often malicious code is found and removed from indexes in a day or two. Adding a cooldown can protect you from some of these large scale attacks that are caught quickly. There’s a problem though; this also means you’ll take longer to get fixes for known vulnerabilities. Also, if everyone does this, it could just delay discovering attacks. So try to strike the appropriate balance.

Dependabot’s cooldown can do that for you (GitHub Actions, pre-commit, lock files for most languages):

version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    cooldown:
      default-days: 3

The cooldown block goes inside each updates: entry; repeat it for every ecosystem you configure.

For uv, you can add hard-coded config with exclude-newer:

[tool.uv]
exclude-newer = "3 days"

You can also override or skip specific packages.

For uv or pip, you can set environment variables:

PIP_UPLOADED_PRIOR_TO="P3D"
UV_EXCLUDE_NEWER="3 days"  # P3D works too

There are also matching command line arguments for both (--uploaded-prior-to/--exclude-newer). You can also use dates instead of durations, which works much like lockfiles without the churn. Use a full date, including time, like "2026-01-01T00:00:00Z", to get matching behavior in pip and uv, otherwise they will be one day off each other.

Footnotes