wpultimatesecurity/wordpress-security-skills

shortcode-block-security

Use when registering a shortcode with addshortcode or a dynamic block with a rendercallback, or processing shortcode / block attributes.

ソースを見る

使い方

  1. インストールコマンドをコピーし、自分の環境へ導入する前にソースを確認します。
  2. 必要なツール、認証情報、対応環境を原文で確認します。
  3. 入力と成果物を指定し、出力を資料と照合します。
npx skills add https://github.com/wpultimatesecurity/wordpress-security-skills --skill shortcode-block-security

開始前の確認

  • 導入にはモデル契約、API枠、ファイルへのアクセス許可は含まれません。
  • スター数や導入数は活動の指標であり、独立した品質・安全性試験ではありません。
リポジトリの原文

見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。

Shortcode & dynamic block security

When to use this skill

Use this skill whenever code renders content from shortcodes or dynamic blocks:

  • Registering a shortcode with add_shortcode().
  • Reading or outputting shortcode attributes ($atts) or enclosed content ($content).
  • Registering a dynamic block with a render_callback.
  • Reading block attributes in a server-side render.
  • Building HTML, URLs, or classes from shortcode/block input.

Shortcodes and dynamic blocks are stored XSS sinks: a contributor enters [my_card title="<script>..."] and the render callback echoes it unescaped. Every attribute must be sanitized and every output escaped.

Related: see the output-escaping skill for context-correct escaping and the gutenberg-block-editor-security skill for broader block-editor surfaces.

Core principles (and why they matter)

  1. `shortcode_atts()` sets defaults; it does NOT sanitize. The returned array still holds

raw user input. Sanitize each value before use.

  1. Block attributes are user input too. Declaring type: 'string' in block.json does not

escape HTML or JavaScript for you; sanitize on render.

  1. Escape at render for the exact context. HTML body → esc_html(). HTML attribute →

esc_attr(). URL → esc_url(). Rich HTML → wp_kses_post() with an allowlist.

  1. Do not store unescaped attribute values. If you persist them, sanitize on save and escape

on read.

  1. Never pass shortcode/block input to `do_shortcode()` or `eval()` uncontrolled. Both can

execute arbitrary shortcodes or code.

  1. Return, don't echo. Shortcode and block render callbacks must return strings; echoing

produces unexpected output placement.

Step-by-step implementation

  1. In the shortcode callback, call shortcode_atts() with a complete default map.
  2. Sanitize each attribute to its expected type (absint, sanitize_text_field, esc_url_raw,

sanitize_key).

  1. In a block render_callback, read attributes from the $attributes array and sanitize them

the same way.

  1. Build the markup by concatenating escaped values.
  2. Return the complete markup string.
  3. For rich content, use wp_kses_post() or a tightly scoped wp_kses() allowlist.

Supporting references

ReferenceLoad when
Shortcode & dynamic block security checklistBefore final verification of the shortcode & dynamic block security controls.
Secure shortcode and dynamic blockImplementing shortcode and dynamic-block render callbacks with validated attributes and escaped markup.

Common AI mistakes / anti-patterns

Mistake 1 — Echoing $atts directly

php
// ❌ Insecure: stored XSS through the title attribute.
function my_plugin_card_shortcode( $atts ) {
    return '<div class="card"><h3>' . $atts['title'] . '</h3></div>';
}
php
// ✅ Secure: default, then sanitize, then escape.
function my_plugin_card_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'title' => '',
            'link'  => '',
        ),
        $atts,
        'my_plugin_card'
    );

    $title = sanitize_text_field( $atts['title'] );
    $link  = esc_url_raw( $atts['link'] );

    $output = '<div class="card">';
    if ( $link ) {
        $output .= '<h3><a href="' . esc_url( $link ) . '">' . esc_html( $title ) . '</a></h3>';
    } else {
        $output .= '<h3>' . esc_html( $title ) . '</h3>';
    }
    $output .= '</div>';

    return $output;
}

Mistake 2 — Trusting shortcode_atts() to sanitize

php
// ❌ Insecure: shortcode_atts only supplies defaults and filters unknown keys.
$atts = shortcode_atts( array( 'class' => '' ), $atts );
echo '<div class="' . $atts['class'] . '">...</div>';
php
// ✅ Secure: sanitize the value after normalizing it.
$atts  = shortcode_atts( array( 'class' => '' ), $atts, 'my_plugin_box' );
$class = sanitize_html_class( $atts['class'] );
echo '<div class="' . esc_attr( $class ) . '">...</div>';

Mistake 3 — Dynamic block render callback echoing attributes

php
// ❌ Insecure: block attributes echoed raw.
function my_plugin_render_banner( $attributes ) {
    ?>
    <div class="banner" style="background: <?php echo $attributes['bgColor']; ?>">
        <?php echo $attributes['heading']; ?>
    </div>
    <?php
}
php
// ✅ Secure: sanitize attributes and escape for the output context.
function my_plugin_render_banner( $attributes ) {
    $bg_color = isset( $attributes['bgColor'] ) ? sanitize_hex_color( $attributes['bgColor'] ) : '#ffffff';
    $heading  = isset( $attributes['heading'] ) ? sanitize_text_field( $attributes['heading'] ) : '';

    return sprintf(
        '<div class="banner" style="background: %s"><h2>%s</h2></div>',
        esc_attr( $bg_color ),
        esc_html( $heading )
    );
}

Mistake 4 — Allowing arbitrary HTML through attributes

php
// ❌ Insecure: an attacker can inject script/event handlers.
function my_plugin_render_note( $attributes ) {
    return '<div>' . $attributes['content'] . '</div>';
}
php
// ✅ Secure: constrain rich markup with wp_kses_post.
function my_plugin_render_note( $attributes ) {
    $content = isset( $attributes['content'] ) ? $attributes['content'] : '';
    return '<div>' . wp_kses_post( $content ) . '</div>';
}

Mistake 5 — Running do_shortcode() on untrusted input

php
// ❌ Insecure: executes arbitrary shortcodes supplied by a visitor.
echo do_shortcode( $_POST['content'] );
php
// ✅ Secure: do not run do_shortcode on user input; if required, sanitize first.
$content = wp_kses_post( wp_unslash( $_POST['content'] ?? '' ) );

Correct code examples

A complete secure shortcode and dynamic-block render callback is in `references/secure-shortcode-block.php`.

Checklist

  • [ ] shortcode_atts() provides defaults for every supported attribute.
  • [ ] Each shortcode attribute is sanitized to its expected type after normalization.
  • [ ] Block attributes are sanitized inside render_callback, not trusted from block.json types.
  • [ ] All rendered output is escaped for its context (esc_html, esc_attr, esc_url, wp_kses_post).
  • [ ] CSS classes use sanitize_html_class() (or esc_attr() with an allowlist).
  • [ ] Colors use sanitize_hex_color() where appropriate.
  • [ ] The callback returns a string; it does not echo directly.
  • [ ] do_shortcode() is never run on untrusted input.

Official references

タスクに関連する項目

同じリポジトリから

関連する Skills

すべての Skills
wpultimatesecurity
コミュニティ

capability-permission-checks

Use when adding admin pages, menu items, AJAX/REST handlers, action links, or any code that performs a privileged operation in WordPress. Gates actions with currentusercan() using the correct capability (not roles), including per-object checks like editpost, and pairs the check with a nonce. Prevents privilege escalation and broken access control. Apply proactively to every privileged code path.

導入数
1
GitHub Stars
50
更新日
9月29日
wpultimatesecurity
コミュニティ

filesystem-security

Use when reading, writing, including, or deleting files from paths that include user input - include / require, readfile, unlink, filegetcontents, or the WPFilesystem API. Validates paths with validatefile, normalizes with wpnormalizepath, confines operations to an allowed base directory, and uses wpdeletefile / WPFilesystem. Prevents path traversal, local file inclusion, and arbitrary file deletion.

導入数
1
GitHub Stars
50
更新日
9月29日
wpultimatesecurity
コミュニティ

multisite-security

Use when writing code that runs on a WordPress multisite network - switchtoblog, network admin pages, getsites, or capabilities that differ between site and network scope. Uses managenetwork / managenetworkoptions and issuperadmin correctly, restores context with restorecurrentblog, isolates per-site data, and never trusts a blog id from input. Prevents cross-site data leakage and network privilege escalation.

導入数
1
GitHub Stars
50
更新日
9月29日
wpultimatesecurity
コミュニティ

nonces-csrf-protection

Use when handling any form submission, AJAX request, admin-post action, settings page, link that triggers an action, or any other user-initiated request in a WordPress plugin or theme. Generates nonces with wpnoncefield / wpcreatenonce and verifies them with checkadminreferer, checkajaxreferer, or wpverifynonce, always paired with a capability check, to prevent CSRF. Apply proactively whenever code accepts or acts on a request.

導入数
1
GitHub Stars
50
更新日
9月29日