0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-04 10:39:43 +08:00
discourse/spec/requests/clicks_controller_spec.rb
Sam b2c3c1cae8
SECURITY: Missing visibility check in click tracking endpoint (#41140)
## Summary

The /clicks/track endpoint allowed users to manipulate click counts for
links inside private messages and restricted topics they could not
access. This integrity issue is resolved by passing the user's guardian
to the model layer and verifying visibility before recording the click.

## Source

- Patch Triage: https://patch.discourse.org/patch-triage/1148
- HackerOne report: https://hackerone.com/reports/3701904

---

🤖 Auto-generated from the patch diff via Patch Triage. Review carefully
before merging.

Co-authored-by: discourse-patch-triage
<272280883+discourse-patch-triage[bot]@users.noreply.github.com>

---------

Co-authored-by: discourse-patch-triage[bot] <272280883+discourse-patch-triage[bot]@users.noreply.github.com>
2026-06-24 15:15:56 +10:00

84 lines
2.6 KiB
Ruby
Vendored

# frozen_string_literal: true
RSpec.describe ClicksController do
fab!(:user, :trust_level_1)
fab!(:recipient, :trust_level_1)
fab!(:unauthorized_user, :user)
let(:url) { "https://discourse.org/" }
let(:headers) { { REMOTE_ADDR: "192.168.0.1" } }
let(:post_with_url) { create_post(raw: "this is a post with a link #{url}") }
describe "#track" do
it "creates a TopicLinkClick" do
sign_in(user)
expect {
post "/clicks/track",
params: {
url: url,
post_id: post_with_url.id,
topic_id: post_with_url.topic_id,
},
headers: headers
}.to change { TopicLinkClick.count }.by(1)
expect(response.status).to eq(200)
expect(response.parsed_body["success"]).to eq("OK")
end
it "creates a TopicLinkClick for a private message post the recipient can see" do
private_url = "https://example.com/private-recipient-click-test"
private_message_post =
create_post(
user: user,
archetype: Archetype.private_message,
target_usernames: [recipient.username],
raw: "this private message has a link #{private_url}",
)
expect(private_message_post.topic_links.find_by(url: private_url)).to be_present
sign_in(recipient)
expect {
post "/clicks/track",
params: {
url: private_url,
post_id: private_message_post.id,
topic_id: private_message_post.topic_id,
},
headers: headers
}.to change { TopicLinkClick.count }.by(1)
expect(response.status).to eq(200)
expect(response.parsed_body["success"]).to eq("OK")
end
it "does not create a TopicLinkClick for a private message post the user cannot see" do
private_url = "https://example.com/private-click-test"
private_message_post =
create_post(
user: user,
archetype: Archetype.private_message,
target_usernames: [recipient.username],
raw: "this private message has a link #{private_url}",
)
expect(private_message_post.topic_links.find_by(url: private_url)).to be_present
sign_in(unauthorized_user)
expect {
post "/clicks/track",
params: {
url: private_url,
post_id: private_message_post.id,
topic_id: private_message_post.topic_id,
},
headers: headers
}.not_to change { TopicLinkClick.count }
expect(response.status).to eq(200)
expect(response.parsed_body["success"]).to eq("OK")
end
end
end