Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rel="noopener" to a link by default #28035

Open
wants to merge 1 commit into
base: master
from
Open
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

Add rel="noopener" to a link by default

rel=“noopener” will be added to a link by default, if target is “_blank” or any other string except an empty string, “_parent”, “_self”, “_top” and href is a url with “http://“ or “https://“. rel=“noopener” prevents possible phishing attacks.
  • Loading branch information...
michald committed Feb 16, 2017
commit 9e363e0e770c5784c43ea68b74a29f58a41f15fd
@@ -17,6 +17,8 @@ module UrlHelper
# (link_to_unless_current, for instance), which must be provided
# as a method called #request on the context.
BUTTON_TAG_METHOD_VERBS = %w{patch put delete}
OPENER_TARGETS = ["", "_parent", "_self", "_top"].freeze

extend ActiveSupport::Concern

include TagHelper
@@ -98,6 +100,13 @@ def _filtered_referrer # :nodoc:
# the link. The drivers each provide mechanisms for listening for the
# completion of the Ajax request and performing JavaScript operations once
# they're complete
# * <tt>opener: true</tt> - This option prevents adding <tt>rel="noopener"</tt>
# to an HTML output by default. <tt>rel="noopener"</tt> will be added to
# a link by default, if <tt>target</tt> is <tt>_blank</tt> or any other
# string except an empty string, <tt>_parent</tt>, <tt>_self</tt>, <tt>_top</tt>
# and <tt>href</tt> is a url with <tt>http://</tt> or <tt>https://</tt>.
# <tt>rel="noopener"</tt> nullifies <tt>window.opener</tt> (supported by selected browsers)
# and prevents possible phishing attacks.
#
# ==== Data attributes
#
@@ -186,6 +195,16 @@ def _filtered_referrer # :nodoc:
#
# link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"
# # => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>
#
# If you add target="_blank" and a full URL, rel="noopener" will appear by default:
#
# link_to("Visit Other Site", "http://www.example.com", target: "_blank")
# # => <a href="http://www.example.com" target="_blank" rel="noopener">Visit Other Site</a>
#
# You can disable adding rel="noopener" by default:
#
# link_to("Visit Other Site", "http://www.example.com", target: "_blank", opener: true)
# # => <a href="http://www.example.com" target="_blank">Visit Other Site</a>
def link_to(name = nil, options = nil, html_options = nil, &block)
html_options, options, name = options, name, block if block_given?
options ||= {}
@@ -195,6 +214,10 @@ def link_to(name = nil, options = nil, html_options = nil, &block)
url = url_for(options)
html_options["href".freeze] ||= url

if noopener?(html_options)
html_options["rel".freeze] = "#{html_options["rel".freeze]} noopener".lstrip
end

content_tag("a".freeze, name || url, html_options, &block)
end

@@ -649,6 +672,29 @@ def to_form_params(attribute, namespace = nil)

params.sort_by { |pair| pair[:name] }
end

def noopener?(html_options)
noopener_href?(html_options["href".freeze]) &&
noopener_target?(html_options["target".freeze]) &&
noopener_rel?(html_options["rel".freeze]) &&
noopener_allowed?(html_options)
end

def noopener_href?(href)
href.start_with?("http://".freeze, "https://".freeze)
end

def noopener_target?(target)
target && OPENER_TARGETS.exclude?(target)
end

def noopener_rel?(rel)
rel !~ /noopener/
end

def noopener_allowed?(html_options)
!html_options.delete("opener".freeze)
end
end
end
end
@@ -310,6 +310,84 @@ def test_link_with_nil_html_options
assert_dom_equal %{<a href="/">Hello</a>}, link
end

def test_link_with_href_http_and_target_blank
link = link_to("Hello", "http://www.example.com", target: "_blank")
expected = %{<a href="http://www.example.com" target="_blank" rel="noopener">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_href_https_and_target_blank
link = link_to("Hello", "https://www.example.com", target: "_blank")
expected = %{<a href="https://www.example.com" target="_blank" rel="noopener">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_href_path_and_target_blank
link = link_to("Hello", "/", target: "_blank")
expected = %{<a href="/" target="_blank">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_target_as_any_string
link = link_to("Hello", "http://www.example.com", target: "foo")
expected = %{<a href="http://www.example.com" target="foo" rel="noopener">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_target_nil
link = link_to("Hello", "http://www.example.com", target: nil)
expected = %{<a href="http://www.example.com">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_target_as_empty_string
link = link_to("Hello", "http://www.example.com", target: "")
expected = %{<a href="http://www.example.com" target="">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_target_parent
link = link_to("Hello", "http://www.example.com", target: "_parent")
expected = %{<a href="http://www.example.com" target="_parent">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_target_self
link = link_to("Hello", "http://www.example.com", target: "_self")
expected = %{<a href="http://www.example.com" target="_self">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_target_top
link = link_to("Hello", "http://www.example.com", target: "_top")
expected = %{<a href="http://www.example.com" target="_top">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_target_blank_and_rel_nonempty
link = link_to("Hello", "http://www.example.com", target: "_blank", rel: "nofollow")
expected = %{<a href="http://www.example.com" target="_blank" rel="nofollow noopener">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_target_blank_and_rel_noopener
link = link_to("Hello", "http://www.example.com", target: "_blank", rel: "noopener")
expected = %{<a href="http://www.example.com" target="_blank" rel="noopener">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_target_blank_and_opener_true
link = link_to("Hello", "http://www.example.com", target: "_blank", opener: true)
expected = %{<a href="http://www.example.com" target="_blank">Hello</a>}
assert_dom_equal expected, link
end

def test_link_with_target_blank_and_opener_false
link = link_to("Hello", "http://www.example.com", target: "_blank", opener: false)
expected = %{<a href="http://www.example.com" target="_blank" rel="noopener">Hello</a>}
assert_dom_equal expected, link
end

def test_link_tag_with_custom_onclick
link = link_to("Hello", "http://www.example.com", onclick: "alert('yay!')")
expected = %{<a href="http://www.example.com" onclick="alert(&#39;yay!&#39;)">Hello</a>}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.