Safeguard
Software Supply Chain Security

Malicious Package Detection: Behavioral vs Signature-Base...

A side-by-side look at signature-based malicious package detection (like Endor Labs) versus behavioral analysis, using real npm attack timelines from Shai-Hulud to chalk/debug.

Vikram Iyer
Security Researcher
7 min read

On November 12, 2025, npm's second Shai-Hulud outbreak began spreading through the registry — a self-replicating worm that trojanized more than 350 packages within days, some maintained by publishers with years of clean history. The payload ran inside a post-install script the moment npm install executed, harvesting GitHub Actions and npm tokens from the host, then automatically publishing itself into every package those stolen credentials could reach. Public advisories and threat feeds caught up only after the worm had already moved through several generations of packages. That lag is the real question behind malicious package detection npm teams now have to answer: does your tooling wait for a known-bad signature to land in a database, or does it watch what a package actually does the moment it enters a build? Endor Labs, like most software composition analysis vendors, leans heavily on the first model. This post walks through what that tradeoff costs in practice, with real incident timelines, and where behavioral detection closes the gap signature databases structurally can't.

What's the actual difference between signature-based and behavioral malicious package detection?

Signature-based detection matches a package against a list of things already known to be bad; behavioral detection watches what a package does and flags it regardless of whether anyone has seen it before. A signature-based engine compares a package's name, version, file hashes, or known-malicious code snippets against a curated database — sources like the GitHub Advisory Database, OSV.dev, npm's own security advisories, and vendor-maintained malware feeds. If a match exists, you get an alert; if it doesn't, the package passes, no matter what it actually does at install or runtime. Behavioral detection instead executes or statically traces the package's install scripts, network calls, filesystem access, and process behavior, and scores it against patterns associated with malicious activity — reaching out to an unregistered domain, reading ~/.ssh or .aws/credentials, spawning a shell, or exfiltrating environment variables. The two aren't mutually exclusive, but the order they run in matters: signature matching only works after something has already been classified as bad by someone else, somewhere else, first.

Why do signature-based tools like Endor Labs miss brand-new npm attacks?

Because a signature has to be written after an attack is discovered, which means every signature-based tool is blind to a specific compromise for the entire window between publication and detection. Endor Labs' platform is built primarily around dependency risk scoring and reachability analysis — determining whether a known CVE in a transitive dependency is actually reachable from your application's call graph — layered with malicious package feeds sourced from public advisories and its own research team's findings. That's a genuinely useful model for triaging the known vulnerability backlog, where a typical enterprise dependency tree can carry 5,000+ open CVEs and reachability analysis cuts the actionable list dramatically. But malicious packages aren't CVEs. There's no CVSS score, no NVD entry, and often no advisory at all until a researcher or an affected company notices the anomaly and writes it up — which for the chalk/debug compromise on September 8, 2025, took roughly two hours of community Slack and Bluesky chatter before npm pulled the packages, and for the November 2025 Shai-Hulud wave took closer to three days before a comprehensive list of affected packages existed anywhere.

How much lag actually exists between a new attack and a usable signature?

Historically, anywhere from a few hours to several weeks, and the gap tends to be worst for exactly the attacks that spread fastest. The event-stream incident in November 2018 — where a maintainer handed off a popular package to an attacker who added a hidden dependency, flatmap-stream, that targeted the Copay Bitcoin wallet specifically — sat undetected in the registry for roughly two and a half months before a routine code review caught it. The ua-parser-js compromise in October 2021 was live for about four hours before npm pulled it, but downstream lockfiles that had already resolved the malicious version kept pulling it into fresh builds for days afterward, because CI caches don't automatically re-check a package that installed cleanly yesterday. The polyfill.io attack in June 2024, which injected malware into a CDN-hosted script used by more than 100,000 websites, went unnoticed for weeks because it wasn't a registry package at all and fell outside most SCA tools' scope entirely. In every one of these cases, a signature-based tool queried at the wrong moment would have returned a clean result.

Can behavioral analysis actually catch attacks a signature database hasn't seen yet?

Yes, because behavioral detection doesn't need the attack to have a name — it only needs the package to act in a way that's inconsistent with what a dependency should do. The Shai-Hulud worm's defining trait wasn't its list of affected package names, which changed hour to hour as it spread; it was a consistent behavioral signature: a post-install script that ran TruffleHog against the local filesystem to hunt for secrets, exfiltrated npm and GitHub tokens to attacker-controlled infrastructure, and then used those tokens to publish itself further. That pattern — credential-scanning tooling invoked from an install script, followed by an outbound network call to a domain the package has no legitimate reason to contact — is detectable the first time it happens, not the thousandth. Similarly, the chalk/debug clipper injected code that watched clipboard contents in the browser and swapped cryptocurrency addresses; a behavioral engine tracing what the shipped code actually touches doesn't need a threat feed to flag DOM and clipboard access in a terminal-formatting library as anomalous. The tradeoff is that behavioral analysis has to actually run or trace the code, which costs more compute and engineering effort than a hash lookup — which is exactly why most vendors, Endor Labs included, default to the cheaper signature model and treat behavioral checks as a smaller, secondary layer if they offer them at all.

Does behavioral detection introduce false positives that make it impractical?

Some, but the false-positive cost of behavioral detection is smaller and easier to manage than the false-negative cost of signature-only tools missing a live attack. A behavioral engine that flags every package making a network call during install would be useless — plenty of legitimate packages fetch prebuilt binaries or fonts on install (node-sass, puppeteer, and cypress all do this routinely). The fix isn't to drop behavioral checks, it's to baseline them: score behavior against what that specific package has historically done, so a longstanding CLI tool that has never touched the network suddenly reaching out to a new domain in a patch release is a meaningfully different signal than a bundler that has always downloaded a binary. Vendors that skip this baselining step do generate noisy alerts, which is a fair criticism of early behavioral tooling — but it's an engineering maturity problem, not a reason to default back to signatures alone. A missed Shai-Hulud infection is a materially worse outcome than an analyst spending two minutes clearing a false positive on a package that always fetches a Chromium binary.

How Safeguard Helps

Safeguard runs behavioral analysis as the primary detection layer, not an afterthought bolted onto a CVE database. Every new package version is inspected for install-time and runtime behavior — network destinations, filesystem access, process spawning, obfuscated payloads — and scored against that package's own publishing history, so a sudden change in what a trusted dependency does gets flagged even when no advisory, CVE, or threat-feed entry exists yet. That would have caught the Shai-Hulud worm's credential-harvesting install script and the chalk/debug clipper's clipboard access on day one, not after a community thread or a vendor's research team wrote it up days or weeks later. Where reachability-and-signature tools like Endor Labs are strong at triaging the known-CVE backlog, Safeguard is built for the problem that sits outside that backlog entirely: packages that are malicious but have never been classified as vulnerable, because nobody has assigned them a CVE and never will. Safeguard also layers in publisher and provenance signals — flagging maintainer account takeovers, unattested publishes, and anomalous version bumps — so teams get both the "was this artifact produced by a process we can trust" check and the "does this artifact do something it shouldn't" check in one pipeline gate, instead of waiting for two different vendors' databases to eventually agree that an attack happened.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.