agentsclimarketplace

Wordpress security

Skill iwritec0de/wp-dev/skills/wordpress-security

WordPress development plugin for Claude Code

Install
npx -y skills add iwritec0de/wp-dev --skill wordpress-security

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

This skill should be used when the user asks to "secure a WordPress plugin", "escape output", "sanitize input", "verify nonces", "check user capabilities", "write safe database queries", or mentions "WordPress security", "esc_html", "sanitize_text_field", "wp_nonce", "wpdb prepare", "XSS", "CSRF", "SQL injection", "capability check", "wp_kses". Provides WordPress security best practices including output escaping, input sanitization, nonce verification, capability checks, and secure database queries.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

6.5 KB, as published. Nobody here has run it

WordPress Security Best Practices

This skill covers WordPress security patterns including output escaping, input sanitization, nonce verification, capability checks, and secure database queries.

Output Escaping

Every piece of dynamic data rendered in HTML must be escaped. Choose the function matching the output context:

ContextFunctionExample
HTML bodyesc_html()<p><?php echo esc_html( $name ); ?></p>
HTML attributeesc_attr()<input value="<?php echo esc_attr( $val ); ?>">
URL/hrefesc_url()<a href="<?php echo esc_url( $link ); ?>">
Textarea contentesc_textarea()<textarea><?php echo esc_textarea( $text ); ?></textarea>
Inline JS valueesc_js()onclick="alert('<?php echo esc_js( $msg ); ?>')"
Rich HTML (post content)wp_kses_post()<?php echo wp_kses_post( $content ); ?>
Custom allowed HTMLwp_kses()echo wp_kses( $html, $allowed_tags );

Translation + escaping combos:

  • esc_html__() / esc_html_e() — translatable escaped strings
  • esc_attr__() / esc_attr_e() — translatable escaped attributes
  • wp_kses_post() on __() output for rich translated content

Input Sanitization

Sanitize all user input immediately upon receipt, before storage or processing:

Data TypeFunction
Plain textsanitize_text_field( wp_unslash( $_POST['field'] ) )
Textareasanitize_textarea_field( wp_unslash( $_POST['field'] ) )
Emailsanitize_email( $_POST['email'] )
Integerabsint( $_POST['id'] ) or intval( $_POST['num'] )
Filenamesanitize_file_name( $_FILES['file']['name'] )
HTML classsanitize_html_class( $_POST['class'] )
Key/slugsanitize_key( $_POST['key'] )
Title/slugsanitize_title( $_POST['title'] )
URLesc_url_raw( $_POST['url'] ) (for storage — use esc_url() for display)

Always wp_unslash() superglobals before sanitizing — WordPress adds slashes to $_GET, $_POST, $_REQUEST.

Nonce Verification

Every form submission and AJAX request must include a nonce for CSRF protection:

Forms

// In the form template:
wp_nonce_field( 'myplugin_save_action', 'myplugin_nonce' );

// In the handler:
if ( ! isset( $_POST['myplugin_nonce'] ) ||
     ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['myplugin_nonce'] ) ), 'myplugin_save_action' ) ) {
    wp_die( esc_html__( 'Security check failed.', 'myplugin' ) );
}

AJAX

// Enqueue with nonce:
wp_localize_script( 'myplugin-script', 'myPluginAjax', array(
    'nonce' => wp_create_nonce( 'myplugin_ajax_nonce' ),
    'url'   => admin_url( 'admin-ajax.php' ),
) );

// In the AJAX handler:
check_ajax_referer( 'myplugin_ajax_nonce', 'nonce' );

Admin pages

check_admin_referer( 'myplugin_settings_action', 'myplugin_settings_nonce' );

Capability Checks

Always verify the current user has permission before performing privileged operations:

// Before saving settings:
if ( ! current_user_can( 'manage_options' ) ) {
    wp_die( esc_html__( 'Unauthorized.', 'myplugin' ) );
}

// Before editing posts:
if ( ! current_user_can( 'edit_post', $post_id ) ) {
    wp_die( esc_html__( 'Unauthorized.', 'myplugin' ) );
}

Common capabilities: manage_options, edit_posts, publish_posts, edit_others_posts, delete_posts, upload_files, manage_categories, edit_users.

Database Security

Never pass unsanitized data into SQL. Always use $wpdb->prepare():

global $wpdb;

// Parameterized query:
$results = $wpdb->get_results(
    $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}custom_table WHERE user_id = %d AND status = %s",
        $user_id,
        $status
    )
);

// Prefer CRUD methods for single-row operations:
$wpdb->insert( $wpdb->prefix . 'custom_table', array(
    'user_id' => $user_id,
    'status'  => $status,
), array( '%d', '%s' ) );

$wpdb->update( $wpdb->prefix . 'custom_table',
    array( 'status' => 'active' ),
    array( 'id' => $row_id ),
    array( '%s' ),
    array( '%d' )
);

$wpdb->delete( $wpdb->prefix . 'custom_table',
    array( 'id' => $row_id ),
    array( '%d' )
);

File Operations

Use the WP_Filesystem API instead of direct PHP file functions:

global $wp_filesystem;
WP_Filesystem();

$wp_filesystem->put_contents( $file_path, $content, FS_CHMOD_FILE );
$content = $wp_filesystem->get_contents( $file_path );

For uploads, use wp_handle_upload():

$uploaded = wp_handle_upload( $_FILES['myfile'], array( 'test_form' => false ) );
if ( isset( $uploaded['error'] ) ) {
    wp_die( esc_html( $uploaded['error'] ) );
}

AJAX & REST Security

AJAX handlers

add_action( 'wp_ajax_myplugin_action', 'myplugin_ajax_handler' );

function myplugin_ajax_handler(): void {
    check_ajax_referer( 'myplugin_nonce', 'nonce' );

    if ( ! current_user_can( 'edit_posts' ) ) {
        wp_send_json_error( 'Unauthorized', 403 );
    }

    $data = sanitize_text_field( wp_unslash( $_POST['data'] ) );
    // ... process ...

    wp_send_json_success( array( 'result' => $data ) );
}

REST endpoints

register_rest_route( 'myplugin/v1', '/items', array(
    'methods'             => 'GET',
    'callback'            => 'myplugin_get_items',
    'permission_callback' => function (): bool {
        return current_user_can( 'read' );
    },
) );

Never set permission_callback to __return_true unless the endpoint is intentionally public.

Redirects & JSON Responses

// Safe redirect (restricts to allowed hosts):
wp_safe_redirect( admin_url( 'admin.php?page=myplugin' ) );
exit;

// JSON responses:
wp_send_json_success( $data );
wp_send_json_error( $message, $status_code );

// Terminate execution properly:
wp_die( $message, $title, array( 'response' => 403 ) );

For the full function reference with signatures and examples, see references/security-functions.md.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.