VYPR
Medium severity5.3NVD Advisory· Published Jun 12, 2026

CVE-2026-47264

CVE-2026-47264

Description

Discourse versions prior to 2026.1.4, 2026.3.1, and 2026.4.1 expose restricted tag group names to anonymous users via the tag info endpoint.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Discourse versions prior to 2026.1.4, 2026.3.1, and 2026.4.1 expose restricted tag group names to anonymous users via the tag info endpoint.

Vulnerability

In Discourse, the DetailedTagSerializer#tag_group_names method returned all tag groups a tag belonged to without filtering against the requesting user's visibility. This affects versions 2026.1.0-latest to before 2026.1.4, 2026.3.0-latest to before 2026.3.1, and 2026.4.0-latest to before 2026.4.1. The issue is exploitable only when SiteSetting.tags_listed_by_group is enabled [1].

Exploitation

An anonymous or unprivileged user can access the TagsController#info endpoint, which is exempt from the requires_login check. By sending a request to this endpoint for a tag that belongs to restricted tag groups, the response includes the names of those groups without any access control [1].

Impact

Successful exploitation allows the attacker to read the names of tag groups that are restricted to specific user groups or non-visible categories. This constitutes an information disclosure of potentially sensitive group names [1].

Mitigation

The issue has been patched in versions 2026.1.4, 2026.3.1, 2026.4.1, and 2026.5.0-latest.1. Users should upgrade to one of these patched versions. No workaround is available other than upgrading [1].

AI Insight generated on Jun 12, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2
  • >=2026.4.0,<2026.4.1+ 1 more
    • (no CPE)range: >=2026.4.0,<2026.4.1
    • (no CPE)range: >=2026.1.0 <2026.1.4 || >=2026.3.0 <2026.3.1 || >=2026.4.0 <2026.4.1

Patches

3
53f8519bed57

SECURITY: Don't leak restricted tag group names via tag info [backport 2026.4]

https://github.com/discourse/discourseJoffrey JAFFEUXMay 18, 2026Fixed in 2026.4.1via llm-release-walk
2 files changed · +38 1
  • app/serializers/detailed_tag_serializer.rb+4 1 modified
    @@ -22,6 +22,9 @@ def include_tag_group_names?
       end
     
       def tag_group_names
    -    object.tag_groups.map(&:name)
    +    TagGroup
    +      .visible(scope)
    +      .where(id: object.tag_group_memberships.select(:tag_group_id))
    +      .pluck(:name)
       end
     end
    
  • spec/requests/tags_controller_spec.rb+34 0 modified
    @@ -706,6 +706,40 @@
               expect(response.parsed_body["categories"]).to be_blank
               expect(response.parsed_body.dig("tag_info", "category_restricted")).to eq(true)
             end
    +
    +        it "doesn't leak the restricted tag group name to users without access" do
    +          SiteSetting.tags_listed_by_group = true
    +          sign_in(user)
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq([])
    +        end
    +
    +        it "doesn't leak the restricted tag group name to anon" do
    +          SiteSetting.tags_listed_by_group = true
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq([])
    +        end
    +
    +        it "still returns the restricted tag group name to admins" do
    +          SiteSetting.tags_listed_by_group = true
    +          sign_in(admin)
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq([tag_group.name])
    +        end
    +
    +        it "returns only visible tag group names when tag is in multiple groups" do
    +          SiteSetting.tags_listed_by_group = true
    +          public_tag_group = Fabricate(:tag_group, name: "public-group", tags: [tag])
    +          sign_in(user)
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq(
    +            [public_tag_group.name],
    +          )
    +        end
           end
         end
       end
    
4d4f411e3d55

SECURITY: Don't leak restricted tag group names via tag info [backport 2026.3]

https://github.com/discourse/discourseJoffrey JAFFEUXMay 18, 2026Fixed in 2026.3.1via llm-release-walk
2 files changed · +38 1
  • app/serializers/detailed_tag_serializer.rb+4 1 modified
    @@ -22,6 +22,9 @@ def include_tag_group_names?
       end
     
       def tag_group_names
    -    object.tag_groups.map(&:name)
    +    TagGroup
    +      .visible(scope)
    +      .where(id: object.tag_group_memberships.select(:tag_group_id))
    +      .pluck(:name)
       end
     end
    
  • spec/requests/tags_controller_spec.rb+34 0 modified
    @@ -688,6 +688,40 @@
               expect(response.parsed_body["categories"]).to be_blank
               expect(response.parsed_body.dig("tag_info", "category_restricted")).to eq(true)
             end
    +
    +        it "doesn't leak the restricted tag group name to users without access" do
    +          SiteSetting.tags_listed_by_group = true
    +          sign_in(user)
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq([])
    +        end
    +
    +        it "doesn't leak the restricted tag group name to anon" do
    +          SiteSetting.tags_listed_by_group = true
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq([])
    +        end
    +
    +        it "still returns the restricted tag group name to admins" do
    +          SiteSetting.tags_listed_by_group = true
    +          sign_in(admin)
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq([tag_group.name])
    +        end
    +
    +        it "returns only visible tag group names when tag is in multiple groups" do
    +          SiteSetting.tags_listed_by_group = true
    +          public_tag_group = Fabricate(:tag_group, name: "public-group", tags: [tag])
    +          sign_in(user)
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq(
    +            [public_tag_group.name],
    +          )
    +        end
           end
         end
       end
    
be790bdcc04b

SECURITY: Don't leak restricted tag group names via tag info [backport 2026.1]

https://github.com/discourse/discourseJoffrey JAFFEUXMay 18, 2026Fixed in 2026.1.4via llm-release-walk
2 files changed · +38 1
  • app/serializers/detailed_tag_serializer.rb+4 1 modified
    @@ -22,6 +22,9 @@ def include_tag_group_names?
       end
     
       def tag_group_names
    -    object.tag_groups.map(&:name)
    +    TagGroup
    +      .visible(scope)
    +      .where(id: object.tag_group_memberships.select(:tag_group_id))
    +      .pluck(:name)
       end
     end
    
  • spec/requests/tags_controller_spec.rb+34 0 modified
    @@ -688,6 +688,40 @@
               expect(response.parsed_body["categories"]).to be_blank
               expect(response.parsed_body.dig("tag_info", "category_restricted")).to eq(true)
             end
    +
    +        it "doesn't leak the restricted tag group name to users without access" do
    +          SiteSetting.tags_listed_by_group = true
    +          sign_in(user)
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq([])
    +        end
    +
    +        it "doesn't leak the restricted tag group name to anon" do
    +          SiteSetting.tags_listed_by_group = true
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq([])
    +        end
    +
    +        it "still returns the restricted tag group name to admins" do
    +          SiteSetting.tags_listed_by_group = true
    +          sign_in(admin)
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq([tag_group.name])
    +        end
    +
    +        it "returns only visible tag group names when tag is in multiple groups" do
    +          SiteSetting.tags_listed_by_group = true
    +          public_tag_group = Fabricate(:tag_group, name: "public-group", tags: [tag])
    +          sign_in(user)
    +          get "/tag/#{tag.name}/info.json"
    +          expect(response.status).to eq(200)
    +          expect(response.parsed_body.dig("tag_info", "tag_group_names")).to eq(
    +            [public_tag_group.name],
    +          )
    +        end
           end
         end
       end
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

1

News mentions

0

No linked articles in our index yet.