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