## Summary

No distinct vulnerability variant or WordPress 7.0.2 bypass was found. The bounded search exercised the original unauthenticated `/wp-json/batch/v1` trust boundary against clean stock WordPress 7.0.1 and exact 7.0.2 targets, tested a materially different data path that moved the shifted scalar `author_exclude` value from the URL query string into the subrequest JSON body, retained the original query-scalar path as a live primitive control, and separately examined malformed-entry placement in batch dispatch. The query-scalar control reflected a forged full-row marker on 7.0.1 and not on 7.0.2; the body-scalar candidate reflected no forged marker on either target. Exact trunk revision `ace9192af868524bdc49cf4fcb91f4c12c73ee5f` retained all three independent guards. The result is therefore negative rather than a fabricated alternate label for the parent chain.

## Fix Coverage / Assumptions

The 7.0.2 fix establishes three independent invariants:

1. `WP_REST_Server::serve_batch_request_v1()` appends a `WP_Error` to `$matches` whenever it appends the same error to `$validation`, preserving one-to-one request/match/validation indexing for every malformed subrequest (`src/wp-includes/rest-api/class-wp-rest-server.php`).
2. `WP_Query::get_posts()` parses every non-empty `author__not_in` value with `wp_parse_id_list()` before interpolating the resulting integer list into SQL (`src/wp-includes/class-wp-query.php`). This is shape-independent at the sink: scalar strings and arrays are normalized.
3. `rest_api_loaded()` and `WP_REST_Server::serve_request()` refuse to start a fresh top-level REST cycle while a dispatch is active (`src/wp-includes/rest-api.php` and `src/wp-includes/rest-api/class-wp-rest-server.php`). Internal subrequests must use `dispatch()`.

These changes assume that every batch parse failure is represented by a `WP_Error`, all author-exclusion flows reaching `WP_Query` use `author__not_in`, and privileged re-entry requires a fresh `serve_request()` cycle. Source scanning supports those assumptions for the parent chain. At the exact tested trunk revision, the sole production call to `serve_request()` remained in `rest_api_loaded()`, which is itself guarded, while the server method also retains a defense-in-depth guard.

The patch does not redesign the oEmbed-cache merge behavior, Customizer per-setting user switching, dynamic post-status/type hooks, or normal REST `dispatch()`. Those paths remain legitimate core behaviors. They are no longer attacker-reachable through the parent chain because the patch cuts the chain at three earlier, independent gates. The only asymmetry noticed in nearby code is that `author__in` retains a separate normalization implementation, but it applies `absint` to every value and does not preserve the parent SQL-injection sink; relabeling it as a bypass would be unsupported.

## Variant / Alternate Trigger

All runtime candidates used unauthenticated HTTP `POST /wp-json/batch/v1` from a separate driver container:

1. **Body-scalar alternate data path.** The shifted request placed `author_exclude` in its JSON body instead of the URL query string, while keeping the malformed nested batch and full-row marker query. This was materially different input placement but did not reach the SQL marker on either 7.0.1 or 7.0.2. On 7.0.1 the shifted GET posts handler executed but did not consume this body value as the needed query parameter.
2. **Query-scalar sink control.** The scalar remained in the shifted subrequest URL query. A synthetic `wp_posts.*` row containing `PRUVA-VARIANT-ROW-MARKER` was reflected on 7.0.1, proving the vulnerable batch-to-`WP_Query` path was live in this stage's fresh environment. The marker was absent on 7.0.2, where aligned handlers stop the shifted route and integer normalization independently removes SQL syntax.
3. **Alternate malformed-entry/alignment check.** A malformed subrequest was followed by post and user routes to compare handler selection. On 7.0.1 the response showed the shifted user-controller permission code (`rest_cannot_create_user`) for the posts request; 7.0.2 showed the correctly aligned posts-controller code (`rest_cannot_create`). This confirms the alignment patch rather than a transport failure. Moving the malformed error later cannot shift an earlier request and leaves no subsequent attacker request to exploit when last; multiple errors are covered by appending one match entry per error.

The relevant path is `WP_REST_Server::serve_batch_request_v1()` -> shifted `WP_REST_Posts_Controller::get_items()` -> `WP_Query::get_posts()` for the primitive, followed in the parent chain by oEmbed/cache persistence, Customizer publication, `rest_api_loaded()`/`serve_request()` re-entry, and `WP_REST_Users_Controller::create_item()`. No alternate stock endpoint was found that both crosses the same unauthenticated network boundary and bypasses all three fixed invariants.

## Impact

- **Component:** WordPress Core REST batch dispatch and `WP_Query` author exclusion handling.
- **Vulnerable control tested:** stock WordPress 7.0.1 source identity `18f793b1f16c1b15b0fc37027f4aeaefab0bfe02`.
- **Fixed target tested:** WordPress 7.0.2 tag/commit `855551c4477bd5a0407221c57dae123c4163b434` over the ticket-pinned PHP/Apache image.
- **Latest checked:** origin/trunk at exact revision `ace9192af868524bdc49cf4fcb91f4c12c73ee5f` on 2026-07-18; all three guards were present. Latest was source-inspected rather than started as a third runtime because it is an unreleased 7.1 development tree and the exact released 7.0.2 fixed target supplied the runtime oracle.
- **Risk of the parent issue:** Critical unauthenticated administrator creation leading to code execution.
- **Risk established by this variant run:** None on the fixed target. No persistent write, user creation, privilege transition, or code execution occurred.

## Impact Parity

- **Disclosed/claimed maximum parent impact:** Unauthenticated creation of a fresh administrator followed by normal login, core plugin upload, and command execution as `www-data`.
- **Reproduced impact in this variant run:** A side-effect-free full-row SQL reflection marker only on the vulnerable 7.0.1 control. No marker reached 7.0.2; both clean targets retained one user and their original post counts.
- **Parity:** `none` for a fixed-version bypass. The vulnerable-control primitive is partial evidence only and is not claimed as a new variant.
- **Not demonstrated:** Fixed-version SQL injection, cache poisoning, persistent post creation, administrator creation, authentication, plugin upload, or code execution.

## Root Cause

There is no surviving same-root-cause path on the tested fixed target. The parent root cause requires composition of (a) batch-array desynchronization, (b) unnormalized scalar `author__not_in` SQL construction, and (c) nested top-level REST dispatch during a temporary privileged context. WordPress 7.0.2 fixes each condition independently in commits merged into tag `855551c4477bd5a0407221c57dae123c4163b434`:

- `85015b84fbc52bf6151a691299896b8971772594` — nested top-level REST dispatch guards.
- `c8bdf1fa12355f79db94054d307d0e3898b501c9` — batch match-array alignment.
- `74d37a344cbf28e9187a1a5ca71b33d186bcd333` — integer normalization of `author__not_in`.

The body-scalar experiment did not reproduce even on 7.0.1 because request-body placement did not become the posts collection's query parameter in this shifted GET-handler path. The exact query-scalar parent primitive did reproduce on 7.0.1, then failed on 7.0.2 as expected. Consequently there is no technical basis to claim that the underlying bug remains reachable.

## Reproduction Steps

1. Run `bundle/vuln_variant/reproduction_steps.sh` from any directory, optionally setting `PRUVA_ROOT` to the bundle directory.
2. The script resolves and records exact 7.0.1, 7.0.2, and trunk source identities; reads the target security policy; verifies the three guards in 7.0.2 and trunk; launches clean isolated MySQL and WordPress 7.0.1/7.0.2 targets; installs each with one owner whose random password is never exposed; and runs all three candidates over unauthenticated HTTP.
3. It requires the vulnerable query-scalar marker to be present, the fixed marker to be absent, and user/post counts to remain unchanged. It exits `1` for this valid negative outcome. Exit `0` is reserved for a marker reaching the fixed target, and exit `2` indicates an infrastructure/oracle failure.
4. Expected final evidence is `verdict=no_distinct_variant_or_bypass` and `NO DISTINCT VARIANT/BYPASS`.

## Evidence

Primary logs are under `bundle/logs/vuln_variant/`:

- `verification_run1.log` and `verification_run2.log`: two independent complete script executions.
- `two_run_verification.log`: both expected exits were `1`.
- `vulnerable_query_scalar.json`: `marker_reflected=true`, proving the vulnerable primitive was live.
- `fixed_query_scalar.json`: `marker_reflected=false`.
- `vulnerable_body_scalar.json` and `fixed_body_scalar.json`: alternate body path was negative on both.
- `vulnerable_batch_alignment.json` and `fixed_batch_alignment.json`: shifted versus aligned controller response codes.
- `vulnerable_before.log`, `vulnerable_after.log`, `fixed_before.log`, and `fixed_after.log`: both targets stayed at `user_count=1`, `post_count=4`, `oembed_count=0`, and `local_infile=0`.
- `source_identity.log`, `fixed_version.txt`, and `latest_version.txt`: exact commit identities.
- `static_coverage.log`: 7.0.2 and trunk each contain the batch, nested-dispatch, and author-ID guards; trunk has one production `serve_request()` call site.
- `fix_diff.txt` and `security_policy.md`: patch and target policy evidence.
- `candidate_matrix.log`: canonical bounded-search outcome.

Key evidence excerpts:

```text
candidate_1=body_scalar_alternate_data_path vulnerable_marker=false fixed_marker=false
candidate_2=query_scalar_original_sink_control vulnerable_marker=true fixed_marker=false
candidate_3=batch_alignment_alternate_error_position inspected_runtime_and_source=true
latest_revision=ace9192af868524bdc49cf4fcb91f4c12c73ee5f guards_retained=true
verdict=no_distinct_variant_or_bypass
run1_exit=1
run2_exit=1
```

## Recommendations / Next Steps

- Keep all three fixes; none should be treated as redundant. Add regression tests that compose malformed batch entries at first, middle, last, and multiple positions and assert exact request/match/validation alignment.
- Add `WP_Query` tests covering scalar strings, arrays, comma-separated strings, nested arrays, and values supplied through query and body parameter bags, requiring integer-only `author__not_in` SQL.
- Retain both nested-dispatch guards. Add a regression assertion that all internal REST subrequests use `dispatch()` and that `serve_request()` cannot re-enter while `is_dispatching()` is true.
- Consider normalizing `author__in` through `wp_parse_id_list()` too for consistency and stable cache-key behavior, although current `absint` mapping did not expose the parent sink.
- Add an end-to-end test that the parent unauthenticated request leaves users, posts, oEmbed rows, and plugin state unchanged on supported releases.

## Additional Notes

- WordPress's repository `SECURITY.md` explicitly covers Core and directs reports to HackerOne. It does not exclude unauthenticated REST privilege transitions; the tested boundary is therefore in scope. The probes do not reframe a user-selected local file or documented behavior as a security issue.
- Runtime probes were intentionally side-effect-free: the SQL candidate only reflected a synthetic row in the REST response and did not persist it. Database counts verify this.
- Idempotency was confirmed by two consecutive complete script runs, each ending with the expected exit code `1` and the same negative matrix.
- The latest default branch was exact-revision source checked, not runtime built, because it was an unreleased 7.1 development revision. The fixed 7.0.2 release was tested live side by side as required.
