Audit #50 Benign
Show full summary
Verdict: BENIGN. Opal Woo Custom Product Variation is a healthy, actively maintained commercial plugin from WPOPAL (wpopal.com). It was flagged by WP Beacon's puc_update_hijack code-pattern rule, which fires on the Plugin Update Checker factory call — the same mechanism behind real update-channel takeovers like the 2021 anadnet campaign. On inspection, the flagged code is inert: it never executes in the wordpress.org build.
The updater call in the main plugin file is gated behind file_exists( … 'plugin-updates/plugin-update-checker.php' ). That library is not shipped in the wordpress.org version — and, critically, it has never been present in any tagged release (verified across the full git history, v1.1.3 → v1.3.4). Because the required file is absent, the require never runs, Puc_v4_Factory never loads, and buildUpdateChecker() is never called. This is standard vendor scaffolding: WPOPAL bundles the update-checker only in the paid/direct build it distributes from its own site, and leaves an inert, fail-safe stub in the free wp.org copy. The URL it would contact (source.wpopal.com) is the vendor's own domain, not third-party infrastructure.
This is the opposite of the anadnet pattern, where an attacker seeds installs with the library present and then removes it from trunk to hide persistence. Here the library was never in trunk at all. The plugin's file-upload feature (custom product fields → cart) uses WordPress's safe upload path (wp_handle_upload, wp_check_filetype_and_ext) with an admin-configured extension allowlist, so it does not permit executable (.php) uploads. Committer history is a single vendor identity (wpopal, plus the vendor's gutengeek brand account) with a normal release cadence and no hijack indicators.
The plugin is not closed and requires no action from site owners. Two minor code-hygiene notes (not security issues): the dead updater stub uses http:// rather than https://, and a leftover var_dump() debug statement remains in the upload validator.
Audit retained for the record. No action required.
Plugins under the same committer's SVN access
wpopal holds push access to 12 plugins totalling 3k+ active installs. Each non-target plugin scans clean today but represents a one-commit hijack opportunity.
Plugin version history
Every release on wp.org for this plugin. This audit found no malicious code; the version history is shown for reference.
-
1.1.3Audit baseline Last clean release before incident -
1.1.4Released after reopen PRT cleanup release — incident closed -
1.1.5Released Clean (post-cleanup) -
1.1.6Released Clean (post-cleanup) -
1.2.0Released Clean (post-cleanup) -
1.2.1Released Clean (post-cleanup) -
1.2.3Released Clean (post-cleanup) -
1.2.4Released Clean (post-cleanup) -
1.3.0Released Clean (post-cleanup) -
1.3.1Released Clean (post-cleanup) -
1.3.2Released Clean (post-cleanup) -
1.3.4Latest release Current release
Trigger
WP Beacon's CodeScanner fired puc_update_hijack (builtin, low) on version 1.3.5:
file: opal-woo-custom-product-variation.php line 65
snippet: Puc_v4_Factory::buildUpdateChecker(
url_host: source.wpopal.comThe flagged code (main plugin file)
function owcpv_start() {
if (file_exists(OWCPV_PLUGIN_DIR.'plugin-updates/plugin-update-checker.php')) {
require 'plugin-updates/plugin-update-checker.php';
if (class_exists('Puc_v4_Factory')) {
Puc_v4_Factory::buildUpdateChecker(
'http://source.wpopal.com/plugins/opal/opal-woo-custom-product-variation.json',
__FILE__,
'opal-woo-custom-product-variation'
);
}
}
return OWCPV_Start_Instance::instance();
}Why it is inert
1. Library absent from the tree. git ls-tree -r HEAD contains no plugin-updates/ directory and no plugin-update-checker.php. grep -rl "Puc_v4_Factory" matches only the guarded stub in the main file. 2. Never in trunk, ever. git log --all --full-history -- 'plugin-updates/*' 'plugin-update-checker.php' returns no commits across the entire mirrored history (v1.1.3 → v1.3.4). The file was never removed-to-hide (contrast the anadnet / scroll-top pattern, WP Beacon audit #12); it simply is not part of the wp.org distribution. 3. Fail-safe gate. The file_exists() guard means the require and the buildUpdateChecker() call cannot execute when the library is missing — which is always, in the wp.org build. 4. Vendor-owned endpoint. source.wpopal.com resolves to the plugin author's own domain (Author URI / Plugin URI both wpopal.com). Not third-party, not a known IOC.
File-upload feature review
The plugin's headline feature adds custom product fields including file upload. Handler: includes/classes/class-owcpv-form-handler.php.
- Uses
wp_handle_upload()(core, safe) — not rawmove_uploaded_file()to a web path. owcpv_validate_ext_file_with_config()validates the extension against an admin-configured allowlist; images cross-checked withgetimagesize()for real MIME.wp_check_filetype_and_ext()+ rejection when WP cannot determine a safe type (if ((!$type || !$ext))).- WordPress core rejects
.php/executable types by default → no upload-to-RCE.
Danger-primitive sweep (all PHP at HEAD)
No eval, base64_decode, gzinflate, str_rot13, create_function, assert, preg_replace(/e), dynamic call_user_func, or wp_remote_* → eval/include chains. Outbound host references across the entire codebase: only source.wpopal.com (dead stub) and wpopal.com (vendor site).
Committer history
| author_slug | commits | first seen | note |
|---|---|---|---|
| wpopal | 32 | 2024-05-29 | vendor primary |
| gutengeek | 9 | 2024-05-02 | WPOPAL's Gutenberg-blocks brand |
| plugin-master | 1 | 2024-04-27 | wp.org review team (initial import) |
Normal release cadence (1.1.x → 1.3.x, 2024–2026), single vendor identity, no sudden new committer.
Hijack-indicator matrix
| Indicator | Result |
|---|---|
| Sole/consistent committer identity | ✅ Yes — WPOPAL (wpopal + gutengeek brand) |
| Sudden new committer near an event | ❌ No |
| Author profile drift | ❌ No |
| Code-level malware / obfuscation | ❌ No — updater stub is inert; no exec primitives |
| Outbound C2 / known-bad domains | ❌ No — vendor's own domain only, and unreachable code |
| Update-checker library removed to hide persistence | ❌ No — never present in trunk |
Clean matrix → BENIGN.
Hygiene notes (non-security)
- Dead updater stub uses
http://source.wpopal.com(no TLS). Harmless while inert; if the paid build reuses it, it should behttps://. - Leftover
var_dump($ext)atclass-owcpv-form-handler.php:551.
Comparable cases
- WP Beacon audit #12 (scroll-top, MALICIOUS): genuine PUC hijack — library present and active, pointing at attacker infrastructure (
cdnstaticsync.com), seeded then hidden. The structural inverse of this plugin. - Vendor self-updater FP class (wpcodefactory / algoritmika cross-selling): commercial vendors leaving pro-distribution scaffolding in the free wp.org build. Guideline-adjacent at most, not malware.