| 1 |
# SDD: WordPress.Security.NonceVerification.Recommended |
| 2 |
|
| 3 |
## Goal |
| 4 |
Ensure that any request data processing in an admin/UI context includes a proper nonce |
| 5 |
verification without breaking existing flows. When the flow is not user-driven |
| 6 |
(cron, webhooks, external jobs), apply equivalent authentication/validation and |
| 7 |
document why a nonce does not apply. |
| 8 |
|
| 9 |
## Scope |
| 10 |
Files in `inc/` and `classes/` that emit the warning `WordPress.Security.NonceVerification.Recommended`. |
| 11 |
|
| 12 |
## Project discovery (before changing) |
| 13 |
1. Map existing patterns with a search for `check_admin_referer`, `wp_verify_nonce`, `wp_nonce_field`. |
| 14 |
2. Reuse the screen's existing action/field whenever possible. |
| 15 |
3. If no helper fits, create a private method in the class. |
| 16 |
4. Do not invent a new action if the screen already uses an action. Align to keep behavior and avoid DRY. |
| 17 |
|
| 18 |
## Out of scope |
| 19 |
- Do not add `phpcs:ignore` for `NonceVerification.Recommended` by default. |
| 20 |
- Do not change how public endpoints work without validating impact on external integrations. |
| 21 |
|
| 22 |
## Decision (quick tree) |
| 23 |
1. Is it a user action on an admin screen? |
| 24 |
- Yes: add required nonce. |
| 25 |
- No: go to 2. |
| 26 |
2. Is it cron/CLI/external endpoint with its own auth (token/secret)? |
| 27 |
- Yes: validate that mechanism and document why nonce does not apply. Use `wp_unslash` + sanitization; avoid nonce. |
| 28 |
- No: re-evaluate. Prefer adding a nonce or a simple auth token. |
| 29 |
|
| 30 |
## Strategy by use type |
| 31 |
### 1) Admin forms and actions (POST) |
| 32 |
- Add `wp_nonce_field( 'action_name', 'nonce_field' )` in the form. |
| 33 |
- In the handler, call `check_admin_referer( 'action_name', 'nonce_field' )` before reading data. |
| 34 |
- Always `wp_unslash()` before sanitizing (`sanitize_text_field`, `absint`, etc.). |
| 35 |
|
| 36 |
### 2) List filters (GET) without side effects |
| 37 |
- If only ordering/filter UI: |
| 38 |
- Verify nonce (`wp_verify_nonce`) and only use values when valid. |
| 39 |
- If invalid/missing, fall back to defaults (no aggressive error/redirect). |
| 40 |
|
| 41 |
### 3) Cron / automatic endpoints |
| 42 |
- If existing auth exists (job key, hash, token): |
| 43 |
- Validate explicitly and document why nonce does not apply. |
| 44 |
- If no auth exists: |
| 45 |
- Propose a simple token in query string or header, and only add nonce for admin endpoints. |
| 46 |
|
| 47 |
## Implementation patterns |
| 48 |
- Always use `wp_unslash()` before sanitizing. |
| 49 |
- Prefer `check_admin_referer()` when state changes. |
| 50 |
- For GET filters/ordering, prefer `wp_verify_nonce()` and a safe fallback. |
| 51 |
- Do not change external behavior without reviewing usage (search/grep for callers). |
| 52 |
|
| 53 |
## Example (admin POST) |
| 54 |
```php |
| 55 |
// Form |
| 56 |
wp_nonce_field( 'imagify_settings_save', 'imagify_settings_nonce' ); |
| 57 |
|
| 58 |
// Handler |
| 59 |
check_admin_referer( 'imagify_settings_save', 'imagify_settings_nonce' ); |
| 60 |
$value = isset( $_POST['foo'] ) ? sanitize_text_field( wp_unslash( $_POST['foo'] ) ) : ''; |
| 61 |
``` |
| 62 |
|
| 63 |
## Example (GET filter) |
| 64 |
```php |
| 65 |
$nonce = filter_input( INPUT_GET, '_wpnonce', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); |
| 66 |
$nonce = $nonce ? sanitize_text_field( $nonce ) : ''; |
| 67 |
if ( $nonce && wp_verify_nonce( $nonce, 'imagify_list_filter' ) ) { |
| 68 |
$order = sanitize_key( wp_unslash( $_GET['order'] ?? '' ) ); |
| 69 |
} else { |
| 70 |
$order = 'desc'; |
| 71 |
} |
| 72 |
``` |
| 73 |
|
| 74 |
## Git Operations |
| 75 |
Do not run `git commit` or `git push`. You may only suggest a commit message. |
| 76 |
|
| 77 |
## Verification |
| 78 |
- Review each warning and classify by type (admin POST, GET filter, cron/external). |
| 79 |
- Check impacts on tests and integrations before irreversible changes. |
| 80 |
|