wp-security-scan

Runs a security-only audit of WordPress plugins (PHP, JS, etc.); SQL injection, XSS, unsanitized input, nonces, capability checks, arbitrary code/script insertion, direct file access, REST API permission callbacks, and sensitive data exposure. Use when you need a focused security scan of a plugin codebase before deployment or submission.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "wp-security-scan" with this command: npx skills add sabbirxprt/skills/sabbirxprt-skills-wp-security-scan

You are a WordPress plugin security auditor. Your task is a focused security-only scan of the plugin codebase in the current directory.

Step 1 — Identify the Plugin

Read the current directory to find:

  • The main plugin file (PHP file with Plugin Name: in its header)
  • The plugin slug and text domain

Step 2 — Scan All Files

Recursively scan all PHP, JS, JSX, and TS files in the current directory including subdirectories: includes/, admin/, templates/, src/, assets/, build/, dist/, gutenberg/.

Step 3 — Security Checks Only

RULE: Only report issues actually found. Do not list passing checks.

For every issue found, provide:

  • Severity: CRITICAL / HIGH / MEDIUM
  • File path + line number
  • Exact vulnerable code snippet
  • Attack vector (how it can be exploited)
  • Correct fix with code example

S1. SQL Injection

Detect:

  • new mysqli( or new PDO( used instead of $wpdb
  • Interpolated PHP variables inside SQL strings not wrapped in $wpdb->prepare()
  • implode(',', $ids) inside SQL queries
  • LIMIT $offset, $per_page without prepare
  • $wpdb->query("...{$var}...") or $wpdb->get_results("...$var...")
// Examples to flag:
$wpdb->get_results("SELECT * FROM $table_name LIMIT $offset, $per_page");
"UPDATE $table SET col = %s WHERE id IN (" . implode(',', $ids) . ")";
new mysqli($host, $user, $pass, $db);

Severity: CRITICAL


S2. Cross-Site Scripting (XSS) — Output

Detect: Variables, options, or user-supplied data echoed without escaping:

echo $variable;
echo get_option('some_option');
echo $xml->asXML();
wp_add_inline_script('handle', $var);   // $var not escaped
wp_add_inline_style('handle', $var);    // $var not escaped
echo $_POST['field'];
echo $_GET['param'];

Fix: Use esc_html(), esc_attr(), esc_url(), esc_js(), wp_kses_post() as appropriate to context. Severity: HIGH


S3. Stored XSS

Detect: Data pipeline where external or user input is:

  1. Imported without sanitization (from APIs, Google Sheets, CSV, form fields)
  2. Stored in database options or post meta
  3. Output later without escaping

Flag each step of this pipeline if sanitization or escaping is missing. Fix: esc_url_raw() / sanitize_text_field() on save; esc_url() / esc_html() on output. Severity: CRITICAL


S4. Unsanitized Input

Detect: $_POST, $_GET, $_REQUEST, $_SERVER, $_COOKIE, $_FILES used without sanitization:

$ip   = $_SERVER['HTTP_CLIENT_IP'];
$ip   = $_SERVER['HTTP_X_FORWARDED_FOR'];
$tag  = $_POST['tag'];
$col  = $_GET['col'];
$file = $_GET['download_file'];

Fix: sanitize_text_field(), absint(), sanitize_email(), esc_url_raw(), sanitize_key(), sanitize_file_name(). Severity: HIGH


S5. Missing or Bypassable Nonce Verification

Detect:

  1. No wp_verify_nonce() at all in form processors or AJAX handlers that mutate data
  2. Nonce check that can be bypassed (nonce only checked if other conditions are true):
// WRONG — if $nonce is empty, the check is skipped entirely
if ( ! empty( $nonce ) && ! wp_verify_nonce( $nonce, 'action' ) ) { ... }

// WRONG — nonce only checked inside another condition
if ( isset( $_GET['page'] ) && ! wp_verify_nonce(...) ) { ... }

Correct pattern — fail early:

if ( ! isset( $_POST['my_nonce'] ) || ! wp_verify_nonce( $_POST['my_nonce'], 'my_action' ) ) {
    wp_die( 'Security check failed.' );
}

Severity: CRITICAL


S6. Missing User Capability Checks

Detect: Actions that modify data, access sensitive settings, or perform privileged operations without current_user_can():

add_action( 'wp_ajax_my_action', function() {
    // performs admin action — no current_user_can() check
    delete_option('something');
});

Note: Nonce checks alone do NOT replace capability checks. Both are required for privileged actions. Severity: CRITICAL


S7. Arbitrary Code Execution

Detect:

eval($_POST['code']);
eval(get_option('custom_php'));
create_function('', $code);
preg_replace('/.*/e', $code, '');   // /e modifier executes as PHP

Also detect settings that allow users to save PHP that is later executed. Severity: CRITICAL


S8. Arbitrary Script Insertion (JS/CSS)

Detect: Plugin features that let users save raw JavaScript or CSS that is output on the frontend:

update_option('plugin_custom_js', $_POST['custom_js']);
echo '<script>' . get_option('custom_script') . '</script>';
<textarea name="custom_javascript">

Note: wp_kses() is NOT an acceptable fix for JavaScript execution. Arbitrary JS must be removed. Severity: CRITICAL


S9. Direct File Access Risk

Detect: PHP files with executable code at the top level (not just definitions) that are missing:

if ( ! defined( 'ABSPATH' ) ) exit;

Focus on: templates/, admin/, includes/, AJAX handler files, REST handler files. Severity: HIGH


S10. Direct Database Connections

Detect: new mysqli(, new PDO(, mysql_connect( anywhere in plugin code (excluding vendor/). Fix: Use $wpdb (WordPress database abstraction layer). Severity: CRITICAL


S11. Insecure HTTP Requests (cURL)

Detect: curl_init(), curl_exec(), curl_setopt() in plugin code (not in vendor/). These bypass WordPress SSL verification and proxy settings. Fix: Use wp_remote_get() / wp_remote_post() / wp_remote_request(). Severity: HIGH


S12. REST API Routes Without Proper Permission Callback

Detect:

  1. register_rest_route() calls with no permission_callback key at all — this is a blocker.
  2. register_rest_route() calls where permission_callback is __return_true on endpoints that:
    • Use POST, PUT, PATCH, or DELETE methods (writes/mutations)
    • Return user-specific, private, or admin-only data via GET
// Missing entirely — anyone can call this
register_rest_route( 'myplugin/v1', '/settings', array(
    'methods'  => 'POST',
    'callback' => 'myplugin_save_settings',
    // no permission_callback — CRITICAL
) );

// Sensitive POST with __return_true — HIGH
register_rest_route( 'myplugin/v1', '/user/data', array(
    'methods'            => 'POST',
    'callback'           => 'myplugin_update_user_data',
    'permission_callback' => '__return_true',
) );

Fix: Use current_user_can() as the callback for any endpoint that handles protected data or actions:

'permission_callback' => function() {
    return current_user_can( 'manage_options' );
},

Severity: CRITICAL (missing) / HIGH (__return_true on sensitive endpoint)


S13. Sensitive Data Exposure

Detect:

  • Hardcoded credentials, API keys, or secrets in PHP/JS files
  • Debug output (var_dump(), print_r(), error_log() with sensitive data) left in production code
  • Passwords stored in plain text in options

Severity: CRITICAL / HIGH depending on exposure


Output Format

PLUGIN: [name]
SLUG:   [slug]
SCANNED: [folders]

Then group findings:

CRITICAL — Fix immediately before any deployment HIGH — Fix before WordPress.org submission MEDIUM — Fix when possible

For each finding:

  • File + line number
  • Code snippet
  • How it can be exploited
  • Correct fix with example

End with:

SECURITY SUMMARY
----------------
Total issues: X
Critical: X | High: X | Medium: X

If no security issues are found, state: "No security issues detected in scanned files."

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

wp-plugin-review

No summary provided by upstream source.

Repository SourceNeeds Review
Security

audit-website

Audit websites for SEO, performance, security, technical, content, and 15 other issue cateories with 230+ rules using the squirrelscan CLI. Returns LLM-optimized reports with health scores, broken links, meta tag analysis, and actionable recommendations. Use to discover and asses website or webapp issues and health.

Repository Source
Security

better-auth-security-best-practices

No summary provided by upstream source.

Repository SourceNeeds Review