From 3a37b9edf956421115b9bfef1e60456e051fd7d6 Mon Sep 17 00:00:00 2001 From: Mac_Weber Date: Sat, 19 Mar 2016 23:28:49 -0300 Subject: [PATCH] Issue #2652236 by Mac_Weber: Insufficient link validation for external URLs --- .../LinkExternalProtocolsConstraintValidator.php | 41 ++++++++++++++++++++-- ...inkExternalProtocolsConstraintValidatorTest.php | 8 ++++- core/modules/system/system.install | 17 +++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php index fad7b6d..fac1050 100644 --- a/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php +++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php @@ -44,9 +44,44 @@ public function validate($value, Constraint $constraint) { catch (\InvalidArgumentException $e) { return; } - // Disallow external URLs using untrusted protocols. - if ($url->isExternal() && !in_array(parse_url($url->getUri(), PHP_URL_SCHEME), UrlHelper::getAllowedProtocols())) { - $this->context->addViolation($constraint->message, array('@uri' => $value->uri)); + + // External URLs validation. + if ($url->isExternal()) { + $uri = $url->getUri(); + + // Disallow external URLs using untrusted protocols. This cannot be + // tested together in a single call to UrlHelper::isValid() because this + // function does not test all allowed protocols. + $protocol = parse_url($uri, PHP_URL_SCHEME); + $is_invalid_protocol = !in_array($protocol, UrlHelper::getAllowedProtocols()); + + // Gets the hostname. + if ($protocol == 'magnet') { + // The different format of magnet links do not work with PHP_URL_HOST. + $hostname = parse_url($uri, PHP_URL_QUERY); + } + else { + // All other protocols. + $hostname = parse_url($uri, PHP_URL_HOST); + } + + // Validates a domain if the needed PHP extensions are available for + // Punycode conversion. + if (function_exists('idn_to_ascii')) { + // Converts Punycode to ASCII. + $hostname = idn_to_ascii($hostname); + // Validates the URL. + $is_invalid_url = !UrlHelper::isValid($hostname); + } + // If cannot do Punycode validation, then just checks the presence of + // a hostname; + else { + $is_invalid_url = !boolval($hostname); + } + + if ($is_invalid_protocol || $is_invalid_url) { + $this->context->addViolation($constraint->message, array('@uri' => $value->uri)); + } } } } diff --git a/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php b/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php index 7a9c19c..d400ca9 100644 --- a/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php +++ b/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php @@ -36,7 +36,7 @@ public function testValidate($value, $valid) { } // Setup some more allowed protocols. - UrlHelper::setAllowedProtocols(['http', 'https', 'magnet']); + UrlHelper::setAllowedProtocols(['http', 'https', 'irc', 'magnet']); $constraint = new LinkExternalProtocolsConstraint(); @@ -54,11 +54,17 @@ public function providerValidate() { // Test allowed protocols. $data[] = ['http://www.drupal.org', TRUE]; $data[] = ['https://www.drupal.org', TRUE]; + $data[] = ['http://Iā™„Drupal', TRUE]; + $data[] = ['irc://freenode/Drupal', TRUE]; $data[] = ['magnet:?xt=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C', TRUE]; // Invalid protocols. $data[] = ['ftp://ftp.funet.fi/pub/standards/RFC/rfc959.txt', FALSE]; + // Malformed protocols. + $data[] = ['irc:', FALSE]; + $data[] = ['irc', FALSE]; + foreach ($data as &$single_data) { $url = Url::fromUri($single_data[0]); $link = $this->getMock('Drupal\link\LinkItemInterface'); diff --git a/core/modules/system/system.install b/core/modules/system/system.install index afa5892..4f9b191 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -799,6 +799,23 @@ function system_requirements($phase) { } } + // Check if the PHP IDN functions are available. + if ($phase == 'runtime') { + $installation_url = '//secure.php.net/manual/intl.setup.php'; + $documentation_url = 'https://secure.php.net/manual/en/book.intl.php'; + $requirements['php_intl'] = [ + 'title' => t('PHP Internationalization Functions'), + 'severity' => REQUIREMENT_INFO, + ]; + if (!function_exists('idn_to_ascii')) { + $requirements['php_intl']['value'] = t('Not available'); + $requirements['php_intl']['description'] = t('Enabling the PHP Internationalization functions helps on Punycode domains validation. Without this extension there will not be validation for Internationalized Domain Names. See the installation instructions for more detail.', [':url' => $installation_url]); + } + else { + $requirements['php_intl']['description'] = t('The PHP Internationalization Functions are available', [':url' => $documentation_url]); + } + } + // Check xdebug.max_nesting_level, as some pages will not work if it is too // low. if (extension_loaded('xdebug')) { -- 2.1.4