diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3224220..093fd3a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## 5.3.2 (2026101500)
+- Fixed IP address validation being too permissive https://github.com/LdesignMedia/moodle-availability_ipaddress/issues/2
+ - Invalid entries like `2001:db8::1garbage` or `192.168.1.0/24garbage` are no longer accepted. Moodle silently ignored them, so a "must not" restriction could let blocked users in.
+ - Only formats Moodle can match are accepted. Rejected: IPv6 written with an IPv4 part (`::ffff:192.168.1.1`), an IPv6 prefix without mask (`2001:db8::`, use `2001:db8::/32`), masks with leading zeros (`/024`) and some compressed IPv6 forms Moodle can't parse (`1::2:3:4:5:6:7`).
+ - Predefined IP ranges are now validated with the same rules (subnet mask range, range end).
+ - Spaces after commas are now allowed in the restriction field, e.g. `192.168.1.1, 10.0.0.1`.
+ - Added support for IPv6 ranges in the last group, e.g. `2001:db8::1-ff`.
+ - Note: existing restrictions and predefined ranges are not changed. If one contains an invalid entry, it now shows a validation error the next time it is edited. Entries that were already invalid never matched.
+
## 5.3.1 (2026101400)
- Solved CI tests https://github.com/LdesignMedia/moodle-availability_ipaddress/issues/20 | Thanks @minhduchoang195
diff --git a/classes/form/range_form.php b/classes/form/range_form.php
index d890246..2f4a90b 100644
--- a/classes/form/range_form.php
+++ b/classes/form/range_form.php
@@ -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;
- }
}
diff --git a/classes/helper.php b/classes/helper.php
index ad16d63..1863816 100644
--- a/classes/helper.php
+++ b/classes/helper.php
@@ -359,4 +359,141 @@ class helper {
return $modified;
}
+
+ /**
+ * Validate a comma separated list of IP addresses.
+ *
+ * @param string $ipaddresses Comma separated list of IP addresses.
+ *
+ * @return bool True if every entry is valid.
+ */
+ public static function is_valid_ipaddress_list(string $ipaddresses): bool {
+ foreach (explode(',', $ipaddresses) as $entry) {
+ if (!self::is_valid_ipaddress_entry(trim($entry))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Validate a single IP address entry.
+ *
+ * Only formats that address_in_subnet() understands are accepted, anything else
+ * would be silently ignored when the restriction is checked.
+ *
+ * - Address: 192.168.1.1 or 2001:db8::1
+ * - Subnet: 192.168.1.0/24 or 2001:db8::/32
+ * - Range in the last group: 192.168.1.1-255 or 2001:db8::1-ff
+ *
+ * @param string $entry A single IP address entry.
+ *
+ * @return bool True if the entry is valid.
+ */
+ public static function is_valid_ipaddress_entry(string $entry): bool {
+ if (strpos($entry, '/') !== false) {
+ $valid = self::is_valid_subnet($entry);
+ } else if (strpos($entry, '-') !== false) {
+ $valid = self::is_valid_range($entry);
+ } else {
+ // An address ending in ":" is treated as an incomplete prefix by address_in_subnet(), which fails to parse it.
+ $valid = self::is_ipv4($entry) || (self::is_ipv6($entry) && substr($entry, -1) !== ':');
+ }
+
+ // Safety net: the entry must match its own start address, so address_in_subnet() is able to parse it.
+ return $valid && address_in_subnet(preg_split('/[\/-]/', $entry)[0], $entry, true);
+ }
+
+ /**
+ * Check if a value is a valid IPv4 address.
+ *
+ * @param string $value The value to check.
+ *
+ * @return bool True if the value is a valid IPv4 address.
+ */
+ private static function is_ipv4(string $value): bool {
+ return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
+ }
+
+ /**
+ * Check if a value is a valid IPv6 address that cleanremoteaddr() can parse.
+ *
+ * cleanremoteaddr() doesn't handle embedded IPv4 notation reliably and only expands "::"
+ * when it replaces at least two groups, or three at the start or end of the address.
+ *
+ * @param string $value The value to check.
+ *
+ * @return bool True if the value is a valid IPv6 address.
+ */
+ private static function is_ipv6(string $value): bool {
+ if (strpos($value, '.') !== false || filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
+ return false;
+ }
+
+ if (strpos($value, '::') === false) {
+ return true;
+ }
+
+ $groups = count(array_filter(explode(':', $value), 'strlen'));
+ if (strpos($value, '::') === 0 || substr($value, -2) === '::') {
+ return $groups <= 5;
+ }
+
+ return $groups <= 6;
+ }
+
+ /**
+ * Validate a subnet in CIDR notation, e.g. 192.168.1.0/24 or 2001:db8::/32.
+ *
+ * @param string $entry The subnet.
+ *
+ * @return bool True if the subnet is valid.
+ */
+ private static function is_valid_subnet(string $entry): bool {
+ $parts = explode('/', $entry);
+
+ // Moodle's is_number() rejects a mask with leading zeros.
+ if (count($parts) !== 2 || !preg_match('/^(0|[1-9]\d{0,2})$/D', $parts[1])) {
+ return false;
+ }
+
+ [$address, $mask] = $parts;
+ if (self::is_ipv4($address)) {
+ return (int) $mask <= 32;
+ }
+
+ return self::is_ipv6($address) && (int) $mask <= 128;
+ }
+
+ /**
+ * Validate a range in the last group of the address, e.g. 192.168.1.1-255 or 2001:db8::1-ff.
+ *
+ * The end of the range may not be lower than the start, it would never match.
+ *
+ * @param string $entry The range.
+ *
+ * @return bool True if the range is valid.
+ */
+ private static function is_valid_range(string $entry): bool {
+ $parts = explode('-', $entry);
+ if (count($parts) !== 2) {
+ return false;
+ }
+
+ [$start, $end] = $parts;
+ if (self::is_ipv4($start)) {
+ $startgroup = (int) substr(strrchr($start, '.'), 1);
+
+ return preg_match('/^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/D', $end) === 1 && (int) $end >= $startgroup;
+ }
+
+ if (self::is_ipv6($start)) {
+ $startgroup = hexdec(substr(strrchr($start, ':'), 1) ?: '0');
+
+ return preg_match('/^[0-9a-f]{1,4}$/iD', $end) === 1 && hexdec($end) >= $startgroup;
+ }
+
+ return false;
+ }
}
diff --git a/lang/en/availability_ipaddress.php b/lang/en/availability_ipaddress.php
index d1ed3c3..7078c52 100644
--- a/lang/en/availability_ipaddress.php
+++ b/lang/en/availability_ipaddress.php
@@ -56,11 +56,12 @@ $string['ipaddresses_help'] = 'Enter IP addresses separated by commas. Supports
$string['ipaddresses_help_help'] = '
Enter one or more IP addresses or ranges, separated by commas.
Examples:
-
Single IP:192.168.1.1
-
IP range:192.168.1.1-255
-
Subnet:192.168.1.0/24
-
Multiple:192.168.1.1,10.0.0.0/8,172.16.0.1-255
-
';
+
Single IP:192.168.1.1 or 2001:db8::1
+
IP range:192.168.1.1-255 or 2001:db8::1-ff
+
Subnet:192.168.1.0/24 or 2001:db8::/32
+
Multiple:192.168.1.1, 10.0.0.0/8, 172.16.0.1-255
+
+
A range only changes the last part of the address. Use a subnet for an IPv6 network prefix, e.g. 2001:db8::/32 instead of 2001:db8::.
';
$string['enabled'] = 'Enabled';
$string['sortorder'] = 'Sort order';
$string['existing_ranges'] = 'Existing IP ranges';
diff --git a/tests/helper_test.php b/tests/helper_test.php
new file mode 100644
index 0000000..7ec16fa
--- /dev/null
+++ b/tests/helper_test.php
@@ -0,0 +1,98 @@
+.
+
+namespace availability_ipaddress;
+
+/**
+ * Unit tests for the IP address validation in the helper class.
+ *
+ * @package availability_ipaddress
+ * @copyright 2026 LdesignMedia.nl - Luuk Verhoeven
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @covers \availability_ipaddress\helper
+ */
+final class helper_test extends \basic_testcase {
+ /**
+ * Data provider for test_is_valid_ipaddress_list.
+ *
+ * @return array
+ */
+ public static function ipaddress_list_provider(): array {
+ return [
+ 'IPv4 address' => ['192.168.1.1', true],
+ 'IPv4 subnet' => ['192.168.1.0/24', true],
+ 'IPv4 subnet all addresses' => ['0.0.0.0/0', true],
+ 'IPv4 range' => ['192.168.1.10-20', true],
+ 'IPv4 range single address' => ['192.168.1.5-5', true],
+ 'IPv6 address' => ['2001:db8::1', true],
+ 'IPv6 loopback' => ['::1', true],
+ 'IPv6 full address' => ['2001:db8:0:0:0:0:0:1', true],
+ 'IPv6 compressed in the middle' => ['1:2:3::4:5:6', true],
+ 'IPv6 compressed at the start' => ['::1:2:3:4:5', true],
+ 'IPv6 subnet' => ['2001:db8::/32', true],
+ 'IPv6 subnet all addresses' => ['::/0', true],
+ 'IPv6 range' => ['2001:db8::1-ff', true],
+ 'IPv6 range from compressed start' => ['2001:db8::-ffff', true],
+ 'List' => ['192.168.1.1,10.0.0.0/8,2001:db8::/32', true],
+ 'List with spaces' => [' 192.168.1.1 , 10.0.0.1 ', true],
+ 'IPv6 with trailing text' => ['2001:db8::1garbage', false],
+ 'IPv6 with zone id' => ['fe80::1%eth0', false],
+ 'IPv6 subnet mask too large' => ['2001:db8::/129', false],
+ 'IPv6 address ending in ::' => ['2001:db8::', false],
+ 'IPv6 unspecified address' => ['::', false],
+ 'IPv6 :: replacing one group' => ['1::2:3:4:5:6:7', false],
+ 'IPv6 :: at the start replacing two groups' => ['::1:2:3:4:5:6', false],
+ 'IPv6 subnet :: at the end replacing two groups' => ['2001:db8:1:2:3:4::/112', false],
+ 'IPv6 with embedded IPv4' => ['::ffff:192.168.1.1', false],
+ 'IPv6 subnet with embedded IPv4' => ['::ffff:192.168.1.0/120', false],
+ 'IPv4 subnet with trailing text' => ['192.168.1.0/24garbage', false],
+ 'IPv4 subnet mask too large' => ['192.168.1.0/33', false],
+ 'IPv4 subnet mask with leading zero' => ['192.168.1.0/024', false],
+ 'IPv4 subnet negative mask' => ['192.168.1.0/-5', false],
+ 'IPv4 subnet exponent mask' => ['192.168.1.0/1e3', false],
+ 'IPv4 subnet trailing newline' => ["192.168.1.0/24\n", true],
+ 'IPv4 with non-breaking space' => ["192.168.1.1\u{a0}", false],
+ 'IPv4 subnet double mask' => ['192.168.1.0/24/8', false],
+ 'IPv4 range with text' => ['192.168.1.1-garbage', false],
+ 'IPv4 range end too large' => ['192.168.1.1-256', false],
+ 'IPv4 range end below start' => ['192.168.1.20-10', false],
+ 'IPv4 range double dash' => ['192.168.1.1-2-3', false],
+ 'IPv6 range end below start' => ['2001:db8::ff-1', false],
+ 'Invalid octet' => ['999.1.1.1', false],
+ 'Leading zero' => ['192.168.01.1', false],
+ 'Text' => ['abc', false],
+ 'Text with subnet' => ['hello/gm', false],
+ 'Empty' => ['', false],
+ 'Zero' => ['0', false],
+ 'Trailing comma' => ['192.168.1.1,', false],
+ 'List with one invalid entry' => ['192.168.1.1, 2001:db8::1garbage', false],
+ ];
+ }
+
+ /**
+ * Test validation of a comma separated list of IP addresses.
+ *
+ * @dataProvider ipaddress_list_provider
+ *
+ * @param string $ipaddresses
+ * @param bool $expected
+ *
+ * @return void
+ */
+ public function test_is_valid_ipaddress_list(string $ipaddresses, bool $expected): void {
+ $this->assertSame($expected, helper::is_valid_ipaddress_list($ipaddresses));
+ }
+}
diff --git a/version.php b/version.php
index 7b34728..b5dccc6 100644
--- a/version.php
+++ b/version.php
@@ -27,8 +27,8 @@
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'availability_ipaddress';
-$plugin->version = 2026101400;
-$plugin->release = '5.3.1';
+$plugin->version = 2026101500;
+$plugin->release = '5.3.2';
$plugin->requires = 2016120500;
$plugin->maturity = MATURITY_STABLE;
$plugin->supported = [400, 503];
diff --git a/yui/build/moodle-availability_ipaddress-form/moodle-availability_ipaddress-form-debug.js b/yui/build/moodle-availability_ipaddress-form/moodle-availability_ipaddress-form-debug.js
index ef6cb18..2a27c2f 100644
--- a/yui/build/moodle-availability_ipaddress-form/moodle-availability_ipaddress-form-debug.js
+++ b/yui/build/moodle-availability_ipaddress-form/moodle-availability_ipaddress-form-debug.js
@@ -15,11 +15,12 @@ YUI.add('moodle-availability_ipaddress-form', function (Y, NAME) {
*/
M.availability_ipaddress = M.availability_ipaddress || {};
+// A single IPv4 octet (0-255) without leading zeros.
+M.availability_ipaddress.octet = '(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)';
+
// MIT https://github.com/sindresorhus/ip-regex
-// Advanced ip-address regex for validating.
-M.availability_ipaddress.v4 = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9]' +
- '[0-9]|[1-9][0-9]|[0-9])){3}';
-M.availability_ipaddress.v6 = "^((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]" +
+// IPv6 address, without anchors so it can be combined.
+M.availability_ipaddress.v6 = "((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]" +
"\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){5}" +
"(?::(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4})" +
"{1,2}|:)|(?:[a-fA-F\\d]{1,4}:){4}(?:(:[a-fA-F\\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]" +
@@ -29,7 +30,7 @@ M.availability_ipaddress.v6 = "^((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|
"\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,5}|:)|(?:[a-fA-F\\d]{1,4}:){1}(?:(:[a-fA-F\\d]{1,4}){0,4}:(?:25[0-5]" +
"|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,6}|:)|(?::" +
"((?::[a-fA-F\\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}" +
- "|(?::[a-fA-F\\d]{1,4}){1,7}|:)))(%[0-9a-zA-Z]{1,})?";
+ "|(?::[a-fA-F\\d]{1,4}){1,7}|:)))";
/**
* @class M.availability_ipaddress.form
@@ -163,60 +164,141 @@ M.availability_ipaddress.form.getNode = function(json) {
};
/**
- * Validate ipaddresses
+ * Check if a value is a valid IPv4 address.
*
- * @param {string[]} ipaddresses
+ * @param {string} value
+ * @returns {boolean}
+ */
+M.availability_ipaddress.isIpv4 = function(value) {
+ 'use strict';
+ var octet = M.availability_ipaddress.octet;
+ return new RegExp('^' + octet + '(?:\\.' + octet + '){3}$').test(value);
+};
+
+/**
+ * Check if a value is a valid IPv6 address that Moodle's cleanremoteaddr() can parse.
+ *
+ * cleanremoteaddr() doesn't handle embedded IPv4 notation reliably and only expands "::"
+ * when it replaces at least two groups, or three at the start or end of the address.
+ *
+ * @param {string} value
+ * @returns {boolean}
+ */
+M.availability_ipaddress.isIpv6 = function(value) {
+ 'use strict';
+ var groups;
+
+ if (value.indexOf('.') !== -1 || !new RegExp('^' + M.availability_ipaddress.v6 + '$').test(value)) {
+ return false;
+ }
+ if (value.indexOf('::') === -1) {
+ return true;
+ }
+
+ groups = value.split(':').filter(function(group) {
+ return group !== '';
+ }).length;
+ if (value.indexOf('::') === 0 || value.slice(-2) === '::') {
+ return groups <= 5;
+ }
+ return groups <= 6;
+};
+
+/**
+ * Trim the same characters as PHP's trim(), which address_in_subnet() uses.
+ *
+ * @param {string} value
+ * @returns {string}
+ */
+M.availability_ipaddress.trim = function(value) {
+ 'use strict';
+ var chars = ' \t\n\r\x00\x0B',
+ start = 0,
+ end = value.length;
+
+ while (start < end && chars.indexOf(value.charAt(start)) !== -1) {
+ start++;
+ }
+ while (end > start && chars.indexOf(value.charAt(end - 1)) !== -1) {
+ end--;
+ }
+ return value.substring(start, end);
+};
+
+/**
+ * Validate a single entry. Only formats that Moodle's address_in_subnet() understands are
+ * accepted, anything else would be silently ignored when the restriction is checked.
+ *
+ * - Address: 192.168.1.1 or 2001:db8::1
+ * - Subnet: 192.168.1.0/24 or 2001:db8::/32
+ * - Range in the last group: 192.168.1.1-255 or 2001:db8::1-ff
+ *
+ * @param {string} entry
+ * @returns {boolean}
+ */
+M.availability_ipaddress.validateEntry = function(entry) {
+ 'use strict';
+ var parts, mask, lastgroup;
+
+ if (entry.indexOf('/') !== -1) {
+ parts = entry.split('/');
+ // Moodle's is_number() rejects a mask with leading zeros.
+ if (parts.length !== 2 || !/^(0|[1-9]\d{0,2})$/.test(parts[1])) {
+ return false;
+ }
+ mask = parseInt(parts[1], 10);
+ if (M.availability_ipaddress.isIpv4(parts[0])) {
+ return mask <= 32;
+ }
+ return M.availability_ipaddress.isIpv6(parts[0]) && mask <= 128;
+ }
+
+ if (entry.indexOf('-') !== -1) {
+ parts = entry.split('-');
+ if (parts.length !== 2) {
+ return false;
+ }
+
+ // The end of the range may not be lower than the start, it would never match.
+ if (M.availability_ipaddress.isIpv4(parts[0])) {
+ return new RegExp('^' + M.availability_ipaddress.octet + '$').test(parts[1]) &&
+ parseInt(parts[1], 10) >= parseInt(parts[0].split('.')[3], 10);
+ }
+ if (M.availability_ipaddress.isIpv6(parts[0])) {
+ lastgroup = parts[0].split(':').pop() || '0';
+ return /^[a-fA-F\d]{1,4}$/.test(parts[1]) && parseInt(parts[1], 16) >= parseInt(lastgroup, 16);
+ }
+ return false;
+ }
+
+ // An address ending in ":" is treated as an incomplete prefix by address_in_subnet(), which fails to parse it.
+ return M.availability_ipaddress.isIpv4(entry) || (M.availability_ipaddress.isIpv6(entry) && entry.slice(-1) !== ':');
+};
+
+/**
+ * Validate a comma separated list of ipaddresses.
+ *
+ * @param {string} ipaddresses
* @returns {boolean}
*/
M.availability_ipaddress.validateIpaddress = function(ipaddresses) {
'use strict';
- Y.log(ipaddresses);
- // Return true for empty string - it's valid to have no custom IPs
- if (!ipaddresses || ipaddresses.trim() === '') {
+ var entries, i;
+
+ // Return true for empty string - it's valid to have no custom IPs.
+ if (!ipaddresses || M.availability_ipaddress.trim(ipaddresses) === '') {
return true;
}
- ipaddresses = ipaddresses.split(',');
- for (var i in ipaddresses) {
- // Test normal ip format.
- // Strict ipv4 check.
- if (new RegExp(/^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/gm)
- .test(ipaddresses[i])) {
- Y.log('Correct ipv4');
- continue;
+ entries = ipaddresses.split(',');
+ for (i = 0; i < entries.length; i++) {
+ // Whitespace around an entry is allowed, address_in_subnet() trims it as well.
+ if (!M.availability_ipaddress.validateEntry(M.availability_ipaddress.trim(entries[i]))) {
+ Y.log('Incorrect ip-address: ' + entries[i]);
+ return false;
}
-
- var ipv4Regex = new RegExp(
- '^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)' +
- '(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}-' +
- '(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)$',
- 'gm'
- );
-
- if (ipv4Regex.test(ipaddresses[i])) {
- Y.log('Correct ipv4 range.');
- continue;
- }
-
- if (new RegExp(M.availability_ipaddress.v6)
- .test(ipaddresses[i])) {
- Y.log('Correct ipv6');
- continue;
- }
-
- // Test subnet with a regex.
- if (new RegExp("^(?:".concat(M.availability_ipaddress.v4 + "\\/(3[0-2]|[12]?[0-9])|(1\\*)", ")|(?:")
- .concat(M.availability_ipaddress.v6 + "\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])", ")?\\/gm"))
- .test(ipaddresses[i])) {
- Y.log('Correct subnet');
- continue;
- }
-
- Y.log('Incorrect ip');
- return false;
}
- Y.log('Valid ipaddresses');
return true;
};
@@ -259,9 +341,8 @@ M.availability_ipaddress.form.fillErrors = function(errors, node) {
var value = {};
this.fillValue(value, node);
- // Basic ipaddresses checks - only validate if not empty.
- if (value.ipaddresses && value.ipaddresses.trim() !== '' &&
- M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
+ // Basic ipaddresses checks - an empty value is valid.
+ if (M.availability_ipaddress.validateIpaddress(value.ipaddresses) === false) {
errors.push('availability_ipaddress:error_ipaddress');
}
};
diff --git a/yui/build/moodle-availability_ipaddress-form/moodle-availability_ipaddress-form-min.js b/yui/build/moodle-availability_ipaddress-form/moodle-availability_ipaddress-form-min.js
index 8aedf57..a30e053 100644
--- a/yui/build/moodle-availability_ipaddress-form/moodle-availability_ipaddress-form-min.js
+++ b/yui/build/moodle-availability_ipaddress-form/moodle-availability_ipaddress-form-min.js
@@ -1 +1 @@
-YUI.add("moodle-availability_ipaddress-form",function(r,d){M.availability_ipaddress=M.availability_ipaddress||{},M.availability_ipaddress.v4="(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])){3}",M.availability_ipaddress.v6="^((?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|:[a-fA-F\\d]{1,4}|:)|(?:[a-fA-F\\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,2}|:)|(?:[a-fA-F\\d]{1,4}:){4}(?:(:[a-fA-F\\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,3}|:)|(?:[a-fA-F\\d]{1,4}:){3}(?:(:[a-fA-F\\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,4}|:)|(?:[a-fA-F\\d]{1,4}:){2}(?:(:[a-fA-F\\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,5}|:)|(?:[a-fA-F\\d]{1,4}:){1}(?:(:[a-fA-F\\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(:[a-fA-F\\d]{1,4}){1,6}|:)|(?::((?::[a-fA-F\\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,7}|:)))(%[0-9a-zA-Z]{1,})?",M.availability_ipaddress.form=r.Object(M.core_availability.plugin),M.availability_ipaddress.form.initInner=function(d){"use strict";this.predefinedRanges=d||[]},M.availability_ipaddress.form.getValue=function(d,a){"use strict";d=a.one("input[name="+d+"]").get("value");return M.availability_ipaddress.validateIpaddress(d),d},M.availability_ipaddress.form.getNode=function(d){"use strict";var a,e,i,s,t="ipaddresses"+M.availability_ipaddress.form.instId,l="predefined"+M.availability_ipaddress.form.instId;if(M.availability_ipaddress.form.instId+=1,a='