mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 02:11:08 -06:00
38cbca3f67
This did not work sometimes if a topic had the same URL with and without query params because it did not try to select the best matching URL.
295 lines
9.8 KiB
Ruby
295 lines
9.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
|
|
describe TopicLinkClick do
|
|
|
|
it { is_expected.to belong_to :topic_link }
|
|
it { is_expected.to belong_to :user }
|
|
it { is_expected.to validate_presence_of :topic_link_id }
|
|
|
|
def test_uri
|
|
URI.parse('http://test.host')
|
|
end
|
|
|
|
context 'topic_links' do
|
|
before do
|
|
@topic = Fabricate(:topic)
|
|
@post = Fabricate(:post_with_external_links, user: @topic.user, topic: @topic)
|
|
TopicLink.extract_from(@post)
|
|
@topic_link = @topic.topic_links.first
|
|
end
|
|
|
|
it 'has 0 clicks at first' do
|
|
expect(@topic_link.clicks).to eq(0)
|
|
end
|
|
|
|
context 'create' do
|
|
before do
|
|
TopicLinkClick.create(topic_link: @topic_link, ip_address: '192.168.1.1')
|
|
end
|
|
|
|
it 'creates the forum topic link click' do
|
|
expect(TopicLinkClick.count).to eq(1)
|
|
|
|
@topic_link.reload
|
|
expect(@topic_link.clicks).to eq(1)
|
|
|
|
expect(TopicLinkClick.first.ip_address.to_s).to eq('192.168.1.1')
|
|
end
|
|
end
|
|
|
|
context 'create_from' do
|
|
|
|
it "works correctly" do
|
|
|
|
# returns nil to prevent exploits
|
|
click = TopicLinkClick.create_from(url: "http://url-that-doesnt-exist.com", post_id: @post.id, ip: '127.0.0.1')
|
|
expect(click).to eq(nil)
|
|
|
|
# redirects if allowlisted
|
|
click = TopicLinkClick.create_from(url: "https://www.youtube.com/watch?v=jYd_5aggzd4", post_id: @post.id, ip: '127.0.0.1')
|
|
expect(click).to eq("https://www.youtube.com/watch?v=jYd_5aggzd4")
|
|
|
|
# does not change own link
|
|
expect {
|
|
TopicLinkClick.create_from(url: @topic_link.url, post_id: @post.id, ip: '127.0.0.0', user_id: @post.user_id)
|
|
}.not_to change(TopicLinkClick, :count)
|
|
|
|
# can handle double # in a url
|
|
# NOTE: this is not compliant but exists in the wild
|
|
click = TopicLinkClick.create_from(url: "http://discourse.org#a#b", post_id: @post.id, ip: '127.0.0.1')
|
|
expect(click).to eq("http://discourse.org#a#b")
|
|
end
|
|
|
|
context 'with a valid url and post_id' do
|
|
before do
|
|
@url = TopicLinkClick.create_from(url: @topic_link.url, post_id: @post.id, ip: '127.0.0.1')
|
|
@click = TopicLinkClick.last
|
|
end
|
|
|
|
it 'creates a click' do
|
|
expect(@click).to be_present
|
|
expect(@click.topic_link).to eq(@topic_link)
|
|
expect(@url).to eq(@topic_link.url)
|
|
|
|
# second click should not record
|
|
expect { TopicLinkClick.create_from(url: @topic_link.url, post_id: @post.id, ip: '127.0.0.1') }.not_to change(TopicLinkClick, :count)
|
|
end
|
|
|
|
end
|
|
|
|
context 'while logged in' do
|
|
fab!(:other_user) { Fabricate(:user) }
|
|
before do
|
|
@url = TopicLinkClick.create_from(url: @topic_link.url, post_id: @post.id, ip: '127.0.0.1', user_id: other_user.id)
|
|
@click = TopicLinkClick.last
|
|
end
|
|
|
|
it 'creates a click without an IP' do
|
|
expect(@click).to be_present
|
|
expect(@click.topic_link).to eq(@topic_link)
|
|
expect(@click.user_id).to eq(other_user.id)
|
|
expect(@click.ip_address).to eq(nil)
|
|
end
|
|
|
|
end
|
|
|
|
context "relative urls" do
|
|
let(:host) { URI.parse(Discourse.base_url).host }
|
|
|
|
it 'returns the url' do
|
|
url = TopicLinkClick.create_from(url: '/relative-url', post_id: @post.id, ip: '127.0.0.1')
|
|
expect(url).to eq("/relative-url")
|
|
end
|
|
|
|
it 'finds a protocol relative urls with a host' do
|
|
url = "//#{host}/relative-url"
|
|
redirect = TopicLinkClick.create_from(url: url)
|
|
expect(redirect).to eq(url)
|
|
end
|
|
|
|
it "returns the url if it's on our host" do
|
|
url = "http://#{host}/relative-url"
|
|
redirect = TopicLinkClick.create_from(url: url)
|
|
expect(redirect).to eq(url)
|
|
end
|
|
|
|
context "cdn links" do
|
|
|
|
before do
|
|
Rails.configuration.action_controller.asset_host = "https://cdn.discourse.org/stuff"
|
|
end
|
|
|
|
after do
|
|
Rails.configuration.action_controller.asset_host = nil
|
|
end
|
|
|
|
it "correctly handles cdn links" do
|
|
|
|
url = TopicLinkClick.create_from(
|
|
url: 'https://cdn.discourse.org/stuff/my_link',
|
|
topic_id: @topic.id,
|
|
ip: '127.0.0.3')
|
|
|
|
expect(url).to eq('https://cdn.discourse.org/stuff/my_link')
|
|
|
|
# cdn exploit
|
|
url = TopicLinkClick.create_from(
|
|
url: 'https://cdn.discourse.org/bad/my_link',
|
|
topic_id: @topic.id,
|
|
ip: '127.0.0.3')
|
|
|
|
expect(url).to eq(nil)
|
|
|
|
# cdn better link track
|
|
path = "/uploads/site/29/5b585f848d8761d5.xls"
|
|
|
|
post = Fabricate(:post, topic: @topic, raw: "[test](#{path})")
|
|
TopicLink.extract_from(post)
|
|
|
|
url = TopicLinkClick.create_from(
|
|
url: "https://cdn.discourse.org/stuff#{path}",
|
|
topic_id: post.topic_id,
|
|
post_id: post.id,
|
|
ip: '127.0.0.3')
|
|
|
|
expect(url).to eq("https://cdn.discourse.org/stuff#{path}")
|
|
|
|
click = TopicLinkClick.order('id desc').first
|
|
|
|
expect(click.topic_link_id).to eq(TopicLink.order('id desc').first.id)
|
|
end
|
|
|
|
end
|
|
|
|
context "s3 cdns" do
|
|
|
|
it "works with s3 urls" do
|
|
setup_s3
|
|
SiteSetting.s3_cdn_url = "https://discourse-s3-cdn.global.ssl.fastly.net"
|
|
|
|
post = Fabricate(:post, topic: @topic, raw: "[test](//test.localhost/uploads/default/my-test-link)")
|
|
TopicLink.extract_from(post)
|
|
|
|
url = TopicLinkClick.create_from(
|
|
url: "https://discourse-s3-cdn.global.ssl.fastly.net/my-test-link",
|
|
topic_id: @topic.id,
|
|
ip: '127.0.0.3')
|
|
|
|
expect(url).to be_present
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
context 'with a HTTPS version of the same URL' do
|
|
before do
|
|
@url = TopicLinkClick.create_from(url: 'https://twitter.com', topic_id: @topic.id, ip: '127.0.0.3')
|
|
@click = TopicLinkClick.last
|
|
end
|
|
|
|
it 'creates a click' do
|
|
expect(@click).to be_present
|
|
expect(@click.topic_link).to eq(@topic_link)
|
|
expect(@url).to eq('https://twitter.com')
|
|
end
|
|
end
|
|
|
|
context 'with a google analytics tracking code' do
|
|
before do
|
|
@url = TopicLinkClick.create_from(url: 'http://twitter.com?_ga=1.16846778.221554446.1071987018',
|
|
topic_id: @topic.id,
|
|
ip: '127.0.0.3')
|
|
@click = TopicLinkClick.last
|
|
end
|
|
|
|
it 'creates a click' do
|
|
expect(@click).to be_present
|
|
expect(@click.topic_link).to eq(@topic_link)
|
|
expect(@url).to eq('http://twitter.com?_ga=1.16846778.221554446.1071987018')
|
|
end
|
|
end
|
|
|
|
context 'with a query param and google analytics' do
|
|
before do
|
|
@topic = Fabricate(:topic)
|
|
@post = Fabricate(:post,
|
|
topic: @topic,
|
|
user: @topic.user,
|
|
raw: "Here's a link to twitter: http://twitter.com?ref=forum"
|
|
)
|
|
TopicLink.extract_from(@post)
|
|
@topic_link = @topic.topic_links.first
|
|
end
|
|
|
|
it 'creates a click' do
|
|
url = TopicLinkClick.create_from(
|
|
url: 'http://twitter.com?ref=forum&_ga=1.16846778.221554446.1071987018',
|
|
topic_id: @topic.id,
|
|
post_id: @post.id,
|
|
ip: '127.0.0.3'
|
|
)
|
|
click = TopicLinkClick.last
|
|
expect(click).to be_present
|
|
expect(click.topic_link).to eq(@topic_link)
|
|
expect(url).to eq('http://twitter.com?ref=forum&_ga=1.16846778.221554446.1071987018')
|
|
end
|
|
end
|
|
|
|
context 'same base URL with different query' do
|
|
it 'are handled differently' do
|
|
post = Fabricate(:post, raw: <<~RAW)
|
|
no query param: http://example.com/a
|
|
with query param: http://example.com/a?b=c
|
|
with two query params: http://example.com/a?b=c&d=e
|
|
RAW
|
|
|
|
TopicLink.extract_from(post)
|
|
|
|
TopicLinkClick.create_from(url: 'http://example.com/a', post_id: post.id, ip: '127.0.0.1', user: Fabricate(:user))
|
|
TopicLinkClick.create_from(url: 'http://example.com/a?b=c', post_id: post.id, ip: '127.0.0.2', user: Fabricate(:user))
|
|
TopicLinkClick.create_from(url: 'http://example.com/a?b=c&d=e', post_id: post.id, ip: '127.0.0.3', user: Fabricate(:user))
|
|
TopicLinkClick.create_from(url: 'http://example.com/a?b=c', post_id: post.id, ip: '127.0.0.4', user: Fabricate(:user))
|
|
|
|
expect(TopicLink.where("url LIKE '%example.com%'").pluck(:url, :clicks)).to contain_exactly(
|
|
['http://example.com/a', 1], ['http://example.com/a?b=c', 2], ['http://example.com/a?b=c&d=e', 1]
|
|
)
|
|
end
|
|
end
|
|
|
|
context 'with a google analytics tracking code and a hash' do
|
|
before do
|
|
@url = TopicLinkClick.create_from(url: 'http://discourse.org?_ga=1.16846778.221554446.1071987018#faq',
|
|
topic_id: @topic.id,
|
|
ip: '127.0.0.3')
|
|
@click = TopicLinkClick.last
|
|
end
|
|
|
|
it 'creates a click' do
|
|
expect(@click).to be_present
|
|
expect(@url).to eq('http://discourse.org?_ga=1.16846778.221554446.1071987018#faq')
|
|
end
|
|
end
|
|
|
|
context 'with a valid url and topic_id' do
|
|
before do
|
|
@url = TopicLinkClick.create_from(url: @topic_link.url, topic_id: @topic.id, ip: '127.0.0.3')
|
|
@click = TopicLinkClick.last
|
|
end
|
|
|
|
it 'creates a click' do
|
|
expect(@click).to be_present
|
|
expect(@click.topic_link).to eq(@topic_link)
|
|
expect(@url).to eq(@topic_link.url)
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|