urlGenerator = $urlGenerator; $this->request = $request; } /** * @param string $data * @return string */ public function convertLinks(string $data): string { $linker = new UrlLinker([ 'allowFtpAddresses' => true, 'allowUpperCaseUrlSchemes' => false, 'htmlLinkCreator' => static fn ($url) // Render full url for the link description. Otherwise, potentially malicious query // params might be hidden. => sprintf('%1$s', htmlspecialchars($url)), ]); $data = $linker->linkUrlsAndEscapeHtml($data); $config = HTMLPurifier_Config::createDefault(); // Append target="_blank" to all link (a) elements $config->set('HTML.TargetBlank', true); // allow cid, http and ftp $config->set('URI.AllowedSchemes', ['http' => true, 'https' => true, 'ftp' => true, 'mailto' => true]); $config->set('URI.Host', Util::getServerHostName()); // Disable the cache since ownCloud has no really appcache // TODO: Fix this - requires https://github.com/owncloud/core/issues/10767 to be fixed $config->set('Cache.DefinitionImpl', null); /** @var HTMLPurifier_HTMLDefinition $uri */ $uri = $config->getDefinition('HTML'); $uri->info_attr_transform_post['noreferrer'] = new TransformNoReferrer(); $purifier = new HTMLPurifier($config); return $purifier->purify($data); } /** * split off the signature * * @param string $body * @return array */ public function parseMailBody(string $body): array { $signature = null; $parts = preg_split("/-- (\n|(\r\n))/", $body); if (count($parts) > 1) { $signature = array_pop($parts); $body = implode("-- \r\n", $parts); } return [ $body, $signature ]; } public function sanitizeHtmlMailBody(string $mailBody, array $messageParameters, Closure $mapCidToAttachmentId): string { $config = HTMLPurifier_Config::createDefault(); // Append target="_blank" to all link (a) elements $config->set('HTML.TargetBlank', true); // allow cid, http and ftp $config->set('URI.AllowedSchemes', ['cid' => true, 'http' => true, 'https' => true, 'ftp' => true, 'mailto' => true]); $config->set('URI.Host', Util::getServerHostName()); $config->set('Filter.ExtractStyleBlocks', true); $config->set('Filter.ExtractStyleBlocks.TidyImpl', false); $config->set('CSS.AllowTricky', true); $config->set('CSS.Proprietary', true); // Disable the cache since ownCloud has no really appcache // TODO: Fix this - requires https://github.com/owncloud/core/issues/10767 to be fixed $config->set('Cache.DefinitionImpl', null); // Rewrite URL for redirection and proxying of content /** @var HTMLPurifier_HTMLDefinition $html */ $html = $config->getDefinition('HTML'); $html->info_attr_transform_post['imagesrc'] = new TransformImageSrc($this->urlGenerator); $html->info_attr_transform_post['cssbackground'] = new TransformStyleURLs($this->urlGenerator); $html->info_attr_transform_post['htmllinks'] = new TransformHTMLLinks($this->urlGenerator); /** @var HTMLPurifier_URIDefinition $uri */ $uri = $config->getDefinition('URI'); $uri->addFilter(new TransformURLScheme($messageParameters, $mapCidToAttachmentId, $this->urlGenerator, $this->request), $config); $uriSchemeRegistry = HTMLPurifier_URISchemeRegistry::instance(); $uriSchemeRegistry->register('cid', new CidURIScheme()); $uriSchemaData = new \HTMLPurifier_URIScheme_data(); $uriSchemaData->allowed_types['image/bmp'] = true; $uriSchemaData->allowed_types['image/tiff'] = true; $uriSchemaData->allowed_types['image/webp'] = true; $uriSchemeRegistry->register('data', $uriSchemaData); $purifier = new HTMLPurifier($config); $result = $purifier->purify($mailBody); // eat xml parse errors within HTMLPurifier libxml_clear_errors(); // Sanitize CSS rules $styles = $purifier->context->get('StyleBlocks'); if ($styles) { $joinedStyles = implode("\n", $styles); $result = $this->sanitizeStyleSheet($joinedStyles) . $result; } return $result; } /** * Block all URLs in the given CSS style sheet and return a formatted html style tag. * * @param string $styles The CSS style sheet to sanitize. * @return string Rendered style tag to be used in a html response. */ public function sanitizeStyleSheet(string $styles): string { $cssParser = new Parser($styles); $css = $cssParser->parse(); // Replace urls with blocked image $blockedUrl = new CSSString($this->urlGenerator->imagePath('mail', 'blocked-image.png')); $hasBlockedContent = false; foreach ($css->getAllValues() as $value) { if ($value instanceof URL) { $value->setURL($blockedUrl); $hasBlockedContent = true; } } // Save original styles to be able to restore them later $savedStyles = ''; if ($hasBlockedContent) { $savedStyles = 'data-original-content="' . htmlspecialchars($styles) . '"'; $styles = $css->render(OutputFormat::createCompact()); } // Render style tag return implode('', [ '', ]); } }