0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-06 13:08:40 +08:00
discourse/plugins/discourse-data-explorer/spec/requests/bookmarks_spec.rb
Sam ad96765bd5
FIX: Prevent hidden Data Explorer queries leaking via bookmarks (#40327)
Bookmark list and ICS feed responses included the name and
description of Data Explorer queries even when the underlying
query was hidden or its query group association had been removed.

Tighten `QueryGroupBookmarkable.can_see_bookmarkable?` so non-admins
can only see bookmarks for queries that are not hidden and that they
have access to via group membership, and exclude hidden queries from
the bookmark list query. Reminder delivery now also respects
`can_see?` to avoid sending reminders for queries the user can no
longer access.

Admins retain visibility of hidden query bookmarks.
2026-05-27 17:25:30 +10:00

143 lines
3.7 KiB
Ruby
Vendored

# frozen_string_literal: true
RSpec.describe "Data Explorer bookmarks" do
fab!(:admin_user, :admin)
fab!(:user)
fab!(:group)
fab!(:query) do
Fabricate(
:query,
name: "Private revenue report",
description: "Secret revenue report description",
sql: "SELECT 1",
user: admin_user,
)
end
before do
SiteSetting.data_explorer_enabled = true
group.add(user)
register_test_bookmarkable(DiscourseDataExplorer::QueryGroupBookmarkable)
end
after { DiscoursePluginRegistry.reset_register!(:bookmarkables) }
it "does not expose hidden query bookmark metadata in bookmark JSON or ICS responses" do
query_group = Fabricate(:query_group, query: query, group: group)
bookmark =
Fabricate(
:bookmark,
user: user,
bookmarkable: query_group,
name: nil,
reminder_at: 1.day.from_now,
)
sign_in(user)
get "/u/#{user.username}/bookmarks.json"
expect(response.status).to eq(200)
expect(
response
.parsed_body
.dig("user_bookmark_list", "bookmarks")
.map { |bookmark_data| bookmark_data["id"] },
).to contain_exactly(bookmark.id)
expect(response.body).to include(query.name)
expect(response.body).to include(query.description)
get "/u/#{user.username}/bookmarks.ics"
expect(response.status).to eq(200)
expect(response.body).to include(query.name)
query.update!(hidden: true)
get "/u/#{user.username}/bookmarks.json"
expect(response.status).to eq(200)
json_body = response.body
get "/u/#{user.username}/bookmarks.ics"
expect(response.status).to eq(200)
ics_body = response.body
aggregate_failures do
expect(json_body).not_to include(query.name)
expect(json_body).not_to include(query.description)
expect(ics_body).not_to include(query.name)
expect(ics_body).not_to include(query.description)
end
end
it "keeps hidden query bookmarks visible to admins" do
query_group = Fabricate(:query_group, query: query, group: group)
Fabricate(
:bookmark,
user: admin_user,
bookmarkable: query_group,
name: nil,
reminder_at: 1.day.from_now,
)
query.update!(hidden: true)
sign_in(admin_user)
get "/u/#{admin_user.username}/bookmarks.json"
expect(response.status).to eq(200)
expect(response.body).to include(query.name)
expect(response.body).to include(query.description)
get "/u/#{admin_user.username}/bookmarks.ics"
expect(response.status).to eq(200)
expect(response.body).to include(query.name)
end
it "does not expose detached query bookmark metadata in bookmark JSON or ICS responses" do
query_group = Fabricate(:query_group, query: query, group: group)
Fabricate(
:bookmark,
user: user,
bookmarkable: query_group,
name: nil,
reminder_at: 1.day.from_now,
)
sign_in(user)
get "/u/#{user.username}/bookmarks.json"
expect(response.status).to eq(200)
expect(response.body).to include(query.name)
expect(response.body).to include(query.description)
get "/u/#{user.username}/bookmarks.ics"
expect(response.status).to eq(200)
expect(response.body).to include(query.name)
query_group.destroy!
get "/u/#{user.username}/bookmarks.json"
expect(response.status).to eq(200)
json_body = response.body
get "/u/#{user.username}/bookmarks.ics"
expect(response.status).to eq(200)
ics_body = response.body
aggregate_failures do
expect(json_body).not_to include(query.name)
expect(json_body).not_to include(query.description)
expect(ics_body).not_to include(query.name)
expect(ics_body).not_to include(query.description)
end
end
end