WordPress Supply Chain Security: Auditing Plugin and Theme Dependencies

WordPress supply chain security: auditing plugins and themes

Every plugin and theme is code from someone else running with full access to your site. The supply chain risk is real: a plugin author’s account is compromised and a malicious update goes out, an abandoned plugin is bought and “updated” with a backdoor, or a premium plugin from an unofficial source comes pre-infected. A serious setup knows what is installed, verifies it against the originals, notices unexpected changes, and removes what nobody maintains.

Problem: A site has dozens of plugins from different sources, nobody knows which of them are still maintained, and a modified plugin file would go unnoticed until something breaks.

Solution: Inventory and verify the installed code with WP-CLI, check how alive each plugin is, keep the code under version control to see every change, and follow a vulnerability feed.

1. Inventory

wp plugin list --fields=name,status,version,update,update_version,auto_update
wp theme list --fields=name,status,version,update

Inactive plugins are still code on the server and can still be attacked through their files: delete what is not used instead of deactivating it.

2. Verify against the originals

wp core verify-checksums
wp plugin verify-checksums --all

WP-CLI compares every file with the checksums published by WordPress.org and lists modified, added and missing files. Plugins that are not on WordPress.org (premium, custom) report that no checksums are available: for those, record your own baseline right after a trusted installation:

cd wp-content/plugins/my-premium-plugin
find . -type f -print0 | sort -z | xargs -0 sha256sum > ~/baselines/my-premium-plugin.sha256

# later
cd wp-content/plugins/my-premium-plugin && sha256sum --quiet --check ~/baselines/my-premium-plugin.sha256

3. Is it still maintained?

The WordPress.org API shows when a plugin was last updated and whether it was closed (removed from the directory, often because of a security issue):

wp plugin list --field=name | while read -r slug; do
    curl -s "https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]=$slug&request[fields][sections]=0" \
      | python3 -c "import json,sys; d=json.load(sys.stdin); print('$slug', d.get('last_updated', d.get('error', '?')))"
done

A plugin without updates for two years, or one that returns “closed”, is a candidate for replacement. An ownership change of a plugin you depend on is worth a look at its next updates.

4. Every change visible: Git

Keep wp-content (or at least plugins and themes) in a Git repository on the server, with a nightly commit. Legitimate updates appear as commits after the update run; any other change is a diff that should not exist:

cd /var/www/example.com/wp-content
git add -A && git commit -q -m "nightly $(date -u +%F)" || true
git log --stat -1

Exclude uploads, caches and logs in .gitignore. If the repository is pushed to a private remote, an alert on unexpected changes (for example a CI job that opens an issue when files under plugins/ change outside the update window) turns this into monitoring. Note that the server itself must not be able to rewrite the remote history.

5. Vulnerability information

Subscribe to a WordPress vulnerability feed (Wordfence Intelligence, Patchstack, WPScan) for the plugins you run: most attacks use publicly known vulnerabilities in plugins that were simply not updated. Auto-updates for plugins with a good record shorten that window; for critical plugins (shop, payments), update quickly but on a staging copy first.

Build dependencies

Plugins you develop yourself bring their own supply chain: Composer and npm packages. Run composer audit and npm audit --omit=dev in CI, commit the lock files, and ship only the built files and production dependencies in the release.

NOTE: Download premium plugins only from the vendor, never from “nulled” sites: they are a common way to spread backdoors. When something suspicious shows up, restore the affected plugin from the original source rather than editing the infected files, and change the passwords and keys. Checksums are covered with other WP-CLI commands in essential WP-CLI commands.