Code Audit
3stoneBrother/code-audit
Professional code security audit skill covering 55+ vulnerability types.
WordPress security code review and vulnerability detection. An agent skill from jorgerosal/wordpress-skills.
$ npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install jorgerosal/wordpress-skills wp-security-review --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/jorgerosal/wordpress-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/claude-skills/wp-security-review .claude/skills/wp-security-review && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "wp-security-review" agent skill from https://github.com/jorgerosal/wordpress-skills/tree/main/claude-skills/wp-security-review into .claude/skills/wp-security-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "wp-security-review", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/jorgerosal/wordpress-skills/tree/main/claude-skills/wp-security-reviewType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install jorgerosal/wordpress-skills wp-security-review --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jorgerosal/wordpress-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/claude-skills/wp-security-review .agents/skills/wp-security-review && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "wp-security-review" agent skill from https://github.com/jorgerosal/wordpress-skills/tree/main/claude-skills/wp-security-review into .agents/skills/wp-security-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "wp-security-review", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install jorgerosal/wordpress-skills wp-security-review --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jorgerosal/wordpress-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/claude-skills/wp-security-review .cursor/skills/wp-security-review && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "wp-security-review" agent skill from https://github.com/jorgerosal/wordpress-skills/tree/main/claude-skills/wp-security-review into .cursor/skills/wp-security-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "wp-security-review", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/jorgerosal/wordpress-skills.git --path claude-skills/wp-security-review--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install jorgerosal/wordpress-skills wp-security-review --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jorgerosal/wordpress-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/claude-skills/wp-security-review .gemini/skills/wp-security-review && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "wp-security-review" agent skill from https://github.com/jorgerosal/wordpress-skills/tree/main/claude-skills/wp-security-review into .gemini/skills/wp-security-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "wp-security-review", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install jorgerosal/wordpress-skills wp-security-reviewInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/jorgerosal/wordpress-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/claude-skills/wp-security-review .github/skills/wp-security-review && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "wp-security-review" agent skill from https://github.com/jorgerosal/wordpress-skills/tree/main/claude-skills/wp-security-review into .github/skills/wp-security-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "wp-security-review", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install jorgerosal/wordpress-skills wp-security-review --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/jorgerosal/wordpress-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/claude-skills/wp-security-review .opencode/skills/wp-security-review && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "wp-security-review" agent skill from https://github.com/jorgerosal/wordpress-skills/tree/main/claude-skills/wp-security-review into .opencode/skills/wp-security-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "wp-security-review", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
wp-security-reviewWordPress security code review and vulnerability detection. An agent skill from jorgerosal/wordpress-skills.
Wp Security Review is an agent skill from jorgerosal/wordpress-skills. WordPress security code review and vulnerability detection. Use when reviewing WordPress PHP code for security issues, auditing themes/plugins for vulnerabilities, checking code before launch, analyzing AJAX/REST handlers for exploits, detecting XSS, SQL injection, CSRF, or authorization bypass, or when user mentions "security review", "vulnerability", "XSS", "SQL injection", "CSRF", "nonce", "sanitization", "escaping", "capability check", "privilege escalation", "file upload security", "insecure code", "auth…
Its SKILL.md is about 6.4k tokens, which your agent loads only when the skill is triggered. The skill folder holds 6 other files, including reference files (for example `references/auth-patterns.md`, `references/escaping-guide.md` and `references/nonce-csrf-guide.md`).
It sits in Security, covering Web application vulnerabilities, Security review and File uploads and storage. It works with WordPress and PHP. The repository describes itself as: ✅ 🎉 Claude skills and Codex skills for Wordpress development❗️. The licence is MIT.
6 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 8c96442. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are php, bash and markdown).
From the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
api.wordpress.orgFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
AUTH_KEYSECURE_AUTH_KEYLOGGED_IN_KEYNONCE_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Wp Security Review loads about 6.4k tokens when it runs, and up to ~39k if it reads all its reference files. Until then it costs about 176 tokens; SKILL.md has 1,114 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from jorgerosal/wordpress-skills at commit 8c96442, republished under its MIT licence (© jorgerosal). 1,114 words, ~6,403 tokens.
.claude/skills/wp-security-review/SKILL.md (or your agent's skills folder). This skill also uses 5 other files; get the full folder from GitHub.Systematic security code review for WordPress themes, plugins, and custom code. Core principle: Three-pillar security model: (1) Sanitize input early, (2) Validate authorization (nonces + capabilities), (3) Escape output late. Scan critical issues first (SQL injection in public code, XSS on unescaped output, missing nonces on state-changing operations), then warnings, then info-level improvements. Report with line numbers, severity, CWE references, and BAD/GOOD code pairs.
Use when:
Don't use for:
functions.php, plugin.php, *.php)Scan for:
defined( 'ABSPATH' ) || exit; at top → WARNING: Direct file access possibleeval( → CRITICAL: Code injection vector (CWE-95)exec(, shell_exec(, system(, passthru( → CRITICAL: Command injection if user input present (CWE-78)base64_decode( $_ → CRITICAL: Possible encoded payload executionunserialize( $_ → CRITICAL: Object injection (CWE-502)$_GET[, $_POST[, $_REQUEST[ without sanitize_* → WARNING: Unsanitized input (CWE-20)include $_, require $_, include_once $_, require_once $_ → CRITICAL: Path traversal/inclusion (CWE-22)move_uploaded_file( → CRITICAL: Use wp_handle_upload() insteadadmin-post.php, admin_post_* hooks)Scan for three-step pattern (ALL must be present):
wp_verify_nonce( $_POST['nonce_field'], 'action_name' ) or check_admin_referer() → CRITICAL if missing (CWE-352)current_user_can( 'capability' ) → CRITICAL if missing (CWE-862)sanitize_text_field(), sanitize_email(), etc. → WARNING if missing (CWE-20)Missing ANY step = vulnerability. State-changing operations MUST have all three.
wp_ajax_*, wp_ajax_nopriv_* hooks)Scan for:
check_ajax_referer( 'action_name', 'nonce_field' ) → CRITICAL if missing on state-changing operations (CWE-352)current_user_can( 'capability' ) → CRITICAL if missing when capability required (CWE-862)wp_ajax_nopriv_* without nonce → CRITICAL: Public endpoint with no CSRF protection$_POST without sanitize_* → WARNING: Unsanitized input (CWE-20)wp_send_json() without escaping when echoing user input → WARNING: JSON injectionregister_rest_route)Scan for:
permission_callback → CRITICAL: WordPress 5.5+ requires this (CWE-862)'permission_callback' => '__return_true' on write operations → CRITICAL: Allows unauthorized access'permission_callback' => '__return_true' on public read endpoints → OK (not a vulnerability)current_user_can() in permission callback → WARNING: May allow privilege escalation (CWE-863)$request->get_param() → WARNING: Unsanitized input (CWE-20)X-WP-Nonce header or application passwords → INFO: Verify client sends header$wpdb->query, $wpdb->get_results, etc.)Scan for:
$wpdb->query( or $wpdb->get_results( with string concatenation or interpolation → CRITICAL: SQL injection (CWE-89)$wpdb->prepare() when user input present → CRITICAL: SQL injection (CWE-89)$wpdb->prepare( "... LIKE '%{$term}%'" ) → WARNING: Should use $wpdb->esc_like() first{$wpdb->prefix} in queries → OK (not a vulnerability, standard pattern)Safe pattern: $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE ID = %d", $post_id ) )
*.php in theme, templates)Scan for:
echo $ or print $ without escaping function → CRITICAL: XSS (CWE-79)<input value="<?php echo $ → CRITICAL: Use esc_attr() (CWE-79)<a href="<?php echo $ → CRITICAL: Use esc_url() (CWE-79)<script> blocks with PHP variables → CRITICAL: Use esc_js() or wp_localize_script() (CWE-79)wp_kses() or wp_kses_post() → WARNING: May allow unsafe tagsSafe pattern: Always escape at point of output, not at input or storage.
Scan for:
move_uploaded_file( instead of wp_handle_upload() → CRITICAL: Bypasses WP security checks (CWE-434)current_user_can() check → CRITICAL: Unauthorized file upload (CWE-862)wp_check_filetype_and_ext() → WARNING: File type spoofing possible (CWE-434)*.js, *.jsx)Scan for:
fetch() or $.ajax() to REST API without X-WP-Nonce header → WARNING: CSRF vulnerability (CWE-352)$.post( ajaxurl, ...) without nonce in data → WARNING: CSRF vulnerability (CWE-352)innerHTML = userInput or dangerouslySetInnerHTML → CRITICAL: DOM-based XSS (CWE-79)eval( → CRITICAL: Code injection vector (CWE-95)# CRITICAL: SQL injection patterns
grep -rn "\$wpdb->query(" . | grep -v "prepare"
grep -rn "\$wpdb->get_results(" . | grep -v "prepare"
grep -rn "\$wpdb->get_var(" . | grep -v "prepare"
grep -rn "\$wpdb->get_row(" . | grep -v "prepare"
# CRITICAL: XSS (unescaped output)
grep -rn "echo \$_" .
grep -rn "print \$_" .
grep -rn "<?php echo \$" . | grep -v "esc_"
# CRITICAL: Dangerous functions
grep -rn "eval(" .
grep -rn "exec(" .
grep -rn "shell_exec(" .
grep -rn "system(" .
grep -rn "passthru(" .
grep -rn "base64_decode(\$_" .
grep -rn "unserialize(\$_" .
# CRITICAL: File upload without WP functions
grep -rn "move_uploaded_file(" .
# CRITICAL: REST API without permission_callback
grep -rn "register_rest_route" . | grep -v "permission_callback"
# CRITICAL: Path traversal / dynamic includes
grep -rn "include \$_" .
grep -rn "require \$_" .
grep -rn "include_once \$_" .
grep -rn "require_once \$_" .
# WARNING: Unsanitized input usage
grep -rn "\$_GET\[" . | grep -v "sanitize_"
grep -rn "\$_POST\[" . | grep -v "sanitize_"
grep -rn "\$_REQUEST\[" . | grep -v "sanitize_"
# WARNING: Missing nonces on public AJAX
grep -rn "wp_ajax_nopriv_" .
# WARNING: Missing capability checks
grep -rn "admin_post_" . | grep -v "current_user_can"
grep -rn "wp_ajax_" . | grep -v "current_user_can"
# INFO: Missing ABSPATH check
grep -rn "<?php" . | head -20 | grep -v "ABSPATH"
# INFO: Security constants missing (check wp-config.php)
grep -n "DISALLOW_FILE_EDIT" wp-config.php
grep -n "FORCE_SSL_ADMIN" wp-config.php
grep -n "DISALLOW_UNFILTERED_HTML" wp-config.phpDifferent hosting environments provide varying security layers:
Managed WordPress Hosts (WP Engine, Pantheon, Pressable, WordPress VIP, etc.):
WordPress VIP:
wpcom_vip_* functions)Self-Hosted / Standard Hosting:
// ❌ CRITICAL: Unescaped output (CWE-79)
<h1><?php echo $_GET['title']; ?></h1>
<input value="<?php echo $user_input; ?>">
<a href="<?php echo $url; ?>">Link</a>
<script>var data = "<?php echo $json; ?>";</script>
// ✅ GOOD: Context-appropriate escaping
<h1><?php echo esc_html( $_GET['title'] ); ?></h1>
<input value="<?php echo esc_attr( $user_input ); ?>">
<a href="<?php echo esc_url( $url ); ?>">Link</a>
<script>var data = <?php echo wp_json_encode( $data ); ?>;</script>
// ❌ CRITICAL: Early escaping (wrong pattern)
$title = esc_html( $_POST['title'] );
update_post_meta( $post_id, 'title', $title ); // Stores HTML entities
// ✅ GOOD: Late escaping (correct pattern)
$title = sanitize_text_field( $_POST['title'] );
update_post_meta( $post_id, 'title', $title );
echo '<h1>' . esc_html( get_post_meta( $post_id, 'title', true ) ) . '</h1>';
// ✅ GOOD: Rich HTML with wp_kses_post() for trusted content
echo wp_kses_post( $content ); // Allows safe HTML tags only
// ✅ GOOD: Custom allowed tags with wp_kses()
$allowed_tags = array(
'a' => array( 'href' => array(), 'title' => array() ),
'br' => array(),
'strong' => array(),
);
echo wp_kses( $user_content, $allowed_tags );// ❌ CRITICAL: Direct interpolation (CWE-89)
$results = $wpdb->get_results( "SELECT * FROM {$wpdb->users} WHERE ID = $user_id" );
$results = $wpdb->query( "UPDATE {$wpdb->posts} SET post_title = '$title'" );
// ✅ GOOD: Use $wpdb->prepare() with placeholders
$results = $wpdb->get_results( $wpdb->prepare(
"SELECT * FROM {$wpdb->users} WHERE ID = %d",
$user_id
) );
$wpdb->query( $wpdb->prepare(
"UPDATE {$wpdb->posts} SET post_title = %s WHERE ID = %d",
$title,
$post_id
) );
// ✅ GOOD: Multiple placeholders
$wpdb->get_results( $wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_status = %s AND post_author = %d",
$status,
$author_id
) );
// ❌ WARNING: LIKE without esc_like()
$wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE post_title LIKE '%%{$term}%%'" );
// ✅ GOOD: Use esc_like() for LIKE queries
$like_term = '%' . $wpdb->esc_like( $term ) . '%';
$wpdb->get_results( $wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_title LIKE %s",
$like_term
) );
// ✅ OK: Hardcoded SQL without user input (admin context)
if ( current_user_can( 'manage_options' ) ) {
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name = 'temp_setting'" );
}// ❌ CRITICAL: Missing nonce verification (CWE-352)
add_action( 'admin_post_save_settings', function() {
update_option( 'my_setting', $_POST['value'] );
} );
// ❌ CRITICAL: Missing capability check (CWE-862)
add_action( 'admin_post_save_settings', function() {
check_admin_referer( 'save_settings_action', 'settings_nonce' );
update_option( 'my_setting', $_POST['value'] );
} );
// ✅ GOOD: Complete three-step pattern
add_action( 'admin_post_save_settings', function() {
// Step 1: Verify nonce
if ( ! isset( $_POST['settings_nonce'] ) ||
! wp_verify_nonce( $_POST['settings_nonce'], 'save_settings_action' ) ) {
wp_die( 'Invalid nonce' );
}
// Step 2: Check capability
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'Insufficient permissions' );
}
// Step 3: Sanitize input
$value = sanitize_text_field( $_POST['value'] );
update_option( 'my_setting', $value );
wp_redirect( admin_url( 'admin.php?page=settings&updated=true' ) );
exit;
} );
// ✅ GOOD: Form with nonce field
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<?php wp_nonce_field( 'save_settings_action', 'settings_nonce' ); ?>
<input type="hidden" name="action" value="save_settings">
<input type="text" name="value" value="<?php echo esc_attr( $current_value ); ?>">
<?php submit_button(); ?>
</form>
// ✅ GOOD: AJAX with nonce
add_action( 'wp_ajax_update_user_meta', function() {
check_ajax_referer( 'update_meta_nonce', 'nonce' );
if ( ! current_user_can( 'edit_users' ) ) {
wp_send_json_error( 'Insufficient permissions' );
}
$user_id = absint( $_POST['user_id'] );
$value = sanitize_text_field( $_POST['value'] );
update_user_meta( $user_id, 'custom_field', $value );
wp_send_json_success();
} );
// JavaScript for AJAX nonce
jQuery.post( ajaxurl, {
action: 'update_user_meta',
nonce: myAjax.nonce, // From wp_localize_script()
user_id: 123,
value: 'new value'
} );// ❌ CRITICAL: No capability check (CWE-862)
add_action( 'admin_post_delete_user', function() {
wp_delete_user( $_POST['user_id'] );
} );
// ❌ WARNING: Incorrect capability (CWE-863)
add_action( 'admin_post_delete_user', function() {
if ( ! current_user_can( 'edit_posts' ) ) { // Wrong capability!
wp_die( 'Insufficient permissions' );
}
wp_delete_user( $_POST['user_id'] );
} );
// ✅ GOOD: Correct capability check
add_action( 'admin_post_delete_user', function() {
if ( ! current_user_can( 'delete_users' ) ) {
wp_die( 'Insufficient permissions' );
}
check_admin_referer( 'delete_user_action', 'delete_nonce' );
wp_delete_user( absint( $_POST['user_id'] ) );
} );
// ✅ GOOD: Check user can edit specific post
if ( ! current_user_can( 'edit_post', $post_id ) ) {
wp_die( 'You cannot edit this post' );
}
// ✅ GOOD: REST API permission callback
register_rest_route( 'myapp/v1', '/users/(?P<id>\d+)', array(
'methods' => 'DELETE',
'callback' => 'myapp_delete_user',
'permission_callback' => function( $request ) {
return current_user_can( 'delete_users' );
},
) );
// ✅ OK: __return_true for public read endpoint
register_rest_route( 'myapp/v1', '/posts', array(
'methods' => 'GET',
'callback' => 'myapp_get_posts',
'permission_callback' => '__return_true', // Public read is OK
) );// ❌ CRITICAL: Direct move_uploaded_file() (CWE-434)
if ( isset( $_FILES['upload'] ) ) {
move_uploaded_file( $_FILES['upload']['tmp_name'], '/uploads/' . $_FILES['upload']['name'] );
}
// ❌ CRITICAL: No capability check (CWE-862)
$file = wp_handle_upload( $_FILES['upload'], array( 'test_form' => false ) );
// ✅ GOOD: Complete file upload security
add_action( 'admin_post_upload_file', function() {
// Step 1: Verify nonce
check_admin_referer( 'upload_file_action', 'upload_nonce' );
// Step 2: Check capability
if ( ! current_user_can( 'upload_files' ) ) {
wp_die( 'Insufficient permissions' );
}
// Step 3: Validate file exists
if ( ! isset( $_FILES['upload'] ) || UPLOAD_ERR_OK !== $_FILES['upload']['error'] ) {
wp_die( 'File upload failed' );
}
// Step 4: Use wp_handle_upload() (validates MIME type, extension)
require_once( ABSPATH . 'wp-admin/includes/file.php' );
$file = wp_handle_upload( $_FILES['upload'], array( 'test_form' => false ) );
if ( isset( $file['error'] ) ) {
wp_die( 'Upload error: ' . esc_html( $file['error'] ) );
}
// Step 5: Store file info
$attachment_id = wp_insert_attachment( array(
'post_title' => sanitize_file_name( $_FILES['upload']['name'] ),
'post_mime_type' => $file['type'],
'post_status' => 'inherit',
), $file['file'] );
} );
// ✅ GOOD: Restrict allowed MIME types
add_filter( 'upload_mimes', function( $mimes ) {
// Remove potentially dangerous types
unset( $mimes['exe'] );
unset( $mimes['php'] );
// Add custom allowed types if needed
$mimes['svg'] = 'image/svg+xml';
return $mimes;
} );
// ✅ GOOD: File type validation
$filetype = wp_check_filetype_and_ext( $_FILES['upload']['tmp_name'], $_FILES['upload']['name'] );
if ( ! $filetype['ext'] ) {
wp_die( 'Invalid file type' );
}// ❌ CRITICAL: Unserialize user input (CWE-502)
$data = unserialize( $_POST['data'] );
// ❌ CRITICAL: Unserialize from cookie
$data = unserialize( $_COOKIE['cart_data'] );
// ✅ GOOD: Use JSON instead of serialize for user-facing data
$data = json_decode( $_POST['data'], true );
if ( JSON_ERROR_NONE !== json_last_error() ) {
wp_die( 'Invalid JSON' );
}
// ✅ OK: Unserialize trusted internal data only
$data = get_option( 'my_internal_setting' ); // Already unserialized by WP
$meta = get_post_meta( $post_id, 'my_field', true ); // Already unserialized by WP// ❌ CRITICAL: eval() with user input (CWE-95)
eval( $_POST['code'] );
// ❌ CRITICAL: exec() with user input (CWE-78)
exec( 'ls -la ' . $_GET['dir'] );
// ❌ CRITICAL: shell_exec() with user input
$output = shell_exec( 'grep ' . $_POST['search'] . ' file.txt' );
// ✅ GOOD: Avoid eval(), exec(), shell_exec() entirely in web context
// If absolutely necessary, whitelist input strictly
$allowed_dirs = array( 'uploads', 'cache' );
$dir = sanitize_key( $_GET['dir'] );
if ( in_array( $dir, $allowed_dirs, true ) ) {
exec( 'ls -la ' . escapeshellarg( $dir ), $output );
}// ❌ WARNING: Missing ABSPATH check
<?php
// Plugin code here without security check
// ✅ GOOD: ABSPATH check at top of every PHP file
<?php
defined( 'ABSPATH' ) || exit;
// Plugin code here
// ✅ GOOD: Alternative pattern
if ( ! defined( 'ABSPATH' ) ) {
die( 'Direct access not permitted.' );
}// ❌ WARNING: No sanitization (CWE-20)
$title = $_POST['title'];
update_post_meta( $post_id, 'title', $title );
// ✅ GOOD: Sanitize based on expected data type
$title = sanitize_text_field( $_POST['title'] );
$email = sanitize_email( $_POST['email'] );
$url = sanitize_url( $_POST['url'] );
$filename = sanitize_file_name( $_POST['filename'] );
$key = sanitize_key( $_POST['key'] );
$int = absint( $_POST['count'] );
$float = floatval( $_POST['price'] );
$textarea = sanitize_textarea_field( $_POST['description'] );
// ✅ GOOD: Array of integers
$ids = array_map( 'absint', (array) $_POST['ids'] );
// ✅ GOOD: Validate against whitelist
$allowed_types = array( 'post', 'page', 'product' );
$type = sanitize_key( $_POST['type'] );
if ( ! in_array( $type, $allowed_types, true ) ) {
wp_die( 'Invalid type' );
}| Severity | Description |
|---|---|
| CRITICAL | Exploitable without authentication OR leads to data breach, RCE, or privilege escalation. Examples: SQL injection on public endpoint, XSS on unescaped output, missing nonce on admin action, file upload without capability check, eval() with user input |
| WARNING | Exploitable with authentication OR requires specific conditions. Examples: XSS in admin-only code, missing capability check on logged-in action, SQL injection in admin context, incomplete input sanitization |
| INFO | Defense-in-depth improvement or hardening opportunity. Examples: missing ABSPATH check, security constants not set in wp-config.php, missing security headers |
Context adjustments:
Group findings by FILE and severity level:
## Security Review: [filename]
### CRITICAL Issues
**Line X** | CWE-79 | XSS via unescaped output
- **Issue**: User input echoed directly without escaping
- **Risk**: Allows attackers to inject malicious JavaScript
- **Context**: Public-facing template (HIGH severity)
❌ **BAD:**
```php
<h1><?php echo $_GET['title']; ?></h1>✅ GOOD:
<h1><?php echo esc_html( $_GET['title'] ); ?></h1>Line Y | CWE-352 | Missing nonce verification
[BAD/GOOD code pair]
Line Z | Defense-in-depth | Missing ABSPATH check
defined( 'ABSPATH' ) || exit; at top of file
## Common Mistakes (SEC-24)
When performing security reviews, avoid these false positives:
| Mistake | Why It's Wrong | Context |
|---------|----------------|---------|
| Flagging missing nonces in WP-CLI commands | WP-CLI runs server-side, no CSRF risk | Nonces only needed for HTTP requests |
| Flagging `wp_kses_post()` as insufficient escaping | `wp_kses_post()` is valid for trusted content with safe HTML | Allows `<p>`, `<a>`, `<strong>`, etc. - appropriate for post content |
| Flagging admin-only `$wpdb->query()` with hardcoded SQL | No user input = no injection risk | Review for user input presence, not just prepare() usage |
| Flagging `current_user_can()` inside REST `permission_callback` as redundant | This is the CORRECT location for capability checks in REST API | REST API auth model, not redundant |
| Flagging `esc_html( get_the_title() )` as double-escaping | WP core sanitizes on storage, but late escaping is still best practice | Not technically double-escaping, WP stores raw data |
| Flagging `update_option()` in admin context as vulnerability | Options API is safe for admin use | Check capability first, then update_option() is fine |
| Flagging REST API with `__return_true` permission on GET endpoints | Public read access is often intentional | Only flag on write operations (POST, PUT, DELETE) |
| Flagging `sanitize_callback` in `register_setting()` as missing sanitization | Settings API handles sanitization via callback | This is the correct pattern for Settings API |
## Security Constants Check (SEC-19)
Review `wp-config.php` for security hardening constants:
```php
// ✅ GOOD: Disable file editor (INFO-level recommendation)
define( 'DISALLOW_FILE_EDIT', true );
// ✅ GOOD: Force SSL for admin (INFO if SSL available)
define( 'FORCE_SSL_ADMIN', true );
// ✅ GOOD: Disable unfiltered HTML for all users including admins
define( 'DISALLOW_UNFILTERED_HTML', true );
// ✅ GOOD: Disable plugin/theme installation (high-security environments)
define( 'DISALLOW_FILE_MODS', true );
// ✅ GOOD: Custom authentication keys (CRITICAL if using defaults)
define( 'AUTH_KEY', 'put-unique-phrase-here' ); // Use https://api.wordpress.org/secret-key/1.1/salt/
define( 'SECURE_AUTH_KEY', 'put-unique-phrase-here' );
define( 'LOGGED_IN_KEY', 'put-unique-phrase-here' );
define( 'NONCE_KEY', 'put-unique-phrase-here' );
define( 'AUTH_SALT', 'put-unique-phrase-here' );
define( 'SECURE_AUTH_SALT', 'put-unique-phrase-here' );
define( 'LOGGED_IN_SALT', 'put-unique-phrase-here' );
define( 'NONCE_SALT', 'put-unique-phrase-here' );Load these references for comprehensive vulnerability patterns and examples:
| Task | Reference to Load |
|---|---|
| Comprehensive vulnerability catalog with CWE mappings | references/vulnerability-patterns.md |
| Output escaping patterns and context-specific functions | references/escaping-guide.md |
| Input sanitization patterns by data type | references/sanitization-guide.md |
| Authorization and capability check patterns | references/auth-patterns.md |
| Nonce generation, verification, and CSRF prevention | references/nonce-csrf-guide.md |
Note: For standard security reviews, this SKILL.md contains all patterns needed. Load references when you need comprehensive CWE catalogs, edge cases, or platform-specific security requirements (WordPress VIP, etc.).
© jorgerosal, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 5 other files (references) in claude-skills/wp-security-review of jorgerosal/wordpress-skills.
Open the folder on GitHubat commit 8c96442
Wp Security Review next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Wp Security Review this skilljorgerosal/wordpress-skills | 102 | — | ~6.4k | Automated safety check: Pass | MIT | |
| Code Audit3stoneBrother/code-audit | 892 | 1 repos | ~2.7k | Automated safety check: Pass | None | |
| Security Reviewgithub/awesome-copilot | 40k | 1 repos | ~2.3k | Automated safety check: Notes | MIT | |
| Wordpresssickn33/agentic-awesome-skills | 47k | 2 repos | ~348 | Automated safety check: Pass | MIT | |
| Php Wordpress Audit0xShe/PHP-Code-Audit-Skill | 402 | 1 repos | ~666 | Automated safety check: Pass | None | |
| Security Reviewtrycompai/comp | 2k | — | ~853 | Automated safety check: Pass | AGPL-3.0 |
3stoneBrother/code-audit
Professional code security audit skill covering 55+ vulnerability types.
github/awesome-copilot
AI-powered codebase security scanner that reasons about code like a security researcher — tracing data flows, understanding component interactions, and catching vulnerabilities that pattern-matching…
sickn33/agentic-awesome-skills
Complete WordPress development workflow covering theme development, plugin creation, WooCommerce integration, performance optimization, and security hardening.
0xShe/PHP-Code-Audit-Skill
WordPress 框架特效安全审计工具。针对 WordPress 常见 nonce/capability/checkadminreferer、AJAX action、escape/sanitize、重定向、安全上传与远程请求等机制进行白盒静态审计,并映射到通用漏洞类型体系(AUTH/CSRF/XSS/SQL/CFG/SSRF 等)。
trycompai/comp
Check code for the most common, high-risk security vulnerabilities (broken access control, tenant isolation, injection, secrets, SSRF, auth/session, unsafe file handling, mass assignment) before it…
secondsky/claude-skills
WordPress plugin development with hooks, security, REST API, custom post types.
jorgerosal/wordpress-skills
WordPress accessibility review for themes, blocks, plugins, and admin interfaces.
jorgerosal/wordpress-skills
WordPress ACF and content modeling review. An agent skill from jorgerosal/wordpress-skills.
jorgerosal/wordpress-skills
WordPress admin UI review and development guidance. An agent skill from jorgerosal/wordpress-skills.
jorgerosal/wordpress-skills
WordPress CI/CD and release engineering review guidance. An agent skill from jorgerosal/wordpress-skills.
jorgerosal/wordpress-skills
Headless WordPress and WPGraphQL review guidance. An agent skill from jorgerosal/wordpress-skills.
jorgerosal/wordpress-skills
WordPress migration and upgrade review. An agent skill from jorgerosal/wordpress-skills.
Categories
WordPress security code review and vulnerability detection. An agent skill from jorgerosal/wordpress-skills. Wp Security Review is an agent skill from jorgerosal/wordpress-skills. WordPress security code review and vulnerability detection.
Wp Security Review fits situations like: reviewing WordPress PHP code for security issues; auditing themes/plugins for vulnerabilities; checking code before launch; analyzing AJAX/REST handlers for exploits.
Run `npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a claude-code`. Or copy the skill folder (claude-skills/wp-security-review in jorgerosal/wordpress-skills) into .claude/skills/wp-security-review in your project. Claude Code loads it when a task matches its description.
Run `npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a codex`. Or copy the skill folder (claude-skills/wp-security-review in jorgerosal/wordpress-skills) into .agents/skills/wp-security-review in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add jorgerosal/wordpress-skills --skill wp-security-review -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/wp-security-review, .gemini/skills/wp-security-review, .github/skills/wp-security-review and .opencode/skills/wp-security-review in your project.
Going by SKILL.md and its folder, Wp Security Review needs credentials named AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY and NONCE_KEY.
SKILL.md names 1 domain. In commands or code: api.wordpress.org; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Wp Security Review is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.4k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 33k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Wp Security Review: Code Audit (3stoneBrother/code-audit, 892 stars), Security Review (github/awesome-copilot, 40k stars), Wordpress (sickn33/agentic-awesome-skills, 47k stars) and Php Wordpress Audit (0xShe/PHP-Code-Audit-Skill, 402 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
jorgerosal (a GitHub user) maintains it in jorgerosal/wordpress-skills, which has 102 GitHub stars. The repository holds 35 skills in this directory. The repository was last updated on June 7, 2026.
Source: jorgerosal/wordpress-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.