4.8 KiB
Pre-Deploy AI Security Audit — v1.1.1
Date: 2026-06-08 Plugin: Documentation Markdown (by ROBOTSTXT) Version: 1.1.0 → 1.1.1 Auditor: Claude Sonnet (Senior WordPress Plugin Security Auditor role) Scope: Full codebase (all 7 PHP plugin files)
Executive Summary
Overall status: CONDITIONAL PASS → PASS (after fixes applied)
All [CRITICAL] findings were resolved before tagging. No SQL injection, no CSRF gaps, no direct object reference, no privilege escalation vectors found. Access is gated behind edit_pages throughout with both UI-level and execution-level checks.
Security risk: Low (after fixes) Version recommendation: stable
Findings Resolved Before Release
[CRITICAL → FIXED] Finding 011 — CommonMark raw HTML passthrough
File: robotstxt-documentation-markdown-map.php:296
Problem: CommonMark's default config allows raw HTML blocks from Markdown. A compromised upstream GitHub repository could inject <script>, <iframe>, or event handlers into synced WordPress post content.
Fix applied: html_input => 'strip', allow_unsafe_links => false in CommonMarkConverter config.
[WARNING → FIXED] Finding 002 — base64 decode without length validation
File: robotstxt-documentation-markdown-functions.php:55
Problem: base64_decode() result not length-checked before substr() IV extraction. Tampered options could cause silent decryption failures or short-IV oracle scenarios.
Fix applied: Added false === $decoded || strlen($decoded) <= $iv_length guard.
[WARNING → FIXED] Finding 008 — Settings option autoloaded
File: robotstxt-documentation-markdown.php:72, 1164
Problem: Encrypted GitHub token loaded with every WordPress request via autoload.
Fix applied: add_option(..., '', false) and update_option(..., false).
[WARNING → FIXED] Finding 018 — wp_update_post() return unchecked
File: robotstxt-documentation-markdown-map.php:324
Problem: Sync silently reported success even if post update failed.
Fix applied: Return value checked; error returned on failure.
[WARNING → FIXED] Finding 020 — base64_decode() unchecked in GitHub fetch
File: robotstxt-documentation-markdown-github.php:289
Problem: Malformed GitHub API response (bad base64) would cause silent empty post.
Fix applied: Added false === $decoded guard returning WP_Error.
[BUG → FIXED] Admin notices not displayed
File: robotstxt-documentation-markdown.php:908
Problem: Messages for mapping_created, mapping_updated, mapping_deleted, sync_complete, and error parameters were redirected to URL but never rendered.
Fix applied: Complete message map added to robotstxt_docmd_render_admin_notices().
Findings Deferred to Future Release
| ID | Severity | Description | Target |
|---|---|---|---|
| 001 | WARNING | Token key derivation should use HKDF instead of raw wp_salt() — would require token migration |
v1.2.0 |
| 003 | WARNING | Debug action nonce/page checks split across two if blocks — refactoring risk |
v1.2.0 |
| 005 | INFO | esc_js() in onclick pattern — safe with fixed string but fragile for future edits |
v1.2.0 |
| 006 | WARNING | get_posts(post_type=any, limit=100) on Add Mapping page — performance on large sites |
v1.2.0 |
| 009 | INFO | GitHub API URL: branch/path not rawurlencode()d — no current attack vector, defensive hardening |
v1.2.0 |
| 010 | INFO | target_post_type not validated against registered post types at save |
v1.2.0 |
| 013 | INFO | Debug "cached vs fresh" indicator always shows cached after first fetch | v1.2.0 |
| 014 | INFO | Discover page refresh=1 lacks nonce — forces GitHub API call via CSRF |
v1.2.0 |
Deferred findings documented in docs/known-issues.md.
Positive Findings (No Action Required)
- All state-changing actions protected with nonces (
wp_nonce_field,wp_nonce_url,check_admin_referer,wp_verify_nonce) - Capability checks (
current_user_can('edit_pages')) applied in both UI rendering and execution logic for all admin pages and handlers - All
$_POST/$_GETaccess goes throughrobotstxt_docmd_input_string()/robotstxt_docmd_input_int()type-narrowing helpers - All output escaping at render time (
esc_html,esc_attr,esc_url,esc_js,wp_kses_post) - All redirects via
wp_safe_redirect()toadmin_url()destinations - No raw SQL; custom SQL (transient cleanup) uses
$wpdb->prepare()with$wpdb->esc_like() - ABSPATH guard on every PHP file
- No
eval(), no suspiciousbase64_decode()(documented necessary use for GitHub API decoding), no shell functions - GitHub token encrypted at rest (AES-256-CBC, key from
wp_salt('auth')) uninstall.phpusesWP_UNINSTALL_PLUGINguard and respects user data-preservation preference