Преглед на файлове

Accept a trusted proxy address given without a subnet (#9301)

TRUSTED_PROXY=192.168.1.1 is ignored and only 192.168.1.1/32 is honoured. checkCIDR() splits the range on the slash and rejects the value when the mask part is missing, so an address written without a subnet never matches.

An address without a subnet is now read as a single host: /32 for IPv4 and /128 for IPv6. That is the format docs/en/admins/09_AccessControl.md points at.

Host names such as gateway.localdomain are still not resolved.

tests/app/Utils/httpUtilTest.php gains 16 cases for checkCIDR, two of which fail without this change.

Refs #9210
Jamal Kamaladdinoglu преди 1 ден
родител
ревизия
2bb901d9c4
променени са 2 файла, в които са добавени 36 реда и са изтрити 2 реда
  1. 3 2
      app/Utils/httpUtil.php
  2. 33 0
      tests/app/Utils/httpUtilTest.php

+ 3 - 2
app/Utils/httpUtil.php

@@ -791,12 +791,13 @@ final class FreshRSS_http_Util {
 			return false;	// Do not mix IPv4 and IPv6
 		}
 
-		$mask_bits_str = $split[1] ?? '';
+		$max_mask_bits = str_contains($ip, ':') ? 128 : 32;
+		// A range without a subnet is a single address, like Apache’s mod_remoteip accepts
+		$mask_bits_str = $split[1] ?? (string)$max_mask_bits;
 		if (!ctype_digit($mask_bits_str)) {
 			return false;
 		}
 		$mask_bits = (int)$mask_bits_str;
-		$max_mask_bits = str_contains($ip, ':') ? 128 : 32;
 		if ($mask_bits < 0 || $mask_bits > $max_mask_bits) {
 			return false;	// Reject invalid mask bits lengths
 		}

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

@@ -13,6 +13,39 @@ class httpUtilTest extends \PHPUnit\Framework\TestCase {
 		self::assertEquals($expected, FreshRSS_http_Util::compareUrlIgnoringHttps($url1, $url2) === 0);
 	}
 
+	#[DataProvider('provideCidrRanges')]
+	public function test_checkCIDR(string $ip, string $range, bool $expected): void {
+		$checkCIDR = new ReflectionMethod(FreshRSS_http_Util::class, 'checkCIDR');
+		self::assertEquals($expected, $checkCIDR->invoke(null, $ip, $range));
+	}
+
+	/** @return list<array{string,string,bool}> */
+	public static function provideCidrRanges(): array {
+		return [
+			// A range without a subnet is a single address
+			['192.168.1.1', '192.168.1.1', true],
+			['192.168.1.2', '192.168.1.1', false],
+			['2001:db8::1', '2001:db8::1', true],
+			['2001:db8::2', '2001:db8::1', false],
+
+			// An explicit subnet keeps working
+			['192.168.1.1', '192.168.1.1/32', true],
+			['192.168.1.42', '192.168.1.0/24', true],
+			['192.168.2.42', '192.168.1.0/24', false],
+			['192.168.1.1', '0.0.0.0/0', true],
+			['2001:db8::1', '2001:db8::/32', true],
+
+			// Invalid input is still rejected
+			['192.168.1.1', '', false],
+			['192.168.1.1', '192.168.1.1/', false],
+			['192.168.1.1', '192.168.1.1/33', false],
+			['192.168.1.1', '192.168.1.1/abc', false],
+			['192.168.1.1', 'gateway.localdomain', false],
+			['192.168.1.1', '2001:db8::1', false],
+			['not-an-ip', '192.168.1.1', false],
+		];
+	}
+
 	/** @return list<array{string,string,bool}> */
 	public static function provideUrlsIgnoringHttps(): array {
 		return [