Skip to content

Commit 04b752a

Browse files
committed
FIX: Ensure translated content is safe for rendering
1 parent c1aa718 commit 04b752a

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed

app/models/concerns/discourse_translator/translatable.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ def set_detected_locale(locale)
1717
(content_locale || build_content_locale).update!(detected_locale: locale)
1818
end
1919

20+
# This method is used to create a translation for a translatable (Post or Topic) and a specific locale.
21+
# If a translation already exists for the locale, it will be updated.
22+
# Texts are put through a Sanitizer to clean them up before saving.
23+
# @param locale [String] the locale of the translation
24+
# @param text [String] the translated text
2025
def set_translation(locale, text)
2126
locale = locale.to_s.gsub("_", "-")
27+
text = DiscourseTranslator::TranslatedContentSanitizer.sanitize(self.class, text)
2228
translations.find_or_initialize_by(locale: locale).update!(translation: text)
2329
end
2430

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
module DiscourseTranslator
4+
class TranslatedContentSanitizer
5+
def self.sanitize(model, content)
6+
case model.to_s
7+
when "Topic"
8+
return ERB::Util.html_escape(content) unless SiteSetting.title_fancy_entities?
9+
Topic.fancy_title(content)
10+
when "Post"
11+
PrettyText.cleanup(content, {})
12+
else
13+
# raise an error if the model is not supported
14+
raise ArgumentError.new("Model not supported")
15+
end
16+
end
17+
end
18+
end
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# frozen_string_literal: true
2+
3+
describe DiscourseTranslator::TranslatedContentSanitizer do
4+
describe "Posts" do
5+
it "sanitizes the content" do
6+
sanitized =
7+
DiscourseTranslator::TranslatedContentSanitizer.sanitize(
8+
Post,
9+
"<script>alert('test')</script><p> <h1>Testing</h1> This is a test post</p>",
10+
)
11+
12+
expect(sanitized).to eq("<p> </p><h1>Testing</h1> This is a test post<p></p>")
13+
end
14+
end
15+
16+
describe "Topics" do
17+
it "escapes and prettifies" do
18+
sanitized =
19+
DiscourseTranslator::TranslatedContentSanitizer.sanitize(
20+
Topic,
21+
"<script>alert('test')</script><p> <h1>Testing</h1> This is a test post</p>",
22+
)
23+
24+
expect(sanitized).to eq(
25+
"&lt;script&gt;alert(&lsquo;test&rsquo;)&lt;/script&gt;&lt;p&gt; &lt;h1&gt;Testing&lt;/h1&gt; This is a test post&lt;/p&gt;",
26+
)
27+
end
28+
end
29+
end

0 commit comments

Comments
 (0)