AI Tools & Scripts
WordPress Plugin Safety Check
Check WordPress plugins and themes before you release: $wpdb queries built from $_GET, AJAX and admin-post handlers without nonces or capability checks, open REST routes, unescaped output, unserialize of request data and readme.txt problems.
What you get: 1 file · 62.78 KB · ZIP
See purchase optionsProduct description
One wp_ajax_ handler that saves settings without check_ajax_referer() lets another site make a logged-in admin change them, and any subscriber can call it when current_user_can() is missing. A register_rest_route() with DELETE and no permission_callback is open to the whole internet, and "WHERE id = " . $_GET['id'] in a $wpdb query is SQL injection even after sanitize_text_field(). These are the mistakes behind most plugin security advisories. This skill finds them and the other WordPress plugin mistakes below before you release or submit to WordPress.org, and tells you how to fix each one.
Your agent runs a scanner on the plugin or theme folder: every .php file and the plugin readme.txt. It follows request data ($_GET, $_POST, $_REQUEST, $_COOKIE, $_FILES, request headers, filter_input(), REST request parameters) through assignments, foreach loops, ternaries, wp_unslash() and string building inside each function, and knows which WordPress functions sanitize (sanitize_text_field, absint, sanitize_key), escape (esc_html, esc_attr, esc_url, wp_kses_post) or prepare ($wpdb->prepare placeholders) a value. It matches add_action() hooks, admin pages and REST routes to their handlers by name across all files, including the plugin boilerplate's hook loader and nonce or permission helpers the handler calls. The examples from the plugin handbook (AJAX with check_ajax_referer, the settings page, REST endpoints with __return_true on GET routes, the REST controller) are not reported as BLOCK. It never runs PHP or WordPress. It returns BLOCK, REVIEW, or NOT FLAGGED, with the file, line, reason, and a safer approach. Secrets in shown lines are never printed.
What it checks
BLOCK (fix before shipping)
- W01: $wpdb queries (query, get_results, get_row, get_var, get_col, or the query string given to prepare) built with request data that was not cast to a number or passed as a placeholder
- W02: REST routes with POST, PUT, PATCH or DELETE methods and no permission_callback (when the callback checks current_user_can() itself, it is W12)
- W03: wp_ajax_, admin_post_, admin page and admin_init handlers that change data because of the request without a nonce check, including wp_verify_nonce() results that are ignored and checks that only run when that nonce field is present (handlers also registered for wp_ajax_nopriv_ are public by design and left to W11)
- W04: request data printed with echo, print, short echo tags, printf, _e or wp_die without escaping, including $_SERVER['PHP_SELF'], REQUEST_URI and request headers
- W05: request data in unserialize(), maybe_unserialize(), extract(), include or require, eval(), call_user_func(), a variable function name, or a shell command
- W06: option names and user meta keys taken from the request (update_option, delete_option, update_user_meta and similar), or a whole $_POST array given to wp_update_user()
REVIEW (check before release)
- W11: handlers that change data without current_user_can() (wp_ajax_ and admin_post_ for any logged-in user; wp_ajax_nopriv_, admin_init and init for settings, users, plugins and files), including is_admin() used as a permission check
- W12: GET routes without permission_callback, write routes whose permission_callback is __return_true, always returns true, or is is_user_logged_in, and write routes without one whose callback checks current_user_can() itself
- W13: request data saved with update_option, update_post_meta, update_user_meta or set_theme_mod without a sanitizing function, and register_setting() without a sanitize_callback
- W14: unescaped output of get_option, get_post_meta, get_user_meta, get_theme_mod, get_transient, add_query_arg(), remove_query_arg(), get_query_var(), REST parameters, and request data that was only sanitized
- W15: $wpdb queries joined together from REST parameters or from esc_sql()/esc_like() escaped request data instead of prepare()
- W16: file calls (unlink, readfile, file_get_contents, fopen, file_put_contents, copy, rename, $wp_filesystem) with paths from the request, move_uploaded_file() with no file type check, and wp_handle_upload() with 'test_type' => false
- W17: TLS checks turned off: 'sslverify' => false, CURLOPT_SSL_VERIFYPEER or CURLOPT_SSL_VERIFYHOST off, 'verify_peer' => false, the https_ssl_verify filter returning false (loopback requests to the site itself are not reported)
- W18: plugin PHP files that run code before any ABSPATH check, and uninstall.php without a WP_UNINSTALL_PLUGIN check (guards such as if ( ! defined( 'ABSPATH' ) ) { header( ... ); exit; } and Akismet's ! function_exists( 'add_action' ) check count)
- W19: readme.txt header problems: no === Plugin Name === line, missing Contributors, Tested up to, Stable tag or License, Stable tag: trunk, a Stable tag that differs from Version in the main plugin file, Tested up to older than the last three major WordPress releases, more than 5 tags, a short description over 150 characters, the template's default text
What's included
- The scanner (Python 3.9+, standard library only)
- A rules guide with the reason, safer approach and official WordPress or PHP source for each rule
- A risky and a safe example plugin (main file, AJAX and REST handlers, readme.txt), and the real report the scanner produces on the risky one
Limits
It reads each file as written. Request data is followed inside one function; values passed between functions, stored in class properties by another method, or passed through filters are not followed, and output of functions it does not know counts as safe. Hooks with names or callbacks built at run time, and nonce or capability checks more than one call away from the handler, are not seen. JavaScript, block.json, vendor and tests folders are not read. Files that only define classes or functions are not asked for an ABSPATH guard (Plugin Check is stricter there), and theme files are not checked for it. Tested up to is compared with a dated cut-off: 6.9, the oldest of the last three major releases in November 2026. NOT FLAGGED is not a guarantee.
Contents
One ZIP file with 17 files: SKILL.md, the scanner in scripts/ (8 Python files), references/rules.md (every rule with its reason and safer approach), and examples/ (6 example files plus report.md, the real scanner output on the risky example).
How to use: This is an agent skill: a folder with a SKILL.md file and Python scripts that an AI coding agent such as Claude Code, Cursor, or Codex CLI can run. Unzip it into your agent's skills folder (for Claude Code: ~/.claude/skills/), then ask your agent to check your project. The scripts also run on their own with Python 3.9 or newer, with nothing to install. They read your files only: no network access, and nothing is sent anywhere.
What you get
1 file · 62.78 KB
- wordpress-plugin-safety-check-1.0.0.zipZIP · 62.78 KB
AI Tools & Scripts
WordPress Plugin Safety Check
Check WordPress plugins and themes before you release: $wpdb queries built from $_GET, AJAX and admin-post handlers without nonces or capability checks, open REST routes, unescaped output, unserialize of request data and readme.txt problems.
Getly opened the uploaded files and found nothing that would stop them opening. It is not a virus scan or a quality rating.
Tags
Frequently asked questions
Do I get access instantly?
Yes. This is a digital product - you can download it right after checkout, straight from your library.
Can I use it for commercial projects?
Yes. Commercial use is allowed: use it in your own business and in things you sell. You may not resell or redistribute the file itself.
What's your refund policy?
You can request a refund within 30 days. If something isn't right, request it from your order and the Getly team reviews it.
What file formats and sizes will I get?
You'll download 1 file - ZIP - 62.78 KB in total.
Do I get free updates?
Yes - when the seller ships a new version, you can re-download the latest files from your library at no extra cost. Check the changelog for recent updates.
Price
$5.00



