| @@ -119,11 +119,12 @@ | ||
| 119 | 119 | return false; |
| 120 | 120 | } |
| 121 | 121 | |
| 122 | 122 | // Check if it's a ThinkRank admin page |
| 123 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reading page parameter for screen detection, not processing form data. | |
| 123 | 124 | return strpos($screen->id, 'thinkrank') !== false || |
| 124 | - strpos($screen->base, 'thinkrank') !== false || | |
| 125 | - (isset($_GET['page']) && strpos($_GET['page'], 'thinkrank') !== false); | |
| 125 | + strpos($screen->base, 'thinkrank') !== false || | |
| 126 | + (isset($_GET['page']) && strpos(sanitize_text_field(wp_unslash($_GET['page'])), 'thinkrank') !== false); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 126 | 127 | } |
| 127 | 128 | |
| 128 | 129 | /** |
| 129 | 130 | * Check if request is to a ThinkRank API endpoint |
| @@ -150,13 +151,17 @@ | ||
| 150 | 151 | $admin_url = admin_url(); |
| 151 | 152 | |
| 152 | 153 | $policies = [ |
| 153 | 154 | "default-src 'self'", |
| 154 | - "script-src 'self' 'unsafe-inline' 'unsafe-eval' {$site_url} {$admin_url}", | |
| 155 | + // 'unsafe-eval' dropped — the React admin bundle doesn't need it. We | |
| 156 | + // still allow 'unsafe-inline' because WordPress core emits unnonced | |
| 157 | + // inline admin scripts; moving the SPA to nonces/hashes is tracked | |
| 158 | + // separately and can't be done without breaking core admin output. | |
| 159 | + "script-src 'self' 'unsafe-inline' {$site_url} {$admin_url}", | |
| 155 | 160 | "style-src 'self' 'unsafe-inline' {$site_url} {$admin_url}", |
| 156 | 161 | "img-src 'self' data: {$site_url}", |
| 157 | 162 | "font-src 'self' {$site_url}", |
| 158 | - "connect-src 'self' {$site_url}", | |
| 163 | + "connect-src 'self' {$site_url} https://api.openai.com https://api.anthropic.com https://generativelanguage.googleapis.com", | |
| 159 | 164 | "frame-src 'none'", |
| 160 | 165 | "object-src 'none'", |
| 161 | 166 | "base-uri 'self'" |
| 162 | 167 | ]; |