From 5187a9ef51980ad1b8e81945ebe0462d28f84f9e Mon Sep 17 00:00:00 2001
From: ooooooo_q <ooooooo-q@users.noreply.github.com>
Date: Fri, 5 Jan 2024 12:00:02 +0900
Subject: [PATCH] fix XSS vulnerability when using translation

[CVE-2024-26143]
---
 actionpack/CHANGELOG.md                       |  4 +++
 .../lib/abstract_controller/translation.rb    | 24 +++++++++++++-
 actionpack/test/abstract/translation_test.rb  | 31 +++++++++++++++++++
 3 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index ef67d64311..2ab374938d 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,7 @@
+*   Fix possible XSS vulnerability with the `translate` method in controllers
+
+    CVE-2024-26143
+
 *   Fix ReDoS in Accept header parsing
 
     CVE-2024-26142
diff --git a/actionpack/lib/abstract_controller/translation.rb b/actionpack/lib/abstract_controller/translation.rb
index 20b1a80e38..8659b9a046 100644
--- a/actionpack/lib/abstract_controller/translation.rb
+++ b/actionpack/lib/abstract_controller/translation.rb
@@ -21,7 +21,25 @@ def translate(key, **options)
         key = "#{path}.#{action_name}#{key}"
       end
 
-      ActiveSupport::HtmlSafeTranslation.translate(key, **options)
+      if options[:default]
+        options[:default] = [options[:default]] unless options[:default].is_a?(Array)
+        options[:default] = options[:default].map do |value|
+          value.is_a?(String) ? ERB::Util.html_escape(value) : value
+        end
+      end
+
+      if options[:raise].nil?
+        options[:default] = [] unless options[:default]
+        options[:default] << MISSING_TRANSLATION
+      end
+
+      result = ActiveSupport::HtmlSafeTranslation.translate(key, **options)
+
+      if result == MISSING_TRANSLATION
+        +"translation missing: #{key}"
+      else
+        result
+      end
     end
     alias :t :translate
 
@@ -30,5 +48,9 @@ def localize(object, **options)
       I18n.localize(object, **options)
     end
     alias :l :localize
+
+    private
+      MISSING_TRANSLATION = -(2**60)
+      private_constant :MISSING_TRANSLATION
   end
 end
diff --git a/actionpack/test/abstract/translation_test.rb b/actionpack/test/abstract/translation_test.rb
index c1bc8a050a..3d5483381c 100644
--- a/actionpack/test/abstract/translation_test.rb
+++ b/actionpack/test/abstract/translation_test.rb
@@ -83,6 +83,22 @@ def test_default_translation
         end
       end
 
+      def test_default_translation_as_safe_html
+        @controller.stub :action_name, :index do
+          translation = @controller.t(".twoz", default: ["<tag>"])
+          assert_equal "&lt;tag&gt;", translation
+          assert_equal true, translation.html_safe?
+        end
+      end
+
+      def test_default_translation_with_raise_as_safe_html
+        @controller.stub :action_name, :index do
+          translation = @controller.t(".twoz", raise: true, default: ["<tag>"])
+          assert_equal "&lt;tag&gt;", translation
+          assert_equal true, translation.html_safe?
+        end
+      end
+
       def test_localize
         time, expected = Time.gm(2000), "Sat, 01 Jan 2000 00:00:00 +0000"
         I18n.stub :localize, expected do
@@ -126,6 +142,21 @@ def test_translate_escapes_interpolations_in_translations_with_a_html_suffix
           assert_equal true, translation.html_safe?
         end
       end
+
+      def test_translate_marks_translation_with_missing_html_key_as_safe_html
+        @controller.stub :action_name, :index do
+          translation = @controller.t("<tag>.html")
+          assert_equal "translation missing: <tag>.html", translation
+          assert_equal false, translation.html_safe?
+        end
+      end
+      def test_translate_marks_translation_with_missing_nested_html_key_as_safe_html
+        @controller.stub :action_name, :index do
+          translation = @controller.t(".<tag>.html")
+          assert_equal "translation missing: abstract_controller.testing.translation.index.<tag>.html", translation
+          assert_equal false, translation.html_safe?
+        end
+      end
     end
   end
 end
-- 
2.37.1

