diff --git a/tests/behat/features/links.feature b/tests/behat/features/links.feature index b622f2f10..8232ec01b 100644 --- a/tests/behat/features/links.feature +++ b/tests/behat/features/links.feature @@ -38,6 +38,10 @@ Feature: Content links processing | field_c_p_content:value | External dark link from overridden domain | | field_c_p_content:format | civictheme_rich_text | | field_c_p_theme | dark | + And "field_c_n_components" in "civictheme_page" "node" with "title" of "[TEST] Page 1" has "civictheme_content" paragraph: + | field_c_p_content:value | Invalid link | + | field_c_p_content:format | civictheme_rich_text | + | field_c_p_theme | dark | And "field_c_n_components" in "civictheme_page" "node" with "title" of "[TEST] Page 1" has "civictheme_content" paragraph: | field_c_p_content:value | Telephone link | | field_c_p_content:format | civictheme_rich_text | @@ -108,4 +112,9 @@ Feature: Content links processing And I should see an ".ct-basic-content a[href='http://exampleoverridden.com/external-dark-link'][target='_blank'].ct-content-link" element And I should not see an ".ct-basic-content a[href='http://exampleoverridden.com/external-dark-link'].ct-content-link.ct-content-link--external" element + And I should see an ".ct-basic-content a[href='///C:/Users/civictheme/invalid'].ct-content-link" element + And I should see an ".ct-basic-content a[href='///C:/Users/civictheme/invalid'].ct-theme-dark" element + And I should see an ".ct-basic-content a[href='///C:/Users/civictheme/invalid'][target='_blank'].ct-content-link" element + And I should not see an ".ct-basic-content a[href='///C:/Users/civictheme/invalid'].ct-content-link.ct-content-link--external" element + And I should see an ".ct-basic-content a[href='mailto:person@test.com'].ct-content-link.ct-theme-dark" element diff --git a/web/themes/contrib/civictheme/includes/utilities.inc b/web/themes/contrib/civictheme/includes/utilities.inc index 79ab667f8..81e7aa3f2 100644 --- a/web/themes/contrib/civictheme/includes/utilities.inc +++ b/web/themes/contrib/civictheme/includes/utilities.inc @@ -653,33 +653,56 @@ function civictheme_get_icon_from_file(FileInterface $file): string { * @SuppressWarnings(PHPMD.StaticAccess) */ function civictheme_url_is_external(string $url, string $base_url, array $override_domains = []): bool { - if (empty($url) || !UrlHelper::isValid($url)) { + if (!civictheme_url_is_valid($url)) { return FALSE; } - $url_parts = parse_url($url); - if (!empty($url_parts['scheme']) && empty($url_parts['host'])) { + if (!UrlHelper::isExternal($url)) { return FALSE; } - if (UrlHelper::isExternal($url)) { - $override_domains[] = $base_url; - foreach ($override_domains as $override_domain) { - $override_domain = civictheme_external_link_normalize_domain($override_domain); + $override_domains[] = $base_url; + foreach ($override_domains as $override_domain) { + $override_domain = civictheme_external_link_normalize_domain($override_domain); - if (!UrlHelper::isValid($override_domain, TRUE)) { - continue; - } + if (!civictheme_url_is_valid($override_domain, TRUE)) { + continue; + } + try { if (UrlHelper::externalIsLocal($url, $override_domain)) { return FALSE; } } + catch (\InvalidArgumentException $exception) { + return FALSE; + } + } - return TRUE; + return TRUE; +} + +/** + * Check if a URL is valid. + * + * @param string $url + * The URL to be checked. + * @param bool $absolute + * Whether the URL is absolute (beginning with a scheme such as "http:"). + * + * @return bool + * TRUE if the provided URL is valid. + * + * @SuppressWarnings(PHPMD.StaticAccess) + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) + */ +function civictheme_url_is_valid(string $url, bool $absolute = FALSE) : bool { + if (empty($url) || !UrlHelper::isValid($url, $absolute)) { + return FALSE; } - return FALSE; + $url_parts = parse_url($url); + return !(!empty($url_parts['scheme']) && empty($url_parts['host'])); } /**