# GitHub Advisory npm malware sweep, 22 July 2026

> On 2026-07-22 GitHub's Advisory Database retired six CWE-506 npm malware advisories. Dominant cluster: three `encryptstring*` packages (`encryptstringadmin`, `encryptstringadmincore`, `encrypt-string-ttak`) with 9 combined versions security-replaced within a 20-second window at 20:37 UTC - a coordinated single-actor `encryptstring`-themed publish burst. Also swept: `react-tabulix-ui` 3-version burst, `vantora` 2-version pair, and the pre-existing `kijai` singleton.

- Published: 2026-07-22
- Severity: high
- Kind: Advisory sweep - A dated batch of GitHub Advisory Database malware entries collected together. A sweep mixes kinds - typosquats, dependency-confusion probes, boilerplate takedowns with no published analysis, and occasionally real payloads - and its severity reflects the worst confirmed item, not the batch as a whole.
- Ecosystems: npm
- Scope: 6 packages, 15 compromised versions
- Tags: credential-theft
- Also known as: 2026-07-22 GHSA npm sweep, encryptstring cluster
- Detected by: GitHub Advisory Database, npm Security
- Incident ID: npm-2026-07-22-ghsa-malware-sweep

## Affected packages (6)

Only the exact versions listed were named by the source advisories. Other versions are not implicated.

| Package | Ecosystem | Compromised versions |
|---|---|---|
| [encrypt-string-ttak](https://dependencywatch.io/package/npm/encrypt-string-ttak) | npm | 1.0.0, 1.0.1, 1.0.2 |
| [encryptstringadmin](https://dependencywatch.io/package/npm/encryptstringadmin) | npm | 1.2.1, 1.3.0, 1.4.2 |
| [encryptstringadmincore](https://dependencywatch.io/package/npm/encryptstringadmincore) | npm | 1.0.1, 1.1.0, 1.2.2 |
| [kijai](https://dependencywatch.io/package/npm/kijai) | npm | 0.0.2 |
| [react-tabulix-ui](https://dependencywatch.io/package/npm/react-tabulix-ui) | npm | 0.1.0, 0.1.1, 0.1.2 |
| [vantora](https://dependencywatch.io/package/npm/vantora) | npm | 1.0.0, 1.0.1 |

## What happened

On 2026-07-22 GitHub's Advisory Database published **six CWE-506 (Embedded Malicious Code) advisories against npm packages**. All six records use the standard "any computer that has this package installed or running should be considered fully compromised - rotate all secrets from a different computer" boilerplate.

## Cluster 1 - `encryptstring*` 3-package cluster (3 packages, 9 versions)

| Package | Versions | Publish window (UTC) | npm security-replace (UTC) |
|---|---|---|---|
| `encryptstringadmin` | `1.2.1` → `1.4.2` (3 versions) | 2026-07-22 16:13:01 → 17:11:27 | 2026-07-22 20:37:45 |
| `encryptstringadmincore` | `1.0.1` → `1.2.2` (3 versions) | 2026-07-22 16:13:30 → 17:11:58 | 2026-07-22 20:37:50 |
| `encrypt-string-ttak` | `1.0.0` → `1.0.2` (3 versions) | 2026-07-20 13:29:58 → 2026-07-21 15:00:10 | 2026-07-22 20:38:01 |

The two `encryptstringadmin*` names were pushed in **synchronised 30-second-offset pairs**: `encryptstringadmin@1.2.1` at 16:13:01 UTC and `encryptstringadmincore@1.0.1` at 16:13:30 UTC, then `encryptstringadmin@1.3.0` at 16:36:04 UTC and `encryptstringadmincore@1.1.0` at 16:36:52 UTC, then `encryptstringadmin@1.4.2` at 17:11:27 UTC and `encryptstringadmincore@1.2.2` at 17:11:58 UTC. The three matched pairs across a 58-minute window indicate a single automated publisher exercising two names in lockstep - likely an operator probing paired resolver behavior (e.g. a monorepo where an admin CLI and its core library are both required).

The third `encrypt-string-ttak` name shares the `encryptstring` theme but has a wider publish spread (nearly two days) and different versioning (`1.0.x` rather than `1.x.x`). The near-simultaneous security replacement across all three names at 20:37:45 → 20:38:01 UTC - a 16-second total window - indicates GitHub's malware-detection pipeline flagged them as a related cluster.

## Cluster 2 - `react-tabulix-ui` 3-version burst (1 package)

| Package | Versions | Publish window (UTC) | npm security-replace (UTC) |
|---|---|---|---|
| `react-tabulix-ui` | `0.1.0` → `0.1.2` (3 versions) | 2026-07-21 15:30:07 → 2026-07-22 17:45:55 | 2026-07-22 20:11:40 |

A 29-hour publish window spanning two calendar days, with the final version (`0.1.2`) landing just 2.5 hours before the security replacement. The name pattern (`react-` prefix + short brand name + `-ui` suffix) is typical of a React component-library typosquat targeting a private-registry scope or a to-be-published-but-not-yet public package name.

## Cluster 3 - `vantora` 2-version pair (1 package)

| Package | Versions | Publish window (UTC) | npm security-replace (UTC) |
|---|---|---|---|
| `vantora` | `1.0.0` → `1.0.1` | 2026-07-21 13:34:04 → 13:48:49 | 2026-07-22 16:43:07 |

Both versions pushed in a **15-minute window**, then sat exposed for just over a day before the security replacement. The short exposure window and tight publish cadence fit a single-actor test of a novel payload against a specific target - the actor didn't bother pushing incremental patches after the initial pair.

## Cluster 4 - `kijai` singleton (1 package)

| Package | Versions | Publish window (UTC) | npm security-replace (UTC) |
|---|---|---|---|
| `kijai` | `0.0.2` | 2026-06-15 01:21:50 | 2026-07-22 17:09:20 |

Unlike the other clusters, `kijai@0.0.2` sat **dormant for 37 days** between publish and security replacement - long enough for downstream builds to bake it into cached `node_modules` snapshots. The delay fits a delayed-detection pattern where an automated scanner (not the actor themselves) surfaced the payload well after the fact.

## Registry state

All 6 packages were npm-security-replaced (`0.0.1-security` sentinel tarball at `latest` dist-tag). Original version tarballs are no longer resolvable on the public registry, but private registry mirrors that cached the tarballs during the publish window WILL keep serving the original versions after the public yank - any lockfile hit must be treated as actionable regardless of what the public registry currently returns.

## Impact

- Any host that installed any of the packages below should be treated as fully compromised - every GHSA record uses the boilerplate CWE-506 "rotate all secrets from a different computer" language, and no patched version exists
- **`encryptstring*` cluster** (3 packages, 9 versions): `encryptstringadmin@1.2.1`/`1.3.0`/`1.4.2` (published 2026-07-22 16:13:01 → 17:11:27 UTC) and `encryptstringadmincore@1.0.1`/`1.1.0`/`1.2.2` (published 2026-07-22 16:13:30 → 17:11:58 UTC) were pushed in **synchronised 30-second-offset pairs** across a 58-minute window - the two names share both a common `encryptstring` theme and near-identical publish timestamps, indicating a single automated publisher. Both were security-replaced within a 6-second window at 2026-07-22 20:37:45 → 20:37:50 UTC. A third theme-adjacent name `encrypt-string-ttak@1.0.0`/`1.0.1`/`1.0.2` published across 2026-07-20 13:29 → 2026-07-21 15:00 UTC was security-replaced 11 seconds later at 20:38:01 UTC - same cluster, wider publish spread
- **`react-tabulix-ui` rapid-burst** (1 package, 3 versions): `0.1.0`/`0.1.1` published within 30 minutes on 2026-07-21 (15:30:07 → 15:59:32 UTC), followed by `0.1.2` on 2026-07-22 17:45:55 UTC - then security-replaced 2026-07-22 20:11:40 UTC. The name pattern (`react-` prefix + short brand name + `-ui` suffix) is typical of a React component-library typosquat
- **`vantora` rapid-burst** (1 package, 2 versions): both versions `1.0.0`/`1.0.1` published within a **15-minute window** on 2026-07-21 (13:34:04 → 13:48:49 UTC), then security-replaced 2026-07-22 16:43:07 UTC - a same-day publish→take-down cadence with just under a day of exposure
- **`kijai` singleton** (1 package): pre-existing name at `0.0.2` (published 2026-06-15 01:21:50 UTC - sat dormant for 37 days) then npm-security-replaced 2026-07-22 17:09:20 UTC. The 37-day dormancy before the security replacement fits a delayed-detection pattern where the payload was flagged by an automated scanner rather than the actor themselves
- None of the 6 packages have surviving version tarballs on the public npm registry - all replaced with `0.0.1-security` sentinel tarballs. Private registry mirrors (Verdaccio, Artifactory, Nexus) that cached the tarballs during the publish window WILL keep serving the original versions after the public yank - lockfile hits on these names must be treated as actionable regardless of what the public registry currently returns

## What to do

1. Grep every lockfile (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`) for each name in the packages map below. Any match is a supply-chain incident: rotate every credential the build runner could reach and re-image the build host
2. **If you had any `encryptstring*` package in a lockfile**: the synchronised publish cadence across three names (`encryptstringadmin` / `encryptstringadmincore` / `encrypt-string-ttak`) confirms a coordinated single actor. Rotate npm, GitHub, and CI/CD tokens the install could have reached; audit `postinstall` logs for the exposure window between the earliest publish (2026-07-20 13:29 UTC) and security-replacement (2026-07-22 20:38 UTC). Because the two `encryptstringadmin*` names share timestamps within seconds, a build that resolved one likely resolved both if the transitive graph included the second
3. **If you had `react-tabulix-ui` in a lockfile**: any of the three versions (`0.1.0`, `0.1.1`, `0.1.2`) should be treated as compromised. The exposure window ran ~29 hours (2026-07-21 15:30 UTC → 2026-07-22 20:11 UTC)
4. **If you had `vantora` in a lockfile**: both `1.0.0` and `1.0.1` should be treated as compromised. The 15-minute publish gap means a lockfile resolved during 2026-07-21 13:34 → 13:48 UTC picked `1.0.0`, and later resolvers picked `1.0.1`
5. **If you had `kijai` in a lockfile**: the singleton `0.0.2` version was the only publish before the security replacement - any pin at `0.0.2` (or any dist-tag pointing there) is affected. The 37-day dormancy means downstream builds may have quietly baked this into `node_modules` snapshots long before the yank
6. Verify none of the 6 listed packages still resolves via your private mirror - internal Artifactory / Nexus / Verdaccio instances routinely cache tarballs and will keep serving the original versions after the public yank
7. For projects using `postinstall`-scripting packages, consider running `npm install --ignore-scripts` in CI as a defense-in-depth measure and re-invoking scripts only for vetted first-party packages

## Check your own dependencies

- Check a package-lock.json against this incident: https://dependencywatch.io/check/package-lock-json

The scan runs entirely in the browser; lockfile contents are never uploaded.

## References

- [GitHub Advisory Database - recent npm malware advisories](https://github.com/advisories?query=type%3Amalware+ecosystem%3Anpm&sort=published-desc) - GitHub
- [GHSA-x92w-56m5-jchf - encryptstringadmin malware advisory](https://github.com/advisories/GHSA-x92w-56m5-jchf) - GitHub
- [GHSA-hgfq-c4x3-jx2r - encryptstringadmincore malware advisory](https://github.com/advisories/GHSA-hgfq-c4x3-jx2r) - GitHub
- [GHSA-mpgp-492w-x7xj - encrypt-string-ttak malware advisory](https://github.com/advisories/GHSA-mpgp-492w-x7xj) - GitHub
- [GHSA-5jr9-f9w4-93v3 - react-tabulix-ui malware advisory](https://github.com/advisories/GHSA-5jr9-f9w4-93v3) - GitHub
- [GHSA-4fm5-fwqx-8r39 - vantora malware advisory](https://github.com/advisories/GHSA-4fm5-fwqx-8r39) - GitHub
- [GHSA-5mgj-24pj-pj6f - kijai malware advisory](https://github.com/advisories/GHSA-5mgj-24pj-pj6f) - GitHub

---

Canonical page: https://dependencywatch.io/incident/npm-2026-07-22-ghsa-malware-sweep
Markdown version: https://dependencywatch.io/incident/npm-2026-07-22-ghsa-malware-sweep.md
Site index for agents: https://dependencywatch.io/llms.txt
DependencyWatch.io is operated by Precursor Security (https://precursorsecurity.com).
