From c2eb2f460b7c1f451cd6bbfeb383464615211920 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 18 Nov 2016 22:05:56 -0500 Subject: [PATCH 1/8] Fix Friendica private image URL replacing - Add baseurl to redir_private_images to prevent empty src attribute in generated HTML - URL encode the URL parameter url and conurl - Formatting - Documentation --- include/text.php | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/include/text.php b/include/text.php index 83eab19270..e0b301ce31 100644 --- a/include/text.php +++ b/include/text.php @@ -771,7 +771,7 @@ function activity_match($haystack,$needle) { /** * @brief Pull out all #hashtags and @person tags from $string. - * + * * We also get @person@domain.com - which would make * the regex quite complicated as tags can also * end a sentence. So we'll run through our results @@ -1170,33 +1170,29 @@ function link_compare($a,$b) { return false; }} - -if(! function_exists('redir_private_images')) { /** - * Find any non-embedded images in private items and add redir links to them + * @brief Find any non-embedded images in private items and add redir links to them * * @param App $a - * @param array $item + * @param array &$item The field array of an item row */ -function redir_private_images($a, &$item) { - +function redir_private_images($a, &$item) +{ $matches = false; $cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER); - if($cnt) { - //logger("redir_private_images: matches = " . print_r($matches, true)); - foreach($matches as $mtch) { - if(strpos($mtch[1], '/redir') !== false) + if ($cnt) { + foreach ($matches as $mtch) { + if(strpos($mtch[1], '/redir') !== false) { continue; + } - if((local_user() == $item['uid']) && ($item['private'] != 0) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) { - //logger("redir_private_images: redir"); - $img_url = 'redir?f=1&quiet=1&url=' . $mtch[1] . '&conurl=' . $item['author-link']; - $item['body'] = str_replace($mtch[0], "[img]".$img_url."[/img]", $item['body']); + if ((local_user() == $item['uid']) && ($item['private'] != 0) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) { + $img_url = $a->get_baseurl() . '/redir?f=1&quiet=1&url=' . urlencode($mtch[1]) . '&conurl=' . urlencode($item['author-link']); + $item['body'] = str_replace($mtch[0], '[img]' . $img_url . '[/img]', $item['body']); } } } - -}} +} function put_item_in_cache(&$item, $update = false) { From 003e6a73717ab9dbc02ff9785139545513c974da Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 19 Nov 2016 18:01:45 -0500 Subject: [PATCH 2/8] Improve BBCode javascript stripping regex --- include/bbcode.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/bbcode.php b/include/bbcode.php index ebafc353a4..6a1630d1fc 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -1161,8 +1161,10 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal $Text = preg_replace('/\"\;/','"',$Text); // fix any escaped ampersands that may have been converted into links - $Text = preg_replace("/\<([^>]*?)(src|href)=(.*?)\&\;(.*?)\>/ism",'<$1$2=$3&$4>',$Text); - $Text = preg_replace("/\<([^>]*?)(src|href)=\"(?!http|ftp|mailto|gopher|cid)(.*?)\>/ism",'<$1$2="">',$Text); + $Text = preg_replace('/\<([^>]*?)(src|href)=(.*?)\&\;(.*?)\>/ism', '<$1$2=$3&$4>', $Text); + + // removes potentially harmful javascript in src/href + $Text = preg_replace('/\<([^>]*?)(src|href)="javascript(.*?)\>/ism', '', $Text); if($saved_image) $Text = bb_replace_images($Text, $saved_image); From 47a370c5e3eca47351ddf7343dd5378125044bac Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 3 Dec 2016 10:39:06 -0500 Subject: [PATCH 3/8] Revert adding baseurl to private image URL --- include/text.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/text.php b/include/text.php index 89ab7254b6..f45dd6391c 100644 --- a/include/text.php +++ b/include/text.php @@ -1187,7 +1187,7 @@ function redir_private_images($a, &$item) } if ((local_user() == $item['uid']) && ($item['private'] != 0) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) { - $img_url = $a->get_baseurl() . '/redir?f=1&quiet=1&url=' . urlencode($mtch[1]) . '&conurl=' . urlencode($item['author-link']); + $img_url = '/redir?f=1&quiet=1&url=' . urlencode($mtch[1]) . '&conurl=' . urlencode($item['author-link']); $item['body'] = str_replace($mtch[0], '[img]' . $img_url . '[/img]', $item['body']); } } From 979fc6d38a842373cfaf17b18daadea003761ed9 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 3 Dec 2016 14:19:57 -0500 Subject: [PATCH 4/8] Add protocol whitelist for href/src attributes - Add strict URL checker for src attributes - Add protocol whitelist for href attributes - Add error styling if URL fails to pass filter - Add doc for new htconfig value - Add emphasis to config key names in htconfig doc --- doc/htconfig.md | 123 +++++++++++++++++++++++---------------------- htconfig.php | 3 ++ include/bbcode.php | 12 +++-- view/global.css | 21 +++++--- 4 files changed, 88 insertions(+), 71 deletions(-) diff --git a/doc/htconfig.md b/doc/htconfig.md index dbdbf15033..b808f630b0 100644 --- a/doc/htconfig.md +++ b/doc/htconfig.md @@ -14,78 +14,79 @@ Example: To set the directory value please add this line to your .htconfig.php: $a->config['system']['directory'] = 'http://dir.friendi.ca'; -## Jabber ## -* debug (Boolean) - Enable debug level for the jabber account synchronisation. -* logfile - Logfile for the jabber account synchronisation. +## jabber ## +* **debug** (Boolean) - Enable debug level for the jabber account synchronisation. +* **logfile** - Logfile for the jabber account synchronisation. -## System ## +## system ## -* birthday_input_format - Default value is "ymd". -* block_local_dir (Boolean) - Blocks the access to the directory of the local users. -* default_service_class - -* delivery_batch_count - Number of deliveries per process. Default value is 1. (Disabled when using the worker) -* diaspora_test (Boolean) - For development only. Disables the message transfer. -* directory - The path to global directory. If not set then "http://dir.friendi.ca" is used. -* disable_email_validation (Boolean) - Disables the check if a mail address is in a valid format and can be resolved via DNS. -* disable_url_validation (Boolean) - Disables the DNS lookup of an URL. -* event_input_format - Default value is "ymd". -* frontend_worker (Boolean) - Activates the frontend worker which acts as a replacement for running the poller via the command line. -* frontend_worker_timeout - Value in minutes after we think that a frontend task was killed by the webserver. Default value is 10. -* ignore_cache (Boolean) - For development only. Disables the item cache. -* like_no_comment (Boolean) - Don't update the "commented" value of an item when it is liked. -* local_block (Boolean) - Used in conjunction with "block_public". -* local_search (Boolean) - Blocks the search for not logged in users to prevent crawlers from blocking your system. -* max_connections - The poller process isn't started when the maximum level of the possible database connections are used. When the system can't detect the maximum numbers of connection then this value can be used. -* max_connections_level - The maximum level of connections that are allowed to let the poller start. It is a percentage value. Default value is 75. -* max_contact_queue - Default value is 500. -* max_batch_queue - Default value is 1000. -* max_processes_backend - Maximum number of concurrent database processes for background tasks. Default value is 5. -* max_processes_frontend - Maximum number of concurrent database processes for foreground tasks. Default value is 20. -* memcache (Boolean) - Use memcache. To use memcache the PECL extension "memcache" has to be installed and activated. -* memcache_host - Hostname of the memcache daemon. Default is '127.0.0.1'. -* memcache_port- Portnumberof the memcache daemon. Default is 11211. -* no_oembed (Boolean) - Don't use OEmbed to fetch more information about a link. -* no_oembed_rich_content (Boolean) - Don't show the rich content (e.g. embedded PDF). -* no_smilies (Boolean) - Don't show smilies. -* no_view_full_size (Boolean) - Don't add the link "View full size" under a resized image. -* optimize_items (Boolean) - Triggers an SQL command to optimize the item table before expiring items. -* ostatus_poll_timeframe - Defines how old an item can be to try to complete the conversation with it. -* paranoia (Boolean) - Log out users if their IP address changed. -* permit_crawling (Boolean) - Restricts the search for not logged in users to one search per minute. -* profiler (Boolean) - Enable internal timings to help optimize code. Needed for "rendertime" addon. Default is false. -* free_crawls - Number of "free" searches when "permit_crawling" is activated (Default value is 10) -* crawl_permit_period - Period in seconds between allowed searches when the number of free searches is reached and "permit_crawling" is activated (Default value is 60) -* png_quality - Default value is 8. -* proc_windows (Boolean) - Should be enabled if Friendica is running under Windows. -* proxy_cache_time - Time after which the cache is cleared. Default value is one day. -* pushpoll_frequency - -* qsearch_limit - Default value is 100. -* relay_server - Experimental Diaspora feature. Address of the relay server where public posts should be send to. For example https://podrelay.net -* relay_subscribe (Boolean) - Enables the receiving of public posts from the relay. They will be included in the search and on the community page when it is set up to show all public items. -* relay_scope - Can be "all" or "tags". "all" means that every public post should be received. "tags" means that only posts with selected tags should be received. -* relay_server_tags - Comma separated list of tags for the "tags" subscription (see "relay_scrope") -* relay_user_tags (Boolean) - If enabled, the tags from the saved searches will used for the "tags" subscription in addition to the "relay_server_tags". -* remove_multiplicated_lines (Boolean) - If enabled, multiple linefeeds in items are stripped to a single one. -* show_unsupported_addons (Boolean) - Show all addons including the unsupported ones. -* show_unsupported_themes (Boolean) - Show all themes including the unsupported ones. -* throttle_limit_day - Maximum number of posts that a user can send per day with the API. -* throttle_limit_week - Maximum number of posts that a user can send per week with the API. -* throttle_limit_month - Maximum number of posts that a user can send per month with the API. -* wall-to-wall_share (Boolean) - Displays forwarded posts like "wall-to-wall" posts. -* worker_cooldown - Cooldown time after each worker function call. Default value is 0 seconds. -* xrd_timeout - Timeout for fetching the XRD links. Default value is 20 seconds. +* **allowed_link_protocols** (Array) - Allowed protocols in links URLs, add at your own risk. http is always allowed. +* **birthday_input_format** - Default value is "ymd". +* **block_local_dir** (Boolean) - Blocks the access to the directory of the local users. +* **default_service_class** - +* **delivery_batch_count** - Number of deliveries per process. Default value is 1. (Disabled when using the worker) +* **diaspora_test** (Boolean) - For development only. Disables the message transfer. +* **directory** - The path to global directory. If not set then "http://dir.friendi.ca" is used. +* **disable_email_validation** (Boolean) - Disables the check if a mail address is in a valid format and can be resolved via DNS. +* **disable_url_validation** (Boolean) - Disables the DNS lookup of an URL. +* **event_input_format** - Default value is "ymd". +* **frontend_worker** (Boolean) - Activates the frontend worker which acts as a replacement for running the poller via the command line. +* **frontend_worker_timeout** - Value in minutes after we think that a frontend task was killed by the webserver. Default value is 10. +* **ignore_cache** (Boolean) - For development only. Disables the item cache. +* **like_no_comment** (Boolean) - Don't update the "commented" value of an item when it is liked. +* **local_block** (Boolean) - Used in conjunction with "block_public". +* **local_search** (Boolean) - Blocks the search for not logged in users to prevent crawlers from blocking your system. +* **max_connections** - The poller process isn't started when the maximum level of the possible database connections are used. When the system can't detect the maximum numbers of connection then this value can be used. +* **max_connections_level** - The maximum level of connections that are allowed to let the poller start. It is a percentage value. Default value is 75. +* **max_contact_queue** - Default value is 500. +* **max_batch_queue** - Default value is 1000. +* **max_processes_backend** - Maximum number of concurrent database processes for background tasks. Default value is 5. +* **max_processes_frontend** - Maximum number of concurrent database processes for foreground tasks. Default value is 20. +* **memcache** (Boolean) - Use memcache. To use memcache the PECL extension "memcache" has to be installed and activated. +* **memcache_host** - Hostname of the memcache daemon. Default is '127.0.0.1'. +* **memcache_port** - Portnumberof the memcache daemon. Default is 11211. +* **no_oembed** (Boolean) - Don't use OEmbed to fetch more information about a link. +* **no_oembed_rich_content** (Boolean) - Don't show the rich content (e.g. embedded PDF). +* **no_smilies** (Boolean) - Don't show smilies. +* **no_view_full_size** (Boolean) - Don't add the link "View full size" under a resized image. +* **optimize_items** (Boolean) - Triggers an SQL command to optimize the item table before expiring items. +* **ostatus_poll_timeframe** - Defines how old an item can be to try to complete the conversation with it. +* **paranoia** (Boolean) - Log out users if their IP address changed. +* **permit_crawling** (Boolean) - Restricts the search for not logged in users to one search per minute. +* **profiler** (Boolean) - Enable internal timings to help optimize code. Needed for "rendertime" addon. Default is false. +* **free_crawls** - Number of "free" searches when "permit_crawling" is activated (Default value is 10) +* **crawl_permit_period** - Period in seconds between allowed searches when the number of free searches is reached and "permit_crawling" is activated (Default value is 60) +* **png_quality** - Default value is 8. +* **proc_windows** (Boolean) - Should be enabled if Friendica is running under Windows. +* **proxy_cache_time** - Time after which the cache is cleared. Default value is one day. +* **pushpoll_frequency** - +* **qsearch_limit** - Default value is 100. +* **relay_server** - Experimental Diaspora feature. Address of the relay server where public posts should be send to. For example https://podrelay.net +* **relay_subscribe** (Boolean) - Enables the receiving of public posts from the relay. They will be included in the search and on the community page when it is set up to show all public items. +* **relay_scope** - Can be "all" or "tags". "all" means that every public post should be received. "tags" means that only posts with selected tags should be received. +* **relay_server_tags** - Comma separated list of tags for the "tags" subscription (see "relay_scrope") +* **relay_user_tags** (Boolean) - If enabled, the tags from the saved searches will used for the "tags" subscription in addition to the "relay_server_tags". +* **remove_multiplicated_lines** (Boolean) - If enabled, multiple linefeeds in items are stripped to a single one. +* **show_unsupported_addons** (Boolean) - Show all addons including the unsupported ones. +* **show_unsupported_themes** (Boolean) - Show all themes including the unsupported ones. +* **throttle_limit_day** - Maximum number of posts that a user can send per day with the API. +* **throttle_limit_week** - Maximum number of posts that a user can send per week with the API. +* **throttle_limit_month** - Maximum number of posts that a user can send per month with the API. +* **wall-to-wall_share** (Boolean) - Displays forwarded posts like "wall-to-wall" posts. +* **worker_cooldown** - Cooldown time after each worker function call. Default value is 0 seconds. +* **xrd_timeout** - Timeout for fetching the XRD links. Default value is 20 seconds. ## service_class ## -* upgrade_link - +* **upgrade_link** - ## experimentals ## -* exp_themes (Boolean) - Show experimental themes as well. +* **exp_themes** (Boolean) - Show experimental themes as well. ## theme ## -* hide_eventlist (Boolean) - Don't show the birthdays and events on the profile and network page +* **hide_eventlist** (Boolean) - Don't show the birthdays and events on the profile and network page # Administrator Options # diff --git a/htconfig.php b/htconfig.php index 469fa7af61..ce418fa750 100644 --- a/htconfig.php +++ b/htconfig.php @@ -78,3 +78,6 @@ $a->config['system']['no_regfullname'] = true; // Location of the global directory $a->config['system']['directory'] = 'http://dir.friendi.ca'; + +// Allowed protocols in link URLs; HTTP protocols always are accepted +$a->config['system']['allowed_link_protocols'] = array('ftp', 'ftps', 'mailto', 'cid', 'gopher'); \ No newline at end of file diff --git a/include/bbcode.php b/include/bbcode.php index 6a1630d1fc..0d0cb0177c 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -1163,11 +1163,17 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal // fix any escaped ampersands that may have been converted into links $Text = preg_replace('/\<([^>]*?)(src|href)=(.*?)\&\;(.*?)\>/ism', '<$1$2=$3&$4>', $Text); - // removes potentially harmful javascript in src/href - $Text = preg_replace('/\<([^>]*?)(src|href)="javascript(.*?)\>/ism', '', $Text); + // sanitizes src attributes (only relative URIs or http URLs) + $Text = preg_replace('#<([^>]*?)(src)="(?!/|http)(.*?)"(.*?)>#ism', '<$1$2=""$4 class="invalid-src" title="' . t('Invalid source protocol') . '">', $Text); - if($saved_image) + // sanitize href attributes (only relative URIs or whitelisted protocols URLs) + $allowed_link_protocols = get_config('system', 'allowed_link_protocols'); + $regex = '#<([^>]*?)(href)="(?!/|http|' . implode('|', $allowed_link_protocols) . ')(.*?)"(.*?)>#ism'; + $Text = preg_replace($regex, '<$1$2="javascript:void(0)"$4 class="invalid-href" title="' . t('Invalid link protocol') . '">', $Text); + + if($saved_image) { $Text = bb_replace_images($Text, $saved_image); + } // Clean up the HTML by loading and saving the HTML with the DOM. // Bad structured html can break a whole page. diff --git a/view/global.css b/view/global.css index 85e45db006..4dd4cd5726 100644 --- a/view/global.css +++ b/view/global.css @@ -365,15 +365,15 @@ a { color: #00a700; } .federation-graph { - width: 400px; - height: 400px; - float: right; + width: 400px; + height: 400px; + float: right; margin: 20px; } .federation-network-graph { - width: 240px; - height: 240px; - float: left; + width: 240px; + height: 240px; + float: left; margin: 20px; } ul.federation-stats, @@ -429,7 +429,7 @@ td.federation-data { } .p-addr { - clear: both; + clear: both; } #live-community { @@ -481,3 +481,10 @@ td.pendingnote > p > span { border-left: 5px solid #f00; font-weight: bold; } + +/* src/href attributes filter error display */ +.invalid-src { border: 1px dotted red;} +.invalid-href { border-bottom: 1px dotted red;} +.invalid-src:after, +.invalid-href:after { content: '⚠️'} +img.invalid-src:after { vertical-align: top;} From 5e20aed42878635e6ac7cff7ac328b812e96e204 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 4 Dec 2016 21:31:28 -0500 Subject: [PATCH 5/8] Revert adding a leading slash to redir links --- include/text.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/text.php b/include/text.php index f45dd6391c..cfd9b167d0 100644 --- a/include/text.php +++ b/include/text.php @@ -1187,7 +1187,7 @@ function redir_private_images($a, &$item) } if ((local_user() == $item['uid']) && ($item['private'] != 0) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) { - $img_url = '/redir?f=1&quiet=1&url=' . urlencode($mtch[1]) . '&conurl=' . urlencode($item['author-link']); + $img_url = 'redir?f=1&quiet=1&url=' . urlencode($mtch[1]) . '&conurl=' . urlencode($item['author-link']); $item['body'] = str_replace($mtch[0], '[img]' . $img_url . '[/img]', $item['body']); } } From 2b75ad5e0afd0d458fb5c0330cd1fedecd40beb3 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 4 Dec 2016 21:33:29 -0500 Subject: [PATCH 6/8] src and href attributes sanitization touchups - Use Config::get - Add default to config call - Add always allowed protocol to href - Remove relative root URLs from allowed forms --- include/bbcode.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/include/bbcode.php b/include/bbcode.php index 0d0cb0177c..52cfa97c8b 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -1,4 +1,6 @@ ]*?)(src|href)=(.*?)\&\;(.*?)\>/ism', '<$1$2=$3&$4>', $Text); - // sanitizes src attributes (only relative URIs or http URLs) - $Text = preg_replace('#<([^>]*?)(src)="(?!/|http)(.*?)"(.*?)>#ism', '<$1$2=""$4 class="invalid-src" title="' . t('Invalid source protocol') . '">', $Text); + // sanitizes src attributes (only relative redir URIs or http URLs) + $Text = preg_replace('#<([^>]*?)(src)="(?!http|redir)(.*?)"(.*?)>#ism', '<$1$2=""$4 class="invalid-src" title="' . t('Invalid source protocol') . '">', $Text); - // sanitize href attributes (only relative URIs or whitelisted protocols URLs) - $allowed_link_protocols = get_config('system', 'allowed_link_protocols'); - $regex = '#<([^>]*?)(href)="(?!/|http|' . implode('|', $allowed_link_protocols) . ')(.*?)"(.*?)>#ism'; + // sanitize href attributes (only whitelisted protocols URLs) + $allowed_link_protocols = Config::get('system', 'allowed_link_protocols', array()); + + // Always allowed protocol even if config isn't set + $allowed_link_protocols[] = 'http'; + + $regex = '#<([^>]*?)(href)="(?!' . implode('|', $allowed_link_protocols) . ')(.*?)"(.*?)>#ism'; $Text = preg_replace($regex, '<$1$2="javascript:void(0)"$4 class="invalid-href" title="' . t('Invalid link protocol') . '">', $Text); if($saved_image) { From e29023dc8c38fcb41643da410f2bfa3dcd175fd5 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 7 Dec 2016 23:11:53 -0500 Subject: [PATCH 7/8] Standards --- include/text.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/text.php b/include/text.php index cfd9b167d0..1197f24ee1 100644 --- a/include/text.php +++ b/include/text.php @@ -1182,7 +1182,7 @@ function redir_private_images($a, &$item) $cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER); if ($cnt) { foreach ($matches as $mtch) { - if(strpos($mtch[1], '/redir') !== false) { + if (strpos($mtch[1], '/redir') !== false) { continue; } From cd354d3a575eb137ba8988c61cff475cb4e66688 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 7 Dec 2016 23:18:10 -0500 Subject: [PATCH 8/8] Adding default protocols on missing config value for backward compatibility --- include/bbcode.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/bbcode.php b/include/bbcode.php index 52cfa97c8b..27213007ce 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -1169,9 +1169,10 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal $Text = preg_replace('#<([^>]*?)(src)="(?!http|redir)(.*?)"(.*?)>#ism', '<$1$2=""$4 class="invalid-src" title="' . t('Invalid source protocol') . '">', $Text); // sanitize href attributes (only whitelisted protocols URLs) - $allowed_link_protocols = Config::get('system', 'allowed_link_protocols', array()); + // default value for backward compatibility + $allowed_link_protocols = Config::get('system', 'allowed_link_protocols', array('ftp', 'mailto', 'gopher', 'cid')); - // Always allowed protocol even if config isn't set + // Always allowed protocol even if config isn't set or not including it $allowed_link_protocols[] = 'http'; $regex = '#<([^>]*?)(href)="(?!' . implode('|', $allowed_link_protocols) . ')(.*?)"(.*?)>#ism';