Open source security at Astral
35 points by freddyb
35 points by freddyb
Lots of words, they should fix uv add (just like pip install) being RCE instead.
Being a python developer is living on the edge, just earlier today I installed a typo squatted numpy variant. Fun.
This is sort of fundamental to how Python packaging (like most other packaging ecosystems) works: if you install an sdist, you're trusting it to run code at install-time. Compare Rust's crate format, Ruby's gem format, etc. for identical behaviors.
That's not to say it's good, but "just change it" is not a tractable choice in an ecosystem where a significant tail of installs still occur through sdists. The right solution is for Python as an ecosystem to align away from sdists, ideally with installers (like uv and pip) coordinating to ensure a consistent deprecation and then disable-by-default timeline. There's renewed interest in that now, as you can imagine.
(And of course note: banning install-time execution will not save you, if you just go on to import the package anyways. You still have to exercise discretion in what you install, because there isn't a single open source ecosystem out there that will stop you from installing malware.)
And of course note: banning install-time execution will not save you, if you just go on to import the package anyways.
Yep. And Python package names don't even have to line up with PyPI names, so the foo package is free to override bar to its hearts' contents. Even if you check for overwriting existing modules.. it's not that uncommon to implement optional dependencies like:
try:
import foo
except ImportError:
foo = None # check that foo exists before before using it
And on top of that, bdist workflows mean that.. 1) it's now way less trivial to verify that the builds actually match the published source code, 2) you tend to end up in a sort of cultural decay where people lose the ability to build their system from scratch at all.
For example, in the Java world it's typical for libraries to publish source jars alongside their (main) binary jars... but they aren't useful for much beyond "jump-to-source" because they don't follow the actual file structure in the repository, and they usually don't even include the build scripts. (Not that they'd be of much use anyway, since, y'know, the file structure doesn't match.)
Which is also a huge usability thing. In Rust, (where sdists are the norm) if it turns out I need to fix something in a dependency.. I can trivially just add a [patch.crates-io] clause to my Cargo.toml. In Java, I have to hope that they use the same build system, and that my build system supports including external projects. (SBT kinda does, but it was also buggy enough that I'd run into some weird edge case whenever I tried to use it.)
The tradeoff just doesn't make sense to me, at all. And it's depressing to see it keep being brought up as The Obvious Solution™ by people who clearly haven't had to deal with both sides of that tradeoff.
Thanks for taking the time to respond. I didn't mean to be dismissive of the security measures outlined in your blog post. In fact, I appreciate them very much, and this attention to detail is precisely why I, as a user of Astral's software, have a great deal of trust in it.
That said, I think there are measures Astral could take to improve the difficult situation users currently face, without waiting for the potentially decades-long process of ecosystem alignment. At least for those users of uv who wish to opt in to higher levels of security.
I didn't read it as dismissive! I appreciated your comment; I think you're right to observe that the defaults are not secure for Python package installation in general and that uv could play a larger role in changing those defaults. But I also (speaking non-authoritatively) don't think it's feasible for uv to do that unilaterally, because the assumption that sdists "just work" is really deep in Python packaging.
At least for those users of uv who wish to opt in to higher levels of security.
FWIW you can do this at the moment with --no-build or UV_NO_BUILD=1. That'll prevent any build system execution during resolutions. pip has a similar thing with --only-binary :all:. Neither. is the default at the moment though.
Interesting! Some of this seems a bit much (I'm unconvinced requiring 2fa for every committer is necessary, just guard the release bit), but there's a few I'm looking to copy over!:
setup-python and codecov...Just as a point of information: we don't require 2FA for every committer (although GitHub does), we require (strong) 2FA for every member of Astral, i.e. people within the GitHub org itself. So it's not quire as onerous as it sounds :-)
This is really great, thanks for writing it up, definitely some stuff we're going to need to work on.
The one thing I still haven't figured out the value for is SHA-1 pinning of workflows, especially when we're only using the official actions/ provided ones. Feels like a lot of work to keep up to date for a minimal benefit vs just actions/checkout@v5.
Or is my trust in the actions being official and therefore less likely to be vulnerable to these attacks misplaced?
Or is my trust in the actions being official and therefore less likely to be vulnerable to these attacks misplaced?
These are extremely juicy targets maintained by a company who has relentlessly embraced non-deterministic computing and whose software quality has been abysmal lately. I would consider them an even higher priority for pinning in 2026 because an attacker would be an idiot not to try compromising them.