mirror of
https://github.com/discourse/discourse.git
synced 2024-11-29 06:53:39 +08:00
FIX: ensure no infinite category loop
If there's ever a circular reference in categories, don't go into an infinite loop when generating the category slug. Instead, keep track of parent ids, and bail out as soon as we're encountering one more than once.
This commit is contained in:
parent
f72f63660a
commit
10f77556cd
|
@ -1091,11 +1091,13 @@ class Category < ActiveRecord::Base
|
||||||
.find_each { |category| category.create_category_definition }
|
.find_each { |category| category.create_category_definition }
|
||||||
end
|
end
|
||||||
|
|
||||||
def slug_path
|
def slug_path(parent_ids = Set.new)
|
||||||
if self.parent_category_id.present?
|
if self.parent_category_id.present?
|
||||||
slug_path = self.parent_category.slug_path
|
if parent_ids.add?(self.parent_category_id)
|
||||||
slug_path.push(self.slug_for_url)
|
self.parent_category.slug_path(parent_ids) << self.slug_for_url
|
||||||
slug_path
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
else
|
else
|
||||||
[self.slug_for_url]
|
[self.slug_for_url]
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,8 +47,7 @@ RSpec.describe Category do
|
||||||
|
|
||||||
it "should delete associated sidebar_section_links when category is destroyed" do
|
it "should delete associated sidebar_section_links when category is destroyed" do
|
||||||
category_sidebar_section_link = Fabricate(:category_sidebar_section_link)
|
category_sidebar_section_link = Fabricate(:category_sidebar_section_link)
|
||||||
category_sidebar_section_link_2 =
|
Fabricate(:category_sidebar_section_link, linkable: category_sidebar_section_link.linkable)
|
||||||
Fabricate(:category_sidebar_section_link, linkable: category_sidebar_section_link.linkable)
|
|
||||||
tag_sidebar_section_link = Fabricate(:tag_sidebar_section_link)
|
tag_sidebar_section_link = Fabricate(:tag_sidebar_section_link)
|
||||||
|
|
||||||
expect { category_sidebar_section_link.linkable.destroy! }.to change {
|
expect { category_sidebar_section_link.linkable.destroy! }.to change {
|
||||||
|
@ -996,7 +995,7 @@ RSpec.describe Category do
|
||||||
)
|
)
|
||||||
category.clear_auto_bump_cache!
|
category.clear_auto_bump_cache!
|
||||||
|
|
||||||
post1 = create_post(category: category, created_at: 15.seconds.ago)
|
create_post(category: category, created_at: 15.seconds.ago)
|
||||||
|
|
||||||
# no limits on post creation or category creation please
|
# no limits on post creation or category creation please
|
||||||
RateLimiter.enable
|
RateLimiter.enable
|
||||||
|
@ -1419,7 +1418,7 @@ RSpec.describe Category do
|
||||||
|
|
||||||
it 'returns nil when input is ["category:invalid-slug:sub-subcategory"] and maximum category nesting is 3' do
|
it 'returns nil when input is ["category:invalid-slug:sub-subcategory"] and maximum category nesting is 3' do
|
||||||
SiteSetting.max_category_nesting = 3
|
SiteSetting.max_category_nesting = 3
|
||||||
sub_subcategory = Fabricate(:category, parent_category: subcategory, slug: "sub-subcategory")
|
Fabricate(:category, parent_category: subcategory, slug: "sub-subcategory")
|
||||||
|
|
||||||
expect(Category.ids_from_slugs(%w[category:invalid-slug:sub-subcategory])).to eq([])
|
expect(Category.ids_from_slugs(%w[category:invalid-slug:sub-subcategory])).to eq([])
|
||||||
end
|
end
|
||||||
|
@ -1464,6 +1463,34 @@ RSpec.describe Category do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#slug_path" do
|
||||||
|
before { SiteSetting.max_category_nesting = 3 }
|
||||||
|
|
||||||
|
fab!(:grandparent) { Fabricate(:category, slug: "foo") }
|
||||||
|
fab!(:parent) { Fabricate(:category, parent_category: grandparent, slug: "bar") }
|
||||||
|
let(:child) { Fabricate(:category, parent_category: parent, slug: "boo") }
|
||||||
|
|
||||||
|
it "returns the slug for categories without parents" do
|
||||||
|
expect(grandparent.slug_path).to eq [grandparent.slug]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the slug for categories with parent" do
|
||||||
|
expect(parent.slug_path).to eq [grandparent.slug, parent.slug]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the slug for categories with grand-parent" do
|
||||||
|
expect(child.slug_path).to eq [grandparent.slug, parent.slug, child.slug]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "avoids infinite loops with circular references" do
|
||||||
|
grandparent.parent_category = parent
|
||||||
|
grandparent.save!(validate: false)
|
||||||
|
|
||||||
|
expect(grandparent.slug_path).to eq [parent.slug, grandparent.slug]
|
||||||
|
expect(parent.slug_path).to eq [grandparent.slug, parent.slug]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "#slug_ref" do
|
describe "#slug_ref" do
|
||||||
fab!(:category) { Fabricate(:category, slug: "foo") }
|
fab!(:category) { Fabricate(:category, slug: "foo") }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user