| 1 |
<?php |
| 2 |
namespace NotificationX\Admin\Scanner; |
| 3 |
|
| 4 |
use NotificationX\Core\Helper; |
| 5 |
use NotificationX\GetInstance; |
| 6 |
use NotificationX\NotificationX; |
| 7 |
use WP_REST_Server; |
| 8 |
use WP_REST_Request; |
| 9 |
use WP_REST_Response; |
| 10 |
|
| 11 |
class Scanner |
| 12 |
{ |
| 13 |
use GetInstance; |
| 14 |
private static $_namespace = 'notificationx'; |
| 15 |
private static $_version = 1; |
| 16 |
private static $_apiBase = ""; |
| 17 |
private static $is_pro = false; |
| 18 |
public function __construct() |
| 19 |
{ |
| 20 |
add_action('rest_api_init', [$this, 'rest_init']); |
| 21 |
if( NotificationX::is_pro() ) { |
| 22 |
self::$is_pro = true; |
| 23 |
} |
| 24 |
self::$_apiBase = (defined('NX_DEBUG') && NX_DEBUG) |
| 25 |
? 'https://notificationx-api.test/cookie-scanner/v1' |
| 26 |
: 'https://api.notificationx.com/cookie-scanner/v1'; |
| 27 |
} |
| 28 |
|
| 29 |
public static function _namespace() |
| 30 |
{ |
| 31 |
return self::$_namespace . '/v' . self::$_version; |
| 32 |
} |
| 33 |
|
| 34 |
/** |
| 35 |
* Registers custom REST API endpoints for scan initiation, scan status, and scan history. |
| 36 |
* |
| 37 |
* - /scan: Initiates a new scan based on the provided URL. |
| 38 |
* - /scan/status: Retrieves the status of a scan using the provided scan ID. |
| 39 |
* - /scan/history: Fetches the scan history based on the notification ID (nx_id). |
| 40 |
* |
| 41 |
* @return void |
| 42 |
*/ |
| 43 |
public function rest_init() |
| 44 |
{ |
| 45 |
$namespace = self::_namespace(); |
| 46 |
|
| 47 |
// Register the scan initiation endpoint |
| 48 |
register_rest_route($namespace, '/scan', array( |
| 49 |
'methods' => WP_REST_Server::CREATABLE, |
| 50 |
'callback' => array($this, 'initiate_scan'), |
| 51 |
'permission_callback' => array($this, 'edit_permission'), |
| 52 |
)); |
| 53 |
|
| 54 |
// Register the scan status endpoint |
| 55 |
register_rest_route($namespace, '/scan/status', array( |
| 56 |
'methods' => WP_REST_Server::EDITABLE, |
| 57 |
'callback' => array($this, 'check_scan_status'), |
| 58 |
'permission_callback' => array($this, 'edit_permission'), |
| 59 |
)); |
| 60 |
|
| 61 |
} |
| 62 |
|
| 63 |
/** |
| 64 |
* Permission check for the scanner routes. |
| 65 |
* The cookie scanner is only used from the notification builder, |
| 66 |
* which requires the edit_notificationx capability. |
| 67 |
* |
| 68 |
* @return bool |
| 69 |
*/ |
| 70 |
public function edit_permission() |
| 71 |
{ |
| 72 |
return current_user_can('edit_notificationx'); |
| 73 |
} |
| 74 |
|
| 75 |
/** |
| 76 |
* Initiates a scan process for the provided URL. |
| 77 |
* |
| 78 |
* Validates the URL parameter and triggers the scan if it's valid. |
| 79 |
* Responds with the scan status. |
| 80 |
* |
| 81 |
* @param WP_REST_Request $request The REST request object containing the URL. |
| 82 |
* |
| 83 |
* @return WP_REST_Response The response indicating the status of the scan initiation. |
| 84 |
*/ |
| 85 |
public function initiate_scan(WP_REST_Request $request) |
| 86 |
{ |
| 87 |
$url = $request->get_param('url'); |
| 88 |
|
| 89 |
// Check if the URL parameter is provided |
| 90 |
if (empty($url)) { |
| 91 |
return new WP_REST_Response(['error' => 'URL parameter is required'], 200); |
| 92 |
} |
| 93 |
|
| 94 |
// Trigger the scan process (implement this function as needed) |
| 95 |
$this->trigger_scan($url); |
| 96 |
|
| 97 |
return new WP_REST_Response(['status' => 'Scan started.'], 200); |
| 98 |
} |
| 99 |
|
| 100 |
/** |
| 101 |
* Retrieves the current status of a scan based on the provided scan ID. |
| 102 |
* |
| 103 |
* Queries the status from an external service and returns the results. |
| 104 |
* If the scan is completed, it processes the result and inserts cookie and stats entries into the database. |
| 105 |
* |
| 106 |
* @param WP_REST_Request $request The REST request object containing the scan ID. |
| 107 |
* |
| 108 |
* @return WP_REST_Response The response with the scan status or an error message. |
| 109 |
*/ |
| 110 |
public function check_scan_status(WP_REST_Request $request) |
| 111 |
{ |
| 112 |
$scanId = $request->get_param('scan_id'); |
| 113 |
|
| 114 |
// Retrieve the scan status from the database (implement this function as needed) |
| 115 |
$status = $this->get_scan_status($scanId); |
| 116 |
|
| 117 |
// Handle invalid scan ID |
| 118 |
if ($status === null && empty( $scanId )) { |
| 119 |
$data = [ 'status' => 'failed', 'message' => __('Invalid scan ID','notificationx') ]; |
| 120 |
return new WP_REST_Response(['data' => $data ], 404); |
| 121 |
} |
| 122 |
|
| 123 |
// Process the scan result if the status is 'completed' |
| 124 |
if ( is_array($status) && !empty($status['status']) && $status['status'] === 'completed') { |
| 125 |
// Update scan count to the options |
| 126 |
$pre_count = get_option('nx_scan_count', 0); |
| 127 |
update_option('nx_scan_count', $pre_count + 1); |
| 128 |
update_option('nx_scan_date', Helper::nx_get_current_datetime() ); |
| 129 |
|
| 130 |
$cookies = $status['result']; // Extract scanned cookies |
| 131 |
$stats = $status['stats']; // Extract scan statistics |
| 132 |
|
| 133 |
// Use the helper function to categorize cookies and get the category count |
| 134 |
$cookieData = $this->categorizeCookiesAndCount($cookies); |
| 135 |
$categoryCount = $cookieData['category_count']; |
| 136 |
$categorized = $cookieData['categorized']; |
| 137 |
$data = [ 'last_scan_date' => Helper::nx_get_current_datetime(), 'status' => 'completed', 'stats' => $stats, 'cookies' => $cookies, 'category_count' => $categoryCount, 'categorized' => $categorized ]; |
| 138 |
return new WP_REST_Response(['data' => $data], 200); |
| 139 |
} |
| 140 |
|
| 141 |
return new WP_REST_Response(['data' => $status], 200); |
| 142 |
} |
| 143 |
|
| 144 |
/** |
| 145 |
* Triggers a scan process by making an HTTP GET request to an external API. |
| 146 |
* |
| 147 |
* Sends the provided URL to the external scan API and handles the response. |
| 148 |
* |
| 149 |
* @param string $url The URL to be scanned. |
| 150 |
* |
| 151 |
* @return WP_REST_Response The response containing the result of the scan. |
| 152 |
*/ |
| 153 |
private function trigger_scan($url) |
| 154 |
{ |
| 155 |
// API endpoint that will process the scan |
| 156 |
if( self::$is_pro ) { |
| 157 |
$apiEndpoint = self::$_apiBase . "?nxpro=active&url=" . urlencode($url); |
| 158 |
}else{ |
| 159 |
$apiEndpoint = self::$_apiBase . "?url=" . urlencode($url); |
| 160 |
} |
| 161 |
|
| 162 |
// Make an HTTP GET request |
| 163 |
$response = wp_remote_get($apiEndpoint, [ |
| 164 |
'timeout' => 200, // Set timeout (adjust as needed) |
| 165 |
'sslverify' => false, // Bypass SSL verification temporarily (for local dev) |
| 166 |
]); |
| 167 |
|
| 168 |
// Check for errors in the response |
| 169 |
if (is_wp_error($response)) { |
| 170 |
return new WP_REST_Response(['error' => $response->get_error_message()], 500); |
| 171 |
} |
| 172 |
|
| 173 |
// Decode response body |
| 174 |
$responseBody = json_decode(wp_remote_retrieve_body($response), true); |
| 175 |
|
| 176 |
return wp_send_json_success($responseBody); |
| 177 |
} |
| 178 |
|
| 179 |
/** |
| 180 |
* Retrieves the current status of a scan from the external scan API using the scan ID. |
| 181 |
* |
| 182 |
* Makes an HTTP GET request to the scan API to fetch the status of the scan. |
| 183 |
* |
| 184 |
* @param string $scanId The scan ID to check the status of. |
| 185 |
* |
| 186 |
* @return array The scan status data from the external API. |
| 187 |
*/ |
| 188 |
private function get_scan_status($scanId) |
| 189 |
{ |
| 190 |
// API endpoint to check the scan status |
| 191 |
$apiEndpoint = self::$_apiBase . "/status.php?scan_id=" . urlencode($scanId); |
| 192 |
|
| 193 |
// Make an HTTP GET request |
| 194 |
$response = wp_remote_get($apiEndpoint, [ |
| 195 |
'timeout' => 200, // Set timeout (adjust as needed) |
| 196 |
'sslverify' => false, // Bypass SSL verification temporarily (for local dev) |
| 197 |
]); |
| 198 |
|
| 199 |
// Check for errors in the response |
| 200 |
if (is_wp_error($response)) { |
| 201 |
return new WP_REST_Response(['error' => $response->get_error_message()], 500); |
| 202 |
} |
| 203 |
|
| 204 |
// Decode response body |
| 205 |
$responseBody = json_decode(wp_remote_retrieve_body($response), true); |
| 206 |
return $responseBody; |
| 207 |
} |
| 208 |
|
| 209 |
|
| 210 |
// Helper function to categorize cookies and calculate category count |
| 211 |
public function categorizeCookiesAndCount($cookies) { |
| 212 |
// Define cookie categories |
| 213 |
$cookieCategoryPrefix = [ |
| 214 |
'necessary' => ['PHPSESSID', 'wordpress_logged_in', 'wp-settings', 'wp-settings-time', 'wpEmojiSettingsSupports', 'cookieyes-consent', 'elementor', 'csrftoken', 'auth', 'session', 'secure', 'cart', 'checkout', 'wp_woocommerce'], |
| 215 |
'functional' => ['lang', 'preferences', 'remember_me', 'theme', 'consent', 'locale', 'user_settings', 'cookie_preference'], |
| 216 |
'analytics' => ['_ga', '_gid', '_gat', 'fbp', 'utm', 'amplitude', 'mixpanel', 'hotjar', 'segment', 'ahoy', 'kissmetrics', 'analytics', 'visitor_id', 'sbjs_udata', 'sbjs_current', 'sbjs_first', 'sbjs_first_add', 'sbjs_current_add'], |
| 217 |
'performance' => ['_hj', 'cf_use_ob', 'cf_clearance', 'AWSALB', 'load_balancer', 'page_speed', 'cdn_cache', 'pingdom', 'new_relic'], |
| 218 |
'advertisement' => ['ads', '_fbp', '_gcl', '_dc_gtm', 'doubleclick', 'IDE', 'adroll', 'criteo', 'twitter_ads', 'bing_ads', 'remarketing', 'test_cookie'], |
| 219 |
]; |
| 220 |
|
| 221 |
// Initialize category counts |
| 222 |
$categorized = [ |
| 223 |
'necessary' => 0, |
| 224 |
'functional' => 0, |
| 225 |
'analytics' => 0, |
| 226 |
'performance' => 0, |
| 227 |
'advertisement' => 0, |
| 228 |
]; |
| 229 |
|
| 230 |
// Categorize cookies based on partial matching of cookie names (prefixes) |
| 231 |
if (!empty($cookies) && is_array($cookies)) { |
| 232 |
foreach ($cookies as $cookie) { |
| 233 |
foreach ($cookieCategoryPrefix as $category => $cookieNames) { |
| 234 |
foreach ($cookieNames as $cookieName) { |
| 235 |
if (isset($cookie['name']) && strpos($cookie['name'], $cookieName) !== false) { // Check if cookie contains the prefix |
| 236 |
$categorized[$category]++; |
| 237 |
break; // No need to check other prefixes for this cookie |
| 238 |
} |
| 239 |
} |
| 240 |
} |
| 241 |
} |
| 242 |
} |
| 243 |
|
| 244 |
// Calculate the category count (only categories with count > 0) |
| 245 |
$categoryCount = count(array_filter($categorized, function($count) { |
| 246 |
return $count > 0; |
| 247 |
})); |
| 248 |
|
| 249 |
return [ |
| 250 |
'category_count' => $categoryCount, |
| 251 |
'categorized' => $categorized |
| 252 |
]; |
| 253 |
} |
| 254 |
|
| 255 |
} |
| 256 |
?> |
| 257 |
|