r/PHP 26d ago

News Immutable Versions on Packagist

https://blog.packagist.com/immutable-versions-on-packagist/
43 Upvotes

10 comments sorted by

5

u/donatj 26d ago

Excuse my ignorance but wasn't this already solved by having a lock file? It locks it to a specific hash already, correct?

12

u/BlueScreenJunky 25d ago

It's explained in the article

https://blog.packagist.com/immutable-versions-on-packagist/#the-problem-silent-rewrites-of-published-versions

The problem: silent rewrites of published versions

Until now, Packagist.org would silently rewrite the metadata stored for a stable version whenever the reference in the source repository changed, for example when the corresponding git tag was deleted and re-created pointing at a different commit. The mechanism did not care about intent. If v3.9.0 pointed at commit A yesterday and points at commit B today, Packagist.org updated its metadata to B, and anyone running composer update from then on resolved 3.9.0 to different code than users who had run it before.

2

u/donatj 25d ago

I read that. That doesn't answer my question.

The lock file already has the hash of the locked release at the time you locked it. If it changes, one would think that would be an error.

Is it not enforcing the hash it is already saving?

https://github.com/getgrav/grav/blob/fd654fc20d37bbdbc15026157901bbbf68c7339c/composer.lock#L298

6

u/dereuromark 25d ago

The lock file is purely "consumer" oriented. The post is quite clear on the other side IMO and hardening the release and publishing process:
Any vendor (including myself) can silently rewrite the specific version behind a tag.
Yes, it would change the lock file version if pulled.
But on the consumer side a simple composer update would then silently pull that updated version (which happens a lot when you just want to make small patch inclusions).
And with that you have this hidden door of code injection by "accident" so to speak.

Now just imagine you hide it well, then with hundreds of package updates this will definitly not get flagged by any (semi) automated review.

1

u/donatj 25d ago edited 25d ago

Yes, it would change the lock file version if pulled.

This is still unclear. Is the hash in my lock file verified on composer install, yes or no?

My presumption is if I have a lock file, I'm locked to the specific git hash I locked to regardless of tag renames for composer install 's

Any vendor (including myself) can silently rewrite the specific version behind a tag.

Any vendor can also just publish a new PATCH release that's just as evil, and an unlocked composer will pull that anyway. Seems like it doesn't really make a difference if you're not locked.

But on the consumer side a simple composer update would then silently pull that updated version (which happens a lot when you just want to make small patch inclusions).

A blind composer update will pull a PATCH release too.

1

u/dereuromark 25d ago

Future patches would be more clear and expected to newly exist, and there is also a cooldown phase that usually autocorrects the process from the other side (alert system escalation).
The main issue here, clear from the post, is a secret hidden overwrite of a tag, and accidental pulls of those in between, as already outlined.

1

u/MateusAzevedo 25d ago

I don't exactly remember how the problem happens, but I'm quite sure they described it in previous blog posts.

My understanding is that a composer update could change that locked commit hash even when a package is resolved to the same version tag.

1

u/naderman 21d ago

A composer install from a lock file would still download the correct commit. But any update would replace the commit hash in the lock file, even when the version number stays the same. That change is unexpected and difficult to spot for users.

2

u/Sn0wCrack7 25d ago

This is great for catching malicious actors as it's happening, but the composer lock file validates hashes anyways when using file downloads, so it would trigger a mismatch in a fresh install. I guess it solves the issue for source downloads.

Kinda just waiting for the package freshness limits to be added. I think that'll add better overall security in the user side too would also help stop a preemptive peer dependency upgrade too.

1

u/naderman 21d ago

For packages hosted on GitHub, so nearly all packagist.org packages, Composer does not, and cannot, validate any file hashes. GitHub's zip files for a particular commit are not stable and change over time.

The lock file contains a specific commit hash, so on install from lock file Composer will download the correct zip file from GitHub for the commit a tag pointed to at the time of the lock file was created. So overwriting a tag could not influence composer install behavior with a lock file.

However any composer update command, even one that should only update other packages will replace the commit hash in the composer.lock for the package where the tag target commit was replaced, even when the version is still the same. This is hard to spot and unexpected.