diff --git a/app/Rules/ValidIpOrCidr.php b/app/Rules/ValidIpOrCidr.php new file mode 100644 index 0000000..d72114c --- /dev/null +++ b/app/Rules/ValidIpOrCidr.php @@ -0,0 +1,30 @@ + $v])); + + return; + } + + [$ip, $mask] = explode('/', $v, 2); + $max = str_contains($ip, ':') ? 128 : 32; + if (filter_var($ip, FILTER_VALIDATE_IP) === false || ! ctype_digit($mask) || (int) $mask < 0 || (int) $mask > $max) { + $fail(__('settings.lp_whitelist_invalid', ['value' => $v])); + } + } +} diff --git a/lang/de/settings.php b/lang/de/settings.php index a54c7fa..0336428 100644 --- a/lang/de/settings.php +++ b/lang/de/settings.php @@ -63,4 +63,7 @@ return [ // Page title 'title' => 'Einstellungen — Clusev', + + // IP/CIDR whitelist validation + 'lp_whitelist_invalid' => ':value ist keine gültige IP/CIDR.', ]; diff --git a/lang/en/settings.php b/lang/en/settings.php index 871eb4d..d4fee99 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -63,4 +63,7 @@ return [ // Page title 'title' => 'Settings — Clusev', + + // IP/CIDR whitelist validation + 'lp_whitelist_invalid' => ':value is not a valid IP/CIDR.', ]; diff --git a/tests/Feature/ValidIpOrCidrTest.php b/tests/Feature/ValidIpOrCidrTest.php new file mode 100644 index 0000000..2fec5bf --- /dev/null +++ b/tests/Feature/ValidIpOrCidrTest.php @@ -0,0 +1,30 @@ + $value], ['v' => [new ValidIpOrCidr]])->passes(); + } + + public function test_accepts_ips_and_cidrs(): void + { + $this->assertTrue($this->passes('203.0.113.4')); + $this->assertTrue($this->passes('10.0.0.0/8')); + $this->assertTrue($this->passes('::1')); + $this->assertTrue($this->passes('2001:db8::/32')); + } + + public function test_rejects_junk(): void + { + $this->assertFalse($this->passes('nope')); + $this->assertFalse($this->passes('10.0.0.0/99')); + $this->assertFalse($this->passes('1.2.3.4/')); + } +}