Agent skill

Wp Security Review

by jorgerosal in jorgerosal/wordpress-skills

WordPress security code review and vulnerability detection. An agent skill from jorgerosal/wordpress-skills.

MITAuto-check passedSecurity

Install Wp Security Review

skills CLI
$ npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install jorgerosal/wordpress-skills wp-security-review --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/jorgerosal/wordpress-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/claude-skills/wp-security-review .claude/skills/wp-security-review && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
wp-security-review
GitHub stars
102
Token cost
~6.4k tokens
SKILL.md length
1,114 words
Files
6 (incl. references)
Skills in repo
35
Repo updated
First seen
Licence
MIT

At a glance

WordPress security code review and vulnerability detection. An agent skill from jorgerosal/wordpress-skills.

  • Works in 6 steps: Identify file type and apply relevant… → Scan for CRITICAL patterns first (SQL… → Check WARNING patterns (authorization… → …
  • Reviewing WordPress PHP code for security issues
  • SKILL.md covers Overview, When to Use, Code Review Workflow and File-Type Specific Checks, plus 5 more sections
  • Reaches api.wordpress.org; needs AUTH_KEY and SECURE_AUTH_KEY

What it does

Wp Security Review is an agent skill from jorgerosal/wordpress-skills. WordPress security code review and vulnerability detection. Use when reviewing WordPress PHP code for security issues, auditing themes/plugins for vulnerabilities, checking code before launch, analyzing AJAX/REST handlers for exploits, detecting XSS, SQL injection, CSRF, or authorization bypass, or when user mentions "security review", "vulnerability", "XSS", "SQL injection", "CSRF", "nonce", "sanitization", "escaping", "capability check", "privilege escalation", "file upload security", "insecure code", "auth…

Its SKILL.md is about 6.4k tokens, which your agent loads only when the skill is triggered. The skill folder holds 6 other files, including reference files (for example `references/auth-patterns.md`, `references/escaping-guide.md` and `references/nonce-csrf-guide.md`).

It sits in Security, covering Web application vulnerabilities, Security review and File uploads and storage. It works with WordPress and PHP. The repository describes itself as: ✅ 🎉 Claude skills and Codex skills for Wordpress development❗️. The licence is MIT.

When your agent uses it

  • Reviewing WordPress PHP code for security issues
  • Auditing themes/plugins for vulnerabilities
  • Checking code before launch
  • Analyzing AJAX/REST handlers for exploits

Example prompts

  • “security review”
  • “vulnerability”
  • “SQL injection”
  • “/wp-security-review”

Workflow steps

6 steps, taken from the first numbered list in SKILL.md.

  1. Identify file type and apply relevant checks below
  2. Scan for CRITICAL patterns first (SQL injection in public code, XSS on unescaped output, missing nonces on state-changing operations, file…
  3. Check WARNING patterns (authorization issues, admin-only XSS, incomplete validation)
  4. Note INFO improvements (defense-in-depth, security constants, hardening opportunities)
  5. Apply context-aware severity adjustment (admin-only code = lower severity, public-facing code = highest severity, WP-CLI/cron = nonces not…
  6. Report with line numbers using output format below

What it can do on your machine

Read from SKILL.md and the folder at commit 8c96442. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are php, bash and markdown).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • api.wordpress.org

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names these keys or tokens, usually read from environment variables:

    • AUTH_KEY
    • SECURE_AUTH_KEY
    • LOGGED_IN_KEY
    • NONCE_KEY

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Wp Security Review loads about 6.4k tokens when it runs, and up to ~39k if it reads all its reference files. Until then it costs about 176 tokens; SKILL.md has 1,114 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~176
When it runs · the whole SKILL.md, loaded when a task matches
~6.4k
With references · SKILL.md plus every file in references/, read only if the agent opens them
~39k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from jorgerosal/wordpress-skills at commit 8c96442, republished under its MIT licence (© jorgerosal). 1,114 words, ~6,403 tokens.

Download SKILL.mdSave it as .claude/skills/wp-security-review/SKILL.md (or your agent's skills folder). This skill also uses 5 other files; get the full folder from GitHub.
name
wp-security-review
description
WordPress security code review and vulnerability detection. Use when reviewing WordPress PHP code for security issues, auditing themes/plugins for vulnerabilities, checking code before launch, analyzing AJAX/REST handlers for exploits, detecting XSS, SQL injection, CSRF, or authorization bypass, or when user mentions "security review", "vulnerability", "XSS", "SQL injection", "CSRF", "nonce", "sanitization", "escaping", "capability check", "privilege escalation", "file upload security", "insecure code", "auth bypass", or "security audit". Detects vulnerabilities in input handling, output escaping, authorization, nonces, database queries, file uploads, and dangerous functions.

WordPress Security Review Skill

Overview

Systematic security code review for WordPress themes, plugins, and custom code. Core principle: Three-pillar security model: (1) Sanitize input early, (2) Validate authorization (nonces + capabilities), (3) Escape output late. Scan critical issues first (SQL injection in public code, XSS on unescaped output, missing nonces on state-changing operations), then warnings, then info-level improvements. Report with line numbers, severity, CWE references, and BAD/GOOD code pairs.

When to Use

Use when:

  • Security audit of WordPress theme or plugin
  • Pre-launch security review
  • Investigating reported vulnerability
  • Reviewing form handlers, AJAX endpoints, or REST API routes
  • Analyzing user input processing or output rendering
  • Code review for exploitable patterns (XSS, SQL injection, CSRF, auth bypass)

Don't use for:

  • Server hardening or infrastructure security (Apache/nginx config, firewall rules)
  • Web Application Firewall (WAF) configuration
  • SSL/TLS certificate management
  • Performance-only audits (use wp-performance-review)
  • General non-WordPress PHP security

Code Review Workflow

  1. Identify file type and apply relevant checks below
  2. Scan for CRITICAL patterns first (SQL injection in public code, XSS on unescaped output, missing nonces on state-changing operations, file upload without validation)
  3. Check WARNING patterns (authorization issues, admin-only XSS, incomplete validation)
  4. Note INFO improvements (defense-in-depth, security constants, hardening opportunities)
  5. Apply context-aware severity adjustment (admin-only code = lower severity, public-facing code = highest severity, WP-CLI/cron = nonces not needed, REST API = different auth model)
  6. Report with line numbers using output format below

File-Type Specific Checks

Plugin/Theme PHP Files (functions.php, plugin.php, *.php)

Scan for:

  • Missing defined( 'ABSPATH' ) || exit; at top → WARNING: Direct file access possible
  • eval( → CRITICAL: Code injection vector (CWE-95)
  • exec(, shell_exec(, system(, passthru( → CRITICAL: Command injection if user input present (CWE-78)
  • base64_decode( $_ → CRITICAL: Possible encoded payload execution
  • unserialize( $_ → CRITICAL: Object injection (CWE-502)
  • $_GET[, $_POST[, $_REQUEST[ without sanitize_* → WARNING: Unsanitized input (CWE-20)
  • include $_, require $_, include_once $_, require_once $_ → CRITICAL: Path traversal/inclusion (CWE-22)
  • Raw move_uploaded_file( → CRITICAL: Use wp_handle_upload() instead
Form Handlers (admin-post.php, admin_post_* hooks)

Scan for three-step pattern (ALL must be present):

  1. Nonce verification → wp_verify_nonce( $_POST['nonce_field'], 'action_name' ) or check_admin_referer() → CRITICAL if missing (CWE-352)
  2. Capability check → current_user_can( 'capability' ) → CRITICAL if missing (CWE-862)
  3. Input sanitization → sanitize_text_field(), sanitize_email(), etc. → WARNING if missing (CWE-20)

Missing ANY step = vulnerability. State-changing operations MUST have all three.

AJAX Handlers (wp_ajax_*, wp_ajax_nopriv_* hooks)

Scan for:

  • check_ajax_referer( 'action_name', 'nonce_field' ) → CRITICAL if missing on state-changing operations (CWE-352)
  • current_user_can( 'capability' ) → CRITICAL if missing when capability required (CWE-862)
  • wp_ajax_nopriv_* without nonce → CRITICAL: Public endpoint with no CSRF protection
  • Input from $_POST without sanitize_* → WARNING: Unsanitized input (CWE-20)
  • wp_send_json() without escaping when echoing user input → WARNING: JSON injection
REST API Endpoints (register_rest_route)

Scan for:

  • Missing permission_callback → CRITICAL: WordPress 5.5+ requires this (CWE-862)
  • 'permission_callback' => '__return_true' on write operations → CRITICAL: Allows unauthorized access
  • 'permission_callback' => '__return_true' on public read endpoints → OK (not a vulnerability)
  • Missing current_user_can() in permission callback → WARNING: May allow privilege escalation (CWE-863)
  • Input validation missing on $request->get_param() → WARNING: Unsanitized input (CWE-20)
  • REST authentication via X-WP-Nonce header or application passwords → INFO: Verify client sends header
Database Code ($wpdb->query, $wpdb->get_results, etc.)

Scan for:

  • $wpdb->query( or $wpdb->get_results( with string concatenation or interpolation → CRITICAL: SQL injection (CWE-89)
  • Missing $wpdb->prepare() when user input present → CRITICAL: SQL injection (CWE-89)
  • $wpdb->prepare( "... LIKE '%{$term}%'" ) → WARNING: Should use $wpdb->esc_like() first
  • Double-preparing (preparing already-prepared strings) → WARNING: Escapes escape characters
  • Direct use of {$wpdb->prefix} in queries → OK (not a vulnerability, standard pattern)

Safe pattern: $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE ID = %d", $post_id ) )

Template/Output Files (*.php in theme, templates)

Scan for:

  • echo $ or print $ without escaping function → CRITICAL: XSS (CWE-79)
  • <input value="<?php echo $ → CRITICAL: Use esc_attr() (CWE-79)
  • <a href="<?php echo $ → CRITICAL: Use esc_url() (CWE-79)
  • <script> blocks with PHP variables → CRITICAL: Use esc_js() or wp_localize_script() (CWE-79)
  • Rich HTML output without wp_kses() or wp_kses_post() → WARNING: May allow unsafe tags

Safe pattern: Always escape at point of output, not at input or storage.

File Upload Handlers

Scan for:

  • move_uploaded_file( instead of wp_handle_upload() → CRITICAL: Bypasses WP security checks (CWE-434)
  • Missing current_user_can() check → CRITICAL: Unauthorized file upload (CWE-862)
  • No MIME type validation → WARNING: May allow executable uploads
  • No file size limit → INFO: Could enable DoS
  • Missing wp_check_filetype_and_ext() → WARNING: File type spoofing possible (CWE-434)
Show full SKILL.md (455 more words)Show less
JavaScript Files (*.js, *.jsx)

Scan for:

  • fetch() or $.ajax() to REST API without X-WP-Nonce header → WARNING: CSRF vulnerability (CWE-352)
  • $.post( ajaxurl, ...) without nonce in data → WARNING: CSRF vulnerability (CWE-352)
  • innerHTML = userInput or dangerouslySetInnerHTML → CRITICAL: DOM-based XSS (CWE-79)
  • eval( → CRITICAL: Code injection vector (CWE-95)

Search Patterns for Quick Detection (SEC-20)

bash
# CRITICAL: SQL injection patterns
grep -rn "\$wpdb->query(" . | grep -v "prepare"
grep -rn "\$wpdb->get_results(" . | grep -v "prepare"
grep -rn "\$wpdb->get_var(" . | grep -v "prepare"
grep -rn "\$wpdb->get_row(" . | grep -v "prepare"

# CRITICAL: XSS (unescaped output)
grep -rn "echo \$_" .
grep -rn "print \$_" .
grep -rn "<?php echo \$" . | grep -v "esc_"

# CRITICAL: Dangerous functions
grep -rn "eval(" .
grep -rn "exec(" .
grep -rn "shell_exec(" .
grep -rn "system(" .
grep -rn "passthru(" .
grep -rn "base64_decode(\$_" .
grep -rn "unserialize(\$_" .

# CRITICAL: File upload without WP functions
grep -rn "move_uploaded_file(" .

# CRITICAL: REST API without permission_callback
grep -rn "register_rest_route" . | grep -v "permission_callback"

# CRITICAL: Path traversal / dynamic includes
grep -rn "include \$_" .
grep -rn "require \$_" .
grep -rn "include_once \$_" .
grep -rn "require_once \$_" .

# WARNING: Unsanitized input usage
grep -rn "\$_GET\[" . | grep -v "sanitize_"
grep -rn "\$_POST\[" . | grep -v "sanitize_"
grep -rn "\$_REQUEST\[" . | grep -v "sanitize_"

# WARNING: Missing nonces on public AJAX
grep -rn "wp_ajax_nopriv_" .

# WARNING: Missing capability checks
grep -rn "admin_post_" . | grep -v "current_user_can"
grep -rn "wp_ajax_" . | grep -v "current_user_can"

# INFO: Missing ABSPATH check
grep -rn "<?php" . | head -20 | grep -v "ABSPATH"

# INFO: Security constants missing (check wp-config.php)
grep -n "DISALLOW_FILE_EDIT" wp-config.php
grep -n "FORCE_SSL_ADMIN" wp-config.php
grep -n "DISALLOW_UNFILTERED_HTML" wp-config.php

Platform Context

Different hosting environments provide varying security layers:

Managed WordPress Hosts (WP Engine, Pantheon, Pressable, WordPress VIP, etc.):

  • Often have platform-level WAF and malware scanning
  • May block certain dangerous functions (eval, exec) at platform level
  • Check host documentation for additional security requirements
  • Code should still be written securely for portability

WordPress VIP:

  • Strict code review requirements
  • Many dangerous functions prohibited
  • Additional security helpers available (wpcom_vip_* functions)
  • See VIP documentation for required patterns

Self-Hosted / Standard Hosting:

  • No platform-level security beyond what code implements
  • More responsibility for secure coding practices
  • Ensure all three pillars (sanitize, authorize, escape) are implemented

Quick Reference: Critical Security Patterns

XSS Prevention (Output Escaping)
php
// ❌ CRITICAL: Unescaped output (CWE-79)
<h1><?php echo $_GET['title']; ?></h1>
<input value="<?php echo $user_input; ?>">
<a href="<?php echo $url; ?>">Link</a>
<script>var data = "<?php echo $json; ?>";</script>

// ✅ GOOD: Context-appropriate escaping
<h1><?php echo esc_html( $_GET['title'] ); ?></h1>
<input value="<?php echo esc_attr( $user_input ); ?>">
<a href="<?php echo esc_url( $url ); ?>">Link</a>
<script>var data = <?php echo wp_json_encode( $data ); ?>;</script>

// ❌ CRITICAL: Early escaping (wrong pattern)
$title = esc_html( $_POST['title'] );
update_post_meta( $post_id, 'title', $title ); // Stores HTML entities

// ✅ GOOD: Late escaping (correct pattern)
$title = sanitize_text_field( $_POST['title'] );
update_post_meta( $post_id, 'title', $title );
echo '<h1>' . esc_html( get_post_meta( $post_id, 'title', true ) ) . '</h1>';

// ✅ GOOD: Rich HTML with wp_kses_post() for trusted content
echo wp_kses_post( $content ); // Allows safe HTML tags only

// ✅ GOOD: Custom allowed tags with wp_kses()
$allowed_tags = array(
    'a' => array( 'href' => array(), 'title' => array() ),
    'br' => array(),
    'strong' => array(),
);
echo wp_kses( $user_content, $allowed_tags );
SQL Injection Prevention
php
// ❌ CRITICAL: Direct interpolation (CWE-89)
$results = $wpdb->get_results( "SELECT * FROM {$wpdb->users} WHERE ID = $user_id" );
$results = $wpdb->query( "UPDATE {$wpdb->posts} SET post_title = '$title'" );

// ✅ GOOD: Use $wpdb->prepare() with placeholders
$results = $wpdb->get_results( $wpdb->prepare(
    "SELECT * FROM {$wpdb->users} WHERE ID = %d",
    $user_id
) );

$wpdb->query( $wpdb->prepare(
    "UPDATE {$wpdb->posts} SET post_title = %s WHERE ID = %d",
    $title,
    $post_id
) );

// ✅ GOOD: Multiple placeholders
$wpdb->get_results( $wpdb->prepare(
    "SELECT * FROM {$wpdb->posts} WHERE post_status = %s AND post_author = %d",
    $status,
    $author_id
) );

// ❌ WARNING: LIKE without esc_like()
$wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE post_title LIKE '%%{$term}%%'" );

// ✅ GOOD: Use esc_like() for LIKE queries
$like_term = '%' . $wpdb->esc_like( $term ) . '%';
$wpdb->get_results( $wpdb->prepare(
    "SELECT * FROM {$wpdb->posts} WHERE post_title LIKE %s",
    $like_term
) );

// ✅ OK: Hardcoded SQL without user input (admin context)
if ( current_user_can( 'manage_options' ) ) {
    $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name = 'temp_setting'" );
}
CSRF/Nonce Verification (Three-Step Pattern)
php
// ❌ CRITICAL: Missing nonce verification (CWE-352)
add_action( 'admin_post_save_settings', function() {
    update_option( 'my_setting', $_POST['value'] );
} );

// ❌ CRITICAL: Missing capability check (CWE-862)
add_action( 'admin_post_save_settings', function() {
    check_admin_referer( 'save_settings_action', 'settings_nonce' );
    update_option( 'my_setting', $_POST['value'] );
} );

// ✅ GOOD: Complete three-step pattern
add_action( 'admin_post_save_settings', function() {
    // Step 1: Verify nonce
    if ( ! isset( $_POST['settings_nonce'] ) ||
         ! wp_verify_nonce( $_POST['settings_nonce'], 'save_settings_action' ) ) {
        wp_die( 'Invalid nonce' );
    }

    // Step 2: Check capability
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die( 'Insufficient permissions' );
    }

    // Step 3: Sanitize input
    $value = sanitize_text_field( $_POST['value'] );
    update_option( 'my_setting', $value );

    wp_redirect( admin_url( 'admin.php?page=settings&updated=true' ) );
    exit;
} );

// ✅ GOOD: Form with nonce field
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
    <?php wp_nonce_field( 'save_settings_action', 'settings_nonce' ); ?>
    <input type="hidden" name="action" value="save_settings">
    <input type="text" name="value" value="<?php echo esc_attr( $current_value ); ?>">
    <?php submit_button(); ?>
</form>

// ✅ GOOD: AJAX with nonce
add_action( 'wp_ajax_update_user_meta', function() {
    check_ajax_referer( 'update_meta_nonce', 'nonce' );

    if ( ! current_user_can( 'edit_users' ) ) {
        wp_send_json_error( 'Insufficient permissions' );
    }

    $user_id = absint( $_POST['user_id'] );
    $value   = sanitize_text_field( $_POST['value'] );

    update_user_meta( $user_id, 'custom_field', $value );
    wp_send_json_success();
} );

// JavaScript for AJAX nonce
jQuery.post( ajaxurl, {
    action: 'update_user_meta',
    nonce: myAjax.nonce, // From wp_localize_script()
    user_id: 123,
    value: 'new value'
} );
Authorization/Capability Checks
php
// ❌ CRITICAL: No capability check (CWE-862)
add_action( 'admin_post_delete_user', function() {
    wp_delete_user( $_POST['user_id'] );
} );

// ❌ WARNING: Incorrect capability (CWE-863)
add_action( 'admin_post_delete_user', function() {
    if ( ! current_user_can( 'edit_posts' ) ) { // Wrong capability!
        wp_die( 'Insufficient permissions' );
    }
    wp_delete_user( $_POST['user_id'] );
} );

// ✅ GOOD: Correct capability check
add_action( 'admin_post_delete_user', function() {
    if ( ! current_user_can( 'delete_users' ) ) {
        wp_die( 'Insufficient permissions' );
    }

    check_admin_referer( 'delete_user_action', 'delete_nonce' );
    wp_delete_user( absint( $_POST['user_id'] ) );
} );

// ✅ GOOD: Check user can edit specific post
if ( ! current_user_can( 'edit_post', $post_id ) ) {
    wp_die( 'You cannot edit this post' );
}

// ✅ GOOD: REST API permission callback
register_rest_route( 'myapp/v1', '/users/(?P<id>\d+)', array(
    'methods'             => 'DELETE',
    'callback'            => 'myapp_delete_user',
    'permission_callback' => function( $request ) {
        return current_user_can( 'delete_users' );
    },
) );

// ✅ OK: __return_true for public read endpoint
register_rest_route( 'myapp/v1', '/posts', array(
    'methods'             => 'GET',
    'callback'            => 'myapp_get_posts',
    'permission_callback' => '__return_true', // Public read is OK
) );
File Upload Security
php
// ❌ CRITICAL: Direct move_uploaded_file() (CWE-434)
if ( isset( $_FILES['upload'] ) ) {
    move_uploaded_file( $_FILES['upload']['tmp_name'], '/uploads/' . $_FILES['upload']['name'] );
}

// ❌ CRITICAL: No capability check (CWE-862)
$file = wp_handle_upload( $_FILES['upload'], array( 'test_form' => false ) );

// ✅ GOOD: Complete file upload security
add_action( 'admin_post_upload_file', function() {
    // Step 1: Verify nonce
    check_admin_referer( 'upload_file_action', 'upload_nonce' );

    // Step 2: Check capability
    if ( ! current_user_can( 'upload_files' ) ) {
        wp_die( 'Insufficient permissions' );
    }

    // Step 3: Validate file exists
    if ( ! isset( $_FILES['upload'] ) || UPLOAD_ERR_OK !== $_FILES['upload']['error'] ) {
        wp_die( 'File upload failed' );
    }

    // Step 4: Use wp_handle_upload() (validates MIME type, extension)
    require_once( ABSPATH . 'wp-admin/includes/file.php' );
    $file = wp_handle_upload( $_FILES['upload'], array( 'test_form' => false ) );

    if ( isset( $file['error'] ) ) {
        wp_die( 'Upload error: ' . esc_html( $file['error'] ) );
    }

    // Step 5: Store file info
    $attachment_id = wp_insert_attachment( array(
        'post_title'     => sanitize_file_name( $_FILES['upload']['name'] ),
        'post_mime_type' => $file['type'],
        'post_status'    => 'inherit',
    ), $file['file'] );
} );

// ✅ GOOD: Restrict allowed MIME types
add_filter( 'upload_mimes', function( $mimes ) {
    // Remove potentially dangerous types
    unset( $mimes['exe'] );
    unset( $mimes['php'] );

    // Add custom allowed types if needed
    $mimes['svg'] = 'image/svg+xml';
    return $mimes;
} );

// ✅ GOOD: File type validation
$filetype = wp_check_filetype_and_ext( $_FILES['upload']['tmp_name'], $_FILES['upload']['name'] );
if ( ! $filetype['ext'] ) {
    wp_die( 'Invalid file type' );
}
Object Injection Prevention
php
// ❌ CRITICAL: Unserialize user input (CWE-502)
$data = unserialize( $_POST['data'] );

// ❌ CRITICAL: Unserialize from cookie
$data = unserialize( $_COOKIE['cart_data'] );

// ✅ GOOD: Use JSON instead of serialize for user-facing data
$data = json_decode( $_POST['data'], true );
if ( JSON_ERROR_NONE !== json_last_error() ) {
    wp_die( 'Invalid JSON' );
}

// ✅ OK: Unserialize trusted internal data only
$data = get_option( 'my_internal_setting' ); // Already unserialized by WP
$meta = get_post_meta( $post_id, 'my_field', true ); // Already unserialized by WP
Dangerous Functions
php
// ❌ CRITICAL: eval() with user input (CWE-95)
eval( $_POST['code'] );

// ❌ CRITICAL: exec() with user input (CWE-78)
exec( 'ls -la ' . $_GET['dir'] );

// ❌ CRITICAL: shell_exec() with user input
$output = shell_exec( 'grep ' . $_POST['search'] . ' file.txt' );

// ✅ GOOD: Avoid eval(), exec(), shell_exec() entirely in web context
// If absolutely necessary, whitelist input strictly
$allowed_dirs = array( 'uploads', 'cache' );
$dir = sanitize_key( $_GET['dir'] );
if ( in_array( $dir, $allowed_dirs, true ) ) {
    exec( 'ls -la ' . escapeshellarg( $dir ), $output );
}
Direct File Access Prevention
php
// ❌ WARNING: Missing ABSPATH check
<?php
// Plugin code here without security check

// ✅ GOOD: ABSPATH check at top of every PHP file
<?php
defined( 'ABSPATH' ) || exit;

// Plugin code here

// ✅ GOOD: Alternative pattern
if ( ! defined( 'ABSPATH' ) ) {
    die( 'Direct access not permitted.' );
}
Input Sanitization
php
// ❌ WARNING: No sanitization (CWE-20)
$title = $_POST['title'];
update_post_meta( $post_id, 'title', $title );

// ✅ GOOD: Sanitize based on expected data type
$title       = sanitize_text_field( $_POST['title'] );
$email       = sanitize_email( $_POST['email'] );
$url         = sanitize_url( $_POST['url'] );
$filename    = sanitize_file_name( $_POST['filename'] );
$key         = sanitize_key( $_POST['key'] );
$int         = absint( $_POST['count'] );
$float       = floatval( $_POST['price'] );
$textarea    = sanitize_textarea_field( $_POST['description'] );

// ✅ GOOD: Array of integers
$ids = array_map( 'absint', (array) $_POST['ids'] );

// ✅ GOOD: Validate against whitelist
$allowed_types = array( 'post', 'page', 'product' );
$type = sanitize_key( $_POST['type'] );
if ( ! in_array( $type, $allowed_types, true ) ) {
    wp_die( 'Invalid type' );
}

Severity Definitions (SEC-22)

SeverityDescription
CRITICALExploitable without authentication OR leads to data breach, RCE, or privilege escalation. Examples: SQL injection on public endpoint, XSS on unescaped output, missing nonce on admin action, file upload without capability check, eval() with user input
WARNINGExploitable with authentication OR requires specific conditions. Examples: XSS in admin-only code, missing capability check on logged-in action, SQL injection in admin context, incomplete input sanitization
INFODefense-in-depth improvement or hardening opportunity. Examples: missing ABSPATH check, security constants not set in wp-config.php, missing security headers

Context adjustments:

  • Admin-only code: Lower severity by one level (CRITICAL → WARNING)
  • WP-CLI/cron context: Nonces not required (no CSRF risk)
  • REST API: Different auth model (permission_callback, not nonces)
  • Public-facing code: Highest severity (no authentication barrier)

Output Format (SEC-23)

Group findings by FILE and severity level:

markdown
## Security Review: [filename]

### CRITICAL Issues
**Line X** | CWE-79 | XSS via unescaped output
- **Issue**: User input echoed directly without escaping
- **Risk**: Allows attackers to inject malicious JavaScript
- **Context**: Public-facing template (HIGH severity)

❌ **BAD:**
```php
<h1><?php echo $_GET['title']; ?></h1>

✅ GOOD:

php
<h1><?php echo esc_html( $_GET['title'] ); ?></h1>
WARNING Issues

Line Y | CWE-352 | Missing nonce verification

  • Issue: Form handler missing wp_verify_nonce()
  • Risk: State can be changed via CSRF attack
  • Context: Admin-only (reduced severity)

[BAD/GOOD code pair]

INFO Recommendations

Line Z | Defense-in-depth | Missing ABSPATH check

  • Add defined( 'ABSPATH' ) || exit; at top of file
Summary
  • Total: 1 CRITICAL, 1 WARNING, 1 INFO
  • Risk level: HIGH (exploitable CRITICAL issue present)
  • Immediate action: Fix line X (unescaped output)

## Common Mistakes (SEC-24)

When performing security reviews, avoid these false positives:

| Mistake | Why It's Wrong | Context |
|---------|----------------|---------|
| Flagging missing nonces in WP-CLI commands | WP-CLI runs server-side, no CSRF risk | Nonces only needed for HTTP requests |
| Flagging `wp_kses_post()` as insufficient escaping | `wp_kses_post()` is valid for trusted content with safe HTML | Allows `<p>`, `<a>`, `<strong>`, etc. - appropriate for post content |
| Flagging admin-only `$wpdb->query()` with hardcoded SQL | No user input = no injection risk | Review for user input presence, not just prepare() usage |
| Flagging `current_user_can()` inside REST `permission_callback` as redundant | This is the CORRECT location for capability checks in REST API | REST API auth model, not redundant |
| Flagging `esc_html( get_the_title() )` as double-escaping | WP core sanitizes on storage, but late escaping is still best practice | Not technically double-escaping, WP stores raw data |
| Flagging `update_option()` in admin context as vulnerability | Options API is safe for admin use | Check capability first, then update_option() is fine |
| Flagging REST API with `__return_true` permission on GET endpoints | Public read access is often intentional | Only flag on write operations (POST, PUT, DELETE) |
| Flagging `sanitize_callback` in `register_setting()` as missing sanitization | Settings API handles sanitization via callback | This is the correct pattern for Settings API |

## Security Constants Check (SEC-19)

Review `wp-config.php` for security hardening constants:

```php
// ✅ GOOD: Disable file editor (INFO-level recommendation)
define( 'DISALLOW_FILE_EDIT', true );

// ✅ GOOD: Force SSL for admin (INFO if SSL available)
define( 'FORCE_SSL_ADMIN', true );

// ✅ GOOD: Disable unfiltered HTML for all users including admins
define( 'DISALLOW_UNFILTERED_HTML', true );

// ✅ GOOD: Disable plugin/theme installation (high-security environments)
define( 'DISALLOW_FILE_MODS', true );

// ✅ GOOD: Custom authentication keys (CRITICAL if using defaults)
define( 'AUTH_KEY',         'put-unique-phrase-here' ); // Use https://api.wordpress.org/secret-key/1.1/salt/
define( 'SECURE_AUTH_KEY',  'put-unique-phrase-here' );
define( 'LOGGED_IN_KEY',    'put-unique-phrase-here' );
define( 'NONCE_KEY',        'put-unique-phrase-here' );
define( 'AUTH_SALT',        'put-unique-phrase-here' );
define( 'SECURE_AUTH_SALT', 'put-unique-phrase-here' );
define( 'LOGGED_IN_SALT',   'put-unique-phrase-here' );
define( 'NONCE_SALT',       'put-unique-phrase-here' );

Deep-Dive References

Load these references for comprehensive vulnerability patterns and examples:

TaskReference to Load
Comprehensive vulnerability catalog with CWE mappingsreferences/vulnerability-patterns.md
Output escaping patterns and context-specific functionsreferences/escaping-guide.md
Input sanitization patterns by data typereferences/sanitization-guide.md
Authorization and capability check patternsreferences/auth-patterns.md
Nonce generation, verification, and CSRF preventionreferences/nonce-csrf-guide.md

Note: For standard security reviews, this SKILL.md contains all patterns needed. Load references when you need comprehensive CWE catalogs, edge cases, or platform-specific security requirements (WordPress VIP, etc.).

© jorgerosal, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 5 other files (references) in claude-skills/wp-security-review of jorgerosal/wordpress-skills.

  • SKILL.md
  • references/auth-patterns.md
  • references/escaping-guide.md
  • references/nonce-csrf-guide.md
  • references/sanitization-guide.md
  • references/vulnerability-patterns.md

Open the folder on GitHubat commit 8c96442

Compare with similar skills

Wp Security Review next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Wp Security Review compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Wp Security Review this skilljorgerosal/wordpress-skills102—~6.4kAutomated safety check: PassMIT
Code Audit3stoneBrother/code-audit8921 repos~2.7kAutomated safety check: PassNone
Security Reviewgithub/awesome-copilot40k1 repos~2.3kAutomated safety check: NotesMIT
Wordpresssickn33/agentic-awesome-skills47k2 repos~348Automated safety check: PassMIT
Php Wordpress Audit0xShe/PHP-Code-Audit-Skill4021 repos~666Automated safety check: PassNone
Security Reviewtrycompai/comp2k—~853Automated safety check: PassAGPL-3.0

Similar skills

  • Code Audit

    3stoneBrother/code-audit

    Professional code security audit skill covering 55+ vulnerability types.

    892 GitHub starsUsed in 1 repo~2.7k tokens
    SecurityAuto-check passed
  • Security Review

    github/awesome-copilot

    Official

    AI-powered codebase security scanner that reasons about code like a security researcher — tracing data flows, understanding component interactions, and catching vulnerabilities that pattern-matching…

    40k GitHub starsUsed in 1 repo~2.3k tokens
    SecurityAuto-check: notes
  • Wordpress

    sickn33/agentic-awesome-skills

    Complete WordPress development workflow covering theme development, plugin creation, WooCommerce integration, performance optimization, and security hardening.

    47k GitHub starsUsed in 2 repos~348 tokens
    SecurityAuto-check passed
  • Php Wordpress Audit

    0xShe/PHP-Code-Audit-Skill

    WordPress 框架特效安全审计工具。针对 WordPress 常见 nonce/capability/checkadminreferer、AJAX action、escape/sanitize、重定向、安全上传与远程请求等机制进行白盒静态审计,并映射到通用漏洞类型体系(AUTH/CSRF/XSS/SQL/CFG/SSRF 等)。

    402 GitHub starsUsed in 1 repo~666 tokens
    SecurityAuto-check passed
  • Security Review

    trycompai/comp

    Check code for the most common, high-risk security vulnerabilities (broken access control, tenant isolation, injection, secrets, SSRF, auth/session, unsafe file handling, mass assignment) before it…

    2k GitHub stars~853 tokensUpdated 2 days ago
    SecurityAuto-check passed
  • Wordpress Plugin Core

    secondsky/claude-skills

    WordPress plugin development with hooks, security, REST API, custom post types.

    227 GitHub stars~4.6k tokensUpdated 11 days ago
    SecurityAuto-check passed

More from jorgerosal/wordpress-skills

All 35 skills in this repo
  • Wp Accessibility Review

    jorgerosal/wordpress-skills

    WordPress accessibility review for themes, blocks, plugins, and admin interfaces.

    102 GitHub stars~1.1k tokensUpdated 4 mo ago
    Auto-check passed
  • Wp Acf And Content Modeling

    jorgerosal/wordpress-skills

    WordPress ACF and content modeling review. An agent skill from jorgerosal/wordpress-skills.

    102 GitHub stars~3.2k tokensUpdated 4 mo ago
    Auto-check passed
  • Wp Admin UI Development

    jorgerosal/wordpress-skills

    WordPress admin UI review and development guidance. An agent skill from jorgerosal/wordpress-skills.

    102 GitHub stars~1.2k tokensUpdated 4 mo ago
    Auto-check passed
  • Wp CI CD And Release Engineering

    jorgerosal/wordpress-skills

    WordPress CI/CD and release engineering review guidance. An agent skill from jorgerosal/wordpress-skills.

    102 GitHub stars~1.7k tokensUpdated 4 mo ago
    Auto-check passed
  • Wp Headless And Wpgraphql

    jorgerosal/wordpress-skills

    Headless WordPress and WPGraphQL review guidance. An agent skill from jorgerosal/wordpress-skills.

    102 GitHub stars~1.7k tokensUpdated 4 mo ago
    Auto-check passed
  • Wp Migration Upgrade Review

    jorgerosal/wordpress-skills

    WordPress migration and upgrade review. An agent skill from jorgerosal/wordpress-skills.

    102 GitHub stars~1.1k tokensUpdated 4 mo ago
    Auto-check passed

Works with

Categories

Questions about Wp Security Review

What does Wp Security Review do?

WordPress security code review and vulnerability detection. An agent skill from jorgerosal/wordpress-skills. Wp Security Review is an agent skill from jorgerosal/wordpress-skills. WordPress security code review and vulnerability detection.

When should I use Wp Security Review?

Wp Security Review fits situations like: reviewing WordPress PHP code for security issues; auditing themes/plugins for vulnerabilities; checking code before launch; analyzing AJAX/REST handlers for exploits.

How do I install Wp Security Review in Claude Code?

Run `npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a claude-code`. Or copy the skill folder (claude-skills/wp-security-review in jorgerosal/wordpress-skills) into .claude/skills/wp-security-review in your project. Claude Code loads it when a task matches its description.

How do I install Wp Security Review in Codex?

Run `npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a codex`. Or copy the skill folder (claude-skills/wp-security-review in jorgerosal/wordpress-skills) into .agents/skills/wp-security-review in your project. Codex loads it when a task matches its description.

Can I use Wp Security Review in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/wp-security-review, .gemini/skills/wp-security-review, .github/skills/wp-security-review and .opencode/skills/wp-security-review in your project.

What does Wp Security Review need to run?

Going by SKILL.md and its folder, Wp Security Review needs credentials named AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY and NONCE_KEY.

Does Wp Security Review access the network?

SKILL.md names 1 domain. In commands or code: api.wordpress.org; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.

Is Wp Security Review safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Wp Security Review use?

Wp Security Review is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Wp Security Review use?

About 6.4k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 33k tokens, read only when the agent opens those files.

What are the alternatives to Wp Security Review?

Skills that share tags, products or a category with Wp Security Review: Code Audit (3stoneBrother/code-audit, 892 stars), Security Review (github/awesome-copilot, 40k stars), Wordpress (sickn33/agentic-awesome-skills, 47k stars) and Php Wordpress Audit (0xShe/PHP-Code-Audit-Skill, 402 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Wp Security Review?

jorgerosal (a GitHub user) maintains it in jorgerosal/wordpress-skills, which has 102 GitHub stars. The repository holds 35 skills in this directory. The repository was last updated on June 7, 2026.

Source: jorgerosal/wordpress-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.