Allowlist character classes for post titles (#16787)

* Normalize article titles

* Change method name to match what it does

* Expand on allowed emoji ranges

This required a few changes to be in compliance with Rubocop because
that regex is a beast.
This commit is contained in:
Jamie Gaskins 2022-03-09 10:36:33 -05:00 committed by GitHub
parent cc7c342bb5
commit 987c0a07a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 0 deletions

View file

@ -31,6 +31,48 @@ class Article < ApplicationRecord
MAX_USER_MENTION_LIVE_AT = Time.utc(2021, 4, 7).freeze
PROHIBITED_UNICODE_CHARACTERS_REGEX = /[\u202a-\u202e]/ # BIDI embedding controls
# Filter out anything that isn't a word, space, punctuation mark, or
# recognized emoji.
# See: https://github.com/forem/forem/pull/16787#issuecomment-1062044359
# rubocop:disable Lint/DuplicateRegexpCharacterClassElement
TITLE_CHARACTERS_ALLOWED = /[^
[:word:]
[:space:]
[:punct:]
\u00a3 # GBP symbol
\u00a9 # Copyright symbol
\u00ae # Registered trademark symbol
\u200d # Zero-width joiner, for multipart emojis such as family
\u203c # !! emoji
\u20e3 # Combining enclosing keycap
\u2122 # Trademark symbol
\u2139 # Information symbol
\u2194-\u2199 # Arrow symbols
\u21a9-\u21aa # More arrows
\u231a # Watch emoji
\u231b # Hourglass emoji
\u2328 # Keyboard emoji
\u23cf # Eject symbol
\u23e9-\u23f3 # Various VCR-actions emoji and clocks
\u23f8-\u23fa # More VCR emoji
\u24c2 # Blue circle with a white M in it
\u25aa # Black box
\u25ab # White box
\u25b6 # VCR-style play emoji
\u25c0 # VCR-style play backwards emoji
\u25fb-\u25fe # More black and white squares
\u2600-\u273f # Weather, zodiac, coffee, hazmat, cards, music, other misc emoji
\u2934 # Curved arrow pointing up to the right
\u2935 # Curved arrow pointing down to the right
\u2b00-\u2bff # More arrows, geometric shapes
\u3030 # Squiggly line
\u303d # Either a line chart plummeting or the letter M, not sure
\u3297 # Circled Ideograph Congratulation
\u3299 # Circled Ideograph Secret
\u{1f000}-\u{1ffff} # More common emoji
]+/m
# rubocop:enable Lint/DuplicateRegexpCharacterClassElement
def self.unique_url_error
I18n.t("models.article.unique_url", email: ForemInstance.contact_email)
end
@ -106,6 +148,7 @@ class Article < ApplicationRecord
before_validation :evaluate_markdown, :create_slug
before_validation :remove_prohibited_unicode_characters
before_validation :normalize_title
before_save :update_cached_user
before_save :set_all_dates
@ -567,6 +610,16 @@ class Article < ApplicationRecord
self.path = calculated_path.downcase
end
def normalize_title
return unless title
self.title = title
.gsub(TITLE_CHARACTERS_ALLOWED, " ")
# Coalesce runs of whitespace into a single space character
.gsub(/\s+/, " ")
.strip
end
def evaluate_markdown
fixed_body_markdown = MarkdownProcessor::Fixer::FixAll.call(body_markdown || "")
parsed = FrontMatterParser::Parser.new(:md).call(fixed_body_markdown)

View file

@ -222,6 +222,20 @@ RSpec.describe Article, type: :model do
end
describe "title validation" do
it "normalizes the title to a narrow set of allowable characters" do
article = create(:article, title: "IAmWarningYouDon'tClick!")
expect(article.title).to eq "I Am Warning You Don't Click!"
end
it "allows useful emojis and extended punctuation" do
allowed_title = "Hello! Title — Emdash⁉ 🤖🤯🔥®™©👨‍👩🏾👦‍👦"
article = create(:article, title: allowed_title)
expect(article.title).to eq allowed_title
end
it "produces a proper title" do
test_article = build(:article, title: "An Article Title")