If you maintain an online shop, security usually means the known flaw: a CVE is published, a patch appears, maintenance applies it. The Shai-Hulud campaigns have shifted that order. The entry point was not an unpatched vulnerability in the installed shop system, but the harmless-looking patch version of a dependency -- delivered through the official registries, properly versioned, unremarkable. Across all its waves, the Mini Shai-Hulud campaign covered 1,055 compromised package versions across 502 packages and spanned npm, PyPI and Composer (Infosecurity Magazine) -- precisely the ecosystems that feed shop themes, build pipelines and extensions. The malicious code ran at install time and harvested CI/CD secrets: deploy access, payment API keys, registry tokens. This article is about provenance rather than panic -- the question of where code comes from and under which conditions it may enter the build. Unlike CVE management for online shops, which prioritises known vulnerabilities in your existing stack, and unlike the plugin audit against bloat and load time, this is about deliberately malicious code in the supply chain: packages with no vulnerability report, because they had no flaw to begin with -- they were the weapon from the start.
When the update itself becomes the entry point
Classic maintenance logic assumes that being current and being secure are the same thing: the faster an update is applied, the smaller the window for attackers. For known vulnerabilities that still holds. Germany's Federal Office for Information Security counts an average of 119 new software vulnerabilities per day in the current reporting period -- around 24 percent more than in the previous period (BSI, The State of IT Security in Germany 2025). Ignoring that stream means accumulating attack surface. The supply chain, however, inverts the assumption at one point: if the malicious version arrives through the official channel, updating quickly does not accelerate protection, it accelerates compromise. The update is then not the response to the attack -- it is its delivery vehicle.
This is no fringe topic, as the current OWASP ranking shows. Software Supply Chain Failures entered the OWASP Top 10 as a category in its own right at position 3; in the accompanying community survey, 50 percent of respondents even ranked the category first (OWASP Top 10:2025). With an average incidence rate of 5.72 percent it leads every other category -- and at the same time only 11 CVEs are mapped to it (OWASP Top 10:2025). This gap between a high incidence rate and almost no CVE coverage is the heart of the problem: the standard tooling of vulnerability management structurally struggles to see these attacks.
Consistent with that, the packages compromised in the Mini Shai-Hulud campaign were assigned no CVE, GHSA or OSV record at all (Cloud Security Alliance). A maintenance process that reacts solely to vulnerability reports would not have raised a single alert here. The packages were not vulnerable -- they were malicious. That is the distinction maintenance practice has to realign around: the question is no longer only 'is a flaw known?', but also 'where does this code come from, and why do we trust it?'.
No CVE, no advisory, no alert
How Mini Shai-Hulud moved through three ecosystems
The origin lies in September 2025. The Cybersecurity and Infrastructure Security Agency published an alert about a self-replicating worm in the npm ecosystem that had compromised more than 500 packages (CISA). The malicious code scanned the environment for credentials and specifically targeted GitHub access tokens as well as API keys for cloud services (CISA). The pattern was set: the target was not the shop, but the workbench on which it is built and shipped.
The real break came in April 2026. Within a 48-hour window on 29 and 30 April 2026, an actor compromised packages in npm, PyPI and Packagist/Composer simultaneously -- the first campaign to serve three package ecosystems in one operation (Cloud Security Alliance). The affected packages together accounted for more than 930,000 weekly downloads (Cloud Security Alliance). The payload, an obfuscated credential stealer of 11.7 MB, searched 134 distinct file paths for secrets and deposited the loot in roughly 1,800 repositories (Cloud Security Alliance).
| Wave and date | Scale | Distinctive trait |
|---|---|---|
| Shai-Hulud (September 2025) | more than 500 packages (CISA) | self-replicating worm targeting GitHub tokens and cloud keys |
| Shai-Hulud 2.0 (December 2025) | hundreds of packages (Microsoft Security) | shift from postinstall to preinstall |
| Mini Shai-Hulud (11 May 2026) | 373 versions / 169 packages (Unit 42) | first coordinated operation spanning npm and PyPI |
| AntV wave (19 May 2026) | 639 versions / 323 packages (Infosecurity Magazine) | entire publishing window lasted about one hour |
| Full campaign | 1,055 versions / 502 packages (Infosecurity Magazine) | npm, PyPI and Composer combined |
The decisive technical detail is when the code runs. The payload executed in the preinstall step, and therefore before tests or security checks came into play at all (Microsoft Security). It located the CI runner process and read its memory -- which rendered the masking of secrets in build logs ineffective, because the values were taken straight from process memory (Microsoft Security). The clean-up after a single wave gives a sense of the reach: 640 malicious packages were removed and 61,274 npm access tokens with write permissions were invalidated (Microsoft Security). The selection is telling: the payload filtered automation tokens specifically for the ability to bypass the two-factor check (Cloud Security Alliance).
The attack largely runs without an attacker
Why shops sit in the middle of the supply chain
A modern online shop is not a monolith but a structure assembled from other people's parts. The shop core and its extensions arrive via Composer, the storefront and theme build pulls hundreds of packages via npm, image and data pipelines reach into PyPI. Those are exactly the three ecosystems Mini Shai-Hulud served. OWASP explicitly notes that it is not only direct but also transitive dependencies that must be tracked (OWASP Top 10:2025) -- the packages your packages bring along, which featured in no selection decision of yours. Anyone running Shopware maintenance is therefore managing a supply chain, not merely an installation.
Composer: core and extensions
The shop system, plugins and libraries arrive as Composer packages. A single compromised package in this chain runs with the permissions of the deploy process.
npm: storefront and theme build
Themes, build tooling and frontend libraries carry the largest number of transitive dependencies -- and have been the preferred target of the waves so far.
CI/CD: access and secrets
The pipeline holds what the attack is looking for: deploy access, payment API keys, registry tokens. It is the actual vault, not the shop itself.
The reach comes from popularity, not from negligence. In the TanStack wave, roughly 520 million cumulative downloads fell inside the compromise window; one of the packages alone sees more than 12.7 million weekly downloads (Unit 42). The SAP CAP packages hit in April added up to around 572,000 weekly downloads, another package to roughly 360,000 (Cloud Security Alliance). In early June 2026, 32 packages of an enterprise namespace with about 80,000 weekly downloads were affected as well (Unit 42). And in April 2026 the Cybersecurity and Infrastructure Security Agency warned about a compromise of the widely used HTTP client axios that delivered a remote access trojan (CISA). So it does not hit the exotic dependency at the margin -- it hits the packages that sit in practically every build.
Make lockfiles binding
The most effective single measure is also the least spectacular: the build must not decide for itself which version it pulls. Version ranges such as ^1.2.3 are a standing invitation -- they allow every freshly published patch version to slip into the next build unnoticed. The Cloud Security Alliance states that pinning dependencies to cryptographic hashes, as supported by the integrity fields in package-lock.json, eliminates exposure to malicious patch-version increments (Cloud Security Alliance). Microsoft Security accordingly recommends pinning known-good versions and avoiding automatic dependency upgrades (Microsoft Security).
For the lockfile to be binding, the pipeline has to use the matching command. Unit 42 puts it plainly: use package-lock.json and run npm ci instead of npm install in CI/CD so that no automatic updates happen during the build (Unit 42). The same logic applies to Composer: composer install reads the lockfile, whereas composer update re-resolves the ranges and therefore belongs on the development machine, not in the deploy.
# Build against the lockfile, not against the version ranges
npm ci --ignore-scripts # installs exactly package-lock.json
composer install --no-scripts \
--no-plugins \
--prefer-dist # installs exactly composer.lock
# Unwanted in the pipeline: re-resolves ranges and pulls in
# every fresh patch version without review
# npm install
# composer update-
package-lock.jsonandcomposer.lockbelong in the repository under version control -- a lockfile in .gitignore is a blind build. - In CI/CD use
npm ciandcomposer installexclusively;npm installandcomposer updatestay reserved for deliberate updates. - Reduce version ranges to exact versions for security-relevant and payment-adjacent packages.
- Treat updates as their own reviewable commit -- not as a side effect of a deploy.
- Do not let tooling overwrite integrity hashes in the lockfile by recomputing them without checking the source.
Disable install scripts in the build
Installing a package in npm and Composer means, by default, executing someone else's code -- before any review. That is exactly where the campaign starts: the payload ran in the preinstall step and therefore before tests or security checks could engage (Microsoft Security). OWASP describes the same pattern for the original wave: the attack used install scripts for exfiltration and the captured npm tokens to automatically publish further malicious versions (OWASP Top 10:2025). The payload did not have to reach the shop at all -- it had already reached its goal on the build runner.
The countermeasure is a single line of configuration. The Cybersecurity and Infrastructure Security Agency recommends adding ignore-scripts=true to the .npmrc to stop potentially malicious scripts from running automatically during installation (CISA). Microsoft Security names the same measure in the form of npm install --ignore-scripts (Microsoft Security). For Composer, the equivalents are the --no-scripts and --no-plugins flags.
; Do not execute install scripts by default
ignore-scripts=true
; Only allow versions that have been publicly visible -- and
; therefore reviewable -- for at least seven days (CISA guidance)
min-release-age=7
; Pull exclusively through our own registry mirror
registry=https://registry.internal.example.com/Grant exceptions deliberately and one by one
A cooldown window before fresh versions
Malicious package versions have a short half-life: they get discovered, reported and pulled. In the AntV wave the entire publishing window was closed after roughly an hour (Infosecurity Magazine). Not adopting a fresh version immediately puts exactly that discovery phase to work for you -- without you having to review anything yourself. The Cybersecurity and Infrastructure Security Agency recommends min-release-age=7 in the .npmrc so that only packages are installed that have been publicly visible, and therefore reviewable, for at least seven days (CISA). Unit 42 sets the threshold more pragmatically and advises blocking any version published within the last 24 to 72 hours via a private registry (Unit 42).
| Cooldown window | What it catches | Assessment |
|---|---|---|
| None | nothing | every fresh version enters the next build unreviewed |
| 24 to 72 hours | waves that surface quickly and get pulled | pragmatic compromise, recommended by Unit 42 |
7 days (min-release-age=7) | versions without a public review phase | CISA guidance for npm |
| Manual approval | anything not deliberately adopted | worthwhile for payment- and checkout-adjacent packages |
A cooldown is not a waiver on security updates. It separates two things that are often conflated in practice: urgently closing a known flaw, and routinely following every new release. For the first case there is deliberate, documented fast-tracking -- for the second, there is no reason to hurry.
Your own registry mirror instead of direct pulls
A cooldown window, a blocklist or an approval can only be enforced where the pull actually happens. Unit 42 spells out the consequence: developer machines and CI runners should not talk directly to the public registry, but route all traffic through a private registry (Unit 42). For Composer the counterpart is a repository of your own holding the approved packages, with the public default index deliberately restricted. The mirror is not just a checkpoint but also a memory: it keeps what you once pulled, even if a package later disappears from the public registry.
One place for rules
Cooldown, blocklists and approvals take effect in a single place instead of being configured separately in every repository and on every machine.
Pulls stay reproducible
Withdrawn or deleted packages do not break the build, because the mirror still holds the version you already reviewed.
Visibility into what comes in
The mirror records which packages and versions were actually pulled -- the basis for any later impact analysis.
Smaller attack surface on the network
Build runners need no open path to the internet, only the path to the mirror -- which makes unusual traffic more likely to stand out.
SBOM as the inventory for the impact question
When a wave becomes public, one question counts: are we affected? Without an inventory that turns into a manual hunt across repositories, lockfiles and build servers -- under time pressure, usually on a weekend. A software bill of materials (SBOM) answers the question in minutes rather than hours. Unit 42 recommends automatically generating an SBOM for every production release so that impact can be analysed instantly once a new compromise is announced (Unit 42). The Cybersecurity and Infrastructure Security Agency requires machine-readable formats in its guidance, specifically SPDX or CycloneDX (CISA), and Microsoft Security names SBOM generation as part of code scanning (Microsoft Security).
- Generate and retain an SBOM for every release -- not only for the current state, but for every state you shipped.
- Capture direct and transitive dependencies, since OWASP explicitly locates the risk in the packages that come along too (OWASP Top 10:2025).
- Choose a machine-readable format (SPDX or CycloneDX) so the impact question is answered by query rather than by visual inspection.
- Store the SBOM together with the deploy timestamp so it can be reconstructed which state was live when.
- Tie retention to your evidence obligations -- the documentation you keep anyway for maintenance evidence towards cyber insurance is produced right here.
The most expensive hour after a supply chain advisory is the one in which nobody can say whether the affected package is even in use. An inventory turns a crisis meeting into a query.
Rotate tokens and separate deploy credentials
The payload is not after customer data, it is after keys. In a single wave, 61,274 npm access tokens with write permissions had to be invalidated (Microsoft Security) -- tokens that allowed new package versions to be published in the name of their owners. In doing so, the malicious code filtered automation tokens specifically for the ability to bypass the two-factor check (Cloud Security Alliance). That is exactly what makes the worm possible: a token without a second factor is a master key to the supply chain. The Cloud Security Alliance additionally points out that builds triggered by pull requests while holding access to publishing credentials create an attack surface of their own (Cloud Security Alliance).
- Separate deploy, publishing and read access: a build that is only meant to build needs no key to publish.
- Keep payment API keys out of the build environment -- they belong in the runtime environment, not in the pipeline.
- Rotate after every suspicion: cloud keys, npm tokens, SSH keys and CI/CD secrets, as CISA recommends for exposed credentials (CISA).
- Remove unnecessary roles and permissions from CI/CD identities, as Microsoft Security recommends following the incidents (Microsoft Security).
- Where possible use short-lived credentials bound to workflow and branch instead of long-lived tokens (Cloud Security Alliance).
- Harden human access separately -- the link to MFA and least-privilege roles in the shop backend is the same: one factor alone does not carry.
The connection to the shop is immediate. Whoever captures deploy access can write code into the delivery -- achieving the same result as a web skimming attack on the checkout page, only one level earlier and without the detour through a vulnerability in the shop itself.
Spotting suspicious patch bumps in the diff
One finding from the April wave deserves particular attention: the campaign also compromised packages carrying valid provenance attestations at SLSA Build Level 3 -- evidence that process integrity controls can be defeated (Cloud Security Alliance). A signature proves that a version passed through the intended pipeline. It does not prove that the contents are harmless. The review therefore has to concern the contents, not just the certificate of origin.
- New install hooks: a patch version that suddenly ships a
preinstallorpostinstallscript is no longer a bug fix. - Large, opaque blobs: the payloads of the waves were around 499 KB (Microsoft Security) and 11.7 MB respectively (Cloud Security Alliance) -- a patch version growing by megabytes deserves a look.
- New outbound connections: CISA recommended blocking outbound connections to the exfiltration domain in use and reviewing firewall logs for connections to suspicious domains (CISA).
- A change of runtime: if a package abruptly requires a different runtime or a foreign interpreter to install, that is a signal.
- A version bump without cause: a patch version with no matching entry in the changelog or no corresponding commit in the source repository needs an explanation.
This review is manual work -- but not for every package. It pays off where the cooldown has elapsed and an update is deliberately adopted, and especially for those packages whose install scripts are on the exception list. Control over your own outbound channels follows the same thinking as DNS maintenance with DNSSEC, registrar lock and monitoring: knowing where your systems are allowed to talk makes it easier to notice when they suddenly talk somewhere else.
The response plan when a package is compromised
A response plan for the supply chain differs from an emergency plan for a hacked shop: it is about credentials first, code second, and the shop itself may keep running inconspicuously throughout. The order determines whether an affected package turns into an incident.
- Stop the build. As long as it is unclear which version gets pulled, every further run produces additional exposure.
- Check impact. Query SBOM and lockfiles against the reported package versions -- direct and transitive dependencies alike.
- Rotate credentials. Renew cloud keys, npm tokens, SSH keys and CI/CD secrets, as CISA prescribes for exposed credentials (CISA).
- Review accounts and repositories. CISA advises watching for unrecognised public repositories, suspicious commits and unexpected changes to workflow files that could establish persistence (CISA).
- Inspect outbound traffic. Review firewall logs for connections to suspicious destinations (CISA).
- Return to the last known good state. Reset the lockfile to the reviewed version and rebuild from a clean environment -- a dependable backup service turns this step into a decision rather than a reconstruction.
- Redeploy cleanly. Discard caches and artefacts originating from the affected window.
- Document. Record timestamps, affected versions, rotated credentials and measures taken -- the basis of any later evidence.
Where this sequence is organised ad hoc during an incident, it costs hours. Where it is prepared, it is a checklist. The same logic applies as with the emergency plan for a hacked website: the decisions have to be made before the incident, not while it unfolds -- and when things get tight, a defined availability in emergency support counts for more than any retrospective analysis.
Provenance checks as part of maintenance
From all of this follows an uncomfortable conclusion: simply applying updates without checking provenance becomes a risk in itself. The answer, however, is not to update less often -- at an average of 119 new vulnerabilities per day (BSI, The State of IT Security in Germany 2025), that would be by far the worse bet. The answer is a procedure: keep updating promptly, but route it through defined controls. Lockfile instead of range, install scripts off, cooldown before adoption, pulls through your own mirror, SBOM as inventory, separated tokens.
Controlled security updates
Known flaws still get closed promptly -- but along a path where the origin of the version has been checked. That is the core of our security updates.
Staging before production
An update emerging from the cooldown runs against a staging environment first. What that looks like in practice is covered in the article on staging environments for safe updates; the framework comes from our maintenance services.
Observation after the deploy
After every rollout, what counts is whether the shop behaves as expected. Our monitoring watches for exactly that deviation -- including when no error message appears.
The effort involved is manageable, but it is permanent. A lockfile does not maintain itself, a cooldown wants enforcing, an SBOM generating and an exception list keeping. That is precisely why supply chain security belongs in an SLA maintenance contract rather than in a one-off project: the campaigns of recent months have shown that the next wave comes unannounced and can move through three ecosystems within an hour. What counts then is not how fast you react -- but which controls were already standing before the advisory arrived.
Sources and studies