CVE-2026-15312
This CVE isn't a missing check — it's a fundamental misapplication of WordPress's role system as a trust boundary. Propovoice created a `ndpv_manager` role that grants capabilities at the WordPress level, then built REST endpoints treating that role as an application-level privilege ceiling rather than a floor. When the `create()` function accepts a `role` parameter without validation, it's not bypassing a plugin boundary — it's being fed the very role system the plugin installed into WordPress. The privilege escalation is trivial: a user with `ndpv_manager` can request `administrator` for a new account, and WordPress complies because the endpoint never checked for `promote_users`. But the real exposure isn't the admin account — it's the CRM data (`ndpv_manager` already has access to client records, communications, proposals, and financial data) that becomes the attacker's foothold before they even escalate. This bug class has appeared before in ACF, Ultimate Member, and other CRM plugins. The pattern recurs because WordPress's `set_role()` looks like a permissioned operation, so developers rationally interpret it as inherently safe. The fix — validate against an allowlist or check `current_user_can('promote_users')` — is trivial but doesn't address why this keeps happening: WordPress's capability model provides no native abstraction for 'can create users within this application context but not across the platform.' Audit other Propovoice endpoints for the same assumption — that custom roles represent permission ceilings rather than floors. Also check whether the plugin properly cleans up `ndpv_manager` on uninstall; orphaned roles with elevated capabilities are a persistent threat that outlives the plugin that created them.
Reviewed through automated stages and approved by a human before publication.