Test WordPress REST skills with roles and real HTTP
Test a WordPress REST API skill with explicit user roles, invalid parameters and observable responses. Our isolated WordPress 7.1.2 fixture recorded 27 HTTP observations, including a deliberately weak permission callback; it did not run an AI assistant or the full upstream skill.
What this experiment can establish
We reviewed WordPress REST API Development and wrote a small synthetic plugin to exercise selected ideas in its instructions. The experiment ran on WordPress 7.1.2, PHP 8.3.35, MariaDB 11.4.13 and a Python 3.13.16 HTTP probe. Docker image IDs, source hashes and individual responses are recorded in the downloadable package.
This is a native protocol fixture. It demonstrates how our own example behaved in that environment. It does not show that an AI agent will implement those ideas correctly, that every plugin is safe, or that a production endpoint has passed a security review. The upstream triage detector and WP-CLI were not executed. Upstream contributor review and AI-assisted authorship are separately disclosed in the original source notices.
The catalog therefore keeps its source-review field runtime_tested: false. The separate scenario record describes the 27 bounded native observations and is tied to this package's version and SHA-256. Read that scope before comparing resources or filtering for current outcome records.
Separate authentication from permission
Successful authentication identifies a user; the route still needs to decide whether that user may perform the requested action. Our restricted POST required edit_others_posts. Anonymous requests received 401. A subscriber's application password identified that subscriber correctly, but the restricted action still received 403. An editor could make the same request successfully.
| Request state | HTTP status | Observed result |
|---|---|---|
| Anonymous POST | 401 | rest_forbidden |
| Subscriber application password | 403 | rest_forbidden |
| Editor application password | 200 | Authorized synthetic response |
| Cookie without nonce | 401 | rest_forbidden |
| Cookie with invalid nonce | 403 | rest_cookie_invalid_nonce |
| Cookie with valid nonce | 200 | Authorized synthetic response |
| Revoked editor application password | 401 | rest_not_logged_in |
A separate endpoint intentionally checked only whether the user was logged in. The subscriber could read its synthetic private marker. That was an intentionally wrong test implementation, not a discovered vulnerability in WordPress or the upstream skill. It is a useful negative control: if a test checks only an editor's successful request, it can overlook the difference between login and authorization. Never install our example plugin on a real site.
When reviewing an implementation, choose at least one authenticated role that should be denied. Confirm that authentication itself worked, so a blocked request is evidence about permission rather than a broken password. For an object-specific action, add separate ownership and capability cases; this fixture did not test object ownership.
Check invalid inputs and application effects
The restricted route declared a required integer count from 1 through 5. We requested a missing count, zero, six and a non-numeric value. We also sent an array for the optional text parameter. All five invalid requests returned 400 with a parameter-related error.
Before any valid request, a separate status endpoint reported an application callback count of zero. That let us observe that the anonymous, subscriber and invalid-input requests had not executed our action callback. The test did not merely inspect whether the response looked like an error.
For an authorized request, a note containing bold markup, a newline and extra spacing became plain text. Its response was count=2, note=example spaced, calls=1. The count parameter used schema-based checks; the text parameter supplied an explicit validator alongside its sanitizer. Check both validation and sanitization when reviewing a route with custom callbacks.
These were selected boundary cases, not an exhaustive input fuzzer. Expand the matrix around arrays, objects, enum values, encodings and nullability that your actual endpoint supports. Define the intended domain before treating every unusual value as a defect.
Exercise Cookie authentication with a real nonce pair
The Cookie requests used a synthetic editor session generated by WordPress core. Without a REST nonce, the request became unauthenticated and received 401. An invalid nonce received 403. The valid Cookie and nonce pair succeeded, bringing our callback counter to two.
We initially generated the pair during the installation bootstrap and got a nonce failure. The corrected fixture creates it in a fresh PHP process after the final site URL is stored, so the Cookie hash uses the installed site's URL. That is a fixture setup lesson, not evidence of a WordPress or skill vulnerability. The final package includes the corrected source and a successful run with matching source hashes.
This checks WordPress's acceptance of the supplied synthetic session and nonce over HTTP. It does not test a human browser login, Google OAuth, session renewal in a frontend app or TLS. On a real site, keep the client, current session and REST nonce paired correctly, and use the authentication method documented for that deployment.
Inspect custom content, fields and metadata
Our synthetic note post type exposed a REST route. A response contained its ID, title and content together with an added computed summary and a structured metadata object. The object reported an approved Boolean. A second request using _fields=id,bb_fixture_summary returned just those requested fields.
Those observations establish the response shapes for the fixture. They do not prove that its write permissions, all metadata schemas or third-party clients work. An implementation that removes core fields globally deserves a separate compatibility check; requesting fewer fields as a client is a different operation.
For project discovery, WordPress Project Triage can help identify the installation and relevant plugin or theme. Its detector outputs are hints rather than certification. Keep the target project explicit when inspecting REST usage or testing changes.
Verify pagination as a complete sequence
The public fixture collection had five synthetic items. With a page size of two, the three responses contained IDs 1/2, 3/4 and 5. Each response declared a total of five and three total pages. Collecting the sequence produced every item once, with no omission or duplicate.
Page four returned 400 with our fixture's explicit out-of-range error. Requesting a page size above 100 also returned 400. A missing route returned 404. These are distinct cases; do not assume that every unsuccessful pagination request uses the same status.
The public HTML catalog on BB Skills uses 404 for a nonexistent page. Our synthetic WordPress collection intentionally used an API error contract. Design and test the contract for the actual interface rather than carrying a status assumption from one site to another.
Confirm that revoked credentials stop working
The runner generated two temporary application passwords for its subscriber and editor. After the main requests, it deleted both through WordPress core. New requests to the current-user endpoint with the old credentials returned 401 and rest_not_logged_in for both roles.
This tests the recorded environment's revocation path. A deployment with an authentication plugin may produce another error code; a protected request must still be denied. Our evidence contains no passwords, Cookie values or nonces. The generated credentials were handled in process memory and the temporary containers and database were removed.
Reproduce the bounded fixture and choose a next check
Download the revised REST skill package and read examples/README.md before running anything. The example plugin, setup, HTTP probe, Docker runner and evidence are included. Use a disposable Linux Docker host; the runner refuses to replace existing containers with its fixture names, publishes no ports and uses an internal network. It records the image identities and exact source hashes in its result.
The recorded run reused the catalog's Python image. The README shows how to use a public Python image on another host. Image tags can be rebuilt, so compare version and digest information before calling a later run equivalent. The test used WordPress's local environment mode to permit application passwords over isolated HTTP. Use HTTPS for a real external client; this run supplies no evidence about encrypted transport.
If the functional route is slow, WordPress Performance Investigation offers measurement and profiling guidance. Its WP-CLI helper actively launches the target installation and has no built-in subprocess timeout. A separate disposable experiment now covers selected native helper behavior: read the diagnostic report guide for the observed autoload and configured-directory limits. That experiment did not measure a speedup. Choose an authorized staging target and an external time limit before using it.
Useful next cases include object-level authorization, write validation for metadata, multiple simultaneous requests and the actual browser or external client you plan to support. Avoid turning one successful fixture into a general compatibility or security claim.
Evidence, sources and authoring method
The package contains examples/native-rest-evidence.json with all 27 named observations, the four fixture source hashes, environment versions and image identities. The published scenario links those observations to package version 34da134e184e.bb2 and its current checksum. All eleven original upstream REST source files remain unchanged; the full GPL text and contributor grant are supplied with the additions.
For the underlying interface, consult the WordPress authentication handbook and custom endpoint handbook. Those references describe the API; the responses and experiment limitations here come from our own synthetic run.
BB Skills used AI assistance to write and review this guide and the hand-authored fixture. No AI client executed the upstream skill in the experiment. We checked the guide against recorded HTTP responses and package-bound source hashes before publication. The article helps readers build a concrete test matrix; it is not a benchmark ranking or an endorsement from WordPress.