CVE-2020-36624: Use of Web Link to Untrusted Target with window.opener Access (CWE-1022)
value: A vulnerability was found in ahorner text-helpers up to 1.0.x. It has been declared as critical. This vulnerability affects unknown code of the file lib/text_helpers/translation.rb. The manipulation of the argument link leads to use of web link to untrusted target with window.opener access. The attack can be initiated remotely. Upgrading to version 1.1.0 is able to address this issue. The name of the patch is 184b60ded0e43c985788582aca2d1e746f9405a3. It is recommended to upgrade the affected component. The identifier of this vulnerability is VDB-216520.
CWE description: The web application produces links to untrusted external sites outside of its sphere of control, but it does not properly prevent the external site from modifying  security-critical properties of the window.opener object, such as the location property.
CVSS v3 base score: 6.1
Published: 2022-12-22T10:15Z
Repository: https://github.com/ahorner/text-helpers
Language: Ruby
Files changed: lib/text_helpers/translation.rb, test/lib/text_helpers/translation_test.rb
Commit date: 2020-12-09 06:52:01 -0700
Commit message: Protect against reverse tabnabbing (#19)

* Protect against reverse tabnabbing

https://owasp.org/www-community/attacks/Reverse_Tabnabbing

* Use faster regular expression method
Diff: diff --git a/lib/text_helpers/translation.rb b/lib/text_helpers/translation.rb
index bf80f06..6c9d314 100644
--- a/lib/text_helpers/translation.rb
+++ b/lib/text_helpers/translation.rb
@@ -10,11 +10,11 @@ module TextHelpers
 
     def link(link, title, content)
       attributes = [
         ("href=\"#{link}\"" if link),
         ("title=\"#{title}\"" if title),
-        ("target=\"_blank\"" if link =~ PROTOCOL_MATCHER),
+        ("target=\"_blank\" rel=\"noopener\"" if link.match?(PROTOCOL_MATCHER)),
       ]
 
       "<a #{attributes.compact.join(" ")}>#{content}</a>"
     end
 
@@ -42,11 +42,11 @@ module TextHelpers
       }.merge(options)).strip
 
       interpolation_options = { cascade: true }.merge(options)
 
       # Interpolate any keypaths (e.g., `!some.lookup.path/key!`) found in the text.
-      while text =~ KEYPATH_MATCHER do
+      while text.match?(KEYPATH_MATCHER) do
         text = text.gsub(KEYPATH_MATCHER) { |match| I18n.t($1, **interpolation_options) }
       end
 
       text = smartify(text) if options.fetch(:smart, true)
       text.html_safe
diff --git a/test/lib/text_helpers/translation_test.rb b/test/lib/text_helpers/translation_test.rb
index 31cce09..0a30650 100644
--- a/test/lib/text_helpers/translation_test.rb
+++ b/test/lib/text_helpers/translation_test.rb
@@ -117,16 +117,16 @@ describe TextHelpers::Translation do
 
       it "correctly combines :orphans and :inline options" do
         assert_equal "<em>#{@scoped_text}</em>\n", @helper.html(:test_key, inline: true, orphans: true)
       end
 
-      it "renders internal links without a target" do
+      it "renders internal links without a target or rel" do
         assert_equal "<a href=\"/internal/path\">Internal&nbsp;link</a>\n", @helper.html(:internal_link, inline: true)
       end
 
-      it "renders external links with target='_blank'" do
-        assert_equal "<a href=\"http://external.com\" target=\"_blank\">External&nbsp;link</a>\n", @helper.html(:external_link, inline: true)
+      it "renders external links with target='_blank' and rel='noopener'" do
+        assert_equal "<a href=\"http://external.com\" target=\"_blank\" rel=\"noopener\">External&nbsp;link</a>\n", @helper.html(:external_link, inline: true)
       end
 
       it "interpolates values wrapped in !!" do
         assert_equal "Global? (#{@global_text})", @helper.text(:interpolated_key)
       end

Vulnerable code:         ("target=\"_blank\"" if link =~ PROTOCOL_MATCHER),
      while text =~ KEYPATH_MATCHER do
      it "renders internal links without a target" do
      it "renders external links with target='_blank'" do
        assert_equal "<a href=\"http://external.com\" target=\"_blank\">External&nbsp;link</a>\n", @helper.html(:external_link, inline: true)
Fixed code:         ("target=\"_blank\" rel=\"noopener\"" if link.match?(PROTOCOL_MATCHER)),
      while text.match?(KEYPATH_MATCHER) do
      it "renders internal links without a target or rel" do
      it "renders external links with target='_blank' and rel='noopener'" do
        assert_equal "<a href=\"http://external.com\" target=\"_blank\" rel=\"noopener\">External&nbsp;link</a>\n", @helper.html(:external_link, inline: true)