sanitizeHttpUrl($clientBaseUrl); if ($normalized !== null) { return $normalized; } } $referer = $request->getHeaderLine('Referer'); if ($referer !== '') { $parts = parse_url($referer); if (!empty($parts['scheme']) && !empty($parts['host'])) { $origin = $parts['scheme'] . '://' . $parts['host']; if (!empty($parts['port'])) { $origin .= ':' . $parts['port']; } $path = $parts['path'] ?? ''; foreach ($stripSuffixes as $suffix) { if ($suffix !== '' && str_ends_with($path, $suffix)) { $path = substr($path, 0, -\strlen($suffix)); break; } } $normalized = $this->sanitizeHttpUrl($origin . rtrim($path, '/')); if ($normalized !== null) { return $normalized; } } } $origin = $request->getHeaderLine('Origin'); if ($origin !== '') { $basePath = (string) $this->grav['uri']->rootUrl(false); $normalized = $this->sanitizeHttpUrl(rtrim($origin, '/') . $basePath); if ($normalized !== null) { return $normalized; } } // Last resort: Grav's own root URL. Wrong in dev when admin-next runs // on a separate origin, but at least a valid URL. return rtrim((string) $this->grav['uri']->rootUrl(true), '/'); } protected function sanitizeHttpUrl(string $url): ?string { $url = trim($url); if ($url === '') { return null; } $parts = parse_url($url); if (empty($parts['scheme']) || empty($parts['host'])) { return null; } if (!in_array(strtolower($parts['scheme']), ['http', 'https'], true)) { return null; } return rtrim($url, '/'); } }