mirror of
https://github.com/coollabsio/coolify.git
synced 2026-06-14 03:19:51 +00:00
34f15c106c
Strip leading port segments from scp-style GitLab repository URLs so manual webhook matching compares the repository path consistently. Cover both ported and unported SSH URL forms.
109 lines
3.2 KiB
PHP
109 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Webhook\Concerns;
|
|
|
|
use App\Models\Application;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Str;
|
|
|
|
trait MatchesManualWebhookApplications
|
|
{
|
|
protected function manualWebhookRepositoryFullName(mixed $fullName): ?string
|
|
{
|
|
if (! is_string($fullName)) {
|
|
return null;
|
|
}
|
|
|
|
$fullName = trim($fullName, " \t\n\r\0\x0B/");
|
|
|
|
if ($fullName === '') {
|
|
return null;
|
|
}
|
|
|
|
if (! preg_match('/\A[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)+\z/', $fullName)) {
|
|
return null;
|
|
}
|
|
|
|
return $this->normalizeManualWebhookRepositoryPath($fullName);
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, Application>
|
|
*/
|
|
protected function manualWebhookApplications(Builder $query, string $fullName): Collection
|
|
{
|
|
return $query->get()
|
|
->filter(fn (Application $application): bool => $this->manualWebhookRepositoryMatches($application->git_repository, $fullName))
|
|
->values();
|
|
}
|
|
|
|
protected function manualWebhookRepositoryMatches(?string $gitRepository, string $fullName): bool
|
|
{
|
|
$repositoryPath = $this->canonicalManualWebhookRepository($gitRepository);
|
|
|
|
if ($repositoryPath === null) {
|
|
return false;
|
|
}
|
|
|
|
// Git hosts (GitHub, GitLab, Gitea, Bitbucket) treat owner/repo names
|
|
// case-insensitively, so compare the canonical paths case-insensitively.
|
|
return hash_equals(mb_strtolower($fullName), mb_strtolower($repositoryPath));
|
|
}
|
|
|
|
/**
|
|
* @return array{status: string, message: string}
|
|
*/
|
|
protected function unauthenticatedManualWebhookFailurePayload(): array
|
|
{
|
|
return [
|
|
'status' => 'failed',
|
|
'message' => 'Invalid signature.',
|
|
];
|
|
}
|
|
|
|
protected function canonicalManualWebhookRepository(?string $gitRepository): ?string
|
|
{
|
|
if (! is_string($gitRepository)) {
|
|
return null;
|
|
}
|
|
|
|
$gitRepository = trim($gitRepository);
|
|
|
|
if ($gitRepository === '') {
|
|
return null;
|
|
}
|
|
|
|
$path = null;
|
|
$parts = parse_url($gitRepository);
|
|
|
|
if (is_array($parts) && isset($parts['scheme'])) {
|
|
$path = data_get($parts, 'path');
|
|
} elseif (Str::startsWith($gitRepository, 'git@') && str_contains($gitRepository, ':')) {
|
|
$path = Str::after($gitRepository, ':');
|
|
// scp-style SSH URLs embed a custom port as "git@host:2222/owner/repo".
|
|
// Strip the leading numeric port segment so the path matches the webhook
|
|
// payload's owner/repo, consistent with convertGitUrl() in shared.php.
|
|
$path = preg_replace('#^\d+/#', '', $path) ?? $path;
|
|
} else {
|
|
$path = $gitRepository;
|
|
}
|
|
|
|
if (! is_string($path) || $path === '') {
|
|
return null;
|
|
}
|
|
|
|
return $this->normalizeManualWebhookRepositoryPath($path);
|
|
}
|
|
|
|
protected function normalizeManualWebhookRepositoryPath(string $path): string
|
|
{
|
|
$path = trim($path);
|
|
$path = strtok($path, '?#') ?: $path;
|
|
$path = trim($path, '/');
|
|
$path = preg_replace('/\.git\z/i', '', $path) ?? $path;
|
|
|
|
return $path;
|
|
}
|
|
}
|