In October 2019, I published a short proof of concept for a remote access trojan (RAT) called (PoC) NodeJS Remote Access Trojan. The point was not that Node.js was uniquely dangerous. The point was that modern software projects had become deeply dependent on code most developers never read.

The proof of concept used npm as the delivery path. A developer installs a package. That package brings in dependencies. Somewhere in that tree, malicious code runs with the same privileges as the developer process. From there, the problem is no longer theoretical. The attacker is not only inside an application dependency. They are on the developer machine or inside the build environment.

At the time, the obvious reaction was to treat it as a toy example. It was small, simple, and intentionally written as a warning. But the underlying model was already clear: dependency trust had become an attack surface.

What Changed

What was a proof-of-concept risk in 2019 became an operational reality as attackers started using package registries to reach developer workstations, CI systems, and publishing credentials.

By 2025, npm supply-chain attacks had stopped looking like isolated package incidents. In September 2025, a phishing-led compromise affected widely used npm packages with billions of weekly downloads, injecting malware into packages that many developers and applications trusted automatically. The Qix compromise was especially visible because it affected packages such as chalk and debug, small utilities that sit deep inside the dependency trees of countless JavaScript projects.

Web3 had already produced direct examples of this failure mode. In December 2023, Ledger's Connect Kit package was compromised and shipped wallet-draining code into dApps that depended on it. In September 2025, compromised npm packages maintained by Qix were modified to intercept crypto and Web3 browser activity and redirect transaction destinations.

That same period also saw the emergence of Shai-Hulud, a self-propagating npm supply-chain campaign that targeted package maintainers, developer environments, GitHub tokens, npm tokens, and cloud secrets. The details varied across incidents, but the direction was consistent: attackers were no longer only trying to compromise applications after deployment. They were targeting the machinery that builds and ships software.

The old Node.js RAT idea was useful because it focused on the install path and trust boundary, not because of the specific payload. The risky assumption was that code pulled into a project is meaningfully separated from the developer's local environment.

Why Developer Machines Matter

A developer workstation is often more valuable than a production server. It may have SSH keys, cloud credentials, GitHub access, npm publishing rights, deployment tokens, wallet keys, environment files, and source code. In many organizations, it also has a softer security posture than production infrastructure.

That makes dependency execution a serious boundary. When install scripts, build steps, test runners, and package imports execute automatically, a compromised dependency can reach places that were never meant to be exposed to third-party code.

Containers help, but they are not a complete answer. A containerized development environment can still expose environment variables, mounted source code, credentials, Docker sockets, package tokens, or cloud configuration. The question is not whether Docker exists. The question is what the untrusted code can see and what it can do from where it runs.

What I Would Emphasize Now

If I wrote the 2019 post again, I would spend less time on the payload and more time on the operating model. The payload was only there to make the risk visible. The real issue was the ease with which code from a package registry could cross from dependency into developer environment.

Lock files help, but they do not solve malicious first installs, compromised maintainers, typosquatting, install-time execution, or secrets already present in the environment. Security scanners help, but they are strongest when combined with tighter workflows around publishing rights, dependency review, credential scope, and secret isolation.

The practical direction is straightforward:

None of this removes the need for open-source dependencies. Modern software depends on them, and that is not going away. The goal is to stop pretending that dependencies are passive files. They are executable trust relationships.

The 2019 proof of concept was crude by design. The attacks that followed were more professional, larger in scale, and better integrated into real software supply chains. But the underlying shape was the same: install trusted-looking code, execute it in a valuable environment, and use that access to move outward.

The registry is not only where software comes from. It is a path into the systems that build the internet.

Read next: Abachi and the First OlympusDAO Bug Bounty