TL;DR

On 2026-10-05 at 11:56 UTC a new version of @subql/common — the shared library under SubQuery's indexing CLI, node and query packages — was published to npm as 5.8.3, 32 minutes after a pre-release 5.8.3-onf-rt1 under a dist-tag named redteam. StepSecurity's analysis of 5.8.3, published the same day, says the version adds a postinstall hook and a 62 KB dist/project/readers/manifest-cache.js that is also exported from index.js, so the payload runs on install and again on every import, in a detached child process. Decoded, it collects .npmrc, .env, AWS/GCP/Azure credentials, SSH keys, kubeconfig, Vault tokens, cryptocurrency wallet paths and AI-agent configuration files; on Linux CI it reads the GitHub Actions runner process for the job token, uses stolen GitHub tokens to push a branch carrying a .github/workflows/codeql_analysis.yml that dumps repository secrets to an artifact, and opens a remote shell. By the time this sweep queried the registry the version was gone (version not found: 5.8.3), latest pointed back at 5.8.2, and the redteam pre-release was still listed. If you installed any @subql package between roughly 11:56 UTC and the removal, treat the machine or runner as compromised, rotate everything it could read, and check your repositories for a CodeQL workflow you did not write. Status is unconfirmed because the only analysis is StepSecurity's and SubQuery had not responded on the issue when this sweep read it; the registry removal is the one independent signal.

What happened

  • The package. @subql/common is the shared types-and-readers library that @subql/cli, @subql/node-core, @subql/query and the chain-specific node packages depend on. The subquery/subql monorepo has ~18k GitHub stars (per the Hacker News submission); the package itself had 4,380 downloads in the week ending 10-04, with @subql/cli at 2,374 and @subql/node-core at 2,011 — a web3 developer audience, not a general one, but one that runs the CLI in CI and holds chain RPC keys, deployer keys and cloud credentials.
  • The publication. The registry's time field shows 5.8.3-onf-rt1 at 11:24 UTC and 5.8.3 at 11:56 UTC on 2026-10-05; the previous release, 5.8.2, was 2025-11-20. The pre-release carries a redteam dist-tag. No source this sweep read explains the pre-release, the tag name or who published either version; StepSecurity's issue asks the maintainers to "investigate commit 506863d and the release workflow for unauthorized modifications" and to "rotate npm trusted-publisher settings and all CI secrets," which is the shape of a publish-pipeline compromise, but that is the reporter's request, not a finding.
  • The payload (StepSecurity's analysis). 5.8.3 adds a postinstall script, a new dist/project/readers/manifest-cache.js (62,124 bytes) and a one-line export of it from index.js. The file unwraps three layers (base64/XOR, PBKDF2-HMAC-SHA256, AES-256-GCM) into an ~83 KB bundle that: harvests environment variables and gh auth token output; reads .npmrc, .env, SSH keys, AWS/GCP/Azure credential files, kubeconfig and Kubernetes service-account tokens, HashiCorp Vault tokens, wallet directories and AI-agent configuration files; on Linux runners reads the Runner.Worker process memory for the GitHub Actions job token; uses any GitHub token it finds to push a branch containing .github/workflows/codeql_analysis.yml that writes the repository's secrets to a workflow artifact; encrypts the collection (RSA-OAEP + AES-256-GCM) and posts it to a command-and-control host; and keeps a channel open for shell command execution and interactive PTY sessions. Because it runs from index.js as well as from postinstall, --ignore-scripts does not prevent execution — the first require('@subql/common') does.
  • Blast radius. StepSecurity reviewed 77 @subql packages and found 19 whose latest version had a dependency path to @subql/common@5.8.3 — 16 directly (including @subql/cli@6.6.3, @subql/node-core@19.3.1, @subql/query@2.25.0) and three through an intermediate package. A floating ^5.8.2 range resolved to the malicious version for the window it was live.
  • Takedown. As of this sweep's registry query (2026-10-05, afternoon UTC) registry.npmjs.org/@subql/common/5.8.3 returns version not found, dist-tags.latest is 5.8.2, and 5.8.3-onf-rt1 is still listed under redteam with its own tarball. Who removed 5.8.3 — npm, or the maintainers — is not stated anywhere this sweep could read. The GitHub issue (subquery/subql #3047, opened by a StepSecurity engineer) had no maintainer reply at fetch time.

The pattern. This is the Shai-Hulud/ChainDrop family's tradecraft on a smaller package: runner-memory token theft, a workflow pushed under a plausible security-tool name to exfiltrate secrets through artifacts, and the AI-agent config directories added to the harvest list — SafeDep's 2026-10-02 reading of Google Cloud's AI Risk and Resilience Report says the Shai-Hulud family now searches 469 credential locations, up from 189, with AI tool settings files among the additions (see the Mandiant case study for the report itself). The on-import execution path is the part a defender most often misses: a lockfile that pinned 5.8.3 during the window keeps re-running the payload on every process start until it is changed.

Am I affected?

# Did the malicious version land in any lockfile or node_modules?
grep -rn '"@subql/common"' package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null | grep -n '5\.8\.3'
find . -path '*/node_modules/@subql/common/package.json' -exec grep -H '"version"' {} \;
# The payload file is the tell (absent in 5.8.2)
find . -path '*/node_modules/@subql/common/dist/project/readers/manifest-cache.js'
# CI: any run of a @subql install on 2026-10-05 after 11:24 UTC on a runner with a GITHUB_TOKEN
# Repos: a workflow you did not write
git log --all --diff-filter=A -- .github/workflows/codeql_analysis.yml

Any hit on 5.8.3 or 5.8.3-onf-rt1, or the manifest-cache.js file, means the payload executed on that machine or runner.

If you are affected

  1. Remove node_modules, pin @subql/common to 5.8.2 (or whatever the maintainers publish as the clean successor) and reinstall from a clean checkout. Rebuild CI runners and any container image that installed during the window from trusted bases.
  2. Rotate everything the payload lists: npm tokens (.npmrc), .env secrets, SSH keys, AWS/GCP/Azure credentials, kubeconfig and service-account tokens, Vault tokens, GitHub PATs and gh CLI tokens, and any API keys stored in AI-agent configuration directories (~/.claude, ~/.codex, ~/.cursor, MCP configs) — rotating-cloud-credentials.md, if-your-github-pat-leaked.md, if-your-npm-token-leaked.md.
  3. In every repository a compromised token could push to: delete unexpected branches and any codeql_analysis.yml you did not author, delete workflow artifacts from those runs, rotate repository and organisation secrets, and audit the Actions log for runs you did not trigger — prevention/ci-cd-hardening.md.
  4. Treat the host as having had an interactive shell: review outbound connections from the install window, and follow if-you-ran-malicious-postinstall.md.

Prevention

  • Pin exact versions and commit lockfiles; a floating ^ range is what resolved to 5.8.3. A cooldown rule (do not adopt a version published in the last N days) would have skipped a 32-minute-old release.
  • --ignore-scripts is necessary but not sufficient against on-import payloads; combine it with install-time scanning (StepSecurity's, Socket's or Aikido's) and run installs in CI on runners with no ambient cloud credentials — prevention/npm-hardening.md, prevention/package-vetting-checklist.md.
  • Keep AI-agent configuration out of the credential blast radius: API keys in the OS keychain or a secrets manager, not in ~/.claude/settings.json-style files, since those directories are now on the harvest list of every major stealer family — prevention/credential-hygiene.md.
  • Alert on new workflow files pushed by a token rather than a person, and on artifacts larger than your builds produce.

Why this matters for vibe coders

A web3 indexer is not a vibe-coding tool, but the payload's harvest list is a snapshot of what attackers now expect to find on a developer machine in 2026: cloud and registry credentials, and the agent config files that hold model-provider keys and MCP server secrets. Whatever package you install next, assume its postinstall and its first import run with everything in your home directory. If you let an agent run npm install for you, that agent's own credentials are in scope.

Update — 2026-10-06: a second, independent analysis (GMO Flatt Security) confirms the payload and supplies the missing "how" — the attacker had push access to subquery/subql and modified the npm publish workflow, so 5.8.3 was built and signed by the project's own GitHub Actions OIDC pipeline; timeline 11:24 pre-release → 11:52 malicious commit → 11:56 publish → 12:46 removal; status moves from unconfirmed to contained

GMO Flatt Security Research published its own write-up on 2026-10-06 (the attack is dated 10-05), reaching Hacker News the same morning. It is an independent read of the same tarball, not a repost of StepSecurity's, and it agrees on every technical point this file took from the first analysis — the AWS/GitHub/SSH/Kubernetes/Vault harvest, the AWS SSM Parameter Store and Secrets Manager enumeration "across all regions," the GitHub Actions runner-memory secret extraction, and the injected workflow "disguised as CodeQL analysis." Two things it adds:

  • The compromise method. "The attacker obtained push access to the subquery/subql GitHub repository and tampered with the CI/CD workflow (.github/workflows/publish.yml) to inject a malicious payload into the legitimate npm publishing flow." The malicious release therefore carries the project's normal trusted-publisher provenance — which is why the 10-05 file could not tell from the registry whether a maintainer or an attacker published it. The malicious commit is 506863d6fb82bd2714970cf8c6f1bf364374b009, the one StepSecurity's issue asked the maintainers to investigate.
  • The timeline (UTC, 2026-10-05). 11:24 5.8.3-onf-rt1 published, with no postinstall script (Flatt calls it a "presumed test release"); 11:52 the malicious commit pushed; 11:56 5.8.3 published via GitHub Actions OIDC; 12:46 package removed from the npm registry. That makes the live window 50 minutes for the malicious version, and it explains the redteam-tagged pre-release the 10-05 file reported without interpreting: it was the dry run.

Flatt also names the command-and-control domain and IP; this file continues not to reproduce them beyond saying that any outbound connection from a build host or runner to the ci-artifacts.dev domain on or after 2026-10-05 is a compromise signal — treat the domain as an IOC in egress logs, not as something to visit.

Status. Two independent analyst firms, a registry removal confirmed by both and by this repo's own query, and a stated compromise method meet this repo's bar; unconfirmed → contained. Still missing at fetch time: any statement from the SubQuery maintainers, and confirmation that the repository's push access and publishing secrets have been rotated — until that appears, treat the @subql namespace as capable of producing another malicious release and keep the pins. The lesson for everyone else is the one Flatt's title implies: with trusted publishing, the publish workflow file is the signing key; protect .github/workflows/publish.yml with required reviews and environment protection rules the same way you would protect an npm token — prevention/ci-cd-hardening.md.

Sources