<?php
declare(strict_types=1);
if (!defined('STORE_ROOT')) { http_response_code(403); exit; }
function telemetryHandler(string $action, array $in): void { App::requirePost(); Security::enforceCSRF(); Security::enforceRateLimit('telemetry', 60, 60); $ip = $_SERVER['REMOTE_ADDR'] ?? ''; $ua = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 512); $token = substr($in['token'] ?? ($_SESSION['token'] ?? ''), 0, 64); if (!$token) $token = hash('sha256', $ip . $ua . date('YmdH')); $ipHash = hash('sha256', $ip); $score = 50; $score -= min(30, (int) ($in['sourceCodeViews'] ?? 0) * 15); $score -= ($in['devToolsDetected'] ?? false) ? 20 : 0; $score -= min(30, (int) ($in['consoleOpens'] ?? 0) * 10); $score -= min(20, (int) ($in['elementInspects'] ?? 0) * 10); $score -= min(25, (int) ($in['rightClicks'] ?? 0) * 5); $score -= ($in['headless'] ?? false) ? 30 : 0; $score -= min(50, (int) ($in['botScore'] ?? 0)); $score -= min(50, (int) ($in['blackholeTriggers'] ?? 0) * 50); $score -= min(15, (int) ($in['copyAttempts'] ?? 0) * 5); $score -= min(15, (int) ($in['printAttempts'] ?? 0) * 5); $score += ($in['hasAccount'] ?? false) ? 10 : 0; $score += min(10, (int) ($in['timeOnPage'] ?? 0) / 12); $score += ((int) ($in['scrollDepth'] ?? 0) > 50) ? 3 : 0; $score += min(10, (int) ($in['formInteractions'] ?? 0) * 2); $score = max(0, min(100, $score)); $admins = array_map('trim', explode(',', App::conf('ADMIN_IPS', ''))); if (in_array($ip, $admins, true) && $score < 60) $score = 60; $tier = match(true) { $score <= 20 => 'blackhole', $score <= 40 => 'glitch', $score <= 60 => 'slow', $score >= 80 => 'trusted', default => 'normal', }; $fpEnc = json_encode(['webgl_v' => $in['webglVendor'] ?? '', 'webgl_r' => $in['webglRenderer'] ?? '', 'canvas' => $in['canvasHash'] ?? '']); $bfEnc = json_encode([ 'languages' => $in['languages'] ?? [], 'cores' => $in['cores'] ?? 0, 'memory' => $in['memory'] ?? 0, 'touch' => $in['touchPoints'] ?? 0, 'pdf' => $in['pdfViewer'] ?? false, 'automation' => $in['automationFlags'] ?? [], 'click_avg' => $in['clickIntervalAvg'] ?? 0, 'hover_avg' => $in['hoverAvg'] ?? 0, 'screenshots' => $in['screenshots'] ?? 0, ]); try { DB::run( "INSERT INTO sec_visitor_sessions (session_hash, ip_hash, ip_address, device_fingerprint_enc, page_views, source_views, source_view_time_ms, total_time_ms, trust_score, risk_score, browser_features, screen_resolution, timezone_offset, language, platform_os, has_account, visit_count, first_seen, last_seen)              VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,1,NOW(),NOW())              ON DUPLICATE KEY UPDATE page_views = page_views + VALUES(page_views), source_views = source_views + VALUES(source_views), source_view_time_ms = source_view_time_ms + VALUES(source_view_time_ms), total_time_ms = total_time_ms + VALUES(total_time_ms), trust_score = VALUES(trust_score), risk_score = VALUES(risk_score), last_seen = NOW(), visit_count = visit_count + 1", [ $token, $ipHash, $ip, $fpEnc, (int) ($in['pageViews'] ?? 1), (int) ($in['sourceCodeViews'] ?? 0), (int) ($in['sourceCodeTime'] ?? 0) + (int) ($in['devToolsTime'] ?? 0), (int) ($in['timeOnPage'] ?? 0) * 1000, $score, 100 - $score, $bfEnc, substr($in['screenRes'] ?? '', 0, 20), (int) ($in['tzOffset'] ?? 0), substr(($in['languages'][0] ?? ''), 0, 20), substr($in['platform'] ?? '', 0, 50), ($in['hasAccount'] ?? false) ? 1 : 0, ] ); } catch (\Throwable $e) { error_log('[Telemetry] ' . $e->getMessage()); } App::ok(['tier' => $tier, 'score' => (int) round($score), 'applied' => true]); if ($action === 'legacy_telemetry') { $telemetryData = $in['telemetry'] ?? []; if (isset($telemetryData['page'])) { try { Telemetry::trackPageView($telemetryData['page']); } catch (\Throwable $e) {} } App::ok(['ok' => true]); } if ($action === 'watchdog') { $event = $in['event'] ?? 'unknown'; try { Telemetry::logEvent('security', 'watchdog.' . $event); } catch (\Throwable $e) {} App::ok(['ok' => true]); } }
