DEV Community

Leo
Leo

Posted on • Originally published at cicd.deployment.to

PyPI stops accepting late file uploads to releases older than 14 days

PyPI now rejects new file uploads against any release older than 14 days, closing off a small but useful window that a compromised publishing token could use to graft malicious content onto a trusted, established package. Per the DevOps.com writeup, PyPI has said it is not aware of the technique being used in a real attack yet. The change is preventative.

What the 14-day rule actually blocks

The version stays. The metadata stays. The wheels and sdist you shipped on release day stay. What stops working is adding another file to that version two weeks after the fact.

Normal publishing is unaffected. Cutting a version and pushing wheels and sdist across the same day, or over two or three days while a build matrix finishes, still works. Adding a Python 3.14 build to a wheel set from three months ago does not.

PyPI checked the impact against its own data before flipping the default. According to the piece, only a tiny fraction of the platform's most popular packages had added a Python 3.14-compatible build more than two weeks after the original release went out. The population of legitimate late uploads is small enough that the restriction is worth it.

The attack shape it constrains

The shape being defended against is credential compromise followed by grafting. An attacker gets hold of a publishing token, a leaked env var pulled out of a build log, or a misconfigured trusted publisher, then reaches for an established release rather than a new one. A new wheel goes up under a familiar version, or an sdist quietly replaces itself, and downstream installers pull the grafted artifact against a version pin that never moved.

The DevOps.com article groups this move with other recent incidents named in the piece: the GhostAction attack against PyPI publishing credentials, compromises tied to widely used npm packages, the s1ngularity campaign, and the Shai-Hulud worm. All of them share the same primary step, which is stealing the ability to publish.

The 14-day rule does nothing about that step. What it does is shorten the payoff window on old releases. On a version cut yesterday, a stolen credential still has fourteen days of writeable release. On a version cut a year ago, the credential is inert against that version.

What it does not cover

The restriction is not a substitute for anything already load-bearing. A fresh compromised credential can still publish an entirely new release the day it is stolen, and everything inside that new release's 14-day window is fair game. A maintainer who was always the attacker has the same reach as any other maintainer. And a package that publishes weekly gives any credential attached to it a rolling window as new versions cut.

Sigstore-backed attestations, provenance metadata, lockfile pinning, scoped tokens and OIDC-based trusted publishers still do the work of stopping the initial credential theft. The 14-day rule is a coarse extra layer on top of them.

The parallel change on the GitHub side

The DevOps.com piece pairs the PyPI restriction with a Dependabot change already live: a default three-day cooldown before Dependabot opens a routine version-update pull request against your repo. Security updates skip the delay. Different vector, same primitive.

If PyPI's rule bounds what a compromised credential can do to an old release, Dependabot's cooldown bounds how quickly a brand-new release reaches your build queue. Both use elapsed time as the control. Neither verifies anything about the file. That is the honest read of both.

Where this pinches in a release pipeline

If your release automation adds late wheels or sdists to a version after the fact, that path stops working two weeks in. The shape that gets bitten hardest is a matrixed build that finishes some artifacts hours or days behind the main upload, and the specific practice of adding new interpreter builds to a stable release. Both are common. Both can be reworked into a small patch bump for the late artifact, but the change lives in the release action, not a knob.

If your publish job still holds a long-lived PyPI API token in a repository secret, that credential is where the risk sits. Moving to a trusted publisher configuration with OIDC removes the long-lived token from the picture. That is the higher-leverage change; the 14-day rule assumes you have already made it or are on your way.

Fourteen days is a window, not a wall. It rules out one specific stealth manoeuvre against old releases and leaves everything else in place. That is the shape a supply-chain primitive is supposed to have.

Top comments (0)