Enforces WordPress security rules for plugin and theme development. Activate when writing new plugins or themes, auditing WordPress code for vulnerabilities, fixing SQL injection, XSS, CSRF, or access control bugs, securing AJAX handlers or REST endpoints, reviewing file upload handlers, or when the user says 'secure WordPress code', 'audit WordPress plugin', 'fix WordPress vulnerability', 'write secure WordPress', 'review WordPress security', 'add nonce', or 'check capabilities'.
Determine mode first. If the user's intent is not clear, ask:
"Are you asking me to write new code or review existing code?"
{baseDir}/references/review-checklist.md.Exit: When all rules are satisfied, or each unsatisfied rule is documented with a justification.
Glob to discover all PHP files in scope.Grep to scan for high-risk patterns: raw $_GET/$_POST usage, echo without escaping, queries without prepare(), handlers without current_user_can().Read to read flagged files and confirm each match in context.Exit: When the user has accepted or dismissed every finding and all accepted fixes have been verified clean in step 8.
Enforce all rules below. Never write or accept code that violates them without explicitly flagging it.
NEVER use string concatenation or interpolation to build SQL queries.
$wpdb->prepare() with %d, %f, %s placeholders$wpdb->insert(), $wpdb->update(), $wpdb->delete() for CRUD — they escape automatically$wpdb->esc_like() to the search term before passing to prepare()absint() or (int) before useFlag immediately: "WHERE id = $user_id", "WHERE id = " . $_GET['id'], any query interpolating $_GET/$_POST directly.
See {baseDir}/references/code-examples.md#sql for correct patterns.
ALWAYS escape data at output time using the correct context-specific function.
| Context | Function |
|---|---|
| HTML content | esc_html() |
| HTML attribute | esc_attr() |
| URL (href/src) | esc_url() |
| JavaScript string | esc_js() |
| Textarea content | esc_textarea() |
| Allow safe HTML | wp_kses_post() or wp_kses($html, $allowed) |
JSON in <script> | wp_json_encode() |
For JS data, prefer wp_localize_script() over inline <script> blocks.
For i18n strings, use esc_html__(), esc_attr__(), esc_html_e() — not echo __().
Flag immediately: echo $_GET['x'], echo $user_input, echo get_post_meta(...) without escaping.
See {baseDir}/references/code-examples.md#xss for correct patterns.
Every state-changing operation MUST include nonce generation and verification.
wp_nonce_field($action, $name) to generate; wp_verify_nonce($_POST[$name], $action) to verifywp_nonce_url($url, $action, $name) to generate; wp_verify_nonce($_GET[$name], $action) to verifywp_localize_script(); verify with check_ajax_referer($action, $name) or wp_verify_nonce()'delete_post_' . $post_idFlag immediately: Any form handler, GET action, or AJAX handler missing nonce verification.
See {baseDir}/references/code-examples.md#csrf for correct patterns.
Every handler (admin action, AJAX, REST endpoint, form processor) MUST call current_user_can() before executing.
delete_post over delete_posts; edit_post over edit_postspermission_callback — never 'permission_callback' => '__return_true' for authenticated actionswp_ajax_nopriv_ for actions that are genuinely publicSee {baseDir}/references/code-examples.md#access-control for common capability reference and patterns.
Sanitize all external input before storing or using it. Use the most specific function available:
sanitize_text_field() — single-line textsanitize_textarea_field() — multi-line textsanitize_email() + is_email() validation — emailesc_url_raw() + filter_var($url, FILTER_VALIDATE_URL) — URL for storageabsint() — positive integersanitize_key() — slug/key formatwp_kses_post() — user content that may contain safe HTMLValidate after sanitizing. Reject inputs that fail validation with a clear error.
Include ALL checks in order: permission → nonce → error → size → MIME whitelist → extension whitelist → image verification → wp_handle_upload().
For images, also verify with getimagesize() and re-render through GD/Imagick to strip embedded payloads.
See {baseDir}/references/code-examples.md#file-upload for the complete handler template.
error_log(). Show only generic messages to users. Never expose $wpdb->last_error, file paths, or stack traces.if (!defined('ABSPATH')) { exit; } at the top of every PHP file.wp-config.php constants or environment variables.When writing new code: apply all rules above without being asked. Self-check against the checklist before presenting code.
When reviewing existing code: follow Mode B workflow. Report file path, line number, rule violated, unsafe pattern, and corrected code for each finding. Do not report theoretical issues — only flag concrete violations visible in the code.
Reference files:
{baseDir}/references/code-examples.md — complete correct code patterns for each rule{baseDir}/references/review-checklist.md — structured checklist for pre-commit review