discourse/spec/lib/onebox/helpers_spec.rb
Bianca Nenciu e7f04a8674
FIX: Use URI#merge to merge base and relative URLs (#17454)
The old implementation did not handle all cases, such as the case when
`src` is a relative URL that starts with `..`.
2022-07-18 14:17:54 +03:00

202 lines
10 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: true
RSpec.describe Onebox::Helpers do
describe '.blank?' do
it { expect(described_class.blank?("")).to be(true) }
it { expect(described_class.blank?(" ")).to be(true) }
it { expect(described_class.blank?("test")).to be(false) }
it { expect(described_class.blank?(["test", "testing"])).to be(false) }
it { expect(described_class.blank?([])).to be(true) }
it { expect(described_class.blank?({})).to be(true) }
it { expect(described_class.blank?(nil)).to be(true) }
it { expect(described_class.blank?(true)).to be(false) }
it { expect(described_class.blank?(false)).to be(true) }
it { expect(described_class.blank?(a: 'test')).to be(false) }
end
describe ".truncate" do
let(:test_string) { "Chops off on spaces" }
it { expect(described_class.truncate(test_string)).to eq(test_string) }
it { expect(described_class.truncate(test_string, 5)).to eq("Chops...") }
it { expect(described_class.truncate(test_string, 7)).to eq("Chops...") }
it { expect(described_class.truncate(test_string, 9)).to eq("Chops off...") }
it { expect(described_class.truncate(test_string, 10)).to eq("Chops off...") }
it { expect(described_class.truncate(test_string, 100)).to eq("Chops off on spaces") }
it { expect(described_class.truncate(" #{test_string} ", 6)).to eq(" Chops...") }
end
describe "fetch_response" do
around do |example|
previous_options = Onebox.options.to_h
Onebox.options = { max_download_kb: 1 }
stub_request(:get, "http://example.com/large-file").to_return(status: 200, body: onebox_response("slides"))
example.run
Onebox.options = previous_options
end
it "raises an exception when responses are larger than our limit" do
expect {
described_class.fetch_response('http://example.com/large-file')
}.to raise_error(Onebox::Helpers::DownloadTooLarge)
end
end
describe "fetch_html_doc" do
it "can handle unicode URIs" do
uri = 'https://www.reddit.com/r/UFOs/comments/k18ukd/𝗨𝗙𝗢_𝗱𝗿𝗼𝗽𝘀_𝗰𝗼𝘄_𝘁𝗵𝗿𝗼𝘂𝗴𝗵_𝗯𝗮𝗿𝗻_𝗿𝗼𝗼𝗳/'
stub_request(:get, uri).to_return(status: 200, body: "<!DOCTYPE html><p>success</p>")
expect(described_class.fetch_html_doc(uri).to_s).to match("success")
end
context "canonical link" do
it "follows canonical link" do
uri = 'https://www.example.com'
stub_request(:get, uri).to_return(status: 200, body: "<!DOCTYPE html><link rel='canonical' href='http://foobar.com/'/><p>invalid</p>")
stub_request(:get, 'http://foobar.com').to_return(status: 200, body: "<!DOCTYPE html><p>success</p>")
stub_request(:head, 'http://foobar.com').to_return(status: 200, body: "")
expect(described_class.fetch_html_doc(uri).to_s).to match("success")
end
it "does not follow canonical link pointing at localhost" do
uri = 'https://www.example.com'
stub_request(:get, uri).to_return(status: 200, body: "<!DOCTYPE html><link rel='canonical' href='http://localhost:3000/'/><p>success</p>")
expect(described_class.fetch_html_doc(uri).to_s).to match("success")
end
end
end
describe "redirects" do
describe "redirect limit" do
before do
codes = [301, 302, 303, 307, 308]
(1..6).each do |i|
code = codes.pop || 302
stub_request(:get, "https://httpbin.org/redirect/#{i}")
.to_return(status: code, body: "", headers: { location: "https://httpbin.org/redirect/#{i - 1}" })
end
stub_request(:get, "https://httpbin.org/redirect/0").to_return(status: 200, body: "<!DOCTYPE html><p>success</p>")
end
it "can follow redirects" do
expect(described_class.fetch_response("https://httpbin.org/redirect/2")).to match("success")
end
it "errors on long redirect chains" do
expect {
described_class.fetch_response("https://httpbin.org/redirect/6")
}.to raise_error(Net::HTTPError, /redirect too deep/)
end
end
describe "cookie handling" do
it "naively forwards cookies to the next request" do
stub_request(:get, "https://httpbin.org/cookies/set/a/b").to_return(
status: 302,
headers: {
location: "/cookies",
"set-cookie": "a=b; Path=/",
}
)
stub_request(:get, "https://httpbin.org/cookies")
.with(headers: { cookie: "a=b; Path=/" })
.to_return(status: 200, body: "success, cookie readback not implemented")
expect(described_class.fetch_response('https://httpbin.org/cookies/set/a/b')).to match("success")
end
it "does not send cookies to the wrong domain" do
skip("unimplemented")
stub_request(:get, "https://httpbin.org/cookies/set/a/b").to_return(
status: 302,
headers: {
location: "https://evil.com/show_cookies",
"set-cookie": "a=b; Path=/",
}
)
stub_request(:get, "https://evil.com/show_cookies")
.with(headers: { cookie: nil })
.to_return(status: 200, body: "success, cookie readback not implemented")
described_class.fetch_response('https://httpbin.org/cookies/set/a/b')
end
end
end
describe "user_agent" do
context "default" do
it "has the default Discourse user agent" do
stub_request(:get, "http://example.com/some-resource")
.with(headers: { "user-agent" => /Discourse Forum Onebox/ })
.to_return(status: 200, body: "test")
described_class.fetch_response('http://example.com/some-resource')
end
end
context "Custom option" do
around do |example|
previous_options = Onebox.options.to_h
Onebox.options = { user_agent: "EvilTroutBot v0.1" }
example.run
Onebox.options = previous_options
end
it "has the custom user agent" do
stub_request(:get, "http://example.com/some-resource")
.with(headers: { "user-agent" => "EvilTroutBot v0.1" })
.to_return(status: 200, body: "test")
described_class.fetch_response('http://example.com/some-resource')
end
end
end
describe '.normalize_url_for_output' do
it { expect(described_class.normalize_url_for_output('http://example.com/fo o')).to eq("http://example.com/fo%20o") }
it { expect(described_class.normalize_url_for_output("http://example.com/fo'o")).to eq("http://example.com/fo&apos;o") }
it { expect(described_class.normalize_url_for_output('http://example.com/fo"o')).to eq("http://example.com/fo&quot;o") }
it { expect(described_class.normalize_url_for_output('http://example.com/fo<o>')).to eq("http://example.com/foo") }
it { expect(described_class.normalize_url_for_output('http://example.com/décran-à')).to eq("http://example.com/décran-à") }
it { expect(described_class.normalize_url_for_output('//example.com/hello')).to eq("//example.com/hello") }
it { expect(described_class.normalize_url_for_output('example.com/hello')).to eq("") }
it { expect(described_class.normalize_url_for_output('linear-gradient(310.77deg, #29AA9F 0%, #098EA6 100%)')).to eq("") }
end
describe '.get_absolute_image_url' do
it { expect(described_class.get_absolute_image_url('//meta.discourse.org/favicon.ico', 'https://meta.discourse.org')).to eq('https://meta.discourse.org/favicon.ico') }
it { expect(described_class.get_absolute_image_url('http://meta.discourse.org/favicon.ico', 'https://meta.discourse.org')).to eq('http://meta.discourse.org/favicon.ico') }
it { expect(described_class.get_absolute_image_url('https://meta.discourse.org/favicon.ico', 'https://meta.discourse.org')).to eq('https://meta.discourse.org/favicon.ico') }
it { expect(described_class.get_absolute_image_url('/favicon.ico', 'https://meta.discourse.org')).to eq('https://meta.discourse.org/favicon.ico') }
it { expect(described_class.get_absolute_image_url('/favicon.ico', 'https://meta.discourse.org/forum/subdir')).to eq('https://meta.discourse.org/favicon.ico') }
it { expect(described_class.get_absolute_image_url('../favicon.ico', 'https://meta.discourse.org/forum/subdir/')).to eq('https://meta.discourse.org/forum/favicon.ico') }
end
describe '.uri_encode' do
it { expect(described_class.uri_encode('http://example.com/f"o&o?[b"ar]')).to eq("http://example.com/f%22o&o?%5Bb%22ar%5D") }
it { expect(described_class.uri_encode("http://example.com/f.o~o;?<ba'r>")).to eq("http://example.com/f.o~o;?%3Cba%27r%3E") }
it { expect(described_class.uri_encode("http://example.com/<pa'th>(foo)?b+a+r")).to eq("http://example.com/%3Cpa'th%3E(foo)?b%2Ba%2Br") }
it { expect(described_class.uri_encode("http://example.com/p,a:t!h-f$o@o*?b!a#r@")).to eq("http://example.com/p,a:t!h-f$o@o*?b%21a#r%40") }
it { expect(described_class.uri_encode("http://example.com/path&foo?b'a<r>&qu(er)y=1")).to eq("http://example.com/path&foo?b%27a%3Cr%3E&qu%28er%29y=1") }
it { expect(described_class.uri_encode("http://example.com/index&<script>alert('XSS');</script>")).to eq("http://example.com/index&%3Cscript%3Ealert('XSS');%3C/script%3E") }
it { expect(described_class.uri_encode("http://example.com/index.html?message=<script>alert('XSS');</script>")).to eq("http://example.com/index.html?message=%3Cscript%3Ealert%28%27XSS%27%29%3B%3C%2Fscript%3E") }
it { expect(described_class.uri_encode("http://example.com/index.php/<IFRAME SRC=source.com onload='alert(document.cookie)'></IFRAME>")).to eq("http://example.com/index.php/%3CIFRAME%20SRC=source.com%20onload='alert(document.cookie)'%3E%3C/IFRAME%3E") }
it { expect(described_class.uri_encode("https://en.wiktionary.org/wiki/greengrocer%27s_apostrophe")).to eq("https://en.wiktionary.org/wiki/greengrocer%27s_apostrophe") }
it { expect(described_class.uri_encode("https://example.com/random%2Bpath?q=random%2Bquery")).to eq("https://example.com/random%2Bpath?q=random%2Bquery") }
it { expect(described_class.uri_encode("https://glitch.com/edit/#!/equinox-watch")).to eq("https://glitch.com/edit/#!/equinox-watch") }
it { expect(described_class.uri_encode("https://gitpod.io/#https://github.com/eclipse-theia/theia")).to eq("https://gitpod.io/#https://github.com/eclipse-theia/theia") }
end
end