__('Insufficient permissions', 'ability')]); } $scan_path = isset($_POST['scan_path']) ? sanitize_text_field($_POST['scan_path']) : ABSPATH; // Run scanner if (class_exists('ABILITY\Scanner')) { $scanner = new \ABILITY\Scanner(); $results = $scanner->scan($scan_path); wp_send_json_success($results); } wp_send_json_error(['message' => __('Scanner not available', 'ability')]); } /** * Save settings. */ public function save_settings() { check_ajax_referer('ability_admin', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('Insufficient permissions', 'ability')]); } $settings = isset($_POST['settings']) ? (array) $_POST['settings'] : []; $sanitized = []; foreach ($settings as $key => $value) { $sanitized[sanitize_key($key)] = sanitize_text_field($value); } if (class_exists('ABILITY\Config')) { \ABILITY\Config::update_settings($sanitized); wp_send_json_success(['message' => __('Settings saved', 'ability')]); } wp_send_json_error(['message' => __('Failed to save settings', 'ability')]); } /** * Get scan results. */ public function get_scan_results() { check_ajax_referer('ability_admin', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('Insufficient permissions', 'ability')]); } global $wpdb; // Sanitize table name - only allow alphanumeric and underscore $table_name = 'ability_scans'; if (!preg_match('/^[a-zA-Z0-9_]+$/', $table_name)) { wp_send_json_error(['message' => __('Invalid table name', 'ability')]); } $table = $wpdb->prefix . $table_name; // Use LIMIT with prepared statement for the limit value // Table name is validated above, so safe to use in query $limit = 100; $results = $wpdb->get_results( $wpdb->prepare("SELECT * FROM `{$table}` ORDER BY created_at DESC LIMIT %d", $limit), ARRAY_A ); wp_send_json_success($results); } /** * Export scan results as CSV with standards mapping. */ public function export_scans_csv() { check_ajax_referer('ability_admin', 'nonce'); if (!current_user_can('manage_options')) { wp_die(__('Insufficient permissions', 'ability')); } global $wpdb; $table_name = 'ability_scans'; if (!preg_match('/^[a-zA-Z0-9_]+$/', $table_name)) { wp_die(__('Invalid table name', 'ability')); } $table = $wpdb->prefix . $table_name; $rows = $wpdb->get_results($wpdb->prepare("SELECT * FROM `{$table}` ORDER BY created_at DESC LIMIT %d", 2000), ARRAY_A); header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=ability-scan-report.csv'); $out = fopen('php://output', 'w'); fputcsv($out, [ 'file_path','issue_type','severity','message','line_number','wcag_sc','technique','standard','section508','tool_version','ruleset_version','created_at' ]); foreach ($rows as $r) { fputcsv($out, [ $r['file_path'] ?? '', $r['issue_type'] ?? '', $r['severity'] ?? '', $r['message'] ?? '', $r['line_number'] ?? '', $r['wcag_sc'] ?? '', $r['technique'] ?? '', $r['standard'] ?? '', $r['section508'] ?? '', $r['tool_version'] ?? '', $r['ruleset_version'] ?? '', $r['created_at'] ?? '', ]); } fclose($out); exit; } /** * Export scan results as JSON with standards mapping. */ public function export_scans_json() { check_ajax_referer('ability_admin', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('Insufficient permissions', 'ability')]); } global $wpdb; $table_name = 'ability_scans'; if (!preg_match('/^[a-zA-Z0-9_]+$/', $table_name)) { wp_send_json_error(['message' => __('Invalid table name', 'ability')]); } $table = $wpdb->prefix . $table_name; $rows = $wpdb->get_results($wpdb->prepare("SELECT * FROM `{$table}` ORDER BY created_at DESC LIMIT %d", 2000), ARRAY_A); $payload = [ 'tool' => 'ability-php-scanner', 'generated_at' => gmdate('c'), 'results' => $rows, ]; header('Content-Type: application/json; charset=utf-8'); header('Content-Disposition: attachment; filename=ability-scan-report.json'); echo wp_json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); exit; } }