Defense in depth needs a last layer.
Sudo is the only solution that provides control over actions just before they're performed.
Firewalls guard the house. Security plugins guard the door. Sudo governs what can happen inside the house.
This exploratory plugin is NOT production-ready. Please help test it and share your feedback.
So full of cracks,
the barrier gatehouse of Fuwa
lets both rain and moonlight in β
quietly exposed, yet enduring.
β Abatsu-ni, Diary of the Waning Moon
In 2024, Broken Access Control was the third-largest source of discovered WordPress vulnerabilities (Patchstack 2025). In 2025, it took the #1 position on the OWASP Top 10. In 2026, Broken Access Control accounted for 57% of all actual exploitation attempts on WordPress sites (Patchstack 2026). These trends indicate attackers are focusing their efforts on the vectors that are difficult to defend against with firewalls but have high rewards: the ability to create admin accounts, install plugins, and change settings. Add Privilege Escalation (20%) and Broken Authentication (3%) β that's 80% of real-world WordPress attacks targeting the operations Sudo gates.
Approximately half of high-impact WordPress vulnerabilities are exploited within 24 hours β median time to first exploit is 5 hours. When the firewall misses it, the plugin hasn't patched it, and the attacker already has a session β Sudo is the barrier gate between access and damage. Plugin installs, user creation, role changes, settings modifications: every potentially destructive action requires reauthentication, regardless of how the attacker got in.
WordPress has rich access control β roles, capabilities, policies on who can do what. It has no native control over when those capabilities and actions can be exercised within a session. Sudo fills that gap. By gating consequential actions behind a mandatory reauthentication whenever those actions are taken, Sudo inserts a final check against potential intrusion and abuse. If a privileged account has been compromised, as is often the case, by an attacker who does not know its password or control its associated email, Sudo's challenge will prevent them from executing privileged actions. Coupled with two-factor-authentication (2FA), Sudo will limit the blast radius or potential harm of any session compromise β regardless of how that compromise occurred, and regardless of the user's role. The attack surface becomes a policy decision.
This is not role-based escalation. Every logged-in user is treated the same: attempt a gated action, get challenged. Sessions are time-bounded and non-extendable, enforcing the zero-trust principle that trust must be continuously earned, never assumed. WordPress capability checks still run after the gate, so Sudo adds a security layer without changing the permission model.
Inspired by the Linux command sudo (superuser do), Sudo for WordPress is represented by the gate ι, a CJK grapheme (Kangxi radical 169) and 3,000-year-old pictograph. At once an entrance, barrier, and threshold, the sign of the gate has endured in East Asian writing systems that descend from the Shang dynasty oracle bone script β where the gate made its earliest known appearance. It appears in the Kanji character for the Japanese frontier pass or barrier gate β seki (ι’) or sekisho (ι’ζ). These were crucial checkpoints that came into use in medieval times but are associated with the centralization of control they enabled in the early modern Edo period (1603β1868) as a way to control traffic along major highways, such as the TΕkaidΕ.
| Category | Operations |
|---|---|
| Plugins | Activate, deactivate, delete, install, update |
| Themes | Switch, delete, install, update |
| Users | Delete, change role, change password, create new user, create application password |
| File editors | Plugin editor, theme editor |
| Critical options | siteurl, home, admin_email, default_role, users_can_register |
| WordPress core | Update, reinstall |
| Site data export | WXR export |
| WP Sudo settings | Self-protected β settings changes require reauthentication |
| Multisite | Network theme enable/disable, site delete/deactivate/archive/spam, super admin grant/revoke, network settings |
Developers can add custom rules via the wp_sudo_gated_actions filter. See Developer Reference.
Browser requests (admin UI): The user sees an interstitial challenge page. After entering their password (and 2FA code if configured), the original request is replayed automatically. AJAX and REST requests receive a sudo_required error; an admin notice on the next page load links to the challenge page.
Non-interactive requests (WP-CLI, Cron, XML-RPC, Application Passwords, and WPGraphQL if installed): Configurable per-surface policies with three modes: Disabled, Limited (default), and Unrestricted.
- Zero-trust architecture β a valid login session is never sufficient on its own. Dangerous operations require explicit identity confirmation every time.
- Role-agnostic β any user attempting a gated action is challenged, including administrators.
- Full attack surface β admin UI, AJAX, REST API, WP-CLI, Cron, XML-RPC, Application Passwords, and WPGraphQL.
- Session binding β sudo sessions are cryptographically bound to the browser via a secure httponly cookie token.
- 2FA browser binding β the two-factor challenge is bound to the originating browser with a one-time challenge cookie.
- Rate limiting β 5 failed password attempts trigger a 5-minute lockout.
- Self-protection β changes to WP Sudo settings require reauthentication.
- Server-side enforcement β gating decisions happen in PHP hooks before action handlers. JavaScript is for UX only.
For the full threat model, boundaries, and environmental considerations, see Security Model.
- Two Factor β Strongly recommended. Makes the sudo challenge a two-step process: password + authentication code (TOTP, email, backup codes). Add WebAuthn Provider for Two Factor for passkey and security key support.
- WP Activity Log or Stream β Recommended for audit visibility. Sudo fires 9 action hooks covering session lifecycle, gated actions, policy decisions, and lockouts.
- Admin bar countdown β a live M:SS timer shows remaining session time. Turns red in the final 60 seconds.
- Keyboard shortcut β
Ctrl+Shift+S/Cmd+Shift+Sto proactively start a sudo session. - Accessible β WCAG 2.1 AA throughout (screen-reader announcements, ARIA labels, focus management, keyboard support).
- Contextual help β 10 help tabs on the settings page.
An optional mu-plugin ensures gate hooks are registered before any other plugin loads. Install it with one click from the settings page.
Settings and sessions are network-wide. The action registry includes 8 additional network admin rules. Settings page appears under Network Admin > Settings > Sudo.
- Upload the
wp-sudofolder to/wp-content/plugins/. - Activate the plugin through the Plugins screen in WordPress.
- Go to Settings > Sudo to configure session duration and entry point policies.
- (Optional) Install the mu-plugin from the settings page for early hook registration.
- (Recommended) Install the Two Factor plugin for two-factor authentication.
It's a unique approach to fundamental security architecture that doesn't exist in the WordPress plugin marketplace today.
Sudo provides a unique, skin-tight, first-and-last layer of defense that complements other security layers.
To understand what Sudo protects and defends against, it's important to understand the threat model.
Patchstack's State of WordPress Security in 2026 reports that plugin vulnerabilities are still surging year after year, and they remain the source of nearly all vulnerabilities in the ecosystem.
- Nearly half (46%) of all WordPress plugin vulnerabilities that emerged (11,334) remain unpatched.
- Broken Access Control was far and away the most exploited vulnerability, accounting for 57% of the total attacks on WordPress sites.
- Traditional WAFs are only blocking 12 to 26% of attacks.
- Patchstack has an excellent 93% success rate, but it is not 100%.
- Nearly half of high-impact vulnerabilities were exploited within 24 hours.
- The average time to exploitation has fallen to just 5 hours.
- Post-breach, the attacker's playbook is predictable:
- 55% of hacked WordPress sites have malicious admin accounts.
- Up to 70% have backdoor plugins.
Another rising threat that firewalls can't detect or deter is stolen cookies. These are the persistent session cookies that are created when you log into Slack, Gmail, WordPress, and all your other sites and apps. Attackers acquire cookies from devices compromised by phishing with infostealers, keyloggers, and other malware.
Once an attacker has control of an active user session, they need WordPress to do what they assume it will always do: obey without challenge.
Sudo breaks that assumption β a key link in the attackers' kill chain.
Your perimeter has failed. Your own user account has been compromised. Now what?
What if there was one last layer of defense to issue a challenge?
That's Sudo.
When a user attempts a gated action, Sudo intercepts the request at admin_init. The original request is stashed, the user is redirected to a challenge page, and after successful reauthentication, the original request is replayed. For AJAX and REST requests, the browser receives a sudo_required error and an admin notice links to the challenge page.
No. Sudo adds a reauthentication layer on top of the existing permission model. WordPress capability checks still run after the gate.
Cookie-authenticated REST requests receive a sudo_required error. Application Password requests are governed by a separate policy (Disabled, Limited, or Unrestricted). Individual application passwords can override the global policy from the user profile page.
Each has its own three-tier policy: Disabled, Limited (default), or Unrestricted. In Limited mode, gated actions are blocked while non-gated commands work normally.
Yes. With the Two Factor plugin, the sudo challenge becomes a two-step process: password + authentication code. Third-party 2FA plugins can integrate via filter hooks.
Yes. Settings and sessions are network-wide. The action registry includes network-specific rules. See the Multisite section above.
Yes. Use the wp_sudo_gated_actions filter. See Developer Reference for the rule structure and code examples.
For more questions, see the full FAQ.
Hook signatures, filter reference, custom rule structure, and testing instructions: docs/developer-reference.md.
WP Sudo is built for correctness and contributor legibility, not just functionality.
Architecture. A single SPL autoloader maps the WP_Sudo\* namespace to includes/class-*.php. The Gate class is the heart of the plugin: it detects the entry surface (admin UI, AJAX, REST, WP-CLI, Cron, XML-RPC, Application Passwords, WPGraphQL), matches the incoming request against a registry of 29+ rules, and either challenges, soft-blocks, or hard-blocks depending on surface and policy. All gating decisions happen server-side in PHP action hooks β JavaScript is used only for UX (countdown timer, keyboard shortcut).
Test-driven development. New code requires a failing test before production code is written. The suite is split into two deliberate tiers:
- Unit tests (397 tests, 944 assertions) β use Brain\Monkey to mock all WordPress functions. Run in ~0.4s with no database. Cover request matching, session state machine, policy enforcement, hook registration.
- Integration tests (92 tests) β run against real WordPress + MySQL via
WP_UnitTestCase. Cover full reauth flows, bcrypt verification, transient TTL, REST and AJAX gating, Two Factor interaction, multisite session isolation, upgrader migrations, and all 9 audit hooks.
Static analysis and code style. PHPStan level 6 (zero errors) and PHPCS (WordPress-Extra + WordPress-Docs + WordPressVIPMinimum) run on every push and pull request via GitHub Actions, alongside the full test matrix (PHP 8.1β8.4, WordPress latest + trunk). A nightly scheduled run catches WordPress trunk regressions early.
Extensibility. The action registry is filterable via wp_sudo_gated_actions. The plugin fires 9 audit hooks covering session lifecycle, gated actions, policy decisions, and lockouts β designed for integration with activity log plugins. Third-party 2FA plugins integrate via four filter hooks. See docs/developer-reference.md for the full hook reference.
Contributing. See CONTRIBUTING.md for setup, the TDD workflow, and code style requirements.
| Component | Size |
|---|---|
Production PHP (includes/, wp-sudo.php, uninstall.php, mu-plugin/, bridges/) |
224 KB Β· 6,688 lines |
| Assets (screenshots, banner images) | 5.0 MB |
Tests (tests/) |
488 KB Β· 11,555 lines |
Docs (docs/ + root-level md/txt) |
432 KB |
| Total PHP (production + tests, excl. vendor) | 18,283 lines |
| Test-to-production ratio | 1.7:1 |
No production dependencies. Dev dependencies (PHPUnit, PHPStan, PHPCS, Brain\Monkey, Mockery) live in vendor/ and are not shipped.
Last updated: 2026-02-27. See CLAUDE.md for the update command.
-
Challenge page β reauthentication interstitial with password field.
-
Two-factor authentication β after password confirmation, users with 2FA enabled enter their authentication code.
-
Settings page β configure session duration and entry point policies.
-
Gate notice (plugins) β when no sudo session is active, a persistent notice links to the challenge page.
-
Gate notice (themes) β the same gating notice appears on the themes page.
-
Gated actions β the settings page lists all gated operations with their categories and surfaces.
-
Active sudo session β the admin bar shows a green countdown timer.
- Docs: threat model kill chain β verified risk reduction data from Patchstack, Sucuri, Verizon DBIR, Wordfence, and OWASP added to security model and FAQ.
- Docs: project size table β readme.md gains a Project Size subsection; stale test counts corrected; missing v2.8.0/v2.9.0 changelog entries added.
wp_sudo_action_allowedaudit hook β fires when a gated action is permitted by an Unrestricted policy. Covers all five non-interactive surfaces: REST App Passwords, WP-CLI, Cron, XML-RPC, and WPGraphQL (mutations only). This is the ninth audit hook.- Docs: CLAUDE.md accuracy audit β corrected six inaccuracies; logged one confabulation in
llm_lies_log.txt. - Docs: manual testing β MANUAL-TESTING.md adds Β§19 (Unrestricted audit hook verification) with forward references from existing Unrestricted subsections.
- 397 unit tests, 944 assertions. 92 integration tests in CI.
- Expire sudo session on password change β hooks
after_password_resetandprofile_updateto invalidate any active sudo session when a user's password changes. Closes the gap where a compromised session persisted after a password reset. - WPGraphQL conditional display β the WPGraphQL policy dropdown, help tab paragraph, and Site Health review all adapt based on whether WPGraphQL is installed.
- 391 unit tests, 929 assertions. 92 integration tests in CI.
wp_sudo_wpgraphql_bypassfilter β new filter for WPGraphQL JWT authentication compatibility. Fires in Limited mode before mutation detection; returntrueto exempt specific requests (e.g. JWT login/refresh mutations). See developer reference for a bridge mu-plugin example.- Fix: WPGraphQL listed in non-interactive entry points β the "How Sudo Works" help tab now includes WPGraphQL in the list of policy-governed surfaces.
- Fix: WPGraphQL integration tests β extract
Gate::check_wpgraphql()to fix pre-existing CI test regression; no behavioral change in production. - Docs: v2.6.0 documentation update β FAQ, ROADMAP, developer-reference.md, security-model.md, MANUAL-TESTING.md updated to reflect v2.6.0 features.
- Login implicitly grants a sudo session β a successful browser-based login now automatically activates a sudo session. No second challenge required immediately after logging in. Application Password and XML-RPC logins are unaffected.
user.change_passwordgated β password changes on the profile and user-edit pages now require a sudo session. Closes the session-theft β silent password change β lockout attack chain. The REST API endpoint is also gated.- Grace period (120 s) β a 2-minute grace window after session expiry lets in-flight form submissions complete without triggering a re-challenge. Session binding is verified throughout the grace window.
- 375 unit tests, 905 assertions. 73 integration tests in CI.
- WPGraphQL surface gating β adds WPGraphQL as a fifth non-interactive surface alongside WP-CLI, Cron, XML-RPC, and Application Passwords. Three-tier policy (Disabled / Limited / Unrestricted); default is Limited. GraphQL mutations are blocked without a sudo session; read-only queries pass through. Fires
wp_sudo_action_blockedon block. wp_sudo_wpgraphql_routefilter β allows the gated endpoint to be overridden for custom WPGraphQL configurations.- Site Health β WPGraphQL policy included in Entry Point Policies health check.
- 364 unit tests, 887 assertions. 73 integration tests in CI.
- AJAX gating integration tests β 11 new tests covering the AJAX surface: rule matching for all 7 declared AJAX actions, full intercept flow, session bypass, non-gated pass-through, blocked transient lifecycle, admin notice fallback (
render_blocked_notice), andwp.updatesslug passthrough. - Action registry filter integration tests β 3 new tests verifying custom rules added via
wp_sudo_gated_actionsare matched by the Gate in a real WordPress environment; including custom admin rules, custom AJAX rules, and filter-based removal of built-in rules. - Audit hook coverage β
wp_sudo_action_blockednow integration-tested for CLI, Cron, and XML-RPC surfaces (in addition to REST app-password). Documents thatwp_sudo_action_allowedis intentionally absent from the production code path. - CI quality gate β new GitHub Actions job runs PHPCS and PHPStan on every push and PR; Composer dependency cache added to unit and integration jobs; nightly scheduled run at 3 AM UTC against WP trunk.
- MU-plugin manual install instructions β fallback copy instructions added to the settings page UI and help tab for environments where the one-click installer fails due to file permissions.
- CONTRIBUTING.md β new contributor guide covering local setup, unit vs integration test distinction, TDD workflow, and lint/analyse requirements.
- 349 unit tests, 863 assertions. 73 integration tests in CI.
- Integration test suite β 55 tests against real WordPress + MySQL (session lifecycle, request stash/replay, full reauth flow, REST gating, upgrader migrations, Two Factor interaction, multisite isolation).
- CI pipeline β GitHub Actions with unit tests across PHP 8.1β8.4 and integration tests against WordPress latest + trunk.
- Fix: multisite site-management gate gap β Archive, Spam, Delete, Deactivate site actions now correctly trigger the sudo challenge.
- Fix: admin bar timer width β expiring (red) state no longer stretches wider than active (green) state.
- Fix: WP 7.0 admin notice background β restored white background lost in WP 7.0's admin visual refresh.
- Fix: 2FA countdown advisory-only β window reduced to 5 minutes; expired codes accepted if provider validates.
- WP 7.0 Beta 1 tested β full manual testing guide completed, all 15 sections PASS.
- 349 unit tests, 863 assertions. 55 integration tests in CI.
- Fix: admin bar sr-only text leak β screen-reader-only milestone text no longer renders in the dashboard canvas when the admin bar node lacks a containing block.
- Documentation overhaul β readmes slimmed; security model, developer reference, FAQ, and full changelog moved to project root. Manual testing guide rewritten for v2.3.1+.
- Composer lock compatibility β
config.platform.phpset to8.1.99so the lock file resolves for PHP 8.1+ regardless of local version. - Housekeeping β removed stale project state file; added
@sincetags; updated CLAUDE.md and Copilot instructions with docs/ references. - 343 unit tests, 853 assertions.
- Fix: Unicode escape rendering β localized JS strings now use actual UTF-8 characters, fixing visible backslash-escape text during challenge replay.
- Fix: screen-reader-only text flash β the sr-only span no longer flashes visible fragments during replay.
- CycloneDX SBOM β
bom.jsonshipped for supply chain transparency. - Help tabs β per-application-password policy section added. Count corrected to 8.
- Copilot coding agent β GitHub Copilot configuration added.
- Accessibility roadmap complete β all items verified resolved.
- 343 unit tests, 853 assertions.
- Per-application-password sudo policies β individual Application Password credentials can override the global REST API policy.
- Challenge page iframe fix β breaks out of
wp_iframe()context. - Accessibility improvements β admin bar cleanup on page unload; lockout countdown SR throttling; settings field defaults.
- PHPStan level 6 static analysis β zero errors.
- Documentation β AI and agentic tool guidance and UI/UX testing prompts.
- 343 unit tests, 853 assertions.
See full changelog for all versions.







