diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index 3f5f31b138..1cb6e83dea 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -434,6 +434,8 @@ class BBCode return $text; } + $data['url'] = self::sanitizedLink($data['url']); + if (isset($data['title'])) { $data['title'] = strip_tags($data['title']); $data['title'] = str_replace(['http://', 'https://'], '', $data['title']); @@ -485,6 +487,7 @@ class BBCode } if (!empty($data['provider_url']) && !empty($data['provider_name'])) { + $data['provider_url'] = self::sanitizedLink($data['provider_url']); if (!empty($data['author_name'])) { $return .= sprintf('%s (%s)', $data['provider_url'], $data['author_name'], $data['provider_name']); } else { @@ -1064,6 +1067,44 @@ class BBCode return $text; } + /** + * Remove invalid parts from an URL + * + * @param string $url + * @return string sanitized URL + */ + private static function sanitizedLink(string $url): string + { + $sanitzed = $url = trim($url); + + foreach (['"', ' '] as $character) { + $pos = strpos($sanitzed, $character); + if ($pos !== false) { + $sanitzed = trim(substr($sanitzed, 0, $pos)); + } + } + + if ($sanitzed != $url) { + Logger::debug('Link got sanitized', ['url' => $url, 'sanitzed' => $sanitzed]); + } + return $sanitzed; + } + + /** + * Callback: Sanitize links from given $match array + * + * @param array $match Array with link match + * @return string BBCode + */ + private static function sanitizeLinksCallback(array $match): string + { + if (count($match) == 3) { + return '[' . $match[1] . ']' . self::sanitizedLink($match[2]) . '[/' . $match[1] . ']'; + } else { + return '[' . $match[1] . '=' . self::sanitizedLink($match[2]) . ']' . $match[3] . '[/' . $match[1] . ']'; + } + } + /** * Callback: Expands links from given $match array * @@ -1717,6 +1758,9 @@ class BBCode // Simplify "video" element $text = preg_replace('(\[video[^\]]*?\ssrc\s?=\s?([^\s\]]+)[^\]]*?\].*?\[/video\])ism', '[video]$1[/video]', $text); + $text = preg_replace_callback("/\[(video)\](.*?)\[\/video\]/ism", [self::class, 'sanitizeLinksCallback'], $text); + $text = preg_replace_callback("/\[(audio)\](.*?)\[\/audio\]/ism", [self::class, 'sanitizeLinksCallback'], $text); + if ($simple_html == self::NPF) { $text = preg_replace( "/\[video\](.*?)\[\/video\]/ism", @@ -1759,6 +1803,7 @@ class BBCode } // Backward compatibility, [iframe] support has been removed in version 2020.12 + $text = preg_replace_callback("/\[(iframe)\](.*?)\[\/iframe\]/ism", [self::class, 'sanitizeLinksCallback'], $text); $text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '$1', $text); $text = self::normalizeVideoLinks($text); @@ -1811,6 +1856,9 @@ class BBCode $text = '' . $text . ''; } + $text = preg_replace_callback("/\[(url)\](.*?)\[\/url\]/ism", [self::class, 'sanitizeLinksCallback'], $text); + $text = preg_replace_callback("/\[(url)\=(.*?)\](.*?)\[\/url\]/ism", [self::class, 'sanitizeLinksCallback'], $text); + // Handle mentions and hashtag links if ($simple_html == self::DIASPORA) { // The ! is converted to @ since Diaspora only understands the @ @@ -1944,6 +1992,7 @@ class BBCode $text = preg_replace('/acct:([^@]+)@((?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63})/', 'acct:$1@$2', $text); // Perform MAIL Search + $text = preg_replace_callback("/\[(mail)\](.*?)\[\/mail\]/ism", [self::class, 'sanitizeLinksCallback'], $text); $text = preg_replace("/\[mail\](.*?)\[\/mail\]/", '$1', $text); $text = preg_replace("/\[mail\=(.*?)\](.*?)\[\/mail\]/", '$2', $text);