APP_ANALYTICS_CONSENT_VERSION . ':granted', APP_ANALYTICS_VISITOR_COOKIE => $visitorId, ]; $server = [ 'HTTP_HOST' => 'example.test:8765', 'HTTP_ORIGIN' => 'http://example.test:8765', 'HTTP_SEC_FETCH_SITE' => 'same-origin', 'SERVER_PORT' => '8765', 'HTTP_USER_AGENT' => 'Relay-Testbrowser/1.0', 'REMOTE_ADDR' => '203.0.113.42', ]; $payload = [ 'visitorId' => $visitorId, 'pageUrl' => 'http://example.test:8765/passwort-zuruecksetzen.php?token=GEHEIM#formular', 'pageTitle' => 'Kaffeeliste Passwort zurücksetzen', 'referrerUrl' => 'http://app.example.test/login.php?source=GEHEIM', 'resolution' => '1440x900', 'language' => 'de-DE', 'h' => 12, 'm' => 34, 's' => 56, ]; $parameters = app_matomo_page_view_parameters($payload, $server, $cookies); $assert('Seiten-URL verliert Query und Fragment', ($parameters['url'] ?? '') === 'http://example.test:8765/passwort-zuruecksetzen.php'); $assert('Interner Referrer verliert Query und Fragment', ($parameters['urlref'] ?? '') === 'http://app.example.test/login.php'); $assert('Zufällige Besucherkennung wird unverändert genutzt', ($parameters['_id'] ?? '') === $visitorId); $assert('Browserdaten werden auf erlaubte Angaben begrenzt', ($parameters['res'] ?? '') === '1440x900' && ($parameters['lang'] ?? '') === 'de-DE' && ($parameters['ua'] ?? '') === 'Relay-Testbrowser/1.0'); $assert('Client-IP wird nicht an Matomo übergeben', !array_key_exists('cip', $parameters) && !in_array('203.0.113.42', $parameters, true)); $capturedUrl = ''; $capturedParameters = []; $forwardResult = app_matomo_forward_page_view( $parameters, static function (string $url, array $values) use (&$capturedUrl, &$capturedParameters): array { $capturedUrl = $url; $capturedParameters = $values; return ['ok' => true, 'status' => 204, 'error' => null]; } ); $assert('Relay verwendet den fest konfigurierten Matomo-Endpunkt', $forwardResult['ok'] && $capturedUrl === 'http://analytics.example.test/matomo.php' && ($capturedParameters['idsite'] ?? '') === '7'); $mustReject = static function (array $changedPayload, array $changedServer, array $changedCookies): bool { try { app_matomo_page_view_parameters($changedPayload, $changedServer, $changedCookies); return false; } catch (Throwable) { return true; } }; $deniedCookies = $cookies; $deniedCookies[APP_ANALYTICS_CONSENT_COOKIE] = APP_ANALYTICS_CONSENT_VERSION . ':denied'; $assert('Ohne Einwilligung wird der Aufruf verworfen', $mustReject($payload, $server, $deniedCookies)); $foreignServer = $server; $foreignServer['HTTP_ORIGIN'] = 'https://evil.example'; $foreignServer['HTTP_SEC_FETCH_SITE'] = 'cross-site'; $assert('Fremder Ursprung wird verworfen', $mustReject($payload, $foreignServer, $cookies)); $foreignPage = $payload; $foreignPage['pageUrl'] = 'https://evil.example/gestohlen?token=1'; $assert('Fremde Seiten-URL wird verworfen', $mustReject($foreignPage, $server, $cookies)); $wrongVisitor = $payload; $wrongVisitor['visitorId'] = 'fedcba9876543210'; $assert('Nicht zum Cookie passende Besucherkennung wird verworfen', $mustReject($wrongVisitor, $server, $cookies)); $relaySource = file_get_contents(APP_ROOT . '/app/analytics-relay.php') ?: ''; $assert('Relay-Code liest die Client-IP nicht', !str_contains($relaySource, "['REMOTE_ADDR']")); foreach ($previousEnvironment as $name => $value) { $value === false ? putenv($name) : putenv($name . '=' . $value); } if ($failures > 0) { echo "\n{$failures} Relay-Prüfung(en) fehlgeschlagen.\n"; exit(1); } echo "\nFirst-Party-Matomo-Relay: alle Prüfungen bestanden.\n";