diff --git a/core/lib/Drupal/Core/Logger/LogMessageParser.php b/core/lib/Drupal/Core/Logger/LogMessageParser.php index 690847e7d5..7f4ca4831f 100644 --- a/core/lib/Drupal/Core/Logger/LogMessageParser.php +++ b/core/lib/Drupal/Core/Logger/LogMessageParser.php @@ -29,8 +29,8 @@ public function parseMessagePlaceholders(&$message, array &$context) { $key = '@' . $key; } } - if (!empty($key) && ($key[0] === '@' || $key[0] === '%' || $key[0] === '!')) { - // The key is now in \Drupal\Component\Render\FormattableMarkup style. + if (!empty($key) && ($key[0] === '@' || $key[0] === '%' || $key[0] === ':')) { + // The key is now in \Drupal\Component\Utility\SafeMarkup::format() style. $variables[$key] = $variable; } } diff --git a/core/modules/dblog/src/Controller/DbLogController.php b/core/modules/dblog/src/Controller/DbLogController.php index 9fe2922ece..48604fd1b3 100644 --- a/core/modules/dblog/src/Controller/DbLogController.php +++ b/core/modules/dblog/src/Controller/DbLogController.php @@ -11,6 +11,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormBuilderInterface; use Drupal\Core\Logger\RfcLogLevel; +use Drupal\Core\Render\Markup; use Drupal\Core\Url; use Drupal\user\Entity\User; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -343,7 +344,7 @@ protected function buildFilterQuery() { * The record from the watchdog table. The object properties are: wid, uid, * severity, type, timestamp, message, variables, link, name. * - * @return string|\Drupal\Core\StringTranslation\TranslatableMarkup|false + * @return \Drupal\Component\Render\MarkupInterface|string|false * The formatted log message or FALSE if the message or variables properties * are not set. */ @@ -353,15 +354,16 @@ public function formatMessage($row) { $variables = @unserialize($row->variables); // Messages without variables or user specified text. if ($variables === NULL) { - $message = Xss::filterAdmin($row->message); + $message = $row->message; } elseif (!is_array($variables)) { $message = $this->t('Log data is corrupted and cannot be unserialized: @message', ['@message' => Xss::filterAdmin($row->message)]); } // Message to translate with injected variables. else { - $message = $this->t(Xss::filterAdmin($row->message), $variables); + $message = $this->t($row->message, $variables); } + $message = Markup::create(Xss::filterAdmin($message)); } else { $message = FALSE; diff --git a/core/modules/dblog/tests/src/Functional/DbLogTest.php b/core/modules/dblog/tests/src/Functional/DbLogTest.php index 4ad02b699c..9b057e4cf9 100644 --- a/core/modules/dblog/tests/src/Functional/DbLogTest.php +++ b/core/modules/dblog/tests/src/Functional/DbLogTest.php @@ -2,12 +2,15 @@ namespace Drupal\Tests\dblog\Functional; +use Drupal\Component\Render\FormattableMarkup; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Unicode; use Drupal\Core\Database\Database; +use Drupal\Component\Utility\Xss; use Drupal\Core\Logger\RfcLogLevel; use Drupal\Core\Url; use Drupal\dblog\Controller\DbLogController; +use Drupal\language\Entity\ConfigurableLanguage; use Drupal\Tests\BrowserTestBase; /** @@ -24,7 +27,7 @@ class DbLogTest extends BrowserTestBase { * * @var array */ - public static $modules = ['dblog', 'node', 'forum', 'help', 'block']; + public static $modules = ['dblog', 'node', 'forum', 'help', 'block', 'locale']; /** * A user with some relevant administrative permissions. @@ -102,7 +105,7 @@ public function testLogEventPage() { 'ip' => '0.0.1.0', 'timestamp' => REQUEST_TIME, ]; - \Drupal::service('logger.dblog')->log(RfcLogLevel::NOTICE, 'Test message', $context); + \Drupal::service('logger.dblog')->notice('Test message', $context); $wid = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField(); // Verify the links appear correctly. @@ -118,6 +121,98 @@ public function testLogEventPage() { } /** + * Make sure log messages in log pages are properly escaped. + */ + public function testLogEventPageMessageEscaped() { + $this->drupalLogin($this->adminUser); + + $context = [ + 'request_uri' => 'http://example.com?dblog=1', + 'referer' => 'http://example.org?dblog=2', + 'uid' => 0, + 'channel' => 'testing', + 'link' => 'foo/bar', + 'ip' => '0.0.1.0', + 'timestamp' => REQUEST_TIME, + ]; + + // Make sure HTML tags are filtered out in admin/reports/dblog/event/. + \Drupal::service('logger.dblog')->notice(" Lorem ipsum", $context); + $wid = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField(); + + $this->drupalGet('admin/reports/dblog/event/' . $wid); + $this->assertResponse(200); + $this->assertNoRaw(""); + $this->assertRaw("alert('foo'); Lorem ipsum"); + } + + /** + * Make sure log messages in log pages are properly translated and filtered. + */ + public function testLogEventPageMessageTranslated() { + $this->drupalLogin($this->adminUser); + + $langcode = 'ja'; + ConfigurableLanguage::createFromLangcode($langcode)->save(); + $this->config('system.site')->set('default_langcode', $langcode)->save(); + + $text_source = '< > & source @one %two :three'; + $text_translation = '< > & translation @one %two :three'; + + $locale_storage = $this->container->get('locale.storage'); + + $source = $locale_storage->createString([ + 'source' => $text_source, + ]); + $source->save(); + $translation = $locale_storage->createTranslation([ + 'lid' => $source->lid, + 'language' => $langcode, + 'translation' => $text_translation, + ]); + $translation->save(); + + $translation_manager = $this->container->get('string_translation'); + $translation_manager->reset(); + + $translation_variables = [ + '@one' => 'foo', + '%two' => 'bar', + ':three' => 'http://example.com', + ]; + + $context = $translation_variables + [ + 'request_uri' => 'http://example.com?dblog=1', + 'referer' => 'http://example.org?dblog=2', + 'uid' => 0, + 'channel' => 'testing', + 'link' => 'foo/bar', + 'ip' => '0.0.1.0', + 'timestamp' => REQUEST_TIME, + ]; + + \Drupal::service('logger.dblog')->notice($text_source, $context); + $wid = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField(); + + // Make sure HTML tags are filtered out after translation. + $this->drupalGet('admin/reports/dblog/event/' . $wid); + $this->assertResponse(200); + $this->assertTranslations($text_source, $translation_variables); + + // Make sure HTML tags are filtered out after translation. + $this->drupalGet('admin/reports/dblog/'); + $this->assertResponse(200); + $this->assertTranslations($text_source, $translation_variables); + } + + /** + * Returns FormattableMarkup object. + */ + protected function assertTranslations($text_source, $translation_variables) { + $this->assertNoRaw(Xss::filterAdmin(new FormattableMarkup($text_source, $translation_variables))); + } + + /** * Verifies setting of the database log row limit. * * @param int $row_limit