Install
$ agentstack add skill-wpultimatesecurity-wordpress-security-skills-shortcode-block-security Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Dangerous shell/eval execution.
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ● Dynamic code execution Used
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.
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
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="..."] 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)
shortcode_atts()sets defaults; it does NOT sanitize. The returned array still holds
raw user input. Sanitize each value before use.
- Block attributes are user input too. Declaring
type: 'string'inblock.jsondoes not
escape HTML or JavaScript for you; sanitize on render.
- 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.
- Do not store unescaped attribute values. If you persist them, sanitize on save and escape
on read.
- Never pass shortcode/block input to
do_shortcode()oreval()uncontrolled. Both can
execute arbitrary shortcodes or code.
- Return, don't echo. Shortcode and block render callbacks must return strings; echoing
produces unexpected output placement.
Step-by-step implementation
- In the shortcode callback, call
shortcode_atts()with a complete default map. - Sanitize each attribute to its expected type (
absint,sanitize_text_field,esc_url_raw,
sanitize_key).
- In a block
render_callback, read attributes from the$attributesarray and sanitize them
the same way.
- Build the markup by concatenating escaped values.
- Return the complete markup string.
- For rich content, use
wp_kses_post()or a tightly scopedwp_kses()allowlist.
Common AI mistakes / anti-patterns
Mistake 1 — Echoing $atts directly
// ❌ Insecure: stored XSS through the title attribute.
function my_plugin_card_shortcode( $atts ) {
return '' . $atts['title'] . '';
}
// ✅ 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 = '';
if ( $link ) {
$output .= '' . esc_html( $title ) . '';
} else {
$output .= '' . esc_html( $title ) . '';
}
$output .= '';
return $output;
}
Mistake 2 — Trusting shortcode_atts() to sanitize
// ❌ Insecure: shortcode_atts only supplies defaults and filters unknown keys.
$atts = shortcode_atts( array( 'class' => '' ), $atts );
echo '...';
// ✅ Secure: sanitize the value after normalizing it.
$atts = shortcode_atts( array( 'class' => '' ), $atts, 'my_plugin_box' );
$class = sanitize_html_class( $atts['class'] );
echo '...';
Mistake 3 — Dynamic block render callback echoing attributes
// ❌ Insecure: block attributes echoed raw.
function my_plugin_render_banner( $attributes ) {
?>
">
%s',
esc_attr( $bg_color ),
esc_html( $heading )
);
}
Mistake 4 — Allowing arbitrary HTML through attributes
// ❌ Insecure: an attacker can inject script/event handlers.
function my_plugin_render_note( $attributes ) {
return '' . $attributes['content'] . '';
}
// ✅ Secure: constrain rich markup with wp_kses_post.
function my_plugin_render_note( $attributes ) {
$content = isset( $attributes['content'] ) ? $attributes['content'] : '';
return '' . wp_kses_post( $content ) . '';
}
Mistake 5 — Running do_shortcode() on untrusted input
// ❌ Insecure: executes arbitrary shortcodes supplied by a visitor.
echo do_shortcode( $_POST['content'] );
// ✅ 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](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 fromblock.jsontypes. - [ ] All rendered output is escaped for its context (
esc_html,esc_attr,esc_url,wp_kses_post). - [ ] CSS classes use
sanitize_html_class()(oresc_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
add_shortcode()shortcode_atts()register_block_type()get_block_wrapper_attributes()esc_html()esc_attr()esc_url()wp_kses_post()sanitize_html_class()sanitize_hex_color()- Shortcode API — Plugin Handbook
- Block Editor Handbook
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.