Be alert: targeted attacks on prominent Rustaceans

58 points by itamarst 15 hours ago on lobsters | 5 comments

ssokolow | 13 hours ago

Wow.

I'm not in any way prominent, but I'm glad I'm already doing things which would make me think twice about something like a "missing codec" message, and treating cold outreach as inherently spammy/scammy.

Now to expedite retrofitting my development setup so my only Rust toolchains exist inside Docker containers so I can trust that I don't accidentally have rust-analyzer execute a compromised package unconstrained. (I'm already working to migrate as many transitive dependencies as possible into WASIp2-constrained plugins with APIs designed in pursuit of nanoprocess sandboxing principles.)

simonw | 9 hours ago

This is legitimately scary.

Any piece of software that depends on open source (which is almost every piece of software) has a network of human beings who are potential attack vectors - everyone with publishing rights to any of the packages in the dependency network for that software.

I guess our best defense right now is dependency cooldowns - giving new package releases a few days before upgrading to them, in the hope that supply chain risks will be spotted by someone else.

recently, systemd-the-organisation looked at introducing rust as a part of systemd-the-init. luca boccassi, one of the senior maintainers[1] of systemd-the-both, raised some good points about it. i've stripped what i'll describe as somewhat extreme exaggerations around cargo and crates.io:

[...] you have thousands upon thousands of recursive dependencies, and one getting typo-squatted or taken over is all that is needed for massive supply chain attacks [...] and all of those thousands of recursive dependencies not only brings in runtime code, but even arbitrary build time code (build.rs) that stealthly runs as your user on your machine during compilation, and thus can access your browser cookies and your ssh keys and whatever else. This not only puts final users at risk, but all developers too.

i'd recommend largely ignoring the rest of the thread because it devolves into the usual byte-wasting seething from both sides ("it's a toy language for toy people", "how very dare you", "your mother was a hamster", and so forth).

i've been thinking a bit lately about (further) securing one's development environment. every now and then i'm reminded of the build.rs file[2] and how normalised it is, even to me, and it makes me shiver a bit. all the way down there, some sub-sub-sub dependency can be compromised and the knock-on effect can be utterly enormous. this process can be (and is by default in every case i'm aware of) launched by simply opening your text editor with a lsp server attached, because the second thing it does is run cargo check.

the library situation in rust is both an incredible strength (you can easily lean on the shoulders of a billion giants) and an incredible vulnerability (any one of those giants can, through intention, negligence, or simply unfortunate circumstances, cause ridiculous damage). likewise, "build scripts as a function built into the language implementation" is extremely powerful both for good and for bad.

docs.rs, which provides a hosting platform for rust's //! doc comments, approaches this by running the build inside a heavily restricted container. rubydoc.info recently suffered a mass attack from openai bots exploiting this same sort of thing[3] (post courtesy of our own ~simonw).

yes, makefiles and such can execute arbitrary code too, but whataboutism pointing to things invented before most people on this website were born isn't a shining argument that the current way is "just fine". i think the problem has been exacerbated by the general proliferation of the idea that you just $toolchain add $dependency, occasionally run $toolchain update $dependency if your github bot ci isn't doing that automatically for you (it really shouldn't, but sadly usually does), and otherwise never again think about it in any capacity until oops, another one.

let's do some quick maths on one of my own "sandbox projects", a very small web application that backs onto a postgres db:

# first five lines in this particular file are
# package metadata.
$ tail -n +6 Cargo.toml | wc -l
25

$ cargo tree --prefix none | cut -d ' ' -f1 | sort -u | wc -l
239

yum. 239 unique transient dependencies, excluding any that are the same dependency but are semver-incompatible, as cargo collapses semver-compatible dependencies into one if their version ranges overlap.

i'm not good enough at programming programming languages to properly identify a better path, but this isn't really ideal. some people might point to go generate as an example of something with a smaller blast radius[4], but build.rs files are largely akin to makefiles vs just "generating code", which is predominantly done in rust via actual macros. i'm unsure there is a way "out" at all, really, especially as both programs and programming have become more and more complex. i'm not entirely convinced the whole "dependency cooldowns" movement is the right direction, because that introduces another set of problems that are then insurmountable with that model. it would be, at best, a sidegrade.

in the meantime, i make use of a combination of "i use nix btw" and bubblewrap. i might look into systemd-nspawn when i get time, as contrary to the bubblewrap readme it does support rootless mode.


  1. i think that's a decent role description, but i'm not 100% sure and there's no formal description as such.
  2. many languages have an equivalent, of course, such as zig, swift, and most (in)famously javascript. this isn't a problem unique to rust.
  3. i can't find a source focused on this specific thing that isn't self-shilling, blogspam garbage, or simply outright plagiarism of ~simonw's post, and i don't want to support any of those.
  4. i'd describe go generate as something of a(n intentionally) primitive macro system. even if it were more, go generate doesn't traverse dependencies, so you won't be running that command and immediately calling the //go:generate blocks in every sub-dependency on the planet.

hyperpape | 20 minutes ago

I’d recommend posting the output of the dependency analysis somewhere—they can be pretty subtle and I don’t think the count of dependencies is always that helpful.

For instance, sometimes a single project will distribute 10 or 20 crates because they make the crates modular.

jarofgreen | 2 hours ago

Dependency Cooldowns in Rust: https://cooldowns.dev/#cargo (And other programming languages)