GitHub Advisory malware sweep — `encryptstring*` 3-package cluster, `react-tabulix-ui` 3-version burst, `vantora` 2-version pair, and `kijai` singleton swept 2026-07-22
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.
- Detected by
- GitHub Advisory Database · npm Security
- Also known as
- 2026-07-22 GHSA npm sweep · encryptstring cluster
- Ecosystems
- npm
- Packages tracked
- 6
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.
Affected packages (6)
- npm
encrypt-string-ttak1.0.01.0.11.0.2 - npm
encryptstringadmin1.2.11.3.01.4.2 - npm
encryptstringadmincore1.0.11.1.01.2.2 - npm
kijai0.0.2 - npm
react-tabulix-ui0.1.00.1.10.1.2 - npm
vantora1.0.01.0.1
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) andencryptstringadmincore@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 commonencryptstringtheme 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 nameencrypt-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-uirapid-burst (1 package, 3 versions):0.1.0/0.1.1published within 30 minutes on 2026-07-21 (15:30:07 → 15:59:32 UTC), followed by0.1.2on 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 +-uisuffix) is typical of a React component-library typosquatvantorarapid-burst (1 package, 2 versions): both versions1.0.0/1.0.1published 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 exposurekijaisingleton (1 package): pre-existing name at0.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-securitysentinel 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
- 1Grep 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; auditpostinstalllogs 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 twoencryptstringadmin*` names share timestamps within seconds, a build that resolved one likely resolved both if the transitive graph included the second - 3If you had
react-tabulix-uiin 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) - 4If you had
vantorain a lockfile: both1.0.0and1.0.1should be treated as compromised. The 15-minute publish gap means a lockfile resolved during 2026-07-21 13:34 → 13:48 UTC picked1.0.0, and later resolvers picked1.0.1 - 5If you had
kijaiin a lockfile: the singleton0.0.2version was the only publish before the security replacement — any pin at0.0.2(or any dist-tag pointing there) is affected. The 37-day dormancy means downstream builds may have quietly baked this intonode_modulessnapshots long before the yank - 6Verify 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
- 7For projects using
postinstall-scripting packages, consider runningnpm install --ignore-scriptsin CI as a defense-in-depth measure and re-invoking scripts only for vetted first-party packages
References
- GitHubGitHub Advisory Database — recent npm malware advisoriesgithub.com
- GitHubGHSA-x92w-56m5-jchf — encryptstringadmin malware advisorygithub.com
- GitHubGHSA-hgfq-c4x3-jx2r — encryptstringadmincore malware advisorygithub.com
- GitHubGHSA-mpgp-492w-x7xj — encrypt-string-ttak malware advisorygithub.com
- GitHubGHSA-5jr9-f9w4-93v3 — react-tabulix-ui malware advisorygithub.com
- GitHubGHSA-4fm5-fwqx-8r39 — vantora malware advisorygithub.com
- GitHubGHSA-5mgj-24pj-pj6f — kijai malware advisorygithub.com