Hi friends 👋 Happy first day of Fall for those of you in the Northern Hemisphere and happy first day of Spring for folks in the South. I hope you all enjoy your seasonal Pumpkin Spice Lattes and Iced Lavender Cream Matchas, respectively.
Another cool fact about today, September 22: there are exactly 100 days left in the year, which is just enough time to finish one of the six hundred projects you started vibe-coding this year. As for me, I'm using this final 100-day sprint to tackle a potentially too ambitious project I hinted at in X last week. I'll share more about it soon.
In today's issue, we're talking supply chain security, the local-first secret master plan, DOOM in SQL, a DB for your browser, and much more.
Let's dive in.
MAXI'S RAMBLINGS
Neatly Packaged Malware
“Have you heard about the NPM supply chain attack?”
If you had asked me that question a month ago, my answer would have probably been “what’s a supply chain attack?”, immediately followed by “wait, that won’t affect Costco’s supply of Greek yogurt and AAA batteries, right? I’m running pretty low on those.”
These days, though, if you ask me whether I heard about the NPM supply chain attack, my answer will invariably be “Which one?”
Unless you have been completely disconnected from tech news this past month, you’re probably aware that NPM has had a rough few weeks.
The chaos started late last month with S1ngularity, an attack that infected several Nx packages with a malicious script that attempted to steal users’ sensitive data and upload it to a public GitHub repo.
Soon after, NPM author Qix fell victim to a phishing email that allowed attackers to publish malicious versions of several popular packages he maintained, including debug
and chalk
. These two packages alone are downloaded over 700 million times per week—enough to earn the attack the title of the “largest supply chain attack in history.”
And last week, just when everyone thought things couldn’t get much worse, another group of attackers (or maybe the same group, for all we know) said “hold my beer” and released a quite novel piece of malware known as the Shai‐Hulud worm. Unlike its predecessors, Shai-Hulud can self-propagate by stealing the infected user’s credentials and using them to inject itself into other repos. So far, it has infected at least 500 packages.
Supply chain attacks are nothing new, of course, and they aren’t exclusive to NPM—all package managers are vulnerable to some degree. But the thing about the JavaScript ecosystem is that it seems to be particularly fond of falling into the depths of dependency hell, which makes NPM a perfect target for this sort of attack.
The average NPM dependency brings 79 transient dependencies with it. Seventy-nine!! Even if we’re very intentional about the dependencies we install and we do our best to keep our package.json as small as possible, chances are we’ll end up with at least a few hundred dependencies in our projects—and each one is a new door a credential-stealing malware could walk through.
It might seem like fighting dependency hell is a lost battle… and to some degree, it is. The only way to avoid it is to lock ourselves down and build our apps with no third-party dependencies and only artisanal, organic, USDA-certified free-range code, which is, at the very least, just very impractical.
But not all is lost. Creating a totally bulletproof system that is guaranteed to keep malware away might be virtually impossible, but there are still things we can do to add a layer of protection to our projects. Here are a few of them:
- Take your time: one of the easiest things you can do to avoid installing one of these malicious scripts is to just… wait. Unless you absolutely need to upgrade a dependency right away, it’s always a good idea to wait at least a couple of days after it’s released before merging it. pnpm recently added a new
minimumReleaseAge
property that forces a package to be a certain number of days old before installing it. Hopefully, other package managers will copy this initiative, but even if they don't, it isn’t too cumbersome to check a package's release date manually either. Whatever you do, never auto-merge patch updates. Take your time and, as much as possible, try to understand what each upgrade is bringing to your codebase. - Audit your dependencies often: I know this might seem like a no-brainer, but it’s another relatively low-effort/high-leverage thing you can do. If you work on a project that is a number of years old, chances are your package.json is still bringing in dependencies that aren’t being used anymore but were never uninstalled. It’s also a good idea to check exactly what you’re using a dependency for. Are you bringing a dependency from NPM just because you need a 20-line function from it? It might be a good idea to copy/paste that function into your codebase (or vendor it) and uninstall the dependency. Remember that each one brings on average 79 other dependencies with them, so make sure you make them all count.
- Disable lifecycle scripts by default: most of these malicious scripts work by exploiting NPM's lifecycle scripts, particularly
postinstall
, which lets the package execute an arbitrary bash script on the user’s behalf. Some packages rely on this feature for legitimate reasons (like building a binary that isn’t shipped in the package itself), but since it’s such a big security risk, it’s always a good idea to disable it by default and manually allow certain packages to use it. You can do this by addingignore-scripts=true
to your .npmrc file and using the LavaMoat/allow-scripts tool. - If you publish packages to NPM, whether they’re open-source or private to your organization, make sure you have 2FA enabled and consider turning on trusted publishers. This allows publishing to NPM from your CI/CD pipeline without relying on long-lived tokens. *taps finger on his head* You can’t get your NPM tokens stolen if you don’t have any tokens to begin with.
But don't listen to me! I'm just a dude on the Internet. Thankfully, many developers and supply chain experts started sharing their NPM security tips, tricks, and visions for a better future as a result of these recent attacks. Here are a few that I recommend checking out:
- Mitigating Postinstall Attacks like the Shai‐Hulud Worm
- How to keep package.json under control
- Less is safer: how Obsidian reduces the risk of supply chain attacks
- Oh no, not again… a meditation on NPM supply chain attacks
- A better future for JavaScript that won’t happen
Title credit: @kanav
✏️ FROM THE BLOG
An Interactive Guide to TanStack DB
In classic Maxi fashion, I'm a bit late sharing this one. But in case you missed it, I published a new article on the Frontend at Scale blog a couple of weeks ago—the first proper blog post in over a year! Oops.
It's about one of the coolest frontend technologies I've been playing with recently: TanStack DB. I talk about how it works, how it compares to TanStack Query, and how you can unlock its full power with a sync engine like ElectricSQL. As I mentioned in a recent issue, I'm very excited about sync technology and what it means for the future of frontend development, and I hope I can spread some of that excitement over to you with this blog post.
Read it on the blog |
WEEKLY SNACKS
Links Worth Checking Out
- Peter van Hardenberg gave a fantastic talk at the latest Local-First Conf about the local-first secret master plan. Don't worry. It doesn't involve World dominance or the creation of a Galactic Empire—it's about the future of collaborative software with universal version control and malleable software that adapts to our needs. Peter's talk does a great job of painting this vision for the future that local-first technology enables, complemented by very cool demos of projects his team at Ink & Switch (and others) is currently working on.
- I enjoyed reading Kyrylo Silin's essay on a question I often ask myself: Why do software developers love complexity? I don't agree with his anti-React take, but it is true that a lot of the problems we have to solve on a daily basis have to do with the complexity that the tools we use bring to the table. Complexity is the root of all evil, so it's always a good idea to take a step back and consider how much of it is actually self-inflicted.
- Speaking of React, the new <Activity> component just landed in Canary to solve the very common problem of conditionally rendering a portion of our component tree without destroying its internal
- Una wrote about how to implement the follow-the-leader pattern with the new anchor positioning API. Like a lot of recent web platform developments, this is one of those things that used to take mountains of JavaScript to get right, and now they're possible with just a few lines of CSS.
- Jake Archibald wrote a great primer on the greatness of fetch response/request streams, but why they're not so great for measuring upload/download progress. Spoiler alert: the old reliable XHR API continues to be the best tool we have for that.
- Emil Kowalski helps us add purposeful animations to our projects without overdoing it in his latest essay: You Don't Need Animations.
- Very insightful write-up on the Netflix Engineering blog about the evolution of their client-server GraphQL APIs, talking about why they use mutations instead of queries to fetch the latest updates to users' feeds. This guarantees sections like "Continue Watching" are always up-to-date—so you know exactly where you left off your 15th watching of K-Pop Demon Hunters.
- Lukas Vogel from the CedarDB team built a multiplayer DOOM-like shooter entirely in SQL. Is it useful? Probably not. But is it cool? SELECT 'YES';
🛠️ NPM Install This
- Mediabunny — A JavaScript library for reading, writing, and converting video and audio files directly in the browser.
- React Bits — An open source collection of high-quality animated React components for building stunning user interfaces.
- RippleJS — A brand new and still experimental UI framework from veteran framework maintainer Dominic Gannaway that combines the best parts of React, Solid, and Svelte.
🥊 CODE SHOWDOWN — LAST ISSUE'S RESULTS
Looping Through an Object
The results are in: no new code showdown this week, but I do want to share the results of last issue's poll.
Your favorite way of looping through an object is Object.keys() + forEach(). This one surprised me a bit. I was certain the more modern for...of method would take the lead, but I guess most of us prefer the comfort of the good old classics.
That’s all for today, friends! Thank you for making it all the way to the end. If you enjoyed the newsletter, it would mean the world to me if you’d share it with your friends and coworkers. (And if you didn't enjoy it, why not share it with an enemy?)
Did someone forward this to you? First of all, tell them how awesome they are, and then consider subscribing to the newsletter to get the next issue right in your inbox.
I read and reply to all of your comments. Feel free to reach out on Twitter, LinkedIn, or reply to this email directly with any feedback or questions.
Have a great week 👋
– Maxi