logoalt Hacker News

sphyesterday at 11:00 AM37 repliesview on HN

It's not "node" or "Javascript" the problem, it's this convenient packaging model.

This is gonna ruffle some feathers, but it's only a matter of time until it'll happen on the Rust ecosystem which loves to depend on a billion subpackages, and it won't be fault of the language itself.

The more I think about it, the more I believe that C, C++ or Odin's decision not to have a convenient package manager that fosters a cambrian explosion of dependencies to be a very good idea security-wise. Ambivalent about Go: they have a semblance of packaging system, but nothing so reckless like allowing third-party tarballs uploaded in the cloud to effectively run code on the dev's machine.


Replies

TheFlyingFishyesterday at 11:25 AM

I've worried about this for a while with Rust packages. The total size of a "big" Rust project's dependency graph is pretty similar to a lot of JS projects. E.g. Tauri, last I checked, introduces about 600 dependencies just on its own.

Like another commenter said, I do think it's partially just because dependency management is so easy in Rust compared to e.g. C or C++, but I also suspect that it has to do with the size of the standard library. Rust and JS are both famous for having minimal standard libraries, and what do you know, they tend to have crazy-deep dependency graphs. On the other hand, Python is famous for being "batteries included", and if you look at Python project dependency graphs, they're much less crazy than JS or Rust. E.g. even a higher-level framework like FastAPI, that itself depends on lower-level frameworks, has only a dozen or so dependencies. A Python app that I maintain for work, which has over 20 top-level dependencies, only expands to ~100 once those 20 are fully resolved. I really think a lot of it comes down to the standard library backstopping the most common things that everybody needs.

So maybe it would improve the situation to just expand the standard library a bit? Maybe this would be hiding the problem more than solving it, since all that code would still have to be maintained and would still be vulnerable to getting pwned, but other languages manage somehow.

show 5 replies
larussoyesterday at 11:23 AM

I agree partly. I love cargo and can’t understand why certain things like package namespaces and proof of ownership isn’t added at a minimum. I was mega annoyed when I had to move all our Java packages from jcenter, which was a mega easy setup and forget affair, to maven central. There I suddenly needed to register a group name (namespace mostly reverse domain) and proof that with a DNS entry. Then all packages have to be signed etc. In the end it was for this time way ahead. I know that these measures won’t help for all cases. But the fact that at least on npm it was possible that someone else grabs a package ID after an author pulled its packages is kind of alarming. Dependency confusion attacks are still possible on cargo because the whole - vs _ as delimiter wasn’t settled in the beginning. But I don’t want to go away from package managers or easy to use/sharable packages either.

show 1 reply
gnfargblyesterday at 11:37 AM

I'm a huge Go proponent but I don't know if I can see much about Go's module system which would really prevent supply-chain attacks in practice. The Go maintainers point [1] at the strong dependency pinning approach, the sumdb system and the module proxy as mitigations, and yes, those are good. However, I can't see what those features do to defend against an attack vector that we have certainly seen elsewhere: project gets compromised, releases a malicious version, and then everyone picks it up when they next run `go get -u ./...` without doing any further checking. Which I would say is the workflow for a good chunk of actual users.

The lack of package install hooks does feel somewhat effective, but what's really to stop an attacker putting their malicious code in `func init() {}`? Compromising a popular and important project in this way would likely be noticed pretty quickly. But compromising something widely-used but boring? I feel like attackers would get away with that for a period of time that could be weeks.

This isn't really a criticism of Go so much as an observation that depending on random strangers for code (and code updates) is fundamentally risky. Anyone got any good strategies for enforcing dependency cooldown?

[1] https://go.dev/blog/supply-chain

show 3 replies
chuckadamsyesterday at 2:39 PM

> It's not "node" or "Javascript" the problem, it's this convenient packaging model.

That and the package runtime runs with all the same privileges and capabilities as the thing you're building, which is pretty insane when you think about it. Why should npm know anything outside of the project root even exists, or be given the full set of environment variables without so much as a deny list, let alone an allow list? Of course if such restrictions are available, why limit them to npm?

The real problem is that the security model hasn't moved substantially since 1970. We already have all the tools to make things better, but they're still unportable and cumbersome to use, so hardly anything does.

show 1 reply
dotancohenyesterday at 11:05 AM

Historically, arguments of "it's popular so that's why it's attacked" have not held up. Notable among them was addressing Windows desktop security vulnerabilities. As Linux and Mac machines became more popular, not to mention Android, the security vulnerabilities in those burgeoning platforms never manifested to the extent that they were in Windows. Nor does cargo or pip seem to be infected with these problems to the extent that npm is.

show 2 replies
alextingleyesterday at 1:53 PM

Every time I look at a new project, my face falls when it's written in Rust. I simply don't trust a system that pulls in gigabytes of god-knows-what off the cloud, and compiles it on my box. It's a real barrier to entry, for me.

When I download a C project, I know that it only depends on my system libraries - which I trust because I trust my distro. Rust seems to expect me to take a leap in the dark, trusting hundreds of packagers and their developers. That might be fine if you're already familiar with the Rust ecosystem, but for someone who just wants to try out a new program - it's intimidating.

show 1 reply
dwrobertsyesterday at 11:54 AM

I think this is right about Rust and Cargo, but I would say that Rust has a major advantage in that it implements frozen + offline mode really well (which if you use, obviously significantly decreases the risks).

Any time I ever did the equivalent with NPM/node world it was basically unusable or completely impractical

show 1 reply
rafaelmnyesterday at 11:16 AM

There are ecosystems that have package managers but also well developed first party packages.

In .NET you can cover a lot of use cases simply using Microsoft libraries and even a lot of OSS not directly a part of Microsoft org maintained by Microsoft employees.

show 1 reply
JD557yesterday at 1:33 PM

I have a similar opinion but I think Java's model with maven and friends hits the sweet spot:

- Packages are always namespaced, so typosquating is harder - Registries like Sonatype require you to validate your domain - Versions are usually locked by default

My professional life has been tied to JVM languages, though, so I might be a bit biased.

I get that there are some issues with the model, especially when it comes to eviction, but it has been "good enough" for me.

Curious on what other people think about it.

show 1 reply
hyperpapeyesterday at 12:36 PM

Supply chain attacks are scary because you do everything "right", but the ecosystem still compromises you.

But realistically, I think the sum total of compromises via package managers attacks is much smaller than the sum total of compromises caused by people rolling their own libraries in C and C++.

It's hard to separate from C/C++'s lack of memory safety, which causes a lot of attacks, but the fact that code reuse is harder is a real source of vulnerabilities.

Maybe if you're Firefox/Chromium, and you have a huge team and invest massive efforts to be safe, you're better off with the low-dependency model. But for the median project? Rolling your own is much more dangerous than NPM/Cargo.

parliament32yesterday at 6:11 PM

Agreed, rust's cargo model is basically the worst part of that ecosystem right now. I've had developers submit pretty simple cli tools with hundreds and hundreds of dependencies. I guess there wasn't any lessons learned from the state of NPM.

PunchyHamsteryesterday at 2:13 PM

Rust (and really, any but JS) ecosystem have a bit more "due dilligence" applied everywhere; I don't doubt someone will try to namesquat but chance of success are far smaller

> The more I think about it, the more I believe that C, C++ or Odin's decision not to have a convenient package manager that fosters a cambrian explosion of dependencies to be a very good idea security-wise.

There was no decision in case of C/C++; it was just not a thing languages had at the time so the language itself (especially C) isn't written in a way to accommodate it nicely

> Ambivalent about Go: they have a semblance of packaging system, but nothing so reckless like allowing third-party tarballs uploaded in the cloud to effectively run code on the dev's machine.

Any code you download and compile is running code on dev machine; and Go does have tools to do that in compile process too.

I do however like the by default namespacing by domain, there is no central repository to compromise, and forks of any defunct libs are easier to manage.

newpavlovyesterday at 12:47 PM

While I agree that dependency tree size can be sometimes a problem in Rust, I think it often gets overblown. Sure, having hundreds of dependencies in a "simple" project can be scary, but:

1) No one forces you to use dependencies with large number of transitive dependencies. For example, feel free to use `ureq` instead of `reqwest` pulling the async kitchen sink with it. If you see an unnecessary dependency, you could also ask maintainers to potentially remove it.

2) Are you sure that your project is as simple as you think?

3) What matters is not number of dependencies, but number of groups who maintain them.

On the last point, if your dependency tree has 20 dependencies maintained by the Rust lang team (such as `serde` or `libc`), your supply chain risks are not multiplied by 20, they stay at one and almost the same as using just `std`.

show 2 replies
ratmiceyesterday at 6:43 PM

My feeling is that languages with other packing models are merely less convenient, and there is no actual tangible difference security-wise. Just take C and replace "look for writable repositories". It just takes more work and is less uniform to say write a worm that looks for writable cmake/autoconf and replicate that way.

What would actually stop this is writing compilers and build systems in a way that isolates builds from one another. It's kind of stupid that all a compiler really needs is an input file, a list of dependencies, and an output file. Yet they all make it easy to root around, replicate and exfiltrate. It can be both convenient and not suffer from these style of attacks.

show 1 reply
lijokyesterday at 9:47 PM

I don’t get this

I installed the package, obviously I intend to run it. How does getting pwned once I run it manually differ from getting pwned once I install it? I’m still getting pwned

show 1 reply
SkiFire13yesterday at 5:53 PM

Not having a convenient package manager doesn't mean you don't need the functionality that's otherwise offered by third-party packages, it just means that you either need other means to obtain those third-party packages (usually reducing the visibility this dependency!) or implement them yourself (sometimes this is good, but sometimes this can also be very bad for security. Your DYI code won't get as many eyes and audits as the popular third party package!).

Must read: https://wiki.alopex.li/LetsBeRealAboutDependencies

rock_artistyesterday at 12:54 PM

Using C++ daily, whenever I do js/ts are some javascript variant, since I don't use it daily, and update becomes a very complex task. frameworks and deps change APIs very frequently.

It's also very confusing (and I think those attack vectors benefit exactly from that), since you have a dependency but the dep itself dependent on another dep version.

Building basic CapacitorJS / Svelte app as an example, results many deps.

It might be a newbie question, but, Is there any solution or workflow where you don't end up with this dependency hell?

show 1 reply
Davidbrczyesterday at 11:54 AM

Don't worry about C or C++, we create the vulnerabilities ourselves !

show 1 reply
poulpy123yesterday at 2:45 PM

> The more I think about it, the more I believe that C, C++ or Odin's decision not to have a convenient package manager that fosters a cambrian explosion of dependencies to be a very good idea security-wise.

The safest code is the code that is not run. There is no lack of attacks targeting C/C++ code, and odin is just a hobby language for now.

moritonalyesterday at 11:25 AM

Not knowing that much about apt, isn't _any_ package system vulnerable, and purely a question of what guards are in place and what rights are software given upon install?

show 1 reply
progbitsyesterday at 11:13 AM

Agreed with the first half, but giving up on convenient packaging isn't the answer.

Things like cargo-vet help as does enforcing non-token auth, scanning and required cooldown periods.

randomint64yesterday at 12:31 PM

Indeed, Rust's supply chains story is an absolute horror, and there are countless articles explaining what should be done instead (e.g. https://kerkour.com/rust-stdx)

TL;DR: ditch crates.io and copy Go with decentralized packages based directly on and an extended standard library.

Centralized package managers only add a layer of obfuscation that attackers can use to their advantage.

On the other hand, C / C++ style dependency management is even worse than Rust's... Both in terms of development velocity and dependencies that never get updated.

show 1 reply
vintagedaveyesterday at 11:06 AM

I believe you, in that package management with dependencies without security mitigation is both convenient and dangerous. And I certainly agree this could happen for other package managers as well.

My real worry, for myself re the parent comment is, it's just a web frontend. There are a million other ways to develop it. Sober, cold risk assessment is: should we, or should we have, and should anyone else, choose something npm-based for new development?

Ie not a question about potential risk for other technologies, but a question about risk and impact for this specific technology.

fouronnes3yesterday at 11:05 AM

Surely in this case the problem is a technical one, and with more work towards a better security model and practices we can have the best of both worlds, no?

zenmacyesterday at 11:46 AM

Just a last month someone was trying to figure the cargo tree on which Rust package got imported implicitly via which package. This will totally happen in rust as well as long as you use some kind of package manager. Go for zero or less decencies.

show 2 replies
woodruffwyesterday at 1:03 PM

It’ll probably happen eventually with Rust, but ecosystem volume and informal packaging processes / a low barrier to entry seem to be significant driver in the npm world.

(These are arguably good things in other contexts.)

kunleyyesterday at 3:50 PM

Why the word "semblance" with regard to Go modules? Are you trying to say this system is lacking something?

vachinayesterday at 11:16 AM

Node is the embodiment of move and break things. Probably will not build anything that should last more than a few months on node.

mschuster91yesterday at 11:31 AM

> The more I think about it, the more I believe that C, C++ or Odin's decision not to have a convenient package manager that fosters a cambrian explosion of dependencies to be a very good idea security-wise. Ambivalent about Go: they have a semblance of packaging system, but nothing so reckless like allowing third-party tarballs uploaded in the cloud to effectively run code on the dev's machine.

The alternative that C/C++/Java end up with is that each and every project brings in their own Util, StringUtil, Helper or whatever class that acts as a "de-facto" standard library. I personally had the misfortune of having to deal with MySQL [1], Commons [2], Spring [3] and indirectly also ATG's [4] variants. One particularly unpleasant project I came across utilized all four of them, on top of the project's own "Utils" class that got copy-and-paste'd from the last project and extended for this project's needs.

And of course each of these Utils classes has their own semantics, their own methods, their own edge cases and, for the "organically grown" domestic class that barely had tests, bugs.

So it's either a billion "small gear" packages with dependency hell and supply chain issues, or it's an amalgamation of many many different "big gear" libraries that make updating them truly a hell on its own.

[1] https://jar-download.com/artifacts/mysql/mysql-connector-jav...

[2] https://commons.apache.org/proper/commons-lang/apidocs/org/a...

[3] https://docs.spring.io/spring-framework/docs/current/javadoc...

[4] https://docs.oracle.com/cd/E55783_02/Platform.11-2/apidoc/at...

show 1 reply
riffraffyesterday at 3:12 PM

maybe the solution is what linux & co used for many years: have a team of people who vet and package dependencies.

trollbridgeyesterday at 12:16 PM

An open question is why PyPI doesn’t have the same problem.

show 1 reply
agumonkeyyesterday at 2:12 PM

do they follow the same process ? or is it harder to submit a package and vet it on rust/cargo ?

dijityesterday at 3:19 PM

> but it's only a matter of time until it'll happen on the Rust ecosystem

Totally 100% agree, though tools like cargo tree make it more of a tractable problem, and running vendored dependencies is first class at least.

The one I am genuinely most concerned of is Golang. The way Dependencies are handled leaves much to be desired, I'm really surprised that there haven't been issues honestly.

eptcykayesterday at 11:45 AM

Go is just as bad.

aa-jvyesterday at 2:08 PM

> C/C++ .. a convenient package manager

Every time I fire up "cmake" I chant a little spell that protects me from the goblins that live on the other side of FetchContent to promise to the Gods of the Repo that I will, eventually, review everything to make sure I'm not shipping poop nuggets .. just as soon as I get the build done, tested .. and shipped, of course .. but I never, ever do.

tjpnzyesterday at 11:13 AM

In the early days the Node ecosystem adopted (from Unix) the notion that everything has to be its own micro package. Not only was there a failure to understand what it was actually talking about, but it was never a good fit for package management to begin with.

I understand that there's been some course correction recently (zero dependency and minimal dependency libs), but there are still many devs who think that the only answer to their problem is another package, or that they have to split a perfectly fine package into five more. You don't find this pattern of behavior outside of Node.

show 1 reply
testdelacc1yesterday at 11:21 AM

I hate to be the guy saying AI will solve it, but this is a case where AI can help. I think in the next couple of years we’ll see people writing small functions with Claude/codex/whatever instead of pulling in a dependency. We might or might not like the quality of software we see, but it will be more resistant to supply chain attacks.

show 5 replies