Fix #2 - Reject IP addresses that Moodle cannot match

The custom IP field and the predefined ranges form accepted invalid
entries such as 2001:db8::1garbage, 192.168.1.0/24garbage or
192.168.1.0/999. address_in_subnet() silently skips those entries, so a
"must not" restriction with a typo let blocked users in.

Validation now only accepts formats address_in_subnet() can match: a
single address, a subnet (mask 0-32 / 0-128) or a range in the last
group with end >= start. IPv6 forms cleanremoteaddr() cannot parse are
rejected as well. JS and PHP share the same rules; PHP also checks each
entry against address_in_subnet() as a safety net.

- Allow spaces after commas in the restriction field
- Support IPv6 ranges, e.g. 2001:db8::1-ff
- Add PHPUnit tests for helper::is_valid_ipaddress_list()
- Bump version to 2026101500 (5.3.2)
This commit is contained in:
Nihaal Shaikh committed 2026-10-15 09:04:58 +02:00
1 parent 150067f43d
commit 06a5f9c8c9
10 files changed
+653 -198

No files matched your search

+5 -44
View File
@@ -25,6 +25,8 @@
namespace availability_ipaddress\form;
use availability_ipaddress\helper;
defined('MOODLE_INTERNAL') || die();
global $CFG;
@@ -102,52 +104,11 @@ class range_form extends \moodleform {
public function validation($data, $files) {
$errors = parent::validation($data, $files);
// Validate IP addresses.
if (!empty($data['ipaddresses'])) {
$ipaddresses = explode(',', $data['ipaddresses']);
foreach ($ipaddresses as $ip) {
$ip = trim($ip);
if (!$this->validate_ip_format($ip)) {
$errors['ipaddresses'] = get_string('error_ipaddress', 'availability_ipaddress');
break;
}
}
// Validate IP addresses, the field is required so an empty value is invalid as well.
if (!helper::is_valid_ipaddress_list($data['ipaddresses'] ?? '')) {
$errors['ipaddresses'] = get_string('error_ipaddress', 'availability_ipaddress');
}
return $errors;
}
/**
* Validate IP address format.
*
* @param string $ip
*
* @return bool
*/
private function validate_ip_format(string $ip): bool {
// Use the same validation logic as the main plugin.
// This is a simplified version - you might want to use the same regex as in JS.
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return true;
}
// Check for CIDR notation.
if (strpos($ip, '/') !== false) {
[$addr, $mask] = explode('/', $ip);
if (filter_var($addr, FILTER_VALIDATE_IP) && is_numeric($mask)) {
return true;
}
}
// Check for IP range.
if (strpos($ip, '-') !== false) {
[$start, $end] = explode('-', $ip);
if (filter_var($start, FILTER_VALIDATE_IP)) {
return true;
}
}
return false;
}
}