From 2b08313cda900e673ec9ff41772347de11e32393 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Fri, 22 May 2026 15:26:10 +0100 Subject: [PATCH] Use parse_url + str_ends_with for endpoint check Substring matching on the full URL hit false positives in three ways: the domain in a query string, a `customer.com.attacker.io` suffix attack, and `fake-customer.com` superstring matches. Parse the host out of the URL and match against `_APP_DOMAIN` exactly or as a `.`-prefixed suffix (to allow region subdomains like `fra.localhost`). --- src/Appwrite/Platform/Workers/Migrations.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index 121ec233d6..d73b72c503 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -208,11 +208,15 @@ class Migrations extends Action $this->sourceProject = $this->dbForPlatform->getDocument('projects', $credentials['projectId']); // Same projectId on source and destination only means "local" when the source - // endpoint points at this installation — otherwise it's an external Appwrite + // endpoint's host is this installation — otherwise it's an external Appwrite // that happens to share an id, and we must go over SDK/HTTP. + $sourceHost = parse_url($credentials['endpoint'] ?? '', PHP_URL_HOST); + $localDomain = System::getEnv('_APP_DOMAIN', ''); + $isLocalEndpoint = is_string($sourceHost) && $localDomain !== '' + && ($sourceHost === $localDomain || str_ends_with($sourceHost, '.' . $localDomain)); + $isLocalSource = !$this->sourceProject->isEmpty() - && (!$isAppwriteToAppwrite - || str_contains($credentials['endpoint'] ?? '', System::getEnv('_APP_DOMAIN', '_'))); + && (!$isAppwriteToAppwrite || $isLocalEndpoint); if ($isLocalSource) { $projectDB = call_user_func($this->getProjectDB, $this->sourceProject);