Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace core;
  18  
  19  /**
  20   * This tests the static helper functions contained in the class '\core\ip_utils'.
  21   *
  22   * @package    core
  23   * @covers     \core\ip_utils
  24   * @copyright  2016 Jake Dallimore <jrhdallimore@gmail.com>
  25   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  class ip_utils_test extends \basic_testcase {
  28      /**
  29       * Test for \core\ip_utils::is_domain_name().
  30       *
  31       * @param string $domainname the domain name to validate.
  32       * @param bool $expected the expected result.
  33       * @dataProvider domain_name_data_provider
  34       */
  35      public function test_is_domain_name($domainname, $expected) {
  36          $this->assertEquals($expected, \core\ip_utils::is_domain_name($domainname));
  37      }
  38  
  39      /**
  40       * Data provider for test_is_domain_name().
  41       *
  42       * @return array
  43       */
  44      public function domain_name_data_provider() {
  45          return [
  46              ["com", true],
  47              ["i.net", true], // Single char, alpha tertiary domain.
  48              ["0.org", true], // Single char, non-alpha tertiary domain.
  49              ["0.a", true], // Single char, alpha top level domain.
  50              ["0.1", false], // Single char, non-alpha top level domain.
  51              ["example.com", true],
  52              ["sub.example.com", true],
  53              ["sub-domain.example-domain.net", true],
  54              ["123.com", true],
  55              ["123.a11", true],
  56              [str_repeat('sub.', 60) . "1-example.com", true], // Max length without null label is 253 octets = 253 ascii chars.
  57              [str_repeat('example', 9) . ".com", true], // Max number of octets per label is 63  = 63 ascii chars.
  58              ["localhost", true],
  59              [" example.com", false],
  60              ["example.com ", false],
  61              ["example.com/", false],
  62              ["*.example.com", false],
  63              ["*example.com", false],
  64              ["example.123", false],
  65              ["-example.com", false],
  66              ["example-.com", false],
  67              [".example.com", false],
  68              ["127.0.0.1", false],
  69              [str_repeat('sub.', 60) . "11-example.com", false], // Name length is 254 chars, which exceeds the max allowed.
  70              [str_repeat('example', 9) . "1.com", false], // Label length is 64 chars, which exceed the max allowed.
  71              ["example.com.", true], // Null label explicitly provided - this is valid.
  72              [".example.com.", false],
  73              ["見.香港", false], // IDNs are invalid.
  74              [null, false], // Non-strings are invalid.
  75          ];
  76      }
  77  
  78      /**
  79       * Test for \core\ip_utils::is_domain_matching_pattern().
  80       *
  81       * @param string $str the string to evaluate.
  82       * @param bool $expected the expected result.
  83       * @dataProvider domain_matching_patterns_data_provider
  84       */
  85      public function test_is_domain_matching_pattern($str, $expected) {
  86          $this->assertEquals($expected, \core\ip_utils::is_domain_matching_pattern($str));
  87      }
  88  
  89      /**
  90       * Data provider for test_is_domain_matching_pattern().
  91       *
  92       * @return array
  93       */
  94      public function domain_matching_patterns_data_provider() {
  95          return [
  96              ["*.com", true],
  97              ["*.example.com", true],
  98              ["*.example.com", true],
  99              ["*.sub.example.com", true],
 100              ["*.sub-domain.example-domain.com", true],
 101              ["*." . str_repeat('sub.', 60) . "example.com", true], // Max number of domain name chars = 253.
 102              ["*." . str_repeat('example', 9) . ".com", true], // Max number of domain name label chars = 63.
 103              ["*com", false],
 104              ["*example.com", false],
 105              [" *.example.com", false],
 106              ["*.example.com ", false],
 107              ["*-example.com", false],
 108              ["*.-example.com", false],
 109              ["*.example.com/", false],
 110              ["sub.*.example.com", false],
 111              ["sub.*example.com", false],
 112              ["*.*.example.com", false],
 113              ["example.com", false],
 114              ["*." . str_repeat('sub.', 60) . "1example.com", false], // Name length is 254 chars, which exceeds the max allowed.
 115              ["*." . str_repeat('example', 9) . "1.com", false], // Label length is 64 chars, which exceed the max allowed.
 116              ["*.example.com.", true], // Null label explicitly provided - this is valid.
 117              [".*.example.com.", false],
 118              ["*.香港", false], // IDNs are invalid.
 119              [null, false], // None-strings are invalid.
 120          ];
 121      }
 122  
 123      /**
 124       * Test for \core\ip_utils::is_ip_address().
 125       *
 126       * @param string $address the address to validate.
 127       * @param bool $expected the expected result.
 128       * @dataProvider ip_address_data_provider
 129       */
 130      public function test_is_ip_address($address, $expected) {
 131          $this->assertEquals($expected, \core\ip_utils::is_ip_address($address));
 132      }
 133  
 134      /**
 135       * Data provider for test_is_ip_address().
 136       *
 137       * @return array
 138       */
 139      public function ip_address_data_provider() {
 140          return [
 141              ["127.0.0.1", true],
 142              ["10.1", false],
 143              ["0.0.0.0", true],
 144              ["255.255.255.255", true],
 145              ["256.0.0.1", false],
 146              ["256.0.0.1", false],
 147              ["127.0.0.0/24", false],
 148              ["127.0.0.0-255", false],
 149              ["::", true],
 150              ["::0", true],
 151              ["0::", true],
 152              ["0::0", true],
 153              ["fe80:fe80:fe80:fe80:fe80:fe80:fe80:fe80", true],
 154              ["fe80::ffff", true],
 155              ["fe80::f", true],
 156              ["fe80::", true],
 157              ["0", false],
 158              ["127.0.0.0/24", false],
 159              ["fe80::fe80/128", false],
 160              ["fe80:fe80:fe80:fe80:fe80:fe80:fe80:fe80/128", false],
 161              ["fe80:", false],
 162              ["fe80:: ", false],
 163              [" fe80::", false],
 164              ["fe80::ddddd", false],
 165              ["fe80::gggg", false],
 166              ["fe80:fe80:fe80:fe80:fe80:fe80:fe80:fe80:fe80", false],
 167          ];
 168      }
 169  
 170      /**
 171       * Test for \core\ip_utils::is_ipv4_address().
 172       *
 173       * @param string $address the address to validate.
 174       * @param bool $expected the expected result.
 175       * @dataProvider ipv4_address_data_provider
 176       */
 177      public function test_is_ipv4_address($address, $expected) {
 178          $this->assertEquals($expected, \core\ip_utils::is_ipv4_address($address));
 179      }
 180  
 181      /**
 182       * Data provider for test_is_ipv4_address().
 183       *
 184       * @return array
 185       */
 186      public function ipv4_address_data_provider() {
 187          return [
 188              ["127.0.0.1", true],
 189              ["0.0.0.0", true],
 190              ["255.255.255.255", true],
 191              [" 127.0.0.1", false],
 192              ["127.0.0.1 ", false],
 193              ["-127.0.0.1", false],
 194              ["127.0.1", false],
 195              ["127.0.0.0.1", false],
 196              ["a.b.c.d", false],
 197              ["localhost", false],
 198              ["fe80::1", false],
 199              ["256.0.0.1", false],
 200              ["256.0.0.1", false],
 201              ["127.0.0.0/24", false],
 202              ["127.0.0.0-255", false],
 203          ];
 204      }
 205  
 206      /**
 207       * Test for \core\ip_utils::is_ipv4_range().
 208       *
 209       * @param string $addressrange the address range to validate.
 210       * @param bool $expected the expected result.
 211       * @dataProvider ipv4_range_data_provider
 212       */
 213      public function test_is_ipv4_range($addressrange, $expected) {
 214          $this->assertEquals($expected, \core\ip_utils::is_ipv4_range($addressrange));
 215      }
 216  
 217      /**
 218       * Data provider for test_is_ipv4_range().
 219       *
 220       * @return array
 221       */
 222      public function ipv4_range_data_provider() {
 223          return [
 224              ["127.0.0.1/24", true],
 225              ["127.0.0.20-20", true],
 226              ["127.0.0.20-50", true],
 227              ["127.0.0.0-255", true],
 228              ["127.0.0.1-1", true],
 229              ["255.255.255.0-255", true],
 230              ["127.0.0.1", false],
 231              ["127.0", false],
 232              [" 127.0.0.0/24", false],
 233              ["127.0.0.0/24 ", false],
 234              ["a.b.c.d/24", false],
 235              ["256.0.0.0-80", false],
 236              ["127.0.0.0/a", false],
 237              ["256.0.0.0/24", false],
 238              ["127.0.0.0/-1", false],
 239              ["127.0.0.0/33", false],
 240              ["127.0.0.0-127.0.0.10", false],
 241              ["127.0.0.30-20", false],
 242              ["127.0.0.0-256", false],
 243              ["fe80::fe80/64", false],
 244          ];
 245      }
 246  
 247      /**
 248       * Test for \core\ip_utils::is_ipv6_address().
 249       *
 250       * @param string $address the address to validate.
 251       * @param bool $expected the expected result.
 252       * @dataProvider ipv6_address_data_provider
 253       */
 254      public function test_is_ipv6_address($address, $expected) {
 255          $this->assertEquals($expected, \core\ip_utils::is_ipv6_address($address));
 256      }
 257  
 258      /**
 259       * Data provider for test_is_ipv6_address().
 260       *
 261       * @return array
 262       */
 263      public function ipv6_address_data_provider() {
 264          return [
 265              ["::", true],
 266              ["::0", true],
 267              ["0::", true],
 268              ["0::0", true],
 269              ["fe80:fe80:fe80:fe80:fe80:fe80:fe80:fe80", true],
 270              ["fe80::ffff", true],
 271              ["fe80::f", true],
 272              ["fe80::", true],
 273              ["0", false],
 274              ["127.0.0.0", false],
 275              ["127.0.0.0/24", false],
 276              ["fe80::fe80/128", false],
 277              ["fe80:fe80:fe80:fe80:fe80:fe80:fe80:fe80/128", false],
 278              ["fe80:", false],
 279              ["fe80:: ", false],
 280              [" fe80::", false],
 281              ["fe80::ddddd", false],
 282              ["fe80::gggg", false],
 283              ["fe80:fe80:fe80:fe80:fe80:fe80:fe80:fe80:fe80", false],
 284          ];
 285      }
 286  
 287      /**
 288       * Test for \core\ip_utils::is_ipv6_range().
 289       *
 290       * @param string $addressrange the address range to validate.
 291       * @param bool $expected the expected result.
 292       * @dataProvider ipv6_range_data_provider
 293       */
 294      public function test_is_ipv6_range($addressrange, $expected) {
 295          $this->assertEquals($expected, \core\ip_utils::is_ipv6_range($addressrange));
 296      }
 297  
 298      /**
 299       * Data provider for test_is_ipv6_range().
 300       *
 301       * @return array
 302       */
 303      public function ipv6_range_data_provider() {
 304          return [
 305              ["::/128", true],
 306              ["::1/128", true],
 307              ["fe80:fe80:fe80:fe80:fe80:fe80:fe80:fe80/128", true],
 308              ["fe80::dddd/128", true],
 309              ["fe80::/64", true],
 310              ["fe80::dddd-ffff", true],
 311              ["::0-ffff", true],
 312              ["::a-ffff", true],
 313              ["0", false],
 314              ["::1", false],
 315              ["fe80::fe80", false],
 316              ["::/128 ", false],
 317              [" ::/128", false],
 318              ["::/a", false],
 319              ["::/-1", false],
 320              ["fe80::fe80/129", false],
 321              ["fe80:fe80:fe80:fe80:fe80:fe80:fe80:fe80", false],
 322              ["fe80::bbbb-aaaa", false],
 323              ["fe80::0-fffg", false],
 324              ["fe80::0-fffff", false],
 325              ["fe80::0 - ffff", false],
 326              [" fe80::0-ffff", false],
 327              ["fe80::0-ffff ", false],
 328              ["192.0.0.0/24", false],
 329              ["fe80:::fe80/128", false],
 330              ["fe80:::aaaa-dddd", false],
 331          ];
 332      }
 333  
 334      /**
 335       * Test checking domains against a list of allowed domains.
 336       *
 337       * @param  bool $expected Expected result
 338       * @param  string $domain domain address
 339       * @dataProvider data_domain_addresses
 340       */
 341      public function test_check_domain_against_allowed_domains($expected, $domain) {
 342          $alloweddomains = ['example.com',
 343                             '*.moodle.com',
 344                             '*.per.this.penny-arcade.com',
 345                             'bad.*.url.com',
 346                             ' trouble.com.au'];
 347          $this->assertEquals($expected, \core\ip_utils::is_domain_in_allowed_list($domain, $alloweddomains));
 348      }
 349  
 350      /**
 351       * Data provider for test_check_domain_against_allowed_domains.
 352       *
 353       * @return array
 354       */
 355      public function data_domain_addresses() {
 356          return [
 357              [true, 'example.com'],
 358              [true, 'ExAmPle.com'],
 359              [false, 'sub.example.com'],
 360              [false, 'example.com.au'],
 361              [false, ' example.com'], // A space at the front of the domain is invalid.
 362              [false, 'example.123'], // Numbers at the end is invalid.
 363              [false, 'test.example.com'],
 364              [false, 'moodle.com'],
 365              [true, 'test.moodle.com'],
 366              [true, 'TeSt.moodle.com'],
 367              [true, 'test.MoOdLe.com'],
 368              [false, 'test.moodle.com.au'],
 369              [true, 'nice.address.per.this.penny-arcade.com'],
 370              [false, 'normal.per.this.penny-arcade.com.au'],
 371              [false, 'bad.thing.url.com'], // The allowed domain (above) has a bad wildcard and so this address will return false.
 372              [false, 'trouble.com.au'] // The allowed domain (above) has a space at the front and so will return false.
 373          ];
 374      }
 375  
 376      /**
 377       * Data provider for test_is_ip_in_subnet_list.
 378       *
 379       * @return array
 380       */
 381      public function data_is_ip_in_subnet_list() {
 382          return [
 383              [true, '1.1.1.1', '1.1.1.1', "\n"],
 384              [false, '1.1.1.1', '2.2.2.2', "\n"],
 385              [true, '1.1.1.1', "1.1.1.5\n1.1.1.1", "\n"],
 386              [true, '1.1.1.1', "1.1.1.5,1.1.1.1", ","],
 387          ];
 388      }
 389  
 390      /**
 391       * Test checking ips against a list of allowed domains.
 392       *
 393       * @param  bool $expected Expected result
 394       * @param  string $ip IP address
 395       * @param  string $list list of  IP subnets
 396       * @param  string $delim delimiter of list
 397       * @dataProvider data_is_ip_in_subnet_list
 398       */
 399      public function test_is_ip_in_subnet_list($expected, $ip, $list, $delim) {
 400          $this->assertEquals($expected, \core\ip_utils::is_ip_in_subnet_list($ip, $list, $delim));
 401      }
 402  
 403  }