Install
$ agentstack add skill-wpultimatesecurity-wordpress-security-skills-secure-plugin-development ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Secure plugin & theme development (baseline)
When to use this skill
Use this skill at the start of any WordPress development work and whenever you add a feature that crosses a trust boundary:
- Creating a new plugin main file or theme
functions.phpaddition. - Registering hooks (
add_action/add_filter) that handle input or render output. - Adding admin pages, settings, shortcodes, blocks, widgets, or REST routes.
- Reviewing an existing plugin to bring it up to a secure baseline.
This is the router skill. Follow the [security decision tree](references/decision-tree.md): choose the entry path (browser/API, renderer, cron, or CLI), then add the relevant data and policy branches. It explains when to combine focused skills and when browser nonce checks do not apply; do not load every skill for every task.
Core principles (and why they matter)
- Never trust input; always escape output. Every value from
$_GET,$_POST,
$_REQUEST, $_COOKIE, the database, or a remote API is untrusted until sanitized, and untrusted again the moment it is echoed. These are two separate jobs.
- Block direct file access. Plugin files are reachable by URL. Without an
ABSPATH
guard, an attacker can execute them outside WordPress, bypassing all your checks.
- Separate authentication, CSRF, and authorization. Use a nonce for
cookie-authenticated state changes and an appropriate capability/object check for privileged actions. REST API credentials, cron, and CLI have different trust models; follow the decision tree rather than adding browser checks everywhere.
- Use core APIs, not hand-rolled code. Prefer maintained sanitize/escape/DB/HTTP
APIs, but choose the API and its arguments for the actual trust boundary.
- Least privilege by default. Default options to the safe value, scope capabilities
tightly, and expose the minimum surface.
- Fail closed. On any failed check, stop and return an error — never fall through.
Step-by-step implementation
- Guard the file:
defined( 'ABSPATH' ) || exit;at the top of every PHP file. - Namespace everything: prefix functions, hooks, options, and globals (e.g.
my_plugin_*) to avoid collisions and accidental overrides.
- Choose the handler trust model using the [decision tree](references/decision-tree.md):
- Apply transport-appropriate authentication and CSRF protection.
- Check authority over the action and specific resource.
- Validate input shape/type, unslash WordPress-slashed request input, and sanitize.
- Use
$wpdb->prepare()for dynamic values in custom queries. - Escape at each output sink for its actual context.
- Set safe defaults for all options; validate on save and on read.
- Enqueue assets properly (
wp_enqueue_script/style) and pass data via
wp_localize_script() rather than inline-echoing PHP into JS.
- Keep secrets out of the repo and out of client-readable output.
Common AI mistakes / anti-patterns
Mistake 1 — No ABSPATH guard
// ❌ Insecure: file executes if requested directly over HTTP.
get_results( "SELECT * FROM t WHERE id = " . $_GET['id'] );
echo "x";
$body = file_get_contents( $remote_url );
// ✅ Secure: prepared query, escaped output, HTTP API.
$id = absint( $_GET['id'] ?? 0 );
$rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}t WHERE id = %d", $id ) );
echo 'x';
$response = wp_remote_get( $remote_url );
$body = is_wp_error( $response ) ? '' : wp_remote_retrieve_body( $response );
Mistake 4 — Unsafe defaults
// ❌ Insecure: feature ships enabled, capability defaults wide open.
add_option( 'my_plugin_allow_uploads', true );
// ✅ Secure: default to the safe value; opt-in to risk.
add_option( 'my_plugin_allow_uploads', false );
Correct code examples
A minimal but complete secure plugin skeleton — ABSPATH guard, an admin page behind a capability, and the full verify → authorize → sanitize → act → escape flow — lives in [references/secure-plugin-skeleton.php](references/secure-plugin-skeleton.php).
Checklist
- [ ] Every PHP file opens with
defined( 'ABSPATH' ) || exit;. - [ ] Functions, hooks, and options are uniquely prefixed.
- [ ] Each request handler verifies nonce, then capability, then sanitizes input.
- [ ] All custom DB access uses
$wpdb->prepare(). - [ ] All dynamic output is escaped at the point of echo.
- [ ] Options have safe defaults and are validated on save.
- [ ] Remote requests use the WP HTTP API (
wp_remote_*), notfile_get_contents/cURL. - [ ] No secrets, keys, or credentials are hard-coded or sent to the browser.
- [ ] Scripts are enqueued and given data via
wp_localize_script().
Official references
- Security — Common APIs Handbook
- Plugin Security — Plugin Handbook
- Checking User Capabilities
- Data Validation
- Escaping Data
- HTTP API
- OWASP Top Ten
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: wpultimatesecurity
- Source: wpultimatesecurity/WordPress-Security-Skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.