## Summary

No distinct bypass or alternate trigger was confirmed for CVE-2026-8441 in this variant stage. The parent issue is an unauthenticated SQL injection in WP Review Slider Pro 12.6.7 where the public AJAX action `wprp_load_more_revs` passes attacker-controlled `notinstring` into a numeric `AND id NOT IN (...)` clause. The variant run revalidated that the original `notinstring` payload delays before the patch and is fast after the integer-casting patch, then tested materially distinct public fields (`textsource`, `textsearch`, `textlang`, `textrtype`, and `shortcodepageid`) that reach the same review-query construction. None produced a timing delay on the fixed control, so this is a negative variant result rather than a confirmed bypass.

## Fix Coverage / Assumptions

The original fix relies on the invariant that values entering the numeric `id NOT IN (...)` list must be review IDs. It changes the vulnerable `explode(',', $notinstring)` path in `public/partials/getreviews_class.php` to cast the CSV list through `absint()` before rejoining it, so SQL metacharacters in `notinstring` are converted to integers and cannot break out of the `NOT IN` clause.

The fix explicitly covers:

- `WP_Review_Pro_Public::wppro_loadmore_revs_ajax()` reading `$_POST['notinstring']` from `/wp-admin/admin-ajax.php?action=wprp_load_more_revs`.
- `GetReviews_Functions::wppro_queryreviews()` building `$notinsearchstring = " AND id NOT IN (".$notinstring.") "` when `$totaltoget > 0`.
- The parent time-based SQL injection payload `1) OR (SELECT SLEEP(3))#`, which was reproduced before the patch and blocked after the patch.

The fix does not rewrite every dynamic query fragment in `wppro_queryreviews()`. The variant analysis therefore reviewed and tested other public filters that are appended to the same `SELECT` statement, including text search, source/page filtering, language filtering, type filtering, and shortcode page IDs. In the tested runtime, those alternate string candidates did not bypass the fix: their attempted quote breaks were escaped in the final `dbcall`, and no timing delay occurred.

The target package did not include a `SECURITY.md`, security policy, or threat-model document in the bundled commercial plugin source. The relevant trust boundary for this assessment is therefore the WordPress public HTTP boundary: unauthenticated visitors can obtain the frontend nonce from a page rendering `[wprevpro_usetemplate]` and can POST to `admin-ajax.php` through the plugin's `wp_ajax_nopriv_wprp_load_more_revs` registration.

## Variant / Alternate Trigger

The variant search tested these candidate alternate public fields on the same AJAX endpoint and review-query sink:

1. `textsource`: reaches `$publicsourcefilter = " AND pageid = '".sanitize_text_field($textsource)."'"` in `public/partials/getreviews_class.php`.
2. `textsearch`: reaches `$textsearchquery` using `LIKE` filters over reviewer/review/type fields.
3. `textlang`: reaches `$publiclangfilter = " AND language_code = '".sanitize_text_field($textlang)."'"`.
4. `textrtype`: reaches `$publicrtypefilter = "AND type = '".sanitize_text_field($textrtype)."'"`.
5. `shortcodepageid`: reaches `$rpagefilter` construction through the `shortcodepageid` argument passed to `wppro_queryreviews()`.

Exact entry point for all attempts:

- `POST /wp-admin/admin-ajax.php`
- `action=wprp_load_more_revs`
- Public nonce field: `wpfb_nonce`, obtained from a frontend page rendering the plugin shortcode.
- Code path: `WP_Review_Pro_Public::wppro_loadmore_revs_ajax()` -> `GetReviews_Functions::wppro_queryreviews()` -> `$wpdb->get_results()`.

All distinct candidate payloads were tested after applying the same notinstring-only fixed control used by the reproduction stage. The fixed-control runtimes were all fast (~0.014-0.017 seconds), so none is claimed as a bypass.

## Impact

- **Package/component affected:** WP Review Slider Pro WordPress plugin, public AJAX action `wprp_load_more_revs`, review-query helper `public/partials/getreviews_class.php`.
- **Affected versions as tested:** Genuine WP Review Slider Pro 12.6.7 source zip (`sha256: 7f6092f4ea58b5c5c0dba72ba014b9395bf9608f9aeeb95206e80dc14f0b17e5`) and a fixed control consisting of that same source with the line-accurate `notinstring` integer-casting patch applied. The advisory reports fixed version 12.7.3, but the official commercial 12.7.3 source was not available in this bundle for direct testing.
- **Risk level and consequences:** The parent vulnerability remains high impact on vulnerable versions because unauthenticated attackers can perform blind SQL injection and infer database contents. No additional impact was reproduced for the tested variants after the fixed control.

## Impact Parity

- **Disclosed/claimed maximum impact for the parent:** Remote unauthenticated SQL injection with database data extraction via the public WordPress AJAX surface.
- **Reproduced impact from this variant run:** The script reproduced the parent timing primitive before patching (`notinstring` `SLEEP(3)` around 3.11s) and showed it was blocked after the notinstring patch (~0.045s). It then tested five alternate public-field candidates on the fixed control; all were fast and did not execute the timing payload.
- **Parity:** `none` for a new variant/bypass, because no distinct alternate trigger was confirmed. Parent impact parity remains full in the original reproduction, not in this negative variant stage.
- **Not demonstrated:** No new data extraction, bulk database dumping, code execution, authentication bypass, or confirmed bypass of the fixed control.

## Root Cause

The parent root cause is SQL construction that treated a public request field as a comma-separated numeric ID list without enforcing numeric IDs. Specifically, `WP_Review_Pro_Public::wppro_loadmore_revs_ajax()` reads `$_POST['notinstring']`, and `GetReviews_Functions::wppro_queryreviews()` concatenates it into `AND id NOT IN ($notinstring)` before executing the query. `sanitize_text_field()` is not an SQL parameterization mechanism, so payloads such as `1) OR (SELECT SLEEP(3))#` break out of the numeric context on vulnerable code.

The fixed control changes that root cause for the parent path by casting each CSV element with `absint()` before the `NOT IN` fragment is built. The variant analysis attempted to find the same underlying SQL-neutralization failure from other request fields appended to the same query. Those candidates reached the query-building code, but did not produce SQL execution on the fixed control; response `dbcall` evidence showed quote characters escaped in string-context filters, and timings stayed below the fast threshold. No public fix commit was available because the plugin is commercially distributed; the tested fixed control is the line-accurate patch recorded in `bundle/logs/vuln_variant/variant_notinstring_patch.diff`.

## Reproduction Steps

1. Run `bundle/vuln_variant/reproduction_steps.sh`.
2. The script:
   - verifies the bundled genuine WP Review Slider Pro 12.6.7 source zip and records source excerpts;
   - installs/uses PHP, MariaDB, WordPress, and WP-CLI;
   - activates the genuine plugin in a live WordPress instance;
   - seeds plugin review/template data and publishes a page containing `[wprevpro_usetemplate tid="1"]`;
   - extracts the public frontend nonce;
   - proves the parent vulnerable `notinstring` timing payload delays before patching;
   - applies the line-accurate `notinstring` integer-casting patch;
   - proves the original payload is fast after patching;
   - tests distinct public-field candidates (`textsource`, `textsearch`, `textlang`, `textrtype`, `shortcodepageid`) on the fixed control.
3. Expected evidence:
   - vulnerable original `notinstring` `SLEEP(3)` takes >= 2.5 seconds;
   - fixed-control original `notinstring` `SLEEP(3)` is fast (< 1.5 seconds);
   - all tested alternate candidate payloads are fast (< 1.5 seconds);
   - the script exits 1 to indicate no variant/bypass was confirmed, but it completes cleanly and is idempotent.

## Evidence

Primary evidence locations:

- `bundle/logs/vuln_variant/reproduction_steps.log`
- `bundle/logs/vuln_variant/variant_evidence.log`
- `bundle/logs/vuln_variant/variant_plugin_source_identity.log`
- `bundle/logs/vuln_variant/variant_notinstring_patch.diff`
- `bundle/logs/vuln_variant/fixed_version.txt`
- `bundle/vuln_variant/artifacts/variant_timing_summary.json`
- `bundle/vuln_variant/artifacts/candidate_matrix.json`
- `bundle/vuln_variant/runtime_manifest.json`

Key second-run timings captured in the evidence log:

- Vulnerable original `notinstring` `SLEEP(3)`: `3.109833s`
- Fixed-control original `notinstring` `SLEEP(3)`: `0.044909s`
- Fixed-control `textsource` candidate: `0.015890s`
- Fixed-control `textsearch` candidate: `0.015887s`
- Fixed-control `textlang` candidate: `0.015000s`
- Fixed-control `textrtype` candidate: `0.016840s`
- Fixed-control `shortcodepageid` candidate: `0.014518s`

The runtime environment includes PHP 8.5.4, MariaDB 11.8.6, WordPress 6.7.1, and WP Review Slider Pro 12.6.7 from the bundled commercial zip.

## Recommendations / Next Steps

- Keep the `notinstring` fix as integer casting or an equivalent `$wpdb->prepare()`/placeholder-based implementation for every ID in the `NOT IN` list.
- As defense in depth, refactor `GetReviews_Functions::wppro_queryreviews()` to build all dynamic filters with `$wpdb->prepare()` placeholders instead of concatenating sanitized strings into SQL fragments.
- Treat every public AJAX field as untrusted even when protected by the frontend nonce, because the nonce is intentionally exposed to visitors on pages using the plugin.
- Add regression tests for the original `notinstring` payload and for the reviewed alternate fields (`textsource`, `textsearch`, `textlang`, `textrtype`, and `shortcodepageid`) to ensure no future change removes escaping or numeric validation.
- If official 12.7.3 source becomes available, rerun the same variant script against that release and confirm whether the vendor implemented only the notinstring patch or a broader query-builder refactor.

## Additional Notes

The reproducer was run twice successfully. Both runs completed cleanly and exited 1 by design because no distinct variant or bypass was found. The official fixed commercial source was unavailable in this bundle, so the fixed target is the line-accurate notinstring patch derived from the reproduction-stage analysis rather than an official 12.7.3 package.