mirror of
https://github.com/discourse/discourse.git
synced 2024-11-26 02:40:53 -06:00
UX: support links in tag descriptions (#22994)
* scrub non-a html tags from tag descriptions on create, strips all tags from tag description when displayed in tag hover * test for tag description links * UX: basic render-tag test * UX: fix linting * UX: fix linting * fix broken tests * Update spec/models/tag_spec.rb Co-authored-by: Penar Musaraj <pmusaraj@gmail.com> * UX: use has_sanitizable_fields instead of has_scrubbable_fields to ensafen tag.description --------- Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
This commit is contained in:
parent
9643151419
commit
77626c088e
@ -49,7 +49,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="tag-description-wrapper">
|
<div class="tag-description-wrapper">
|
||||||
{{this.tagInfo.description}}
|
{{html-safe this.tagInfo.description}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,13 +39,17 @@ export function defaultRenderTag(tag, params) {
|
|||||||
classes.push(params.size);
|
classes.push(params.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove all html tags from hover text
|
||||||
|
const hoverDescription =
|
||||||
|
params.description && params.description.replace(/<.+?>/g, "");
|
||||||
|
|
||||||
let val =
|
let val =
|
||||||
"<" +
|
"<" +
|
||||||
tagName +
|
tagName +
|
||||||
href +
|
href +
|
||||||
" data-tag-name=" +
|
" data-tag-name=" +
|
||||||
tag +
|
tag +
|
||||||
(params.description ? ' title="' + escape(params.description) + '" ' : "") +
|
(params.description ? ' title="' + escape(hoverDescription) + '" ' : "") +
|
||||||
" class='" +
|
" class='" +
|
||||||
classes.join(" ") +
|
classes.join(" ") +
|
||||||
"'>" +
|
"'>" +
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import renderTag from "discourse/lib/render-tag";
|
||||||
|
import { module, test } from "qunit";
|
||||||
|
import { setupTest } from "ember-qunit";
|
||||||
|
|
||||||
|
module("Unit | Utility | render-tag", function (hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
|
||||||
|
test("defaultRenderTag", function (assert) {
|
||||||
|
assert.strictEqual(
|
||||||
|
renderTag("foo", { description: "foo description" }),
|
||||||
|
"<a href='/tag/foo' data-tag-name=foo title=\"foo description\" class='discourse-tag simple'>foo</a>",
|
||||||
|
"formats tag as link with plain description in hover"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
renderTag("foo", {
|
||||||
|
description: 'foo description <a href="localhost">link</a>',
|
||||||
|
}),
|
||||||
|
"<a href='/tag/foo' data-tag-name=foo title=\"foo description link\" class='discourse-tag simple'>foo</a>",
|
||||||
|
"removes any html tags from description"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -3,6 +3,7 @@
|
|||||||
class Tag < ActiveRecord::Base
|
class Tag < ActiveRecord::Base
|
||||||
include Searchable
|
include Searchable
|
||||||
include HasDestroyedWebHook
|
include HasDestroyedWebHook
|
||||||
|
include HasSanitizableFields
|
||||||
|
|
||||||
self.ignored_columns = [
|
self.ignored_columns = [
|
||||||
"topic_count", # TODO(tgxworld): Remove on 1 July 2023
|
"topic_count", # TODO(tgxworld): Remove on 1 July 2023
|
||||||
@ -56,6 +57,8 @@ class Tag < ActiveRecord::Base
|
|||||||
has_many :synonyms, class_name: "Tag", foreign_key: "target_tag_id", dependent: :destroy
|
has_many :synonyms, class_name: "Tag", foreign_key: "target_tag_id", dependent: :destroy
|
||||||
has_many :sidebar_section_links, as: :linkable, dependent: :delete_all
|
has_many :sidebar_section_links, as: :linkable, dependent: :delete_all
|
||||||
|
|
||||||
|
before_save :sanitize_description
|
||||||
|
|
||||||
after_save :index_search
|
after_save :index_search
|
||||||
after_save :update_synonym_associations
|
after_save :update_synonym_associations
|
||||||
|
|
||||||
@ -244,6 +247,9 @@ class Tag < ActiveRecord::Base
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def sanitize_description
|
||||||
|
self.description = sanitize_field(self.description) if description_changed?
|
||||||
|
end
|
||||||
def name_validator
|
def name_validator
|
||||||
errors.add(:name, :invalid) if name.present? && RESERVED_TAGS.include?(self.name.strip.downcase)
|
errors.add(:name, :invalid) if name.present? && RESERVED_TAGS.include?(self.name.strip.downcase)
|
||||||
end
|
end
|
||||||
|
@ -382,4 +382,15 @@ RSpec.describe Tag do
|
|||||||
expect(Tag.topic_count_column(Guardian.new(user))).to eq("staff_topic_count")
|
expect(Tag.topic_count_column(Guardian.new(user))).to eq("staff_topic_count")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "description" do
|
||||||
|
it "uses the HTMLSanitizer to remove unsafe tags and attributes" do
|
||||||
|
tag.description =
|
||||||
|
"<div>hi</div><script>a=0;</script> <a onclick='const a=0;' href=\"https://www.discourse.org\">discourse</a>"
|
||||||
|
tag.save!
|
||||||
|
expect(tag.description.strip).to eq(
|
||||||
|
"<div>hi</div>a=0; <a href=\"https://www.discourse.org\">discourse</a>",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -54,7 +54,7 @@ describe "Viewing sidebar as anonymous user", type: :system do
|
|||||||
expect(sidebar).to have_tags_section
|
expect(sidebar).to have_tags_section
|
||||||
expect(sidebar).to have_all_tags_section_link
|
expect(sidebar).to have_all_tags_section_link
|
||||||
expect(sidebar).to have_tag_section_links([tag3, tag2, tag4, tag5, tag1])
|
expect(sidebar).to have_tag_section_links([tag3, tag2, tag4, tag5, tag1])
|
||||||
expect(sidebar).to have_tag_section_link_with_title(tag1, "tag 1 description <script>")
|
expect(sidebar).to have_tag_section_link_with_title(tag1, "tag 1 description ")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should display the site's top tags when `default_navigation_menu_tags` site setting has been set but the tags configured are hidden to the user" do
|
it "should display the site's top tags when `default_navigation_menu_tags` site setting has been set but the tags configured are hidden to the user" do
|
||||||
@ -66,7 +66,7 @@ describe "Viewing sidebar as anonymous user", type: :system do
|
|||||||
expect(sidebar).to have_tags_section
|
expect(sidebar).to have_tags_section
|
||||||
expect(sidebar).to have_all_tags_section_link
|
expect(sidebar).to have_all_tags_section_link
|
||||||
expect(sidebar).to have_tag_section_links([tag3, tag2, tag4, tag1, tag6])
|
expect(sidebar).to have_tag_section_links([tag3, tag2, tag4, tag1, tag6])
|
||||||
expect(sidebar).to have_tag_section_link_with_title(tag1, "tag 1 description <script>")
|
expect(sidebar).to have_tag_section_link_with_title(tag1, "tag 1 description ")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should display the tags configured in `default_navigation_menu_tags` site setting when it has been set" do
|
it "should display the tags configured in `default_navigation_menu_tags` site setting when it has been set" do
|
||||||
|
@ -162,7 +162,7 @@ describe "Viewing sidebar as logged in user", type: :system do
|
|||||||
expect(sidebar).to have_tags_section
|
expect(sidebar).to have_tags_section
|
||||||
expect(sidebar).to have_tag_section_links([tag3, tag2, tag4, tag5, tag1])
|
expect(sidebar).to have_tag_section_links([tag3, tag2, tag4, tag5, tag1])
|
||||||
expect(sidebar).to have_tag_section_link_with_title(tag3, "tag 3 description")
|
expect(sidebar).to have_tag_section_link_with_title(tag3, "tag 3 description")
|
||||||
expect(sidebar).to have_tag_section_link_with_title(tag1, "tag 1 description <script>")
|
expect(sidebar).to have_tag_section_link_with_title(tag1, "tag 1 description ")
|
||||||
expect(sidebar).to have_all_tags_section_link
|
expect(sidebar).to have_all_tags_section_link
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ describe "Viewing sidebar as logged in user", type: :system do
|
|||||||
expect(sidebar).to have_tags_section
|
expect(sidebar).to have_tags_section
|
||||||
expect(sidebar).to have_tag_section_links([tag1, tag2, tag3])
|
expect(sidebar).to have_tag_section_links([tag1, tag2, tag3])
|
||||||
expect(sidebar).to have_tag_section_link_with_title(tag3, "tag 3 description")
|
expect(sidebar).to have_tag_section_link_with_title(tag3, "tag 3 description")
|
||||||
expect(sidebar).to have_tag_section_link_with_title(tag1, "tag 1 description <script>")
|
expect(sidebar).to have_tag_section_link_with_title(tag1, "tag 1 description ")
|
||||||
expect(sidebar).to have_all_tags_section_link
|
expect(sidebar).to have_all_tags_section_link
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user