2019-04-29 20:27:42 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-10-11 05:41:23 -04:00
|
|
|
require 'rails_helper'
|
2014-05-28 03:15:10 -04:00
|
|
|
|
|
|
|
describe Oneboxer do
|
|
|
|
it "returns blank string for an invalid onebox" do
|
2018-02-24 06:35:57 -05:00
|
|
|
stub_request(:head, "http://boom.com")
|
2017-04-15 00:11:02 -04:00
|
|
|
stub_request(:get, "http://boom.com").to_return(body: "")
|
|
|
|
|
2015-01-09 11:34:37 -05:00
|
|
|
expect(Oneboxer.preview("http://boom.com")).to eq("")
|
|
|
|
expect(Oneboxer.onebox("http://boom.com")).to eq("")
|
2014-05-28 03:15:10 -04:00
|
|
|
end
|
2016-10-24 06:46:22 -04:00
|
|
|
|
2019-11-27 16:48:29 -05:00
|
|
|
describe "#invalidate" do
|
|
|
|
let(:url) { "http://test.com" }
|
|
|
|
it "clears the cached preview for the onebox URL and the failed URL cache" do
|
|
|
|
Discourse.cache.write(Oneboxer.onebox_cache_key(url), "test")
|
|
|
|
Discourse.cache.write(Oneboxer.onebox_failed_cache_key(url), true)
|
|
|
|
Oneboxer.invalidate(url)
|
|
|
|
expect(Discourse.cache.read(Oneboxer.onebox_cache_key(url))).to eq(nil)
|
|
|
|
expect(Discourse.cache.read(Oneboxer.onebox_failed_cache_key(url))).to eq(nil)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-02-13 18:39:44 -05:00
|
|
|
context "local oneboxes" do
|
|
|
|
|
|
|
|
def link(url)
|
|
|
|
url = "#{Discourse.base_url}#{url}"
|
|
|
|
%{<a href="#{url}">#{url}</a>}
|
|
|
|
end
|
|
|
|
|
2018-02-19 16:40:14 -05:00
|
|
|
def preview(url, user = nil, category = nil, topic = nil)
|
|
|
|
Oneboxer.preview("#{Discourse.base_url}#{url}",
|
|
|
|
user_id: user&.id,
|
|
|
|
category_id: category&.id,
|
|
|
|
topic_id: topic&.id).to_s
|
2018-02-13 18:39:44 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it "links to a topic/post" do
|
|
|
|
staff = Fabricate(:user)
|
|
|
|
Group[:staff].add(staff)
|
|
|
|
|
|
|
|
secured_category = Fabricate(:category)
|
|
|
|
secured_category.permissions = { staff: :full }
|
|
|
|
secured_category.save!
|
|
|
|
|
2018-02-20 13:49:39 -05:00
|
|
|
replier = Fabricate(:user)
|
|
|
|
|
2018-10-10 06:39:03 -04:00
|
|
|
public_post = Fabricate(:post, raw: "This post has an emoji :+1:")
|
|
|
|
public_topic = public_post.topic
|
|
|
|
public_reply = Fabricate(:post, topic: public_topic, post_number: 2, user: replier)
|
|
|
|
public_hidden = Fabricate(:post, topic: public_topic, post_number: 3, hidden: true)
|
|
|
|
public_moderator_action = Fabricate(:post, topic: public_topic, post_number: 4, user: staff, post_type: Post.types[:moderator_action])
|
2018-02-13 18:39:44 -05:00
|
|
|
|
|
|
|
user = public_post.user
|
|
|
|
public_category = public_topic.category
|
|
|
|
|
|
|
|
secured_topic = Fabricate(:topic, user: staff, category: secured_category)
|
|
|
|
secured_post = Fabricate(:post, user: staff, topic: secured_topic)
|
|
|
|
secured_reply = Fabricate(:post, user: staff, topic: secured_topic, post_number: 2)
|
|
|
|
|
|
|
|
expect(preview(public_topic.relative_url, user, public_category)).to include(public_topic.title)
|
2018-02-26 05:16:53 -05:00
|
|
|
onebox = preview(public_post.url, user, public_category)
|
|
|
|
expect(onebox).to include(public_topic.title)
|
|
|
|
expect(onebox).to include("/images/emoji/")
|
2018-02-20 13:49:39 -05:00
|
|
|
|
|
|
|
onebox = preview(public_reply.url, user, public_category)
|
|
|
|
expect(onebox).to include(public_reply.excerpt)
|
2018-02-26 10:05:35 -05:00
|
|
|
expect(onebox).to include(%{data-post="2"})
|
2018-02-20 13:49:39 -05:00
|
|
|
expect(onebox).to include(PrettyText.avatar_img(replier.avatar_template, "tiny"))
|
|
|
|
|
2020-06-23 11:18:38 -04:00
|
|
|
short_url = "#{Discourse.base_uri}/t/#{public_topic.id}"
|
|
|
|
expect(preview(short_url, user, public_category)).to include(public_topic.title)
|
|
|
|
|
2018-10-10 06:39:03 -04:00
|
|
|
onebox = preview(public_moderator_action.url, user, public_category)
|
|
|
|
expect(onebox).to include(public_moderator_action.excerpt)
|
|
|
|
expect(onebox).to include(%{data-post="4"})
|
|
|
|
expect(onebox).to include(PrettyText.avatar_img(staff.avatar_template, "tiny"))
|
|
|
|
|
2018-02-20 13:49:39 -05:00
|
|
|
onebox = preview(public_reply.url, user, public_category, public_topic)
|
|
|
|
expect(onebox).not_to include(public_topic.title)
|
|
|
|
expect(onebox).to include(replier.avatar_template.sub("{size}", "40"))
|
|
|
|
|
2018-02-13 18:39:44 -05:00
|
|
|
expect(preview(public_hidden.url, user, public_category)).to match_html(link(public_hidden.url))
|
|
|
|
expect(preview(secured_topic.relative_url, user, public_category)).to match_html(link(secured_topic.relative_url))
|
|
|
|
expect(preview(secured_post.url, user, public_category)).to match_html(link(secured_post.url))
|
|
|
|
expect(preview(secured_reply.url, user, public_category)).to match_html(link(secured_reply.url))
|
|
|
|
|
|
|
|
expect(preview(public_topic.relative_url, user, secured_category)).to match_html(link(public_topic.relative_url))
|
|
|
|
expect(preview(public_reply.url, user, secured_category)).to match_html(link(public_reply.url))
|
|
|
|
expect(preview(secured_post.url, user, secured_category)).to match_html(link(secured_post.url))
|
|
|
|
expect(preview(secured_reply.url, user, secured_category)).to match_html(link(secured_reply.url))
|
|
|
|
|
|
|
|
expect(preview(public_topic.relative_url, staff, secured_category)).to include(public_topic.title)
|
|
|
|
expect(preview(public_post.url, staff, secured_category)).to include(public_topic.title)
|
2018-02-19 16:40:14 -05:00
|
|
|
expect(preview(public_reply.url, staff, secured_category)).to include(public_reply.excerpt)
|
2018-02-13 18:39:44 -05:00
|
|
|
expect(preview(public_hidden.url, staff, secured_category)).to match_html(link(public_hidden.url))
|
|
|
|
expect(preview(secured_topic.relative_url, staff, secured_category)).to include(secured_topic.title)
|
|
|
|
expect(preview(secured_post.url, staff, secured_category)).to include(secured_topic.title)
|
2018-02-19 16:40:14 -05:00
|
|
|
expect(preview(secured_reply.url, staff, secured_category)).to include(secured_reply.excerpt)
|
|
|
|
expect(preview(secured_reply.url, staff, secured_category, secured_topic)).not_to include(secured_topic.title)
|
2018-02-13 18:39:44 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it "links to an user profile" do
|
|
|
|
user = Fabricate(:user)
|
|
|
|
|
2018-02-19 16:40:14 -05:00
|
|
|
expect(preview("/u/does-not-exist")).to match_html(link("/u/does-not-exist"))
|
|
|
|
expect(preview("/u/#{user.username}")).to include(user.name)
|
2018-02-13 18:39:44 -05:00
|
|
|
end
|
|
|
|
|
2019-03-25 03:20:14 -04:00
|
|
|
it "should respect enable_names site setting" do
|
|
|
|
user = Fabricate(:user)
|
|
|
|
|
|
|
|
SiteSetting.enable_names = true
|
|
|
|
expect(preview("/u/#{user.username}")).to include(user.name)
|
|
|
|
SiteSetting.enable_names = false
|
|
|
|
expect(preview("/u/#{user.username}")).not_to include(user.name)
|
|
|
|
end
|
|
|
|
|
2018-02-13 18:39:44 -05:00
|
|
|
it "links to an upload" do
|
|
|
|
path = "/uploads/default/original/3X/e/8/e8fcfa624e4fb6623eea57f54941a58ba797f14d"
|
|
|
|
|
2018-02-19 16:40:14 -05:00
|
|
|
expect(preview("#{path}.pdf")).to match_html(link("#{path}.pdf"))
|
|
|
|
expect(preview("#{path}.MP3")).to include("<audio ")
|
|
|
|
expect(preview("#{path}.mov")).to include("<video ")
|
2018-02-13 18:39:44 -05:00
|
|
|
end
|
|
|
|
|
2019-09-17 16:12:50 -04:00
|
|
|
it "strips HTML from user profile location" do
|
|
|
|
user = Fabricate(:user)
|
|
|
|
profile = user.reload.user_profile
|
|
|
|
|
|
|
|
expect(preview("/u/#{user.username}")).not_to include("<span class=\"location\">")
|
|
|
|
|
|
|
|
profile.update!(
|
|
|
|
location: "<img src=x onerror=alert(document.domain)>",
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(preview("/u/#{user.username}")).to include("<span class=\"location\">")
|
|
|
|
expect(preview("/u/#{user.username}")).not_to include("<img src=x")
|
|
|
|
|
|
|
|
profile.update!(
|
|
|
|
location: "Thunderland",
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(preview("/u/#{user.username}")).to include("Thunderland")
|
|
|
|
end
|
2018-02-13 18:39:44 -05:00
|
|
|
end
|
|
|
|
|
2018-03-15 10:27:55 -04:00
|
|
|
context ".onebox_raw" do
|
|
|
|
it "should escape the onebox URL before processing" do
|
|
|
|
post = Fabricate(:post, raw: Discourse.base_url + "/new?'class=black")
|
|
|
|
cpp = CookedPostProcessor.new(post, invalidate_oneboxes: true)
|
|
|
|
cpp.post_process_oneboxes
|
2018-04-11 15:33:45 -04:00
|
|
|
expect(cpp.html).to eq("<p><a href=\"#{Discourse.base_url}/new?%27class=black\">http://test.localhost/new?%27class=black</a></p>")
|
2018-03-15 10:27:55 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-07-26 20:23:54 -04:00
|
|
|
it "does not crawl blocklisted URLs" do
|
|
|
|
SiteSetting.blocked_onebox_domains = "git.*.com|bitbucket.com"
|
2018-08-26 10:31:02 -04:00
|
|
|
url = 'https://github.com/discourse/discourse/commit/21b562852885f883be43032e03c709241e8e6d4f'
|
|
|
|
stub_request(:head, 'https://discourse.org/').to_return(status: 302, body: "", headers: { location: url })
|
|
|
|
|
|
|
|
expect(Oneboxer.external_onebox(url)[:onebox]).to be_empty
|
|
|
|
expect(Oneboxer.external_onebox('https://discourse.org/')[:onebox]).to be_empty
|
|
|
|
end
|
|
|
|
|
2020-07-26 20:23:54 -04:00
|
|
|
it "does not consider ignore_redirects domains as blocklisted" do
|
2018-09-17 14:00:16 -04:00
|
|
|
url = 'https://store.steampowered.com/app/271590/Grand_Theft_Auto_V/'
|
|
|
|
stub_request(:head, url).to_return(status: 200, body: "", headers: {})
|
|
|
|
stub_request(:get, url).to_return(status: 200, body: "", headers: {})
|
|
|
|
|
|
|
|
expect(Oneboxer.external_onebox(url)[:onebox]).to be_present
|
|
|
|
end
|
2020-02-06 10:32:42 -05:00
|
|
|
|
|
|
|
it "uses the Onebox custom user agent on specified hosts" do
|
2020-02-06 11:56:54 -05:00
|
|
|
SiteSetting.force_custom_user_agent_hosts = "http://codepen.io|https://video.discourse.org/"
|
2020-02-06 10:32:42 -05:00
|
|
|
url = 'https://video.discourse.org/presentation.mp4'
|
|
|
|
|
|
|
|
stub_request(:head, url).to_return(status: 403, body: "", headers: {})
|
|
|
|
stub_request(:get, url).to_return(status: 403, body: "", headers: {})
|
|
|
|
stub_request(:head, url).with(headers: { "User-Agent" => Onebox.options.user_agent }).to_return(status: 200, body: "", headers: {})
|
|
|
|
stub_request(:get, url).with(headers: { "User-Agent" => Onebox.options.user_agent }).to_return(status: 200, body: "", headers: {})
|
|
|
|
|
|
|
|
expect(Oneboxer.preview(url, invalidate_oneboxes: true)).to be_present
|
|
|
|
end
|
2020-08-27 15:12:13 -04:00
|
|
|
|
|
|
|
context "with youtube stub" do
|
|
|
|
let(:html) do
|
|
|
|
<<~HTML
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<meta property="og:title" content="Onebox1">
|
|
|
|
<meta property="og:description" content="this is bodycontent">
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<p>body</p>
|
|
|
|
</body>
|
|
|
|
<html>
|
|
|
|
HTML
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
stub_request(:any, "https://www.youtube.com/watch?v=dQw4w9WgXcQ").to_return(status: 200, body: html)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "allows restricting engines based on the allowed_onebox_iframes setting" do
|
|
|
|
output = Oneboxer.onebox("https://www.youtube.com/watch?v=dQw4w9WgXcQ", invalidate_oneboxes: true)
|
|
|
|
expect(output).to include("<iframe") # Regular youtube onebox
|
|
|
|
|
|
|
|
# Disable all onebox iframes:
|
|
|
|
SiteSetting.allowed_onebox_iframes = ""
|
|
|
|
output = Oneboxer.onebox("https://www.youtube.com/watch?v=dQw4w9WgXcQ", invalidate_oneboxes: true)
|
|
|
|
expect(output).not_to include("<iframe") # Generic onebox
|
|
|
|
expect(output).to include("allowlistedgeneric")
|
|
|
|
|
|
|
|
# Just enable youtube:
|
|
|
|
SiteSetting.allowed_onebox_iframes = "https://www.youtube.com"
|
|
|
|
output = Oneboxer.onebox("https://www.youtube.com/watch?v=dQw4w9WgXcQ", invalidate_oneboxes: true)
|
|
|
|
expect(output).to include("<iframe") # Regular youtube onebox
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "allows iframes from generic sites via the allowed_iframes setting" do
|
|
|
|
allowlisted_body = '<html><head><link rel="alternate" type="application/json+oembed" href="https://allowlist.ed/iframes.json" />'
|
|
|
|
blocklisted_body = '<html><head><link rel="alternate" type="application/json+oembed" href="https://blocklist.ed/iframes.json" />'
|
|
|
|
|
|
|
|
allowlisted_oembed = {
|
|
|
|
type: "rich",
|
|
|
|
height: "100",
|
|
|
|
html: "<iframe src='https://ifram.es/foo/bar'></iframe>"
|
|
|
|
}
|
|
|
|
|
|
|
|
blocklisted_oembed = {
|
|
|
|
type: "rich",
|
|
|
|
height: "100",
|
|
|
|
html: "<iframe src='https://malicious/discourse.org/'></iframe>"
|
|
|
|
}
|
|
|
|
|
|
|
|
stub_request(:any, "https://blocklist.ed/iframes").to_return(status: 200, body: blocklisted_body)
|
|
|
|
stub_request(:any, "https://blocklist.ed/iframes.json").to_return(status: 200, body: blocklisted_oembed.to_json)
|
|
|
|
|
|
|
|
stub_request(:any, "https://allowlist.ed/iframes").to_return(status: 200, body: allowlisted_body)
|
|
|
|
stub_request(:any, "https://allowlist.ed/iframes.json").to_return(status: 200, body: allowlisted_oembed.to_json)
|
|
|
|
|
|
|
|
SiteSetting.allowed_iframes = "discourse.org|https://ifram.es"
|
|
|
|
|
|
|
|
expect(Oneboxer.onebox("https://blocklist.ed/iframes", invalidate_oneboxes: true)).to be_empty
|
|
|
|
expect(Oneboxer.onebox("https://allowlist.ed/iframes", invalidate_oneboxes: true)).to match("iframe src")
|
|
|
|
end
|
|
|
|
|
2014-05-28 03:15:10 -04:00
|
|
|
end
|