Skip to content

Commit 3c00e50

Browse files
committed
docs(05-01): write Abilities API assessment document (WP70-04)
- Create docs/abilities-api-assessment.md - Documents all 3 read-only core abilities (core/get-site-info, core/get-user-info, core/get-environment-info) verified against official WP sources in 05-RESEARCH.md - Documents REST endpoints and HTTP method semantics for /run route - Explains permission_callback pattern vs WP Sudo reauthentication model - Lists all 6 existing Gate surfaces (admin, ajax, rest, cli, cron, xmlrpc) - Documents gating strategy for future destructive abilities via REST rule in Action_Registry (no new surface type required) - Defines trigger conditions for adding ability surface type to Gate - Recommendation: no Gate changes for WP 7.0 Verification sources: make.wordpress.org/core/2025/11/10/abilities-api-in-wordpress-6-9/ and developer.wordpress.org/apis/abilities-api/rest-api-endpoints/
1 parent f7a2ea8 commit 3c00e50

1 file changed

Lines changed: 198 additions & 0 deletions

File tree

docs/abilities-api-assessment.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Abilities API Assessment
2+
3+
**Date:** 2026-02-19
4+
**WP version evaluated:** 7.0 Beta 1
5+
**Status:** No gating changes required for WP 7.0
6+
7+
---
8+
9+
## Overview
10+
11+
The WordPress Abilities API, introduced in WP 6.9, exposes registered "abilities" via
12+
REST endpoints and (optionally) WP-CLI. This document evaluates the current surface,
13+
explains why WP Sudo does not need to gate any abilities for WP 7.0, and documents
14+
the strategy for when gating will become necessary.
15+
16+
**Verification sources for ability names and REST routes:**
17+
18+
- [Abilities API in WordPress 6.9 — Make WordPress Core](https://make.wordpress.org/core/2025/11/10/abilities-api-in-wordpress-6-9/) — 3 core abilities, `permission_callback` pattern
19+
- [Abilities API REST Endpoints — developer.wordpress.org](https://developer.wordpress.org/apis/abilities-api/rest-api-endpoints/) — REST route structure
20+
- [From Abilities to AI Agents: Introducing the WordPress MCP Adapter — developer.wordpress.org](https://developer.wordpress.org/news/2026/02/from-abilities-to-ai-agents-introducing-the-wordpress-mcp-adapter/) — confirms 3 read-only abilities as of 7.0 Beta 1
21+
22+
Ability names were verified against official sources listed above and in
23+
`.planning/phases/05-wp-7-0-readiness/05-RESEARCH.md`, not inferred from training data.
24+
25+
---
26+
27+
## Current Abilities Surface in WP 7.0
28+
29+
As of WP 7.0 Beta 1, WordPress core registers exactly three abilities. All three are
30+
read-only: they expose information but do not modify or destroy site state.
31+
32+
| Ability ID | Label | Permission Callback | Destructive? |
33+
|------------|-------|---------------------|--------------|
34+
| `core/get-site-info` | Get Site Information | `current_user_can('read')` | No |
35+
| `core/get-user-info` | Get User Information | `current_user_can('read')` | No |
36+
| `core/get-environment-info` | Get Environment Info | `current_user_can('read')` | No |
37+
38+
Abilities are registered inside the `wp_abilities_api_init` action using
39+
`wp_register_ability()`. Each registration specifies a `permission_callback`
40+
(capability check) and an `execute_callback` (returns data).
41+
42+
---
43+
44+
## REST Endpoints (WP Abilities API v1)
45+
46+
The Abilities API registers the following REST routes under the `wp-abilities/v1`
47+
namespace:
48+
49+
| Method | Route | Description |
50+
|--------|-------|-------------|
51+
| `GET` | `/wp-json/wp-abilities/v1/abilities` | List all registered abilities |
52+
| `GET` | `/wp-json/wp-abilities/v1/categories` | List ability categories |
53+
| `GET` | `/wp-json/wp-abilities/v1/{ns}/{name}` | Get a single ability by namespace and name |
54+
| `GET\|POST\|DELETE` | `/wp-json/wp-abilities/v1/{ns}/{name}/run` | Execute an ability |
55+
56+
The HTTP method for the `/run` endpoint is determined by the ability type:
57+
58+
- Read-only operations use `GET`
59+
- Operations requiring input parameters use `POST`
60+
- Destructive operations use `DELETE`
61+
62+
As of WP 7.0 Beta 1, no registered core abilities use `DELETE` on `/run`. All three
63+
core abilities use `GET`.
64+
65+
---
66+
67+
## Analysis: Does WP Sudo Need to Gate Abilities?
68+
69+
### Current state: No gating required
70+
71+
WP Sudo's gating model intercepts operations that **modify or destroy site state**:
72+
activating plugins, deleting users, changing critical settings, installing themes,
73+
and so on. Read-only operations are explicitly outside WP Sudo's scope.
74+
75+
All three core abilities in WP 7.0 are read-only. They expose information about the
76+
site, user, and environment — but they do not change anything. No reauthentication
77+
is warranted for information retrieval.
78+
79+
### `permission_callback` pattern vs. WP Sudo gating
80+
81+
The Abilities API uses `permission_callback` (a standard WordPress capability check
82+
such as `current_user_can('read')`) to control access. This is authorization — it
83+
answers "is this user allowed to call this ability at all?"
84+
85+
WP Sudo provides reauthentication — it answers "has this user recently confirmed
86+
their identity, regardless of their role?" These are complementary controls, not
87+
substitutes. The `permission_callback` check runs inside WordPress before the
88+
`execute_callback` fires. WP Sudo would intercept at the REST layer (via
89+
`rest_request_before_callbacks`) before the `permission_callback` even runs.
90+
91+
For read-only abilities, the `permission_callback` check is sufficient. WP Sudo
92+
would add no additional security value by intercepting them.
93+
94+
### Current Gate surfaces: no `ability` surface type
95+
96+
The Gate class (`includes/class-gate.php`) currently recognizes six surfaces:
97+
98+
| Surface | Interception point |
99+
|---------|--------------------|
100+
| `admin` | `admin_init` at priority 1 |
101+
| `ajax` | `admin_init` at priority 1 (also fires for `admin-ajax.php`) |
102+
| `rest` | `rest_request_before_callbacks` filter |
103+
| `cli` | `init` at priority 0 via function-level hooks |
104+
| `cron` | `init` at priority 0 via function-level hooks |
105+
| `xmlrpc` | `init` at priority 0 via `xmlrpc_enabled` filter and function hooks |
106+
107+
There is no `ability` surface type. The Abilities API REST routes are served through
108+
the standard WordPress REST API and are therefore already covered by the existing
109+
`rest` surface interception — no special handling is required.
110+
111+
---
112+
113+
## Gating Strategy for Future Destructive Abilities
114+
115+
When a destructive ability appears in WordPress core or a plugin (indicated by a
116+
`DELETE` method on a `/wp-abilities/v1/{ns}/{name}/run` route), WP Sudo can gate it
117+
without adding a new surface type.
118+
119+
### REST-exposed abilities (browser and App Password callers)
120+
121+
The existing `intercept_rest()` method in `Gate` already intercepts all REST requests
122+
via `rest_request_before_callbacks` and routes them through `match_request('rest')`.
123+
A new rule in `Action_Registry` matching the destructive ability's route is all that
124+
is needed:
125+
126+
```php
127+
// Example: hypothetical destructive ability
128+
[
129+
'id' => 'abilities.delete_plugin',
130+
'label' => __( 'Delete plugin via Abilities API', 'wp-sudo' ),
131+
'rest' => [
132+
'route' => '#^/wp-abilities/v1/core/delete-plugin/run$#',
133+
'methods' => [ 'DELETE' ],
134+
],
135+
],
136+
```
137+
138+
The existing `matches_rest()` method in `Gate` checks route pattern and HTTP method,
139+
so a regex matching `/wp-abilities/v1/.*/run` with `DELETE` would catch all destructive
140+
ability runs in a single rule:
141+
142+
```php
143+
[
144+
'id' => 'abilities.run_destructive',
145+
'label' => __( 'Run destructive ability', 'wp-sudo' ),
146+
'rest' => [
147+
'route' => '#^/wp-abilities/v1/[^/]+/[^/]+/run$#',
148+
'methods' => [ 'DELETE' ],
149+
],
150+
],
151+
```
152+
153+
No new surface type is required for REST-exposed abilities.
154+
155+
### WP-CLI `wp ability run` (CLI callers)
156+
157+
For abilities executed via WP-CLI's `wp ability run` command, the existing CLI
158+
surface gating via function-level hooks in `register_function_hooks()` applies. A
159+
hook on the appropriate WordPress action that fires before the ability's
160+
`execute_callback` would be added to the function hook registration block.
161+
162+
### When to add an `ability` surface type to Gate
163+
164+
An `ability` surface type in `Gate` would only be warranted if abilities gain a
165+
non-REST execution path that bypasses all existing surfaces — for example, if a
166+
future WordPress version introduces a PHP-level `do_ability()` function that
167+
third-party code can call directly outside of REST or CLI contexts. As of WP 7.0,
168+
no such path exists. The REST layer is the primary execution path for abilities,
169+
and it is already covered.
170+
171+
**Trigger conditions for adding `ability` surface type:**
172+
173+
1. A non-REST, non-CLI ability execution path is introduced in WordPress core
174+
2. That path bypasses `rest_request_before_callbacks` and `admin_init`
175+
3. Destructive abilities are registered that use this new path
176+
177+
None of these conditions exist in WP 7.0.
178+
179+
---
180+
181+
## Recommendation
182+
183+
**No Gate changes are needed for WP 7.0.**
184+
185+
All three core abilities are read-only. The existing REST surface interception in
186+
`Gate::intercept_rest()` already covers the `/wp-abilities/v1/` namespace routes if
187+
a matching rule is ever added to `Action_Registry`.
188+
189+
**Monitoring action items:**
190+
191+
1. Watch the [abilities-api](https://github.com/WordPress/abilities-api) GitHub
192+
repository for new ability registrations, especially any using `DELETE` on `/run`.
193+
2. When destructive abilities appear, add a REST rule to `Action_Registry` matching
194+
`/wp-abilities/v1/.*/run` with `DELETE` method. No `Gate` class changes required.
195+
3. For WP-CLI `wp ability run` with destructive abilities, add a function-level hook
196+
in `Gate::register_function_hooks()` targeting the appropriate WordPress action.
197+
4. Reassess the need for an `ability` surface type only if a non-REST, non-CLI
198+
ability execution path is introduced.

0 commit comments

Comments
 (0)