wp-security-audit

Audits WordPress plugin or theme PHP code for the most common security mistakes — missing nonce checks, capability checks, input sanitization, output escaping, unslashing, SQL preparation, AJAX nopriv exposure, file/path traversal, and unsafe redirects. Use when reviewing pull requests, before releasing a plugin, when the user asks "is this secure", or when handling code that touches $_GET / $_POST / $_REQUEST / $_COOKIE / $_FILES / $_SERVER, admin-ajax / admin-post, REST endpoints, options, user meta, custom DB queries, or file uploads.

Best use case

wp-security-audit is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Audits WordPress plugin or theme PHP code for the most common security mistakes — missing nonce checks, capability checks, input sanitization, output escaping, unslashing, SQL preparation, AJAX nopriv exposure, file/path traversal, and unsafe redirects. Use when reviewing pull requests, before releasing a plugin, when the user asks "is this secure", or when handling code that touches $_GET / $_POST / $_REQUEST / $_COOKIE / $_FILES / $_SERVER, admin-ajax / admin-post, REST endpoints, options, user meta, custom DB queries, or file uploads.

Teams using wp-security-audit should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/wp-security-audit/SKILL.md --create-dirs "https://raw.githubusercontent.com/nvdigitalsolutions/mcp-ai-wpoos/main/.agents/skills/wp-security-audit/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/wp-security-audit/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How wp-security-audit Compares

Feature / Agentwp-security-auditStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Audits WordPress plugin or theme PHP code for the most common security mistakes — missing nonce checks, capability checks, input sanitization, output escaping, unslashing, SQL preparation, AJAX nopriv exposure, file/path traversal, and unsafe redirects. Use when reviewing pull requests, before releasing a plugin, when the user asks "is this secure", or when handling code that touches $_GET / $_POST / $_REQUEST / $_COOKIE / $_FILES / $_SERVER, admin-ajax / admin-post, REST endpoints, options, user meta, custom DB queries, or file uploads.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# WordPress security audit

A defensive checklist-driven review for WP plugin and theme PHP. Goal:
catch the boring, repeatable mistakes that ship to production because no
one ran through the basics. This is **not** a substitute for a real
security review of cryptography, business logic, or third-party deps.

## When to use this skill

Trigger this skill when ANY of the following is true:

- The user asks for a security review, audit, or "is this safe".
- The diff or file under discussion contains: `$_GET`, `$_POST`,
  `$_REQUEST`, `$_COOKIE`, `$_FILES`, `$_SERVER`, `wp_unslash`,
  `wp_verify_nonce`, `check_admin_referer`, `current_user_can`,
  `add_action( 'wp_ajax`, `add_action( 'admin_post`,
  `register_rest_route`, `$wpdb->`, `update_option`, `update_user_meta`,
  `wp_redirect`, `wp_safe_redirect`, `file_get_contents`, `move_uploaded_file`.
- The user is preparing a plugin for release or wp.org submission.
- The user is reviewing a contributor's PR.

## How to run the audit

Work through the **Critical checks** below in order. For each finding:

1. State the file and line.
2. Name the issue using its conventional WP terminology
   (e.g. "missing nonce", "unescaped output", "broken access control").
3. Show the offending code (1–3 lines).
4. Show the fix.
5. Mark severity: **HIGH** (exploitable now), **MEDIUM** (exploitable under
   conditions), **LOW** (hardening / best practice).

Do NOT silently rewrite the file. Produce a report first; only edit if the
user asks you to apply fixes.

## Critical checks

### 1. Nonce verification on state-changing requests

Any handler that *writes* (saves option, updates meta, deletes a post,
sends an email, mutates anything) must verify a nonce.

- Forms: `wp_nonce_field( 'action_name', '_wpnonce' )` →
  `check_admin_referer( 'action_name' )` in handler.
- AJAX: `wp_create_nonce( 'action_name' )` → `check_ajax_referer( 'action_name', 'nonce' )`.
- REST: rely on cookie auth + the built-in `_wpnonce` (`wp-api` nonce) for
  logged-in routes; for `permission_callback` use a real capability check.

**Common mistake:** verifying the nonce inside an `if` whose `else` branch
still does the write. The nonce check must short-circuit.

### 2. Capability checks (authorization)

Authentication ≠ authorization. A logged-in subscriber is still a user.

- Admin actions: `current_user_can( 'manage_options' )` or a more
  specific capability (`edit_posts`, `edit_post` with object ID,
  `manage_woocommerce` etc.).
- Object-level actions MUST pass the object ID:
  `current_user_can( 'edit_post', $post_id )` — the ID-less form is wrong.
- REST `permission_callback` must NEVER be `__return_true` for
  state-changing routes. Returning `true` unconditionally is the #1 plugin
  vulnerability pattern on wp.org.

### 3. Input: unslash → sanitize → validate

WordPress magically slashes superglobals. The pipeline is fixed:

```php
$raw      = isset( $_POST['email'] ) ? wp_unslash( $_POST['email'] ) : '';
$email    = sanitize_email( $raw );
if ( ! is_email( $email ) ) { /* reject */ }
```

- Missing `wp_unslash` before sanitization → escaped quotes leak through.
- Wrong sanitizer for the data type. Map by intent:
  - text field: `sanitize_text_field`
  - textarea: `sanitize_textarea_field`
  - email: `sanitize_email`
  - URL: `esc_url_raw` (storage) / `esc_url` (output)
  - key/slug: `sanitize_key`, `sanitize_title`
  - integer: `absint` or `(int)` with range check
  - HTML allowed: `wp_kses_post` or `wp_kses` with explicit allowlist
  - file path: `sanitize_file_name` + `realpath` containment check
- Never trust `$_SERVER['HTTP_*']` headers without sanitizing; they're
  attacker-controlled.

### 4. Output escaping (XSS)

Escape **at the point of output**, in the right context:

- HTML body: `esc_html( $x )`
- HTML attribute: `esc_attr( $x )`
- URL in `href`/`src`: `esc_url( $x )`
- Inside `<script>` JSON: `wp_json_encode( $x )`, never raw concatenation
- Translated strings with placeholders: escape the **template** AND
  the **substituted value** separately. `esc_html__()` only escapes
  the static template; `printf( esc_html__( '%s', 'td' ), $name )` is
  XSS if `$name` contains markup. Correct form:
  `printf( esc_html__( 'Hello, %s', 'td' ), esc_html( $name ) )`.
- Already-HTML content (post content): `wp_kses_post( $x )`

`echo $foo;` where `$foo` came from input or DB without escaping → XSS.
This is the most common finding in plugin audits.

### 5. SQL: always prepare

```php
// WRONG
$wpdb->get_results( "SELECT * FROM x WHERE id = $id" );

// RIGHT
$wpdb->get_results( $wpdb->prepare( "SELECT * FROM x WHERE id = %d", $id ) );
```

- `%d` integers, `%f` floats, `%s` strings. Table/column names CANNOT be
  parameterized — validate against an allowlist before interpolating.
- `LIKE` needs `$wpdb->esc_like()` BEFORE `prepare()`:
  `$like = '%' . $wpdb->esc_like( $term ) . '%';`
- Prefer WP_Query / get_posts / get_users over raw SQL where possible.

### 6. AJAX endpoints

Two hooks, two meanings — confuse them and you ship a vulnerability:

- `wp_ajax_{action}` — fires only for **logged-in** users.
- `wp_ajax_nopriv_{action}` — fires for **logged-out** users.

Rules:
- Register `nopriv` ONLY if the feature is genuinely meant for guests
  (e.g. public search, login form). Never copy-paste both registrations
  "to be safe".
- Both handlers still need `check_ajax_referer()`.
- The `nopriv` handler must NOT perform actions that only logged-in users
  should do (saving prefs, accessing other users' data, etc.).
- End with `wp_send_json_success` / `wp_send_json_error`, not `echo` + `die`.

### 7. admin-post and form handlers

`admin_post_{action}` and `admin_post_nopriv_{action}` follow the same
rules as AJAX. Plus: redirect with `wp_safe_redirect()` + `exit;`. Never
redirect with `wp_redirect( $_GET['redirect_to'] )` without validating
against an allowlist — that's an open-redirect.

### 8. REST API routes

```php
register_rest_route( 'myplugin/v1', '/save', [
    'methods'             => 'POST',
    'callback'            => 'myplugin_save',
    'permission_callback' => function () {
        return current_user_can( 'manage_options' );
    },
    'args' => [
        'id' => [
            'required'          => true,
            'validate_callback' => 'is_numeric',
            'sanitize_callback' => 'absint',
        ],
    ],
] );
```

Findings to flag:
- `permission_callback` missing, or `__return_true` on a non-public route.
- No `args` schema — input is unsanitized.
- Returning raw DB rows including sensitive columns (`user_pass`,
  `user_activation_key`, private meta).

### 9. File operations

- Uploads: validate MIME via `wp_check_filetype_and_ext()`, store via
  `wp_handle_upload()`, never trust the client-provided extension or MIME.
- Path joins with user input: after building, `realpath()` and check the
  result starts with the intended base dir. Otherwise: path traversal.
- Never `include` / `require` a path containing user input.

### 10. Secrets and information disclosure

- No API keys or DB credentials in the plugin source. Use options or
  constants in `wp-config.php`.
- `WP_DEBUG_DISPLAY` should be off in prod; flag any `var_dump`,
  `print_r`, `error_log( $sensitive )` left in handlers.
- Don't leak stack traces, full SQL, or user enumeration via error
  messages ("user not found" vs "wrong password" — pick one).

### 11. Redirects

- Use `wp_safe_redirect()` for any URL that may be influenced by input.
- Always `exit;` after a redirect — execution continues otherwise.

### 12. Cron and background jobs

- `wp_schedule_event` callbacks run with no current user. If the job
  performs privileged work, do not trust any "stored intent" without
  re-validating; treat persisted user input as untrusted.

## What this skill does NOT cover

- Cryptographic correctness (key derivation, signing schemes).
- Business-logic flaws (race conditions, IDOR beyond capability checks).
- Third-party library CVEs — run `composer audit` separately.
- Frontend JS XSS — different skill.
- Server / hosting hardening (file perms, disable_functions, etc.).
- Object injection, SSRF, CSRF on GET, mass assignment, file include,
  mail/zip injection, timing comparison, TOCTOU races — these are
  covered by **`wp-security-deep`**. Run it after this one.
- Hardcoded credentials, weak randomness for tokens, password
  storage, cookie flags, secrets in logs — covered by
  **`wp-security-secrets`**. Run it whenever auth or third-party
  integrations are in scope.

State this scope explicitly in the report. If the reviewed code
clearly falls into one of the deeper categories above, recommend the
appropriate skill in the report's footer.

## Report format

```
# Security audit: <plugin name>
Scope: <files reviewed>
Date: <YYYY-MM-DD>

## HIGH
1. <file>:<line> — <issue>
   <code>
   Fix: <code>

## MEDIUM
...

## LOW / Hardening
...

## Out of scope
- <thing not checked>
```

## References

- Detailed examples of each finding type, before/after: `reference.md`
- Real-world snippets with the fix applied: `examples/`
- WordPress core: [Plugin Security Handbook](https://developer.wordpress.org/plugins/security/)
- Capability map: [Roles and Capabilities](https://wordpress.org/documentation/article/roles-and-capabilities/)

Related Skills

wp-security-secrets

5
from nvdigitalsolutions/mcp-ai-wpoos

Audits WordPress plugin/theme code for secret-handling and credential issues — hardcoded API keys, DB credentials, or signing secrets in source; weak randomness (rand, mt_rand, uniqid) used for security tokens, password reset links, nonces, or session IDs; password storage with md5/sha1/crypt instead of password_hash; insecure cookie flags (missing Secure, HttpOnly, SameSite) on sensitive cookies; secrets logged via error_log / var_dump in production code paths. Use before plugin release, when reviewing auth/registration/login features, when integrating third-party APIs, or when the user mentions "API key", "token", "session", "password reset".

wp-security-deep

5
from nvdigitalsolutions/mcp-ai-wpoos

Deep security audit for WordPress plugin/theme PHP code, covering issues beyond the basic sanitize/escape/nonce checklist — PHP object injection (unserialize), SSRF in remote requests, CSRF on state-changing GET handlers, mass assignment via $_POST loops, insecure file include / template injection, mail header injection, ZipSlip in archive extraction, type-juggling in auth comparisons, and TOCTOU race patterns in option/meta locks. Use after or alongside wp-security-audit when reviewing complex plugins, REST APIs, integrations that fetch remote URLs, file processors, or any code that handles uploads, archives, or self-rolled auth tokens.

wp-i18n-audit

5
from nvdigitalsolutions/mcp-ai-wpoos

Audits WordPress plugin or theme PHP code for internationalization (i18n) correctness — text-domain consistency, use of escaped translation helpers (esc_html__, esc_attr__, esc_html_e), correct placeholder helpers (sprintf with translator comments, _n for plurals, _x for context), no variable text-domains, no concatenation inside __() calls, presence of load_plugin_textdomain or load_theme_textdomain, and matching declared Text Domain in the plugin/theme header. Use before plugin/theme release, when reviewing contributor PRs, when adding new strings, when migrating to a new text domain, or when a translator reports issues with the .pot file.

webapp-testing

5
from nvdigitalsolutions/mcp-ai-wpoos

Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.

web-artifacts-builder

5
from nvdigitalsolutions/mcp-ai-wpoos

Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.

valyu

5
from nvdigitalsolutions/mcp-ai-wpoos

Search the live web and 36+ specialised data sources including SEC filings, PubMed, ChEMBL, clinical trials, FRED economic indicators, and patent databases. Use when current, authoritative, or paywalled data is required.

ui-ux-pro-max

5
from nvdigitalsolutions/mcp-ai-wpoos

AI-powered design intelligence with 67 UI styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 15+ tech stacks. Generates complete design systems for any product type with industry-specific reasoning rules.

theme-factory

5
from nvdigitalsolutions/mcp-ai-wpoos

Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.

slack-gif-creator

5
from nvdigitalsolutions/mcp-ai-wpoos

Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like "make me a GIF of X doing Y for Slack."

skill-creator

5
from nvdigitalsolutions/mcp-ai-wpoos

Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, update or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.

shannon

5
from nvdigitalsolutions/mcp-ai-wpoos

Autonomous AI security pen testing. Executes real exploits against web applications to find SQL injection, XSS, SSRF, authentication flaws, and IDOR vulnerabilities. Reports only confirmed, reproducible findings — no false positives.

remotion

5
from nvdigitalsolutions/mcp-ai-wpoos

Create programmatic videos using React and Remotion. Translate natural language descriptions into working Remotion components for product demos, release announcements, explainer videos, and animated content.