If you want to unpublish a package that you have published accidentally just minutes ago, you can never unpublish it if there is another package anywhere on npm that declares a "*" dependency on that package name.
With this knowledge, anyone can "attack" arbitrary packages, even ones that don't yet exist, simply by declaring a dependency on them. The "attacked" packages can never be unpublished anymore, even if they get published years after the attacking package has been created or updated for the last time.
Unpublishing a package should work as long as the version to be unpublished is not explicitly being dependent upon and that the depending package has been declared to depend on that exact version after the package in question was published. Declaring a "*" dependency on other packages should never prevent them from ever unpublishing again.
Create a package with a declared dependency to a fictional package or an existing package and publish it. Use "*" as the version you depend on.
Publish a package under that latter name with any version and immediately try to unpublish it.
NPM will error out, claiming that there is a dependency and thus the package can never be unpublished, again.
any
I would suggest a slight modification - you shouldn't be able to unpublish something that would result in another dependency being unsatisfiable. In other words, *
if your package has a v1.0.0 and a v1.0.1, and the only deps are on *
, then it should be fine to unpublish v1.0.0, but not v1.0.1 because someone might be relying on that bugfix. If you publish a v1.0.2 then v1.0.1 would become unpublishable.
(It's more nuanced than this, of course)
I don't know whether this is feasible to implement, but in a way a depending package should also never block another package from being unpublished, if at the time the dependency was declared the other package did not even exist. This should never have been a true dependency. So even if the dependency is 1.0.1 you should be able to unpublish 1.0.1 if the dependency was declared at a point in time where there was no 1.0.1 - which is basically what also happens with "*".
Because otherwise an attacker could just be depending exactly on the next (future) bugfix releases of other librarys and thus block them from being unpublished.
It just turned out that in my case the "blocking package" actually was deprecated(!) and still it is blocking other packages that have been released literally years after its own deprecation - that really should not be necessary and should not result in blocking.
Deprecated "rogue" package are even less likely to be maintained and in my case even more difficult to modify. Even if you can find someone to adjust the dependency, it's unlikely that you can do that within the 72 hours time-frame.
we very strongly discourage unpublishing at the registry level, these checks are in place to prevent someone from unpublishing a widely depended upon package and breaking the ecosystem as has happened in the past.
if you have a use case where you're unable to unpublish a package on your own due to the registry preventing it, please do reach out to our support team and explain the situation and they will be happy to help you.
we are constantly evaluating ways to refine our unpublish requirements to balance allowing users to unpublish when it seems safe, and avoiding breakage as much as possible. these are some good suggestions, and we will consider them moving forward, thank you!
i should note - we do offer public replication endpoints, so even if you do unpublish from the npm registry, it's possible and even likely that it already has been mirrored to somewhere else on the internet.
reference to current unpublish policy: https://www.npmjs.com/policies/unpublish
It just turned out that in my case the "blocking package" actually was deprecated(!) and still it is blocking other packages that have been released literally years after its own deprecation - that really should not be necessary and should not result in blocking.
Deprecated "rogue" package are even less likely to be maintained and in my case even more difficult to modify. Even if you can find someone to adjust the dependency, it's unlikely that you can do that within the 72 hours time-frame.
unfortunately "deprecated" doesn't mean "not being used" so we still can't use that as a signal that it's safe to potentially break
@nlf - all valid points. thanks. However they do not apply here. Maybe I wasn't clear:
Anyone can publish a package, now, event empty (apartf from the package.json that references a few hundred other packages using the "*" dependencies.) Years later, with literally no usage at all of the former package (who uses an empty package?), all the packages that were referenced "into the future" by this package cannot be unpublished, even just minutes after they have been published.
So in this situation no one is using either package and no one should be using it (because the maintainer wants to unpublish it because, e.g., it is buggy, violates licenses, contains malware, etc), but still the broken one cannot be unpublished minutes or seconds after publishing without users having to reach out to the npm team, which can take hours or even days and during which damage will be done.
This happened in my case: Some idiot "accidentally" published a commercial closed-source software on npm. This package did not even exist before he published it. For historical reasons there was another package that depended on that software by name using the "*" syntax, three years old, empty, deprecated. The idiot realized their mistake and the license breach and tried to "unpublish" almost immediately. But it took 36 hours to remove the package from the registry and this only worked through a DMCA take-down because of the above bug. With the proposed changes in place, the package could have been removed within minutes and the damage would have been minimal.
Anyone can literally almost trivially break the unpublish logic, by writing a bot that publishes an empty package that depends on a huge number of random (popular) npm packages. It doesn't even have to be quick. Unless the package is brand new, you can prevent thousands of package managers from ever unpublishing their software again without having to go through npm support.
This can't be your recommended approach and resolution....
What if I was the one publishing this "questionable" package in order to raise attention to the issue? According to your logic that would be perfectly fine, because it could be that someone is actually "using" my package.
I am not saying you should ignore "dependencies" from deprecated packages. But you should ignore links from deprecated packages that reference versions (or even packages!) that did not even exist at the time they were deprecated. And as long as a certain dependency is still met after the unpublishing, I argue it should not be blocking unpublishing, either, at least not minutes after it has been published.
I understand your points quite well, which is why I thanked you for the suggestions.
As of today, the registry does not look at what version any dependents of a given package rely on, nor does it look at the timestamps of when a dependent was published versus the dependency. This is a key limitation in the improvement that you're requesting.
What you're suggesting is a good idea and one that I've passed on to the registry team, however due to these limitations and the engineering efforts required to resolve them, what I'm suggesting is that if the current unpublish limitations are preventing you from doing what you want that you reach out to our support team who can bypass these limitations and assist you. A DMCA takedown is definitely not the only way to get a package unpublished when the self service approach fails. I understand that contacting support is slower than unpublishing yourself, which is why we are continuing to evaluate and improve our services and policies for self service.
We fully understand that the limitations in place are rather restrictive and appreciate your feedback for improvements. I would ask that in the future you refrain from calling other human beings names. We all make mistakes.
Most helpful comment
unfortunately "deprecated" doesn't mean "not being used" so we still can't use that as a signal that it's safe to potentially break