Build1 publisher3 min readPublished
The npm audit that works because it never installs the package
A dev.to walkthrough puts metadata and tarball inspection first, on the grounds that most npm malware fires during install and not at import. It needs nothing you have to buy.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction
What happened
- Malicious code in npm packages usually runs at install time, not at import time; the workflow's most important rule is not to install the package in order to inspect it.
- Hundreds of malicious packages were caught on npm last year, many of them typosquats of popular libraries that stole credentials the moment they were installed.
- The article presents a 10-minute manual audit workflow for any npm package you do not fully trust, aimed at JavaScript/Node.js developers and DevOps engineers, requiring no paid tools, just a terminal.
- Step one is running npm view on the package and reading four things: a very young package or very few versions, a name that looks almost like a popular package, a maintainer mismatch such as a brand new email publishing a package claiming to belong to a known project, and a sudden maintainer change on an old package, checked with npm view maintainers and compared against the GitHub repo.
- Typosquatting is the number one delivery method, with examples given as lodash-utilz, reakt-dom and crossenv.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
A walkthrough published on dev.to sets out a ten-minute manual audit for any npm package you do not fully trust, and its defining feature is that no step installs the package [3]. That ordering carries the whole argument: malicious code in npm packages usually runs at install time rather than at import time, so the first rule of the workflow is not to install the thing in order to look at it [1].
The exposure is worth stating plainly. Running npm install hands a stranger's code access to your machine, your environment variables and your CI pipeline [18]. According to the piece, hundreds of malicious packages were caught on npm last year, many of them typosquats of popular libraries that stole credentials the moment they were installed [2], and typosquatting is described as the number one delivery method, with lodash-utilz, reakt-dom and crossenv as the shape to look for [5].
Step one is npm view, read for four things: a very young package or very few versions, a name that nearly matches a popular one, a maintainer mismatch such as a brand new email publishing a package claiming to belong to a known project, and a sudden maintainer change on an old package, checked with npm view maintainers against the GitHub repo [4].
Step two is the manifest, because lifecycle scripts named preinstall, install and postinstall execute arbitrary shell commands automatically during npm install [6]. You can pull the manifest straight from the registry with npm view --json and print the scripts field through node, without installing anything [7]. The legitimate exceptions are known quantities: native builds like node-gyp, binary downloads like esbuild [8]. The stated heuristic for everything else is that a postinstall script in a small, unknown package is guilty until proven innocent [9].
Step three is npm pack, which downloads exactly what the registry serves with zero script execution [10]. This is the step that most reviews skip. The code on GitHub and the code on npm are not guaranteed to match, and attackers routinely publish a clean repo alongside a poisoned tarball, so the tarball is the artefact to audit [11]. Inside it, look for files that should not be there at all: .env, unexplained .node binaries, minified blobs in a package that claims to ship readable sources [12].
Step four is four grep sweeps over the extracted JavaScript: dynamic execution (eval, Function, child_process, exec, spawn), sensitive reads (process.env, .npmrc, .ssh, .aws, hosts), outbound network calls (http.request, https.request, fetch, XMLHttpRequest, dns.resolve) and obfuscation (base64, fromCharCode, hex escapes) [13]. The .npmrc hit matters because that file holds your npm token [14]. The worked example is a two-line lib/setup.js that base64-decodes a URL and POSTs JSON.stringify(process.env) to it, which is the entire environment including CI secrets and npm tokens, and it is why the pattern sweep comes before reading files in order [15].
Only then does the piece look at what the package drags in, using npm install --dry-run and npm view dependencies, with a tiny helper library pulling 40 transitive dependencies flagged as a reason to stop [16].
The practical consequence is that four of the five steps produce a verdict without any package code running on the host [17]. What to watch is the repetition cost: because npm pack fetches one specific version and the metadata read is version-scoped, this is a per-version audit, and it has to be redone at every upgrade rather than once at adoption [19]. Teams that cannot absorb that cadence will end up auditing the first install and trusting every one after it.