diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index cd773f6a9f..011c149c93 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -3660,13 +3660,21 @@ function convertGitUrl(string $gitRepository, string $deploymentType, GithubApp| } } - preg_match('/(?<=:)\d+(?=\/)/', $gitRepository, $matches); + $normalizedRepository = $repository; - if (count($matches) === 1) { - $providerInfo['port'] = $matches[0]; - $gitHost = str($gitRepository)->before(':'); - $gitRepo = str($gitRepository)->after('/'); - $repository = "$gitHost:$gitRepo"; + if (str($normalizedRepository)->contains('://')) { + $parsedRepository = parse_url($normalizedRepository); + + if ($parsedRepository !== false && array_key_exists('port', $parsedRepository)) { + $providerInfo['port'] = (string) $parsedRepository['port']; + } + } else { + preg_match('/^(?[^:]+):(?\d+)\/(?.+)$/', $normalizedRepository, $matches); + + if (! empty($matches['port'])) { + $providerInfo['port'] = $matches['port']; + $repository = "{$matches['host']}:{$matches['path']}"; + } } return [ diff --git a/tests/Feature/ConvertingGitUrlsTest.php b/tests/Feature/ConvertingGitUrlsTest.php index 5bcdea1a1b..96b19fcc96 100644 --- a/tests/Feature/ConvertingGitUrlsTest.php +++ b/tests/Feature/ConvertingGitUrlsTest.php @@ -60,3 +60,47 @@ 'port' => '766', ]); }); + +test('convertGitUrlsForSourceAndSshUrlSchemeWithCustomPort', function () { + $result = convertGitUrl('ssh://git@192.168.56.11:22222/User/Repo.git', 'source', null); + expect($result)->toBe([ + 'repository' => 'ssh://git@192.168.56.11:22222/User/Repo.git', + 'port' => '22222', + ]); +}); + +test('convertGitUrlsForSourceAndSshUrlSchemeWithCustomPortAndIpv6Host', function () { + $result = convertGitUrl('ssh://git@[2001:db8::10]:22222/group/project.git', 'source', null); + expect($result)->toBe([ + 'repository' => 'ssh://git@[2001:db8::10]:22222/group/project.git', + 'port' => '22222', + ]); +}); + +test('convertGitUrlsForDeployKeyAndGithubAppWithCustomPort', function () { + $githubApp = new GithubApp([ + 'html_url' => 'https://github.example.com', + 'custom_user' => 'git', + 'custom_port' => 22222, + ]); + + $result = convertGitUrl('andrasbacsai/coolify-examples.git', 'deploy_key', $githubApp); + expect($result)->toBe([ + 'repository' => 'ssh://git@github.example.com:22222/andrasbacsai/coolify-examples.git', + 'port' => '22222', + ]); +}); + +test('convertGitUrlsForDeployKeyAndGithubAppWithCustomPortAndIpv6Host', function () { + $githubApp = new GithubApp([ + 'html_url' => 'https://[2001:db8::10]', + 'custom_user' => 'git', + 'custom_port' => 22222, + ]); + + $result = convertGitUrl('andrasbacsai/coolify-examples.git', 'deploy_key', $githubApp); + expect($result)->toBe([ + 'repository' => 'ssh://git@[2001:db8::10]:22222/andrasbacsai/coolify-examples.git', + 'port' => '22222', + ]); +}); diff --git a/tests/Unit/ApplicationGitSecurityTest.php b/tests/Unit/ApplicationGitSecurityTest.php index 3603b18db2..f983d4bf06 100644 --- a/tests/Unit/ApplicationGitSecurityTest.php +++ b/tests/Unit/ApplicationGitSecurityTest.php @@ -99,3 +99,23 @@ // The malicious payload should be escaped (escapeshellarg wraps and escapes quotes) expect($command)->toContain("'https://github.com/user/repo.git'\\''"); }); + +it('preserves ssh scheme URLs with custom ports in deploy_key commands', function () { + $deploymentUuid = 'test-deployment-uuid'; + + $application = new Application; + $application->git_branch = 'master'; + $application->git_repository = 'ssh://git@192.168.56.11:22222/User/Repo.git'; + $application->private_key_id = 1; + + $privateKey = new PrivateKey; + $privateKey->private_key = 'fake-private-key'; + $application->setRelation('private_key', $privateKey); + + $result = $application->generateGitLsRemoteCommands($deploymentUuid, false); + + expect($result['commands']) + ->toContain("'ssh://git@192.168.56.11:22222/User/Repo.git'") + ->toContain('-p 22222') + ->not->toContain('ssh:/git@192.168.56.11:22222/User/Repo.git'); +});