Переглянути джерело

WebSub: ignore http/https scheme difference in Self URL comparison (#9005)

* WebSub: ignore http/https scheme difference in Self URL comparison

The PubSubHubbub endpoint logged 'Self URL does not match registered
canonical URL' whenever a feed's self URL differed from the registered
canonical URL only by http vs https (common with http->https redirects),
spamming the log. Compare the URLs ignoring the http/https scheme while
still requiring host/path/query to match exactly.

Closes #3087

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Stylistic preferences

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
Jam Balaya 1 день тому
батько
коміт
7744f1fed9
3 змінених файлів з 51 додано та 2 видалено
  1. 8 0
      app/Utils/httpUtil.php
  2. 2 2
      p/api/pshb.php
  3. 41 0
      tests/app/Utils/httpUtilTest.php

+ 8 - 0
app/Utils/httpUtil.php

@@ -295,6 +295,14 @@ final class FreshRSS_http_Util {
 			($url1['port'] ?? '') === ($url2['port'] ?? '');
 	}
 
+	/**
+	 * Return 0 if values on either side are equal ignoring the HTTP vs HTTPS differences, or 1/-1 if they differ.
+	 */
+	public static function compareUrlIgnoringHttps(string $url1, string $url2): int {
+		$normalizeScheme = static fn(string $url): string => preg_replace('#^https?://#i', '//', trim($url)) ?? $url;
+		return $normalizeScheme($url1) <=> $normalizeScheme($url2);
+	}
+
 	/**
 	 * Returns a value for CURLOPT_RESOLVE as an array, null if no allowed IPs were found, false if the domain failed to resolve.
 	 *

+ 2 - 2
p/api/pshb.php

@@ -121,14 +121,14 @@ if ($httpLink !== '' && preg_match_all('/<([^>]+)>;\\s*rel="([^"]+)"/', $httpLin
 	// }
 	if (!empty($links['self'])) {
 		$httpSelf = FreshRSS_http_Util::checkUrl($links['self']) ?: '';
-		if ($self !== '' && $self !== $httpSelf) {
+		if ($self !== '' && FreshRSS_http_Util::compareUrlIgnoringHttps($self, $httpSelf) !== 0) {
 			Minz_Log::warning('Warning: Self URL mismatch between XML [' . $self . '] and HTTP!: ' . $httpSelf, PSHB_LOG);
 		}
 		$self = $httpSelf;
 	}
 }
 
-if ($self !== $canonical) {
+if (FreshRSS_http_Util::compareUrlIgnoringHttps($self, $canonical) !== 0) {
 	//header('HTTP/1.1 422 Unprocessable Entity');
 	Minz_Log::warning('Warning: Self URL [' . $self . '] does not match registered canonical URL!: ' . $canonical, PSHB_LOG);
 	//die('Self URL does not match registered canonical URL!');

+ 41 - 0
tests/app/Utils/httpUtilTest.php

@@ -0,0 +1,41 @@
+<?php
+declare(strict_types=1);
+
+use PHPUnit\Framework\Attributes\DataProvider;
+
+/**
+ * Tests for FreshRSS_http_Util
+ */
+class httpUtilTest extends \PHPUnit\Framework\TestCase {
+
+	#[DataProvider('provideUrlsIgnoringHttps')]
+	public function test_compareUrlIgnoringHttps(string $url1, string $url2, bool $expected): void {
+		self::assertEquals($expected, FreshRSS_http_Util::compareUrlIgnoringHttps($url1, $url2) === 0);
+	}
+
+	/** @return list<array{string,string,bool}> */
+	public static function provideUrlsIgnoringHttps(): array {
+		return [
+			// Only the scheme differs → equal
+			['http://www.blogger.com/feeds/1/posts', 'https://www.blogger.com/feeds/1/posts', true],
+			['https://example.net/feed.xml?a=1&b=2', 'http://example.net/feed.xml?a=1&b=2', true],
+			['HTTP://Example.net/Feed', 'https://Example.net/Feed', true],
+			['HTTPS://Example.net/Feed', 'http://Example.net/Feed', true],
+
+			// Fully identical → equal
+			['https://example.net/feed', 'https://example.net/feed', true],
+			['', '', true],
+
+			// Path differs → not equal (scheme-only tolerance must not hide real mismatches)
+			['http://example.net/a', 'https://example.net/b', false],
+			// Trailing slash is a path difference → not equal
+			['http://example.net/', 'https://example.net', false],
+			// Host differs → not equal
+			['http://a.example.net/feed', 'https://b.example.net/feed', false],
+			// Query differs → not equal
+			['https://example.net/feed?a=1', 'http://example.net/feed?a=2', false],
+			// Non-http(s) schemes are compared as-is
+			['ftp://example.net/feed', 'https://example.net/feed', false],
+		];
+	}
+}