SECURITY: Escape watched word in error message (#14434)

SECURITY: Escape watched word in error message (#14434)

diff --git a/lib/new_post_manager.rb b/lib/new_post_manager.rb
index 1dc38fc..777d93b 100644
--- a/lib/new_post_manager.rb
+++ b/lib/new_post_manager.rb
@@ -201,10 +201,10 @@ class NewPostManager
       result = NewPostResult.new(:created_post, false)
       if matches.size == 1
         key = 'contains_blocked_word'
-        translation_args = { word: matches[0] }
+        translation_args = { word: CGI.escapeHTML(matches[0]) }
       else
         key = 'contains_blocked_words'
-        translation_args = { words: matches.join(', ') }
+        translation_args = { words: CGI.escapeHTML(matches.join(', ')) }
       end
       result.errors.add(:base, I18n.t(key, translation_args))
       return result
diff --git a/lib/validators/watched_words_validator.rb b/lib/validators/watched_words_validator.rb
index cfa7e5a..4174e51 100644
--- a/lib/validators/watched_words_validator.rb
+++ b/lib/validators/watched_words_validator.rb
@@ -5,10 +5,10 @@ class WatchedWordsValidator < ActiveModel::EachValidator
     if matches = WordWatcher.new(value).should_block?.presence
       if matches.size == 1
         key = 'contains_blocked_word'
-        translation_args = { word: matches[0] }
+        translation_args = { word: CGI.escapeHTML(matches[0]) }
       else
         key = 'contains_blocked_words'
-        translation_args = { words: matches.join(', ') }
+        translation_args = { words: CGI.escapeHTML(matches.join(', ')) }
       end
       record.errors.add(:base, I18n.t(key, translation_args))
     end
diff --git a/spec/integration/watched_words_spec.rb b/spec/integration/watched_words_spec.rb
index b8f5fff..7fb3c04 100644
--- a/spec/integration/watched_words_spec.rb
+++ b/spec/integration/watched_words_spec.rb
@@ -32,6 +32,14 @@ describe WatchedWord do
       }.to_not change { Post.count }
     end
 
+    it "escapes the blocked word in error message" do
+      block_word = Fabricate(:watched_word, action: WatchedWord.actions[:block], word: "<a>")
+      manager = NewPostManager.new(tl2_user, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id)
+      result = manager.perform
+      expect(result).to_not be_success
+      expect(result.errors[:base]&.first).to eq(I18n.t('contains_blocked_word', word: "&lt;a&gt;"))
+    end
+
     it "should prevent the post from being created" do
       manager = NewPostManager.new(tl2_user, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id)
       should_block_post(manager)

GitHub sha: 1f57b29147f570178d4317cca9e61449d7c62856

This commit appears in #14434 which was approved by ZogStriP. It was merged by nbianca.