From b84ae82f603442ca8d669ee7b66b3d56511cc90d Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 24 Aug 2026 03:05:36 -0600 Subject: [PATCH 1/3] Update helpers --- app/Util/ActivityPub/Helpers.php | 62 +++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/app/Util/ActivityPub/Helpers.php b/app/Util/ActivityPub/Helpers.php index c152b608f..35350c6c5 100644 --- a/app/Util/ActivityPub/Helpers.php +++ b/app/Util/ActivityPub/Helpers.php @@ -733,21 +733,42 @@ class Helpers */ public static function validateStatusUrls(string $url, array $activity): bool { - $id = isset($activity['id']) ? - self::pluckval($activity['id']) : - self::pluckval($url); + $id = self::extractActivityPubUrl( + $activity['id'] ?? $url + ); + + if (! $id) { + return false; + } $idDomain = parse_url($id, PHP_URL_HOST); $urlDomain = parse_url($url, PHP_URL_HOST); - if (! $idDomain || ! $urlDomain) { + if (! is_string($idDomain) || ! is_string($urlDomain)) { return false; } - $attributedTo = $activity['attributedTo'] ?? $activity['object']['attributedTo'] ?? null; - if ($attributedTo) { - $authorDomain = parse_url(self::pluckval($attributedTo), PHP_URL_HOST); - if ($authorDomain && strtolower($idDomain) !== strtolower($authorDomain)) { + if (strcasecmp($idDomain, $urlDomain) !== 0) { + return false; + } + + $attributedTo = $activity['attributedTo'] + ?? $activity['object']['attributedTo'] + ?? null; + + if ($attributedTo !== null) { + $author = self::extractActivityPubUrl($attributedTo); + + if (! $author) { + return false; + } + + $authorDomain = parse_url($author, PHP_URL_HOST); + + if ( + ! is_string($authorDomain) || + strcasecmp($idDomain, $authorDomain) !== 0 + ) { return false; } } @@ -755,6 +776,31 @@ class Helpers return true; } + private static function extractActivityPubUrl($value): ?string + { + $value = self::pluckval($value); + + if (is_string($value)) { + return $value !== '' ? $value : null; + } + + if (is_array($value)) { + foreach ($value as $item) { + $item = self::pluckval($item); + + if (is_string($item) && $item !== '') { + return $item; + } + + if (is_array($item) && isset($item['id']) && is_string($item['id'])) { + return $item['id']; + } + } + } + + return null; + } + /** * Get reply-to status ID */ From bb33409fea4cb60b8ac76f0d73158eaf0a26176e Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 24 Aug 2026 03:05:48 -0600 Subject: [PATCH 2/3] Lint --- app/Util/ActivityPub/Helpers.php | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/app/Util/ActivityPub/Helpers.php b/app/Util/ActivityPub/Helpers.php index 35350c6c5..371bf8e96 100644 --- a/app/Util/ActivityPub/Helpers.php +++ b/app/Util/ActivityPub/Helpers.php @@ -5,6 +5,7 @@ namespace App\Util\ActivityPub; use App\Instance; use App\Jobs\AvatarPipeline\RemoteAvatarFetch; use App\Jobs\HomeFeedPipeline\FeedInsertRemotePipeline; +use App\Jobs\InstancePipeline\FetchNodeinfoPipeline; use App\Jobs\MediaPipeline\MediaStoragePipeline; use App\Jobs\StatusPipeline\StatusReplyPipeline; use App\Jobs\StatusPipeline\StatusTagsPipeline; @@ -26,7 +27,6 @@ use App\Util\Media\License; use Cache; use Carbon\Carbon; use Illuminate\Validation\Rule; -use League\Uri\Exceptions\UriException; use League\Uri\Uri; use Purify; use Validator; @@ -52,7 +52,6 @@ class Helpers 'ip6-loopback', ]; - /** * Validate an ActivityPub object */ @@ -213,7 +212,6 @@ class Helpers return $uri->toString(); } - /** * Normalize URL input */ @@ -244,7 +242,6 @@ class Helpers return $url; } - /** * Validate basic URI requirements */ @@ -326,7 +323,6 @@ class Helpers return $host; } - public static function resolvePublicIps(string $host): array { $host = self::normalizeHost($host); @@ -335,14 +331,14 @@ class Helpers return []; } - $key = self::URL_CACHE_PREFIX . - 'public-ips:sha256-' . + $key = self::URL_CACHE_PREFIX. + 'public-ips:sha256-'. hash('sha256', $host); return Cache::remember($key, 60, function () use ($host) { $ips = []; - $aRecords = @dns_get_record($host . '.', DNS_A); + $aRecords = @dns_get_record($host.'.', DNS_A); if (is_array($aRecords)) { foreach ($aRecords as $record) { @@ -352,7 +348,7 @@ class Helpers } } - $aaaaRecords = @dns_get_record($host . '.', DNS_AAAA); + $aaaaRecords = @dns_get_record($host.'.', DNS_AAAA); if (is_array($aaaaRecords)) { foreach ($aaaaRecords as $record) { @@ -446,7 +442,7 @@ class Helpers public static function hasValidDNS(string $host): bool { $hash = hash('sha256', $host); - $key = self::URL_CACHE_PREFIX . "valid-dns:sha256-{$hash}"; + $key = self::URL_CACHE_PREFIX."valid-dns:sha256-{$hash}"; return Cache::remember($key, self::CACHE_TTL, function () use ($host) { return DomainService::hasValidDns($host); @@ -469,7 +465,7 @@ class Helpers $host = strtolower(rtrim($host, '.')); $bannedInstances = array_map( - fn($domain) => strtolower(rtrim($domain, '.')), + fn ($domain) => strtolower(rtrim($domain, '.')), InstanceService::getBannedDomains() ); @@ -720,7 +716,7 @@ class Helpers if (is_array($attributedTo)) { return collect($attributedTo) - ->filter(fn($o) => $o && isset($o['type']) && $o['type'] == 'Person') + ->filter(fn ($o) => $o && isset($o['type']) && $o['type'] == 'Person') ->pluck('id') ->first(); } @@ -1488,7 +1484,7 @@ class Helpers $instance = Instance::updateOrCreate(['domain' => $domain]); if ($instance->wasRecentlyCreated) { - \App\Jobs\InstancePipeline\FetchNodeinfoPipeline::dispatch($instance) + FetchNodeinfoPipeline::dispatch($instance) ->onQueue('low'); } @@ -1551,6 +1547,7 @@ class Helpers if ($url === null) { return null; } + return self::profileFirstOrNew($url); } From c44689a7e7fefc5fb7a8aa58b0991b43ce3090b8 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Mon, 24 Aug 2026 03:05:55 -0600 Subject: [PATCH 3/3] Update cors config --- config/cors.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/cors.php b/config/cors.php index 1e81a015c..ef8fa50f5 100644 --- a/config/cors.php +++ b/config/cors.php @@ -50,8 +50,7 @@ return [ /* * Sets the Access-Control-Expose-Headers response header with these headers. */ - // TODO: Add support for rate-limit related headers - 'exposed_headers' => ['Link'], + 'exposed_headers' => ['Link', 'X-RateLimit-Limit', 'X-RateLimit-Remaining'], /* * Sets the Access-Control-Max-Age response header when > 0.