2019-04-29 20:27:42 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2022-07-27 22:27:38 -04:00
|
|
|
RSpec.describe UrlHelper do
|
2018-12-11 02:03:13 -05:00
|
|
|
describe "#relaxed parse" do
|
|
|
|
it "can handle double #" do
|
|
|
|
url = UrlHelper.relaxed_parse("https://test.com#test#test")
|
|
|
|
expect(url.to_s).to eq("https://test.com#test%23test")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-11-20 07:10:08 -05:00
|
|
|
describe "#is_local" do
|
|
|
|
it "is true when the file has been uploaded" do
|
|
|
|
store = stub
|
|
|
|
store.expects(:has_been_uploaded?).returns(true)
|
|
|
|
Discourse.stubs(:store).returns(store)
|
2015-06-12 06:02:36 -04:00
|
|
|
expect(UrlHelper.is_local("http://discuss.site.com/path/to/file.png")).to eq(true)
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it "is true for relative assets" do
|
|
|
|
store = stub
|
|
|
|
store.expects(:has_been_uploaded?).returns(false)
|
|
|
|
Discourse.stubs(:store).returns(store)
|
2015-06-12 06:02:36 -04:00
|
|
|
expect(UrlHelper.is_local("/assets/javascripts/all.js")).to eq(true)
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|
|
|
|
|
2019-04-13 13:35:23 -04:00
|
|
|
it "is true for relative assets for subfolders" do
|
|
|
|
store = stub
|
|
|
|
store.expects(:has_been_uploaded?).returns(false)
|
|
|
|
Discourse.stubs(:store).returns(store)
|
2019-11-15 00:48:24 -05:00
|
|
|
|
|
|
|
set_subfolder "/subpath"
|
2019-04-13 13:35:23 -04:00
|
|
|
expect(UrlHelper.is_local("/subpath/assets/javascripts/all.js")).to eq(true)
|
|
|
|
end
|
|
|
|
|
2014-01-14 17:51:36 -05:00
|
|
|
it "is true for plugin assets" do
|
|
|
|
store = stub
|
|
|
|
store.expects(:has_been_uploaded?).returns(false)
|
|
|
|
Discourse.stubs(:store).returns(store)
|
2015-06-12 06:02:36 -04:00
|
|
|
expect(UrlHelper.is_local("/plugins/all.js")).to eq(true)
|
2014-01-14 17:51:36 -05:00
|
|
|
end
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
describe "#absolute" do
|
2016-09-05 01:57:46 -04:00
|
|
|
it "returns an absolute URL for CDN" do
|
|
|
|
begin
|
|
|
|
Rails.configuration.action_controller.asset_host = "//cdn.awesome.com"
|
|
|
|
expect(UrlHelper.absolute("/test.jpg")).to eq("https://cdn.awesome.com/test.jpg")
|
|
|
|
|
|
|
|
Rails.configuration.action_controller.asset_host = "https://cdn.awesome.com"
|
|
|
|
expect(UrlHelper.absolute("/test.jpg")).to eq("https://cdn.awesome.com/test.jpg")
|
|
|
|
|
|
|
|
Rails.configuration.action_controller.asset_host = "http://cdn.awesome.com"
|
|
|
|
expect(UrlHelper.absolute("/test.jpg")).to eq("http://cdn.awesome.com/test.jpg")
|
|
|
|
ensure
|
|
|
|
Rails.configuration.action_controller.asset_host = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-11-20 07:10:08 -05:00
|
|
|
it "does not change non-relative url" do
|
2015-06-12 06:02:36 -04:00
|
|
|
expect(UrlHelper.absolute("http://www.discourse.org")).to eq("http://www.discourse.org")
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|
|
|
|
|
2013-12-16 18:35:34 -05:00
|
|
|
it "changes a relative url to an absolute one using base url by default" do
|
2015-06-12 06:02:36 -04:00
|
|
|
expect(UrlHelper.absolute("/path/to/file")).to eq("http://test.localhost/path/to/file")
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|
|
|
|
|
2013-12-16 18:35:34 -05:00
|
|
|
it "changes a relative url to an absolute one using the cdn when enabled" do
|
|
|
|
Rails.configuration.action_controller.stubs(:asset_host).returns("http://my.cdn.com")
|
2015-06-12 06:02:36 -04:00
|
|
|
expect(UrlHelper.absolute("/path/to/file")).to eq("http://my.cdn.com/path/to/file")
|
2013-12-16 18:35:34 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#absolute_without_cdn" do
|
|
|
|
it "changes a relative url to an absolute one using base url even when cdn is enabled" do
|
|
|
|
Rails.configuration.action_controller.stubs(:asset_host).returns("http://my.cdn.com")
|
2015-06-12 06:02:36 -04:00
|
|
|
expect(UrlHelper.absolute_without_cdn("/path/to/file")).to eq("http://test.localhost/path/to/file")
|
2013-12-16 18:35:34 -05:00
|
|
|
end
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
describe "#schemaless" do
|
2015-07-24 00:08:32 -04:00
|
|
|
it "removes http schemas only" do
|
2015-06-12 06:02:36 -04:00
|
|
|
expect(UrlHelper.schemaless("http://www.discourse.org")).to eq("//www.discourse.org")
|
2015-07-24 00:08:32 -04:00
|
|
|
expect(UrlHelper.schemaless("https://secure.discourse.org")).to eq("https://secure.discourse.org")
|
2015-06-12 06:02:36 -04:00
|
|
|
expect(UrlHelper.schemaless("ftp://ftp.discourse.org")).to eq("ftp://ftp.discourse.org")
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-08-09 06:28:29 -04:00
|
|
|
describe "#normalized_encode" do
|
2022-08-09 00:42:23 -04:00
|
|
|
it "does not double escape %3A (:)" do
|
|
|
|
url = "http://discourse.org/%3A/test"
|
2022-08-09 06:28:29 -04:00
|
|
|
expect(UrlHelper.normalized_encode(url)).to eq(url)
|
2022-08-09 00:42:23 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "does not double escape %2F (/)" do
|
|
|
|
url = "http://discourse.org/%2F/test"
|
2022-08-09 06:28:29 -04:00
|
|
|
expect(UrlHelper.normalized_encode(url)).to eq(url)
|
2022-08-09 00:42:23 -04:00
|
|
|
end
|
|
|
|
|
2017-12-12 11:50:39 -05:00
|
|
|
it "doesn't escape simple URL" do
|
2022-08-09 06:28:29 -04:00
|
|
|
url = UrlHelper.normalized_encode('http://example.com/foo/bar')
|
2017-12-12 11:50:39 -05:00
|
|
|
expect(url).to eq('http://example.com/foo/bar')
|
|
|
|
end
|
|
|
|
|
|
|
|
it "escapes unsafe chars" do
|
2022-08-09 06:28:29 -04:00
|
|
|
url = UrlHelper.normalized_encode("http://example.com/?a=\11\15")
|
2017-12-12 11:50:39 -05:00
|
|
|
expect(url).to eq('http://example.com/?a=%09%0D')
|
|
|
|
end
|
|
|
|
|
|
|
|
it "escapes non-ascii chars" do
|
2022-08-09 06:28:29 -04:00
|
|
|
url = UrlHelper.normalized_encode('http://example.com/ماهی')
|
2017-12-12 11:50:39 -05:00
|
|
|
expect(url).to eq('http://example.com/%D9%85%D8%A7%D9%87%DB%8C')
|
|
|
|
end
|
|
|
|
|
2020-12-03 17:16:01 -05:00
|
|
|
it "doesn't escape already escaped chars (space)" do
|
2022-08-09 06:28:29 -04:00
|
|
|
url = UrlHelper.normalized_encode('http://example.com/foo%20bar/foo bar/')
|
2017-12-12 11:50:39 -05:00
|
|
|
expect(url).to eq('http://example.com/foo%20bar/foo%20bar/')
|
|
|
|
end
|
2020-01-30 18:09:34 -05:00
|
|
|
|
2020-12-03 17:16:01 -05:00
|
|
|
it "doesn't escape already escaped chars (hash)" do
|
2022-08-09 00:42:23 -04:00
|
|
|
url = 'https://calendar.google.com/calendar/embed?src=en.uk%23holiday@group.v.calendar.google.com&ctz=Europe%2FLondon'
|
2022-08-09 06:28:29 -04:00
|
|
|
escaped = UrlHelper.normalized_encode(url)
|
2022-08-09 00:42:23 -04:00
|
|
|
expect(escaped).to eq(url)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "leaves reserved chars alone in edge cases" do
|
|
|
|
skip "see: https://github.com/sporkmonger/addressable/issues/472"
|
|
|
|
url = "https://example.com/ article/id%3A1.2%2F1/bar"
|
|
|
|
expected = "https://example.com/%20article/id%3A1.2%2F1/bar"
|
2022-08-09 06:28:29 -04:00
|
|
|
escaped = UrlHelper.normalized_encode(url)
|
2022-08-09 00:42:23 -04:00
|
|
|
expect(escaped).to eq(expected)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "handles emoji domain names" do
|
|
|
|
url = "https://💻.example/💻?computer=💻"
|
|
|
|
expected = "https://xn--3s8h.example/%F0%9F%92%BB?computer=%F0%9F%92%BB"
|
2022-08-09 06:28:29 -04:00
|
|
|
escaped = UrlHelper.normalized_encode(url)
|
2022-08-09 00:42:23 -04:00
|
|
|
expect(escaped).to eq(expected)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "handles special-character domain names" do
|
|
|
|
url = "https://éxample.com/test"
|
|
|
|
expected = "https://xn--xample-9ua.com/test"
|
2022-08-09 06:28:29 -04:00
|
|
|
escaped = UrlHelper.normalized_encode(url)
|
2022-08-09 00:42:23 -04:00
|
|
|
expect(escaped).to eq(expected)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "performs basic normalization" do
|
|
|
|
url = "http://EXAMPLE.com/a"
|
2022-08-09 06:28:29 -04:00
|
|
|
escaped = UrlHelper.normalized_encode(url)
|
2022-08-09 00:42:23 -04:00
|
|
|
expect(escaped).to eq("http://example.com/a")
|
2020-12-03 17:16:01 -05:00
|
|
|
end
|
|
|
|
|
2020-01-30 18:09:34 -05:00
|
|
|
it "doesn't escape S3 presigned URLs" do
|
|
|
|
# both of these were originally real presigned URLs and have had all
|
|
|
|
# sensitive information stripped
|
|
|
|
presigned_url = "https://test.com/original/3X/b/5/575bcc2886bf7a39684b57ca90be85f7d399bbc7.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AK8888999977%2F20200130%2Fus-west-1%2Fs3%2Faws4_request&X-Amz-Date=20200130T064355Z&X-Amz-Expires=15&X-Amz-SignedHeaders=host&X-Amz-Security-Token=blahblah%2Bblahblah%2Fblah%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQAR&X-Amz-Signature=test"
|
|
|
|
encoded_presigned_url = "https://test.com/original/3X/b/5/575bcc2886bf7a39684b57ca90be85f7d399bbc7.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AK8888999977/20200130/us-west-1/s3/aws4_request&X-Amz-Date=20200130T064355Z&X-Amz-Expires=15&X-Amz-SignedHeaders=host&X-Amz-Security-Token=blahblah+blahblah/blah//////////wEQA==&X-Amz-Signature=test"
|
2022-08-09 06:28:29 -04:00
|
|
|
expect(UrlHelper.normalized_encode(presigned_url)).not_to eq(encoded_presigned_url)
|
2020-01-30 18:09:34 -05:00
|
|
|
end
|
2017-12-12 11:50:39 -05:00
|
|
|
end
|
|
|
|
|
2019-10-14 02:09:16 -04:00
|
|
|
describe "#local_cdn_url" do
|
2019-12-18 00:51:57 -05:00
|
|
|
let(:url) { "/#{Discourse.store.upload_path}/1X/575bcc2886bf7a39684b57ca90be85f7d399bbc7.png" }
|
2019-10-14 02:09:16 -04:00
|
|
|
let(:asset_host) { "//my.awesome.cdn" }
|
|
|
|
|
|
|
|
it "should return correct cdn url for local relative urls" do
|
2019-10-14 03:19:11 -04:00
|
|
|
set_cdn_url asset_host
|
2019-10-14 02:09:16 -04:00
|
|
|
cdn_url = UrlHelper.local_cdn_url(url)
|
|
|
|
expect(cdn_url).to eq("#{asset_host}#{url}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-28 17:06:07 -04:00
|
|
|
describe "#rails_route_from_url" do
|
|
|
|
it "recognizes a user path" do
|
|
|
|
result = UrlHelper.rails_route_from_url('http://example.com/u/john_smith')
|
|
|
|
expect(result[:controller]).to eq("users")
|
|
|
|
expect(result[:action]).to eq("show")
|
|
|
|
expect(result[:username]).to eq("john_smith")
|
|
|
|
end
|
|
|
|
|
|
|
|
it "recognizes a user path with unicode characters in the username" do
|
|
|
|
result = UrlHelper.rails_route_from_url('http://example.com/u/björn_ulvaeus')
|
|
|
|
expect(result[:controller]).to eq("users")
|
|
|
|
expect(result[:action]).to eq("show")
|
|
|
|
expect(result[:username].force_encoding('UTF-8')).to eq("björn_ulvaeus")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-11-22 00:29:31 -05:00
|
|
|
describe "#cook_url" do
|
2020-09-14 07:32:25 -04:00
|
|
|
let(:url) { "//s3bucket.s3.dualstack.us-west-1.amazonaws.com/dev/original/3X/2/e/2e6f2ef81b6910ea592cd6d21ee897cd51cf72e4.jpeg" }
|
2019-11-22 00:29:31 -05:00
|
|
|
|
|
|
|
before do
|
2020-09-14 07:32:25 -04:00
|
|
|
setup_s3
|
2019-11-22 00:29:31 -05:00
|
|
|
SiteSetting.s3_upload_bucket = "s3bucket"
|
|
|
|
SiteSetting.login_required = true
|
2020-09-14 07:32:25 -04:00
|
|
|
Rails.configuration.action_controller.asset_host = "https://test.some-cdn.com/dev"
|
|
|
|
|
|
|
|
FileStore::S3Store.any_instance.stubs(:has_been_uploaded?).returns(true)
|
2021-06-08 13:25:51 -04:00
|
|
|
|
|
|
|
SiteSetting.secure_media = true
|
2019-11-22 00:29:31 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def cooked
|
|
|
|
UrlHelper.cook_url(url, secure: secure)
|
|
|
|
end
|
|
|
|
|
|
|
|
context "when the upload for the url is secure" do
|
|
|
|
let(:secure) { true }
|
|
|
|
|
|
|
|
it "returns the secure_proxy_without_cdn url, with no asset host URL change" do
|
|
|
|
expect(cooked).to eq(
|
|
|
|
"//test.localhost/secure-media-uploads/dev/original/3X/2/e/2e6f2ef81b6910ea592cd6d21ee897cd51cf72e4.jpeg"
|
|
|
|
)
|
|
|
|
end
|
2021-06-08 13:25:51 -04:00
|
|
|
|
2022-07-27 12:14:14 -04:00
|
|
|
context "when secure_media setting is disabled" do
|
2021-06-08 13:25:51 -04:00
|
|
|
before { SiteSetting.secure_media = false }
|
|
|
|
|
|
|
|
it "returns the local_cdn_url" do
|
|
|
|
expect(cooked).to eq(
|
|
|
|
"//s3bucket.s3.dualstack.us-west-1.amazonaws.com/dev/original/3X/2/e/2e6f2ef81b6910ea592cd6d21ee897cd51cf72e4.jpeg"
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
2019-11-22 00:29:31 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
context "when the upload for the url is not secure" do
|
|
|
|
let(:secure) { false }
|
|
|
|
|
|
|
|
it "returns the local_cdn_url" do
|
|
|
|
expect(cooked).to eq(
|
2020-09-14 07:32:25 -04:00
|
|
|
"//s3bucket.s3.dualstack.us-west-1.amazonaws.com/dev/original/3X/2/e/2e6f2ef81b6910ea592cd6d21ee897cd51cf72e4.jpeg"
|
2019-11-22 00:29:31 -05:00
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
after do
|
|
|
|
Rails.configuration.action_controller.asset_host = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-09-30 01:20:00 -04:00
|
|
|
describe "rails_route_from_url" do
|
|
|
|
it "returns a rails route from the path" do
|
|
|
|
expect(described_class.rails_route_from_url("/u")).to eq({ controller: "users", action: "index" })
|
|
|
|
end
|
|
|
|
|
|
|
|
it "does not raise for invalid URLs" do
|
|
|
|
url = "http://URL:%20https://google.com"
|
|
|
|
expect(described_class.rails_route_from_url(url)).to eq(nil)
|
|
|
|
end
|
2020-10-05 14:12:33 -04:00
|
|
|
|
|
|
|
it "does not raise for invalid mailtos" do
|
|
|
|
url = "mailto:eviltrout%2540example.com"
|
|
|
|
expect(described_class.rails_route_from_url(url)).to eq(nil)
|
|
|
|
end
|
2020-09-30 01:20:00 -04:00
|
|
|
end
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|