package.json - package-lock.json - typosquatting

A Typo in package.json Let a Lookalike Package Compromise the Pipeline

A One-Character Mistake That Shipped Malware

It started with a typo. Somewhere in the massive rush of feature pushes and PR merges, someone typed @utils_core instead of @utils-core into the package.json. That single character slip didn’t throw an error. Instead, it quietly pulled in a malicious lookalike package during the next CI/CD run, leveraging trust in pinned versions and automation. Welcome to the world of typosquatting in open source.

Typosquatting: The Attack Vector That Thrives on Human Error

Typosquatting is exactly what it sounds like: malicious actors registering packages with names nearly identical to legitimate ones. In fast-paced environments, this works because developers trust that their package.json and package-lock.json reflect what they expect. One character off? It might as well be invisible in a PR diff.

NPM has a history of being exploited through typosquatting. Packages like crossenv instead of cross-env or eventstream backdoors have already shown how effective these lookalikes can be. These fake packages often pass reviews simply because they look right and don’t throw immediate runtime errors.

Common traps include:

  • Hyphen vs underscore: lodash-core vs lodash_core
  • Plurals: request vs requests
  • Substituted characters: exress instead of express

Where package-lock.json Becomes a Blind Spot

The developer fixes their typo. Or so they think. But by now, package-lock.json has already locked in the attacker’s package. And this is where the real risk hides.

Unlike package.json, which is edited manually and gets more scrutiny, package-lock.json is autogenerated by NPM. As a result, it’s often treated like a formality, skipped, or only skimmed in pull request reviews. It’s not uncommon for teams to mark it as “too noisy” or to trust it implicitly without a deep look.

Attackers know this. They rely on it. Once a malicious package is referenced, thanks to a typo in package.json, package-lock.json records the resolved version. Even if the typo is corrected later, the malicious entry can persist unless the lockfile is explicitly regenerated.

To make things worse, version pinning, which is supposed to ensure consistency, can backfire. Attackers can version their fake package identically to the legitimate one. If the CI/CD system trusts pinned versions blindly, it won’t detect that it’s installing a package with a known-good version number but from a different, malicious source.

This silent persistence is what makes package-lock.json so dangerous. It ensures deterministic builds, yes, but it also guarantees that a bad package sticks around unless caught and cleaned out manually.

CI/CD: Where the Malware Hits – package.json

In a typical CI/CD pipeline, the malicious package doesn’t need to wait until runtime. It gets resolved and triggered much earlier in the flow:

Dependency Resolution
The pipeline pulls the exact dependency versions from package-lock.json, which now includes the typosquatted package due to the typo in package.json.

Install Phase
During npm ci, all dependencies are installed, including the malicious one. No warnings, no prompts, just silent installation.

Post-Install Script Execution
The lookalike package includes a post-install script that runs automatically once installation completes. This is where the compromise happens.

Pseudocode example:

if (installation_phase_active) {
runHiddenPayload()
}

Pseudocode description:
During the install phase, if the post-install lifecycle hook is present, the fake package triggers its hidden logic—often before builds or tests even run. This might involve making external requests, injecting backdoors, or other unauthorized actions.

⚠️ Warning: This pseudocode is for demonstration purposes only and must not be used in real environments.

No alerts are triggered. Nothing fails. The environment is already compromised, before your pipeline even reaches the testing stage.

Spotting the Difference Before It’s Too Late

A common mistake: assuming package.json tells the whole story. It doesn’t. The real power lies in the combo of package.json + package-lock.json.

Here’s what to look for:

  • Does the package-lock.json include unexpected packages?
  • Are any dependencies sourced from unknown registries or have weird scopes?
  • Are version numbers overly specific or misaligned?

Use CLI tools like:

  • npm audit to flag known issues
  • npm ls to view the full dependency tree
  • diff to compare versions between package.json and package-lock.json

Harden Your Pipeline: Defenses That Work

To defend against typosquatting:

  • Enforce scope restrictions in package.json.
  • Add pre-merge hooks to lint and validate dependencies.
  • Use npm ci to avoid unintentional version drift.
  • Regularly scan package-lock.json for anomalies.

And most importantly: treat unverified entries in package-lock.json as potential risks. Every commit should be treated as a supply chain checkpoint.

Automated Detection: How Tools Like Xygeni Help

While not a silver bullet, automated tools like Xygeni play a critical role in reducing the human error factor that typosquatting thrives on. Xygeni integrates directly into your DevOps workflow, adding a layer of real-time protection against dependency hijacking before it ever reaches execution.

Here’s how Xygeni helps:

  • Suspicious Package Name Detection:
    Uses smart heuristics to detect typosquatting patterns in package.json, looking for minor variations in well-known package names (like added underscores, transpositions, or letter swaps).
  • Unknown Hash Verification:
    Compares the hash of every dependency in package-lock.json against a database of known-good artifacts from trusted registries. Even if a package name and version look fine, a hash mismatch raises a red flag.
  • Blocking Before the Build:
    Intercepts and blocks installation of any unverified or suspicious packages before they reach the install or postinstall phases in the CI/CD pipeline.
  • Dependency Graph Analysis:
    Continuously inspects the full dependency tree for indirect typosquatting attempts or malicious transitive dependencies.
  • Alerts and Reporting:
    Provides detailed, actionable alerts, showing what was flagged, why, and where it came from in the dependency chain.

As package ecosystems grow more complex, tools like Xygeni become essential. Manual review doesn’t scale with dependency sprawl or rapid iteration. Xygeni steps in to automate the critical checks modern pipelines need, closing the gap between trust and verification.

One Character. Real Consequences.

This wasn’t an exotic zero-day. It was a typo. One misplaced character in package.json, silently reinforced by package-lock.json, and malware was shipped, automatically, during a routine CI/CD run.

That’s the real danger of typosquatting: it doesn’t need complexity. It exploits speed, trust, and automation. This compromise could have been prevented with the right controls in place:

  • Automated Scanning to catch suspicious package names and versions before install.
  • Lockfile Auditing to detect unreviewed or unexpected entries in package-lock.json.
  • Name Verification Heuristics to flag near-matches to trusted packages.

One character was all it took. The right checks would have stopped it before it touched your pipeline. Trust is not enough. In modern DevSecOps, you verify everything, or risk everything.

sca-tools-software-composition-analysis-tools
Prioritize, remediate, and secure your software risks
7-day free trial
No credit card required

Secure your Software Development and Delivery

with Xygeni Product Suite