12 min read
High
By

vpmdhaj Typosquat Wave: 14 Malicious npm Packages in Four Hours, AWS and Vault Tokens as the Loot — Why --ignore-scripts Becomes a Standard Reflex Today

29 May 2026. Microsoft Threat Intelligence disclosed an active npm supply-chain campaign yesterday: an actor under the freshly created maintainer alias vpmdhaj published 14 malicious packages in a four-hour window on 28 May, typosquatting OpenSearch, ElasticSearch, DevOps and environment-configuration libraries and pointing the repository.url field at the official OpenSearch GitHub repository on purpose — Microsoft names opensearch-setup and elastic-opensearch-helper as examples; the full package list has been withheld to avoid imitation waves. The payload runs through preinstall lifecycle hooks in two stager variants (Gen-1 via preinstall.js/index.js, Gen-2 via setup.mjs) and harvests AWS credentials, HashiCorp Vault tokens and CI/CD pipeline secrets from the host. Methodologically the finding sits next to the TanStack/Nx Console line of the past two weeks: not a compromised maintainer identity but a fresh alias identity with visual cover. For the Mittelstand the reflex question is three steps long: does --ignore-scripts run as default, are the token-rotation paths documented, are the SBOM strict modes configured for new maintainer identities?

AI-generated · gpt-image 2.0

TL;DR — the 90-second summary

What was disclosed?

Microsoft Threat Intelligence finding from 28 May 2026: a single actor under the maintainer alias vpmdhaj publishes 14 malicious npm packages in a four-hour window, typosquatting OpenSearch, ElasticSearch, DevOps and environment-configuration libraries. Examples: opensearch-setup, elastic-opensearch-helper — package names that fail to register as a typosquat in a rushed setup flow or a quick copy-paste from a tutorial. The repository.url field in the respective package.json points to the official opensearch-project/opensearch GitHub address — visual cover designed to survive a casual audit of the repository link.

How serious?

High on the credential class. The payload runs through the preinstall lifecycle hook, i.e. automatically on every npm install — no developer interaction and no need for the package to be imported in application code. Microsoft documents two stager variants: Gen-1 (≤ 1.0.7265) invokes preinstall.js/index.js from the three hooks install/preinstall/postinstall; Gen-2 (≥ 1.0.7266) loads setup.mjs as a quieter loader through preinstall alone. Loot class per Microsoft: AWS credentials (IAM/STS), HashiCorp Vault tokens and CI/CD pipeline secrets from the host environment. Microsoft does not name the exact read paths; in the wave practice (TanStack/@antv comparison) the canonical sources are ~/.aws/credentials plus the EC2 Instance Metadata Service, the VAULT_TOKEN environment variable, the _authToken entry in ~/.npmrc and GitHub Actions OIDC.

Am I affected as a Moselwal customer?

Moselwal's own pipelines do not pull the malicious packages — we run no OpenSearch/ElasticSearch stack in the default platforms. You are directly affected if one of your platforms or your CI/CD runners hosts an npm workspace that lists OpenSearch or ElasticSearch packages as a dependency — typical in custom search integrations, logging pipelines (e.g. Symfony apps with an OpenSearch backend instead of MySQL fulltext) and frontend components with OpenSearch dashboards. Indirectly you are affected through every CI/CD runner that has triggered npm install since the 28 May window and does not have the lifecycle hook disabled.

Immediate mitigation?

Three steps. First, set npm config set ignore-scripts true globally (pnpm and yarn equivalents accordingly) and enforce the setting in the CI/CD runners. Second, lockfile audit: grep the two package names that Microsoft has named publicly across all package-lock.json and pnpm-lock.yaml files, then run a second search across all package.json references with repository.url=opensearch-project/opensearch combined with publish date 2026-05-28. On a hit: immediate token rotation of the AWS IAM/STS keys, Vault tokens, npm publish tokens and GitHub Actions tokens. Third, configure the SBOM and audit pipeline to surface new maintainer identities: a maintainer with account age < 30 days and one or a handful of published packages is an audit trigger from today onward.

Criticality?

See the hero badge high — act within the 48-hour window, because Microsoft documents active exploitation and the lifecycle hook auto-execution effectively removes the entry bar.

 

What happened

Microsoft Threat Intelligence documented an active npm supply-chain campaign on 28 May 2026. A single threat actor under the freshly created maintainer alias vpmdhaj uploaded 14 malicious packages into the npm registry in a four-hour window. The package names typosquat OpenSearch, ElasticSearch, generic DevOps and environment-configuration libraries; concretely Microsoft cites opensearch-setup and elastic-opensearch-helper as examples. The package.json metadata points the repository.url field at the official OpenSearch project GitHub address (github.com/opensearch-project/opensearch) on purpose — visual cover designed to survive a quick audit of the repository link.

The payload runs through the preinstall lifecycle hook. That is the built-in npm mechanism that runs scripts from the package automatically before the package is installed — so it runs even when the package is never imported in application code. As soon as a developer or a CI/CD runner triggers npm install and the malicious package lands in the dependency resolution (typically as a transitive dependency or through a typo in a manual npm install <name> call), the payload starts without further interaction. Microsoft documents two stager variants: Gen-1 in package versions ≤ 1.0.7265 calls preinstall.js or index.js from the three hooks install, preinstall and postinstall in parallel; Gen-2 in versions ≥ 1.0.7266 uses a single preinstall hook that loads setup.mjs as a newer, quieter loader.

The payload aims, according to the Microsoft report, at AWS credentials (IAM/STS), HashiCorp Vault tokens and CI/CD pipeline secrets from the host environment. Microsoft does not name the concrete read paths in the published blog post; from the “CI/CD pipeline secrets” class and the usual stealer patterns of this wave (TanStack, Mini-Shai-Hulud @antv), the canonical sources can be sketched as a typical cross-section: the AWS default profiles (~/.aws/credentials, EC2 Instance Metadata Service), the VAULT_TOKEN environment variable or ~/.vault-token file, the _authToken entry in ~/.npmrc, GitHub Actions OIDC via ACTIONS_ID_TOKEN_REQUEST_TOKEN. Read this list as a generic class enumeration, not as a verified vector catalogue of the concrete stealer.

Microsoft reported the packages to the npm security team. The full list of fourteen package names is being held back in the blog post on purpose, to keep further imitation waves at bay; the examples cited are opensearch-setup and elastic-opensearch-helper. The question “who installed during the four hours” is left to every affected organisation to answer individually — npm has not published download counts for that window.

Technical analysis

Structurally the vpmdhaj wave traces a different class from the TanStack wave (11 May) or the Nx Console wave (18 May). TanStack and Nx Console were compromises of established maintainer identities — TeamPCP took over existing maintainer tokens through the “pwn request” pull-request-target pattern, GitHub Actions cache poisoning and OIDC token extraction and published under established package names. The vpmdhaj wave uses a freshly created maintainer alias with visual cover — the repository.url points at the official OpenSearch repository, the package name reads plausibly in a setup flow, the lifecycle hook fires on the first npm install.

Methodologically this is the next escalation step in an npm threat spectrum that now holds two clearly separated classes. Class A: maintainer-identity compromise with publication under established names (TanStack, Nx Console). Class B: fresh alias identity with typosquat cover and lifecycle-hook trigger (vpmdhaj). The two classes need their own detection patterns. Class A can be caught with lockfile diff audits and maintainer-change alerting — if a package suddenly carries new maintainers in the maintainers field, that is a trigger. Class B cannot be detected that way, because the package was never in a lockfile — the trigger is the first npm install <name> call that introduces the package name at all.

Two generalisable lessons. First, --ignore-scripts as the default is no longer a paranoid configuration but a reasonable baseline — lifecycle-hook auto-execution is the central entry threshold for class-B attacks, and disabling it only costs the convenience of postinstall build steps in legitimate software (which can be re-enabled explicitly where really required via npm install <pkg> --foreground-scripts). Second, SBOM audit pipelines have to take maintainer-account age as their own risk axis — a maintainer with account age below 30 days and one to three published packages is an audit trigger, not default trust. Tools such as socket-security, snyk advisor and npm audit signatures already export those metadata; setting the reflex threshold in your own pipeline is a matter of configuration.

What this means for the Mittelstand

Moselwal's own pipelines do not carry the named packages — we do not run OpenSearch or ElasticSearch stacks in the default TYPO3/Sylius platforms, and our package-lock.json files in the 29 May sample have no hit on the two package names that Microsoft has named publicly. With some of our customers the search-stack picture does sit in the platform stack: in Sylius storefronts with an OpenSearch backend instead of MySQL fulltext (performance requirements > 100k products), in Symfony platforms with an ElasticSearch backend for logging aggregation (stack variant with friendsofsymfony/elastica-bundle), in Drupal sites with search_api_elasticsearch or search_api_opensearch, in custom frontends with OpenSearch Dashboards components. Wherever a frontend or build team pulls JavaScript packages for OpenSearch integration, the vpmdhaj class sits in the entry path.

The operational pivot sits in the CI/CD runner. A single npm install on a GitHub Actions runner is enough to exfiltrate GitHub Actions OIDC tokens, AWS credentials (when the runner is configured with the AWS provider) and npm publish tokens to an external endpoint. The follow-up wave is then not the pipeline itself but whatever hangs off the pipeline identity: push rights into the platform repository, container registry push rights, deployment rollout identities. Anyone running OIDC federation against AWS or Azure in their pipeline has handed away a cloud identity — and the jump from “pipeline token stolen” to “production cloud account compromised” is trivial, because OIDC federation in most setups maps workload identity to cloud identity.

Compliance-wise the finding lands on the standard axes. GDPR Art. 32 applies to any platform whose pipeline processes personal data — customer data sets in migration pipelines, test datasets with real-data samples. NIS-2 Art. 21 requires supply-chain discipline in the wider definition; npm packages are unambiguously supply-chain components, and an SBOM that does not surface maintainer-account age does not carry the inventory in full. For DORA-bound organisations and MaRisk-bound institutions every npm lifecycle-hook execution sits in the third-party risk inventory.

What this means for technical development

Methodologically the vpmdhaj wave forces a hardening convention on the pipeline level that has been recommended for years but rarely enforced. npm config set ignore-scripts true as a global CI/CD runner configuration plus the .npmrc variant (ignore-scripts=true) in the repository is the clean line. If you need build steps from postinstall hooks (for example husky for Git hooks, puppeteer for the Chromium download), call them explicitly as your own script step after npm install — that adds one line to the build pipeline and exposes the invisible build steps as visible pipeline stages.

Architecturally the second lesson is token-rotation discipline. AWS IAM keys, Vault tokens, npm publish tokens and GitHub Actions OIDC tokens must be rotatable without manual pipeline reconfiguration afterwards — via rotation workflows in AWS Secrets Manager, Vault renewal, npm granular access tokens with short lifetimes, GitHub federated OIDC without long-lived secrets. Without that discipline the incident-response window is days where it should be hours.

Third, SBOM pipelines must export maintainer metadata. CycloneDX 1.5 has the component.authors field that maps maintainer identity; cyclonedx-bom for npm pulls it by default. The next layer is the audit pipeline that takes maintainer-account age, number of published packages and maintainer constancy per package as a risk score. Open-source tools such as socket-security, snyk advisor and dependency-track offer evaluation hooks for this; the discipline sits in the configuration of the strict modes.

Concrete recommendations

In this order. First, set npm config set ignore-scripts true globally on every developer machine and CI/CD runner; ship .npmrc with ignore-scripts=true in the repository; configure pnpm and yarn equivalents (pnpm config set side-effects-cache false, yarn config set enableScripts false). Second, lockfile audit across all projects: grep -r "opensearch-setup\|elastic-opensearch-helper" package-lock.json pnpm-lock.yaml yarn.lock as the first sweep. Microsoft is withholding the full list of fourteen package names in the blog post on purpose, to avoid imitation waves — the more generic search by maintainer alias and publish window (all package.json references with "repository.url": "https://github.com/opensearch-project/opensearch" combined with publish date 2026-05-28) catches the second indicator class. If you have access to an npm audit service with a threat-intelligence feed (socket-security, snyk advisor, npm audit signatures), pull the Microsoft indicators into the audit run.

Third, on every hit: immediate token rotation of the AWS IAM/STS keys, HashiCorp Vault tokens, npm publish tokens and GitHub Actions tokens configured on the affected runner; audit sweep of the CI/CD logs for unusual push/release/deployment activity since the 28 May four-hour window. Fourth, SBOM and audit pipeline configuration: set maintainer-account age < 30 days and number of published packages < 5 as audit triggers — tools such as socket-security and snyk advisor offer those filters directly. Fifth, document the lifecycle-hook discipline and the token-rotation paths in the platform runbook. If these steps do not run on their own, talk to us: Moselwal builds npm and Composer pipelines in which the --ignore-scripts default, token rotation and SBOM maintainer audits are mandatory configurations.

This post reflects our technical and strategic assessment. It does not replace legal counsel or a data-protection impact assessment.

Sources

About the author

[Translate to English:] Foto von Kai Ole Hartwig.

Kai Ole Hartwig

Founder · Moselwal Digitalagentur · OnlyOle

Programming since 2002 – self-taught, set up my own business with KO-Web in 2012, now Moselwal. Over 100 projects, with a focus on security, performance, automation and quality.