require 'spec_helper' shared_examples 'an external link with rel attribute' do it 'adds rel="nofollow" to external links' do expect(doc.at_css('a')).to have_attribute('rel') expect(doc.at_css('a')['rel']).to include 'nofollow' end it 'adds rel="noreferrer" to external links' do expect(doc.at_css('a')).to have_attribute('rel') expect(doc.at_css('a')['rel']).to include 'noreferrer' end it 'adds rel="noopener" to external links' do expect(doc.at_css('a')).to have_attribute('rel') expect(doc.at_css('a')['rel']).to include 'noopener' end end describe Banzai::Filter::ExternalLinkFilter do include FilterSpecHelper it 'ignores elements without an href attribute' do exp = act = %q(Ignore Me) expect(filter(act).to_html).to eq exp end it 'ignores non-HTTP(S) links' do exp = act = %q(IRC) expect(filter(act).to_html).to eq exp end it 'skips internal links' do internal = Gitlab.config.gitlab.url exp = act = %Q(Login) expect(filter(act).to_html).to eq exp end context 'for root links on document' do let(:doc) { filter %q(Google) } it_behaves_like 'an external link with rel attribute' end context 'for nested links on document' do let(:doc) { filter %q(

Google

) } it_behaves_like 'an external link with rel attribute' end context 'for invalid urls' do it 'adds rel and target attributes to broken hrefs' do doc = filter %q(

Google

) expected = %q(

Google

) expect(doc.to_html).to eq(expected) end it 'adds rel and target to improperly formatted mailtos' do doc = filter %q(

Email

) expected = %q(

Email

) expect(doc.to_html).to eq(expected) end it 'adds rel and target to improperly formatted autolinks' do doc = filter %q(

mailto://jblogs@example.com

) expected = %q(

mailto://jblogs@example.com

) expect(doc.to_html).to eq(expected) end end context 'for links with a username' do context 'with a valid username' do let(:doc) { filter %q(Google) } it_behaves_like 'an external link with rel attribute' end context 'with an impersonated username' do let(:internal) { Gitlab.config.gitlab.url } let(:doc) { filter %Q(Reverse Tabnabbing) } it_behaves_like 'an external link with rel attribute' end end context 'for non-lowercase scheme links' do context 'with http' do let(:doc) { filter %q(

Google

) } it_behaves_like 'an external link with rel attribute' end context 'with https' do let(:doc) { filter %q(

Google

) } it_behaves_like 'an external link with rel attribute' end it 'skips internal links' do internal_link = Gitlab.config.gitlab.url + "/sign_in" url = internal_link.gsub(/\Ahttp/, 'HtTp') act = %Q(Login) exp = %Q(Login) expect(filter(act).to_html).to eq(exp) end it 'skips relative links' do exp = act = %q(Relative URL) expect(filter(act).to_html).to eq(exp) end end context 'for protocol-relative links' do let(:doc) { filter %q(

Google

) } it_behaves_like 'an external link with rel attribute' end context 'links with RTLO character' do # In rendered text this looks like "http://example.com/evilexe.mp3" let(:doc) { filter %Q(http://example.com/evil\u202E3pm.exe) } it_behaves_like 'an external link with rel attribute' it 'escapes RTLO in link text' do expected = %q(http://example.com/evil%E2%80%AE3pm.exe) expect(doc.to_html).to include(expected) end it 'does not mangle the link text' do doc = filter %Q(Oneand\u202Eexe.mp3) expect(doc.to_html).to include('Oneand%E2%80%AEexe.mp3') end end context 'for generated autolinks' do context 'with an IDN character' do let(:doc) { filter(%q(http://exa😄mple.com)) } let(:doc_email) { filter(%q(http://exa😄mple.com), emailable_links: true) } it_behaves_like 'an external link with rel attribute' it 'does not change the link text' do expect(doc.to_html).to include('http://exa😄mple.com') end it 'uses punycode for emails' do expect(doc_email.to_html).to include('http://xn--example-6p25f.com/') end end end context 'for links that look malicious' do context 'with an IDN character' do let(:doc) { filter %q(http://exa😄mple.com) } it 'adds a toolip with punycode' do expect(doc.to_html).to include('http://exa😄mple.com') expect(doc.to_html).to include('class="has-tooltip"') expect(doc.to_html).to include('title="http://xn--example-6p25f.com/"') end end context 'with RTLO character' do let(:doc) { filter %q(Evil Test) } it 'adds a toolip with punycode' do expect(doc.to_html).to include('Evil Test') expect(doc.to_html).to include('class="has-tooltip"') expect(doc.to_html).to include('title="http://example.com/evil%E2%80%AE3pm.exe"') end end end end