Packaging WordPress Plugins Using the wp-scripts Package
Set up the plugin project for wp-scripts For a WordPress block plugin, keep the PHP file WordPress loads in the plugin’s root…
A WordPress plugin can download and run code from its vendor after installation. So even an authentic, current plugin can run malicious code if an attacker takes over the vendor’s system for distributing that code. This kind of attack is a software supply-chain attack: an attacker targets a trusted provider or delivery path to reach the provider’s customers.
The June 2026 incident involving OptinMonster and TrustPulse showed how that risk works. Attackers altered JavaScript served from a vendor-controlled content delivery network, or CDN, which is a network of servers that delivers files to websites. The compromised script ran in logged-in administrators’ browsers and attempted to add hidden access to affected WordPress sites.
Check more than a plugin’s version and local files. Find out which remote scripts the plugin loads, what those scripts can do in a logged-in browser, and how to inspect the site after a suspected compromise.
According to OptinMonster’s incident notice, an attacker exploited a known vulnerability in UpdraftPlus on a server hosting the company’s marketing website. The attacker gained access to a CDN API key stored on that server and used it to change JavaScript files sent to customers. The company said its application servers and systems storing OptinMonster and TrustPulse account information were hosted separately and were not breached. It reported no evidence that customer account data or personal information had been accessed. These findings about the incident’s scope came from the company, not an independent forensic audit.
The compromised files belonged to OptinMonster and TrustPulse. Researchers also documented malicious code in a PushEngage JavaScript file, which had a separate exposure timeline. The Sansec incident analysis describes exposure across the products, while PushEngage issued its own incident notice.
The malicious JavaScript targeted logged-in WordPress administrators. Researchers said the code first checked whether an administrator was logged in. It then tried to use that administrator’s browser to create rogue accounts and install a concealed backdoor plugin. A backdoor is code that gives an attacker a way to regain access later. Researchers also reported that the malicious code sent data to attacker-controlled infrastructure. They found that the backdoor included functions for running code. These findings describe observed malicious behavior; they do not establish that every exposed site was compromised.
The code ran on a WordPress page while an administrator was logged in, so it had access to that browser session. When WordPress uses cookie authentication for REST API requests, each request uses the user’s login cookies and a nonce, a security token that helps protect against cross-site request forgery. The WordPress REST API documentation explains that the user must also have the capability required for an action. Malicious code running in the administrator’s browser could use the session and token available there. A nonce helps protect requests from outside the session; it does not make code running inside a trusted page safe.
Sansec and Patchstack estimated that more than 1.2 million sites were potentially exposed across the affected products. That figure represents possible reach, not a count of infected sites. Separately, Patchstack reported blocking 271 rogue-account attempts across 13 sites during roughly 36 hours of monitoring. Those numbers describe Patchstack’s observed firewall activity, not the total number of victims.
The exposure window also depends on what a measurement captures. OptinMonster said its CDN logs indicated that the unauthorized configuration was in place for approximately a few hours. Sansec and Patchstack reported narrower periods when they verified the malicious OptinMonster and TrustPulse code. Researchers also found that the PushEngage script remained malicious at some CDN edges after the other files had been cleaned. A clean file at one observed point does not prove that every site or cached copy received the clean version at the same time.
For an individual site, exposure required more than having a plugin installed: the affected script had to load while an administrator was logged in. If that condition might have occurred, check the site rather than treating the potential-exposure estimate as proof of infection or proof of safety.
WordPress.org delivers plugin downloads through one path. A plugin that fetches a script from a vendor’s CDN uses another. WordPress.org documents security review for releases distributed through its update system. That safeguard does not, by itself, verify a JavaScript file that a plugin retrieves separately from a vendor-controlled CDN.
The OptinMonster listing on WordPress.org describes the plugin, while the product’s incident notice documents the altered remote scripts. Together, they show that plugin trust depends on four things. The installed package must be intact, and the vendor’s release and account systems must be secure. Remotely delivered code must retain its integrity, and its privileges in a visitor’s or administrator’s browser matter too.
A local checksum check addresses only part of that picture. WP-CLI’s wp plugin verify-checksums --all command compares supported WordPress.org plugin files with published checksums. The WP-CLI documentation notes that plugins outside WordPress.org may not have available checksums. Even a successful check does not verify JavaScript fetched later from a CDN.
The 2024 compromise of five WordPress.org plugins shows a different route through the supply chain. In that incident, attackers compromised developer accounts and injected malicious code into plugins distributed through the repository, as Wordfence reported. The OptinMonster incident involved remote scripts. Both cases show why checking only one distribution path leaves other paths unchecked.
Inspect the site if OptinMonster or TrustPulse was active and an administrator was logged in when an affected script loaded during the exposure period. PushEngage users should also check the separate product notice because its script had a distinct timeline. If you find signs of compromise, use this response sequence:
For regular checks, use WP-CLI to verify checksums for supported plugins. This checks local files, not the entire supply chain. Also inventory the external scripts loaded by plugins, themes, tag managers, and marketing services. Browser developer tools can show which script URLs a page requests; server-side inspection can help determine whether an attacker left files or accounts behind.
Before installing a plugin that loads remote JavaScript, find out which vendor domains it contacts and whether the service requires the vendor to change code instantly. Ask the vendor whether it offers versioned, immutable script files, publishes trusted hashes, and sends security incident notifications. Those answers help you judge whether the service’s benefits are worth relying on its live delivery system.
Subresource Integrity (SRI) lets a website specify a cryptographic hash for an external script. The browser checks the script against that hash and refuses to run it if the contents do not match. As MDN’s SRI documentation explains, this works best when the site owner can pin a particular file and its expected contents. A service that changes its SDK frequently needs to support a compatible versioning and hash-publishing approach for this control to remain practical.
A Content Security Policy (CSP) can restrict the domains a page loads scripts from, controlling script sources without verifying the contents served by a domain the site already trusts. The OWASP guidance for managing third-party JavaScript discusses controls such as SRI and hosting scripts locally. Self-hosting or pinning a supported version reduces reliance on live vendor delivery, while placing the update and compatibility work on the site owner.
Keep WordPress core, themes, and plugins updated, and maintain tested backups in separate locations. Give administrator access only to people who need it and enable two-factor authentication for those accounts. These measures reduce the chance that an attacker can enter through an unpatched component or reuse an exposed password, and they give site owners a better recovery path. They do not prevent a trusted remote script from running malicious code, so they work best alongside scrutiny of external dependencies.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com