Control: tags 1030047 + patch
Control: tags 1030047 + pending

Dear maintainer,

I've prepared an NMU for ruby-sanitize (versioned as 6.0.0-1.1) and
uploaded it to DELAYED/2. Please feel free to tell me if I
should delay it longer.

Regards,
Salvatore
diff -Nru ruby-sanitize-6.0.0/debian/changelog ruby-sanitize-6.0.0/debian/changelog
--- ruby-sanitize-6.0.0/debian/changelog	2022-01-27 20:56:32.000000000 +0100
+++ ruby-sanitize-6.0.0/debian/changelog	2023-02-20 20:28:45.000000000 +0100
@@ -1,3 +1,13 @@
+ruby-sanitize (6.0.0-1.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * Update tests to remove deprecated minitest 'must_be'
+  * Forcibly escape content in "unescaped text" elements inside math or svg
+    namespaces
+  * Always remove `<noscript>` elements (CVE-2023-23627) (Closes: #1030047)
+
+ -- Salvatore Bonaccorso <car...@debian.org>  Mon, 20 Feb 2023 20:28:45 +0100
+
 ruby-sanitize (6.0.0-1) unstable; urgency=medium
 
   * Team upload.
diff -Nru ruby-sanitize-6.0.0/debian/patches/Always-remove-noscript-elements.patch ruby-sanitize-6.0.0/debian/patches/Always-remove-noscript-elements.patch
--- ruby-sanitize-6.0.0/debian/patches/Always-remove-noscript-elements.patch	1970-01-01 01:00:00.000000000 +0100
+++ ruby-sanitize-6.0.0/debian/patches/Always-remove-noscript-elements.patch	2023-02-20 20:28:23.000000000 +0100
@@ -0,0 +1,143 @@
+From: Ryan Grove <r...@wonko.com>
+Date: Thu, 26 Jan 2023 13:42:39 -0800
+Subject: Always remove `<noscript>` elements
+Origin: https://github.com/rgrove/sanitize/commit/ec14265e530dc3fe31ce2ef773594d3a97778d22
+Bug-Debian: https://bugs.debian.org/1030047
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2023-23627
+
+...even if `noscript` is in the allowlist.
+
+A `<noscript>` element's content is parsed differently in browsers
+depending on whether or not scripting is enabled. Since Nokogiri doesn't
+support scripting, it always parses `<noscript>` elements as if
+scripting is disabled. This results in edge cases where it's not
+possible to reliably sanitize the contents of a `<noscript>` element
+because Nokogiri can't fully replicate the parsing behavior of a
+scripting-enabled browser. The safest thing to do is to simply remove
+all `<noscript>` elements.
+
+Fixes GHSA-fw3g-2h3j-qmm7
+---
+ HISTORY.md                                 | 27 ++++++++++++++++++++++
+ README.md                                  | 14 +++++++----
+ lib/sanitize/transformers/clean_element.rb | 10 ++++++++
+ test/test_clean_element.rb                 |  7 ++++++
+ test/test_malicious_html.rb                | 20 +++++++++++++++-
+ 5 files changed, 73 insertions(+), 5 deletions(-)
+
+diff --git a/README.md b/README.md
+index 0f6efb56fde0..5f5e73816f68 100644
+--- a/README.md
++++ b/README.md
+@@ -12,10 +12,10 @@ properties, @ rules, and URL protocols in elements or attributes containing CSS.
+ Any HTML or CSS that you don't explicitly allow will be removed.
+ 
+ Sanitize is based on the [Nokogumbo HTML5 parser][nokogumbo], which parses HTML
+-exactly the same way modern browsers do, and [Crass][crass], which parses CSS
+-exactly the same way modern browsers do. As long as your allowlist config only
+-allows safe markup and CSS, even the most malformed or malicious input will be
+-transformed into safe output.
++the same way modern browsers do, and [Crass][crass], which parses CSS the same
++way modern browsers do. As long as your allowlist config only allows safe markup
++and CSS, even the most malformed or malicious input will be transformed into
++safe output.
+ 
+ [![Gem Version](https://badge.fury.io/rb/sanitize.svg)](http://badge.fury.io/rb/sanitize)
+ [![Tests](https://github.com/rgrove/sanitize/workflows/Tests/badge.svg)](https://github.com/rgrove/sanitize/actions?query=workflow%3ATests)
+@@ -427,6 +427,12 @@ elements not in this array will be removed.
+ >
+ > By default, Sanitize will remove all MathML and SVG elements. If you add MathML or SVG elements to a custom element allowlist, you must assume that any content inside them will be allowed, even if that content would otherwise be removed or escaped by Sanitize. This may create a security vulnerability in your application.
+ 
++> **Note**
++>
++> Sanitize always removes `<noscript>` elements and their contents, even if `noscript` is in the allowlist.
++>
++> This is because a `<noscript>` element's content is parsed differently in browsers depending on whether or not scripting is enabled. Since Nokogiri doesn't support scripting, it always parses `<noscript>` elements as if scripting is disabled. This results in edge cases where it's not possible to reliably sanitize the contents of a `<noscript>` element because Nokogiri can't fully replicate the parsing behavior of a scripting-enabled browser.
++
+ #### :parser_options (Hash)
+ 
+ [Parsing options](https://github.com/rubys/nokogumbo/tree/master#parsing-options) to be supplied to `nokogumbo`.
+diff --git a/lib/sanitize/transformers/clean_element.rb b/lib/sanitize/transformers/clean_element.rb
+index a2bacd8198f7..98208a7c15f3 100644
+--- a/lib/sanitize/transformers/clean_element.rb
++++ b/lib/sanitize/transformers/clean_element.rb
+@@ -252,6 +252,16 @@ class Sanitize; module Transformers; class CleanElement
+ 
+         node['content'] = node['content'].gsub(/;\s*charset\s*=.+\z/, ';charset=utf-8')
+       end
++
++    # A `<noscript>` element's content is parsed differently in browsers
++    # depending on whether or not scripting is enabled. Since Nokogiri doesn't
++    # support scripting, it always parses `<noscript>` elements as if scripting
++    # is disabled. This results in edge cases where it's not possible to
++    # reliably sanitize the contents of a `<noscript>` element because Nokogiri
++    # can't fully replicate the parsing behavior of a scripting-enabled browser.
++    # The safest thing to do is to simply remove all `<noscript>` elements.
++    when 'noscript'
++      node.unlink
+     end
+   end
+ 
+diff --git a/test/test_clean_element.rb b/test/test_clean_element.rb
+index 1293cea17ba2..916b0f934251 100644
+--- a/test/test_clean_element.rb
++++ b/test/test_clean_element.rb
+@@ -541,5 +541,12 @@ describe 'Sanitize::Transformers::CleanElement' do
+       )).must_equal "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"></head><body>Howdy!</body></html>"
+     end
+ 
++    it 'always removes `<noscript>` elements even if `noscript` is in the allowlist' do
++      assert_equal(
++        '',
++        Sanitize.fragment('<noscript>foo</noscript>', elements: ['noscript'])
++      )
++    end
++
+   end
+ end
+diff --git a/test/test_malicious_html.rb b/test/test_malicious_html.rb
+index 377e6deb0fb5..7b33f68880d3 100644
+--- a/test/test_malicious_html.rb
++++ b/test/test_malicious_html.rb
+@@ -244,7 +244,6 @@ describe 'Malicious HTML' do
+     unescaped_content_elements = %w[
+       noembed
+       noframes
+-      noscript
+       plaintext
+       script
+       xmp
+@@ -255,6 +254,7 @@ describe 'Malicious HTML' do
+     ]
+ 
+     removed_elements = %w[
++      noscript
+       style
+     ]
+ 
+@@ -318,4 +318,22 @@ describe 'Malicious HTML' do
+       end
+     end
+   end
++
++  describe 'sanitization bypass by exploiting scripting-disabled <noscript> behavior' do
++    before do
++      @s = Sanitize.new(
++        Sanitize::Config.merge(
++          Sanitize::Config::RELAXED,
++          elements: Sanitize::Config::RELAXED[:elements] + ['noscript']
++        )
++      )
++    end
++
++    it 'is prevented by removing `<noscript>` elements regardless of the allowlist' do
++      assert_equal(
++        '',
++        @s.fragment(%[<noscript><div id='</noscript>&lt;img src=x onerror=alert(1)&gt; '>])
++      )
++    end
++  end
+ end
+-- 
+2.39.1
+
diff -Nru ruby-sanitize-6.0.0/debian/patches/Forcibly-escape-content-in-unescaped-text-elements-i.patch ruby-sanitize-6.0.0/debian/patches/Forcibly-escape-content-in-unescaped-text-elements-i.patch
--- ruby-sanitize-6.0.0/debian/patches/Forcibly-escape-content-in-unescaped-text-elements-i.patch	1970-01-01 01:00:00.000000000 +0100
+++ ruby-sanitize-6.0.0/debian/patches/Forcibly-escape-content-in-unescaped-text-elements-i.patch	2023-02-20 20:27:46.000000000 +0100
@@ -0,0 +1,241 @@
+From: Ryan Grove <r...@wonko.com>
+Date: Wed, 25 Jan 2023 17:42:24 -0800
+Subject: Forcibly escape content in "unescaped text" elements inside math or
+ svg namespaces
+Origin: https://github.com/rgrove/sanitize/commit/b4ee521df0d0616340c9648444be488381c238b1
+
+This fixes an edge case in which the contents of an "unescaped text"
+element (such as `<noembed>` or `<xmp>`) were not properly escaped if
+that element was allowlisted and was also inside an allowlisted `<math>`
+or `<svg>` element.
+
+The only way to encounter this situation was to ignore multiple warnings
+in the readme and create a custom config that allowlisted all the
+elements involved, including `<math>` or `<svg>`.
+
+Please let this be a reminder that Sanitize cannot safely sanitize
+MathML or SVG content and does not support this use case. The default
+configs don't allow MathML or SVG elements, and allowlisting MathML or
+SVG elements in a custom config may create a security vulnerability in
+your application.
+
+Documentation has been updated to add more warnings and to make the
+existing warnings about this more prominent.
+---
+ HISTORY.md                                 | 25 +++++++
+ README.md                                  | 19 ++---
+ lib/sanitize/config/default.rb             |  5 ++
+ lib/sanitize/transformers/clean_element.rb | 35 +++++++++
+ test/test_malicious_html.rb                | 86 ++++++++++++++++++++++
+ 5 files changed, 161 insertions(+), 9 deletions(-)
+
+diff --git a/README.md b/README.md
+index 0f1f3fbfb352..0f6efb56fde0 100644
+--- a/README.md
++++ b/README.md
+@@ -72,10 +72,11 @@ Sanitize can sanitize the following types of input:
+ * Standalone CSS stylesheets
+ * Standalone CSS properties
+ 
+-However, please note that Sanitize _cannot_ fully sanitize the contents of
+-`<math>` or `<svg>` elements, since these elements don't follow the same parsing
+-rules as the rest of HTML. If this is something you need, you may want to look
+-for another solution.
++> **Warning**
++>
++> Sanitize cannot fully sanitize the contents of `<math>` or `<svg>` elements. MathML and SVG elements are [foreign elements](https://html.spec.whatwg.org/multipage/syntax.html#foreign-elements) that don't follow normal HTML parsing rules.
++>
++> By default, Sanitize will remove all MathML and SVG elements. If you add MathML or SVG elements to a custom element allowlist, you may create a security vulnerability in your application.
+ 
+ ### HTML Fragments
+ 
+@@ -420,11 +421,11 @@ elements not in this array will be removed.
+ ]
+ ```
+ 
+-**Warning:** Sanitize cannot fully sanitize the contents of `<math>` or `<svg>`
+-elements, since these elements don't follow the same parsing rules as the rest
+-of HTML. If you add `math` or `svg` to the allowlist, you must assume that any
+-content inside them will be allowed, even if that content would otherwise be
+-removed by Sanitize.
++> **Warning**
++>
++> Sanitize cannot fully sanitize the contents of `<math>` or `<svg>` elements. MathML and SVG elements are [foreign elements](https://html.spec.whatwg.org/multipage/syntax.html#foreign-elements) that don't follow normal HTML parsing rules.
++>
++> By default, Sanitize will remove all MathML and SVG elements. If you add MathML or SVG elements to a custom element allowlist, you must assume that any content inside them will be allowed, even if that content would otherwise be removed or escaped by Sanitize. This may create a security vulnerability in your application.
+ 
+ #### :parser_options (Hash)
+ 
+diff --git a/lib/sanitize/config/default.rb b/lib/sanitize/config/default.rb
+index 4dd1d22d01f7..d22c1e84c326 100644
+--- a/lib/sanitize/config/default.rb
++++ b/lib/sanitize/config/default.rb
+@@ -54,6 +54,11 @@ class Sanitize
+ 
+       # HTML elements to allow. By default, no elements are allowed (which means
+       # that all HTML will be stripped).
++      #
++      # Warning: Sanitize cannot safely sanitize the contents of foreign
++      # elements (elements in the MathML or SVG namespaces). Do not add `math`
++      # or `svg` to this list! If you do, you may create a security
++      # vulnerability in your application.
+       :elements => [],
+ 
+       # HTML parsing options to pass to Nokogumbo.
+diff --git a/lib/sanitize/transformers/clean_element.rb b/lib/sanitize/transformers/clean_element.rb
+index a73994b0f0a8..a2bacd8198f7 100644
+--- a/lib/sanitize/transformers/clean_element.rb
++++ b/lib/sanitize/transformers/clean_element.rb
+@@ -1,5 +1,6 @@
+ # encoding: utf-8
+ 
++require 'cgi'
+ require 'set'
+ 
+ class Sanitize; module Transformers; class CleanElement
+@@ -18,6 +19,18 @@ class Sanitize; module Transformers; class CleanElement
+   # http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#embedding-custom-non-visible-data-with-the-data-*-attributes
+   REGEX_DATA_ATTR = /\Adata-(?!xml)[a-z_][\w.\u00E0-\u00F6\u00F8-\u017F\u01DD-\u02AF-]*\z/u
+ 
++  # Elements whose content is treated as unescaped text by HTML parsers.
++  UNESCAPED_TEXT_ELEMENTS = Set.new(%w[
++    iframe
++    noembed
++    noframes
++    noscript
++    plaintext
++    script
++    style
++    xmp
++  ])
++
+   # Attributes that need additional escaping on `<a>` elements due to unsafe
+   # libxml2 behavior.
+   UNSAFE_LIBXML_ATTRS_A = Set.new(%w[
+@@ -185,6 +198,28 @@ class Sanitize; module Transformers; class CleanElement
+       @add_attributes[name].each {|key, val| node[key] = val }
+     end
+ 
++    # Make a best effort to ensure that text nodes in invalid "unescaped text"
++    # elements that are inside a math or svg namespace are properly escaped so
++    # that they don't get parsed as HTML.
++    #
++    # Sanitize is explicitly documented as not supporting MathML or SVG, but
++    # people sometimes allow `<math>` and `<svg>` elements in their custom
++    # configs without realizing that it's not safe. This workaround makes it
++    # slightly less unsafe, but you still shouldn't allow `<math>` or `<svg>`
++    # because Nokogiri doesn't parse them the same way browsers do and Sanitize
++    # can't guarantee that their contents are safe.
++    unless node.namespace.nil?
++      prefix = node.namespace.prefix
++
++      if (prefix == 'math' || prefix == 'svg') && UNESCAPED_TEXT_ELEMENTS.include?(name)
++        node.children.each do |child|
++          if child.type == Nokogiri::XML::Node::TEXT_NODE
++            child.content = CGI.escapeHTML(child.content)
++          end
++        end
++      end
++    end
++
+     # Element-specific special cases.
+     case name
+ 
+diff --git a/test/test_malicious_html.rb b/test/test_malicious_html.rb
+index d0fc57bbc2ad..377e6deb0fb5 100644
+--- a/test/test_malicious_html.rb
++++ b/test/test_malicious_html.rb
+@@ -232,4 +232,90 @@ describe 'Malicious HTML' do
+       end
+     end
+   end
++
++  # These tests cover an unsupported and unsafe custom config that allows MathML
++  # and SVG elements, which Sanitize's docs specifically say multiple times in
++  # big prominent warnings that you SHOULD NOT DO because Sanitize doesn't
++  # support MathML or SVG.
++  #
++  # Do not use the custom configs you see in these tests! If you do, you may be
++  # creating XSS vulnerabilities in your application.
++  describe 'foreign content bypass in unsafe custom config that allows MathML or SVG' do
++    unescaped_content_elements = %w[
++      noembed
++      noframes
++      noscript
++      plaintext
++      script
++      xmp
++    ]
++
++    removed_content_elements = %w[
++      iframe
++    ]
++
++    removed_elements = %w[
++      style
++    ]
++
++    before do
++      @s = Sanitize.new(
++        Sanitize::Config.merge(
++          Sanitize::Config::RELAXED,
++          elements: Sanitize::Config::RELAXED[:elements] +
++            unescaped_content_elements +
++            removed_content_elements +
++            %w[math svg]
++        )
++      )
++    end
++
++    unescaped_content_elements.each do |name|
++      it "forcibly escapes text content inside `<#{name}>` in a MathML namespace" do
++        assert_equal(
++          "<math><#{name}>&lt;img src=x onerror=alert(1)&gt;</#{name}></math>",
++          @s.fragment("<math><#{name}>&lt;img src=x onerror=alert(1)&gt;</#{name}>")
++        )
++      end
++
++      it "forcibly escapes text content inside `<#{name}>` in an SVG namespace" do
++        assert_equal(
++          "<svg><#{name}>&lt;img src=x onerror=alert(1)&gt;</#{name}></svg>",
++          @s.fragment("<svg><#{name}>&lt;img src=x onerror=alert(1)&gt;</#{name}>")
++        )
++      end
++    end
++
++    removed_content_elements.each do |name|
++      it "removes text content inside `<#{name}>` in a MathML namespace" do
++        assert_equal(
++          "<math><#{name}></#{name}></math>",
++          @s.fragment("<math><#{name}>&lt;img src=x onerror=alert(1)&gt;</#{name}>")
++        )
++      end
++
++      it "removes text content inside `<#{name}>` in an SVG namespace" do
++        assert_equal(
++          "<svg><#{name}></#{name}></svg>",
++          @s.fragment("<svg><#{name}>&lt;img src=x onerror=alert(1)&gt;</#{name}>")
++        )
++      end
++    end
++
++    removed_elements.each do |name|
++      it "removes `<#{name}>` elements in a MathML namespace" do
++        assert_equal(
++          '<math></math>',
++          @s.fragment("<math><#{name}>&lt;img src=x onerror=alert(1)&gt;</#{name}>")
++        )
++      end
++
++      it "removes `<#{name}>` elements in an SVG namespace" do
++        assert_equal(
++          '<svg></svg>',
++          @s.fragment("<svg><#{name}>&lt;img src=x onerror=alert(1)&gt;</#{name}>")
++        )
++      end
++    end
++  end
+ end
+-- 
+2.39.1
+
diff -Nru ruby-sanitize-6.0.0/debian/patches/Update-tests-to-remove-deprecated-minitest-must_be.patch ruby-sanitize-6.0.0/debian/patches/Update-tests-to-remove-deprecated-minitest-must_be.patch
--- ruby-sanitize-6.0.0/debian/patches/Update-tests-to-remove-deprecated-minitest-must_be.patch	1970-01-01 01:00:00.000000000 +0100
+++ ruby-sanitize-6.0.0/debian/patches/Update-tests-to-remove-deprecated-minitest-must_be.patch	2023-02-20 20:27:46.000000000 +0100
@@ -0,0 +1,1672 @@
+From: Roman Vakulchik <rvakulc...@gmail.com>
+Date: Fri, 20 Aug 2021 16:43:30 +0200
+Subject: Update tests to remove deprecated minitest 'must_be'
+Origin: https://github.com/rgrove/sanitize/commit/a37a51b583fa4a97486d47e32625dae1baf0574c
+
+---
+ .github/workflows/tests.yml |   2 +-
+ test/test_clean_comment.rb  |  32 +++----
+ test/test_clean_css.rb      |  10 +-
+ test/test_clean_doctype.rb  |  30 +++---
+ test/test_clean_element.rb  | 184 ++++++++++++++++++------------------
+ test/test_config.rb         |  18 ++--
+ test/test_malicious_css.rb  |  14 +--
+ test/test_malicious_html.rb |  62 ++++++------
+ test/test_parser.rb         |  16 ++--
+ test/test_sanitize.rb       |  48 +++++-----
+ test/test_sanitize_css.rb   | 106 ++++++++++-----------
+ test/test_transformers.rb   |  74 +++++++--------
+ 12 files changed, 298 insertions(+), 298 deletions(-)
+
+diff --git a/test/test_clean_comment.rb b/test/test_clean_comment.rb
+index d118c827e438..a02c76c46b2f 100644
+--- a/test/test_clean_comment.rb
++++ b/test/test_clean_comment.rb
+@@ -11,18 +11,18 @@ describe 'Sanitize::Transformers::CleanComment' do
+     end
+ 
+     it 'should remove comments' do
+-      @s.fragment('foo <!-- comment --> bar').must_equal 'foo  bar'
+-      @s.fragment('foo <!-- ').must_equal 'foo '
+-      @s.fragment('foo <!-- - -> bar').must_equal 'foo '
+-      @s.fragment("foo <!--\n\n\n\n-->bar").must_equal 'foo bar'
+-      @s.fragment("foo <!-- <!-- <!-- --> --> -->bar").must_equal 'foo  --&gt; --&gt;bar'
+-      @s.fragment("foo <div <!-- comment -->>bar</div>").must_equal 'foo <div>&gt;bar</div>'
++      _(@s.fragment('foo <!-- comment --> bar')).must_equal 'foo  bar'
++      _(@s.fragment('foo <!-- ')).must_equal 'foo '
++      _(@s.fragment('foo <!-- - -> bar')).must_equal 'foo '
++      _(@s.fragment("foo <!--\n\n\n\n-->bar")).must_equal 'foo bar'
++      _(@s.fragment("foo <!-- <!-- <!-- --> --> -->bar")).must_equal 'foo  --&gt; --&gt;bar'
++      _(@s.fragment("foo <div <!-- comment -->>bar</div>")).must_equal 'foo <div>&gt;bar</div>'
+ 
+       # Special case: the comment markup is inside a <script>, which makes it
+       # text content and not an actual HTML comment.
+-      @s.fragment("<script><!-- comment --></script>").must_equal ''
++      _(@s.fragment("<script><!-- comment --></script>")).must_equal ''
+ 
+-      Sanitize.fragment("<script><!-- comment --></script>", :allow_comments => false, :elements => ['script'])
++      _(Sanitize.fragment("<script><!-- comment --></script>", :allow_comments => false, :elements => ['script']))
+         .must_equal '<script><!-- comment --></script>'
+     end
+   end
+@@ -33,14 +33,14 @@ describe 'Sanitize::Transformers::CleanComment' do
+     end
+ 
+     it 'should allow comments' do
+-      @s.fragment('foo <!-- comment --> bar').must_equal 'foo <!-- comment --> bar'
+-      @s.fragment('foo <!-- ').must_equal 'foo <!-- -->'
+-      @s.fragment('foo <!-- - -> bar').must_equal 'foo <!-- - -> bar-->'
+-      @s.fragment("foo <!--\n\n\n\n-->bar").must_equal "foo <!--\n\n\n\n-->bar"
+-      @s.fragment("foo <!-- <!-- <!-- --> --> -->bar").must_equal 'foo <!-- <!-- <!-- --> --&gt; --&gt;bar'
+-      @s.fragment("foo <div <!-- comment -->>bar</div>").must_equal 'foo <div>&gt;bar</div>'
+-
+-      Sanitize.fragment("<script><!-- comment --></script>", :allow_comments => true, :elements => ['script'])
++      _(@s.fragment('foo <!-- comment --> bar')).must_equal 'foo <!-- comment --> bar'
++      _(@s.fragment('foo <!-- ')).must_equal 'foo <!-- -->'
++      _(@s.fragment('foo <!-- - -> bar')).must_equal 'foo <!-- - -> bar-->'
++      _(@s.fragment("foo <!--\n\n\n\n-->bar")).must_equal "foo <!--\n\n\n\n-->bar"
++      _(@s.fragment("foo <!-- <!-- <!-- --> --> -->bar")).must_equal 'foo <!-- <!-- <!-- --> --&gt; --&gt;bar'
++      _(@s.fragment("foo <div <!-- comment -->>bar</div>")).must_equal 'foo <div>&gt;bar</div>'
++
++      _(Sanitize.fragment("<script><!-- comment --></script>", :allow_comments => true, :elements => ['script']))
+         .must_equal '<script><!-- comment --></script>'
+     end
+   end
+diff --git a/test/test_clean_css.rb b/test/test_clean_css.rb
+index 4bffaee60059..d10c943d44f1 100644
+--- a/test/test_clean_css.rb
++++ b/test/test_clean_css.rb
+@@ -10,15 +10,15 @@ describe 'Sanitize::Transformers::CSS::CleanAttribute' do
+   end
+ 
+   it 'should sanitize CSS properties in style attributes' do
+-    @s.fragment(%[
++    _(@s.fragment(%[
+       <div style="color: #fff; width: expression(alert(1)); /* <-- evil! */"></div>
+-    ].strip).must_equal %[
++    ].strip)).must_equal %[
+       <div style="color: #fff;  /* <-- evil! */"></div>
+     ].strip
+   end
+ 
+   it 'should remove the style attribute if the sanitized CSS is empty' do
+-    @s.fragment('<div style="width: expression(alert(1))"></div>').
++    _(@s.fragment('<div style="width: expression(alert(1))"></div>')).
+       must_equal '<div></div>'
+   end
+ end
+@@ -46,7 +46,7 @@ describe 'Sanitize::Transformers::CSS::CleanElement' do
+       </style>
+     ].strip
+ 
+-    @s.fragment(html).must_equal %[
++    _(@s.fragment(html)).must_equal %[
+       <style>
+       /* Yay CSS! */
+       .foo { color: #fff; }
+@@ -62,6 +62,6 @@ describe 'Sanitize::Transformers::CSS::CleanElement' do
+   end
+ 
+   it 'should remove the <style> element if the sanitized CSS is empty' do
+-    @s.fragment('<style></style>').must_equal ''
++    _(@s.fragment('<style></style>')).must_equal ''
+   end
+ end
+diff --git a/test/test_clean_doctype.rb b/test/test_clean_doctype.rb
+index f474ceaf3b7e..9ad356171cc2 100644
+--- a/test/test_clean_doctype.rb
++++ b/test/test_clean_doctype.rb
+@@ -11,18 +11,18 @@ describe 'Sanitize::Transformers::CleanDoctype' do
+     end
+ 
+     it 'should remove doctype declarations' do
+-      @s.document('<!DOCTYPE html><html>foo</html>').must_equal "<html>foo</html>"
+-      @s.fragment('<!DOCTYPE html>foo').must_equal 'foo'
++      _(@s.document('<!DOCTYPE html><html>foo</html>')).must_equal "<html>foo</html>"
++      _(@s.fragment('<!DOCTYPE html>foo')).must_equal 'foo'
+     end
+ 
+     it 'should not allow doctype definitions in fragments' do
+-      @s.fragment('<!DOCTYPE html><html>foo</html>')
++      _(@s.fragment('<!DOCTYPE html><html>foo</html>'))
+         .must_equal "foo"
+ 
+-      @s.fragment('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"><html>foo</html>')
++      _(@s.fragment('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"><html>foo</html>'))
+         .must_equal "foo"
+ 
+-      @s.fragment("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\";><html>foo</html>")
++      _(@s.fragment("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\";><html>foo</html>"))
+         .must_equal "foo"
+     end
+   end
+@@ -33,38 +33,38 @@ describe 'Sanitize::Transformers::CleanDoctype' do
+     end
+ 
+     it 'should allow doctype declarations in documents' do
+-      @s.document('<!DOCTYPE html><html>foo</html>')
++      _(@s.document('<!DOCTYPE html><html>foo</html>'))
+         .must_equal "<!DOCTYPE html><html>foo</html>"
+ 
+-      @s.document('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"><html>foo</html>')
++      _(@s.document('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"><html>foo</html>'))
+         .must_equal "<!DOCTYPE html><html>foo</html>"
+ 
+-      @s.document("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\";><html>foo</html>")
++      _(@s.document("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\";><html>foo</html>"))
+         .must_equal "<!DOCTYPE html><html>foo</html>"
+     end
+ 
+     it 'should not allow obviously invalid doctype declarations in documents' do
+-      @s.document('<!DOCTYPE blah blah blah><html>foo</html>')
++      _(@s.document('<!DOCTYPE blah blah blah><html>foo</html>'))
+         .must_equal "<!DOCTYPE html><html>foo</html>"
+ 
+-      @s.document('<!DOCTYPE blah><html>foo</html>')
++      _(@s.document('<!DOCTYPE blah><html>foo</html>'))
+         .must_equal "<!DOCTYPE html><html>foo</html>"
+ 
+-      @s.document('<!DOCTYPE html BLAH "-//W3C//DTD HTML 4.01//EN"><html>foo</html>')
++      _(@s.document('<!DOCTYPE html BLAH "-//W3C//DTD HTML 4.01//EN"><html>foo</html>'))
+         .must_equal "<!DOCTYPE html><html>foo</html>"
+ 
+-      @s.document('<!whatever><html>foo</html>')
++      _(@s.document('<!whatever><html>foo</html>'))
+         .must_equal "<html>foo</html>"
+     end
+ 
+     it 'should not allow doctype definitions in fragments' do
+-      @s.fragment('<!DOCTYPE html><html>foo</html>')
++      _(@s.fragment('<!DOCTYPE html><html>foo</html>'))
+         .must_equal "foo"
+ 
+-      @s.fragment('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"><html>foo</html>')
++      _(@s.fragment('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"><html>foo</html>'))
+         .must_equal "foo"
+ 
+-      @s.fragment("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\";><html>foo</html>")
++      _(@s.fragment("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\";><html>foo</html>"))
+         .must_equal "foo"
+     end
+   end
+diff --git a/test/test_clean_element.rb b/test/test_clean_element.rb
+index 5b3299c193b2..1293cea17ba2 100644
+--- a/test/test_clean_element.rb
++++ b/test/test_clean_element.rb
+@@ -163,94 +163,94 @@ describe 'Sanitize::Transformers::CleanElement' do
+ 
+   describe 'Default config' do
+     it 'should remove non-allowlisted elements, leaving safe contents behind' do
+-      Sanitize.fragment('foo <b>bar</b> <strong><a href="#a">baz</a></strong> quux')
++      _(Sanitize.fragment('foo <b>bar</b> <strong><a href="#a">baz</a></strong> quux'))
+         .must_equal 'foo bar baz quux'
+ 
+-      Sanitize.fragment('<script>alert("<xss>");</script>')
++      _(Sanitize.fragment('<script>alert("<xss>");</script>'))
+         .must_equal ''
+ 
+-      Sanitize.fragment('<<script>script>alert("<xss>");</<script>>')
++      _(Sanitize.fragment('<<script>script>alert("<xss>");</<script>>'))
+         .must_equal '&lt;'
+ 
+-      Sanitize.fragment('< script <>> alert("<xss>");</script>')
++      _(Sanitize.fragment('< script <>> alert("<xss>");</script>'))
+         .must_equal '&lt; script &lt;&gt;&gt; alert("");'
+     end
+ 
+     it 'should surround the contents of :whitespace_elements with space characters when removing the element' do
+-      Sanitize.fragment('foo<div>bar</div>baz')
++      _(Sanitize.fragment('foo<div>bar</div>baz'))
+         .must_equal 'foo bar baz'
+ 
+-      Sanitize.fragment('foo<br>bar<br>baz')
++      _(Sanitize.fragment('foo<br>bar<br>baz'))
+         .must_equal 'foo bar baz'
+ 
+-      Sanitize.fragment('foo<hr>bar<hr>baz')
++      _(Sanitize.fragment('foo<hr>bar<hr>baz'))
+         .must_equal 'foo bar baz'
+     end
+ 
+     it 'should not choke on several instances of the same element in a row' do
+-      Sanitize.fragment('<img src="http://www.google.com/intl/en_ALL/images/logo.gif";><img src="http://www.google.com/intl/en_ALL/images/logo.gif";><img src="http://www.google.com/intl/en_ALL/images/logo.gif";><img src="http://www.google.com/intl/en_ALL/images/logo.gif";>')
++      _(Sanitize.fragment('<img src="http://www.google.com/intl/en_ALL/images/logo.gif";><img src="http://www.google.com/intl/en_ALL/images/logo.gif";><img src="http://www.google.com/intl/en_ALL/images/logo.gif";><img src="http://www.google.com/intl/en_ALL/images/logo.gif";>'))
+         .must_equal ''
+     end
+ 
+     it 'should not preserve the content of removed `iframe` elements' do
+-      Sanitize.fragment('<iframe>hello! <script>alert(0)</script></iframe>')
++      _(Sanitize.fragment('<iframe>hello! <script>alert(0)</script></iframe>'))
+         .must_equal ''
+     end
+ 
+     it 'should not preserve the content of removed `math` elements' do
+-      Sanitize.fragment('<math>hello! <script>alert(0)</script></math>')
++      _(Sanitize.fragment('<math>hello! <script>alert(0)</script></math>'))
+         .must_equal ''
+     end
+ 
+     it 'should not preserve the content of removed `noembed` elements' do
+-      Sanitize.fragment('<noembed>hello! <script>alert(0)</script></noembed>')
++      _(Sanitize.fragment('<noembed>hello! <script>alert(0)</script></noembed>'))
+         .must_equal ''
+     end
+ 
+     it 'should not preserve the content of removed `noframes` elements' do
+-      Sanitize.fragment('<noframes>hello! <script>alert(0)</script></noframes>')
++      _(Sanitize.fragment('<noframes>hello! <script>alert(0)</script></noframes>'))
+         .must_equal ''
+     end
+ 
+     it 'should not preserve the content of removed `noscript` elements' do
+-      Sanitize.fragment('<noscript>hello! <script>alert(0)</script></noscript>')
++      _(Sanitize.fragment('<noscript>hello! <script>alert(0)</script></noscript>'))
+         .must_equal ''
+     end
+ 
+     it 'should not preserve the content of removed `plaintext` elements' do
+-      Sanitize.fragment('<plaintext>hello! <script>alert(0)</script>')
++      _(Sanitize.fragment('<plaintext>hello! <script>alert(0)</script>'))
+         .must_equal ''
+     end
+ 
+     it 'should not preserve the content of removed `script` elements' do
+-      Sanitize.fragment('<script>hello! <script>alert(0)</script></script>')
++      _(Sanitize.fragment('<script>hello! <script>alert(0)</script></script>'))
+         .must_equal ''
+     end
+ 
+     it 'should not preserve the content of removed `style` elements' do
+-      Sanitize.fragment('<style>hello! <script>alert(0)</script></style>')
++      _(Sanitize.fragment('<style>hello! <script>alert(0)</script></style>'))
+         .must_equal ''
+     end
+ 
+     it 'should not preserve the content of removed `svg` elements' do
+-      Sanitize.fragment('<svg>hello! <script>alert(0)</script></svg>')
++      _(Sanitize.fragment('<svg>hello! <script>alert(0)</script></svg>'))
+         .must_equal ''
+     end
+ 
+     it 'should not preserve the content of removed `xmp` elements' do
+-      Sanitize.fragment('<xmp>hello! <script>alert(0)</script></xmp>')
++      _(Sanitize.fragment('<xmp>hello! <script>alert(0)</script></xmp>'))
+         .must_equal ''
+     end
+ 
+     strings.each do |name, data|
+       it "should clean #{name} HTML" do
+-        Sanitize.fragment(data[:html]).must_equal(data[:default])
++        _(Sanitize.fragment(data[:html])).must_equal(data[:default])
+       end
+     end
+ 
+     protocols.each do |name, data|
+       it "should not allow #{name}" do
+-        Sanitize.fragment(data[:html]).must_equal(data[:default])
++        _(Sanitize.fragment(data[:html])).must_equal(data[:default])
+       end
+     end
+   end
+@@ -262,13 +262,13 @@ describe 'Sanitize::Transformers::CleanElement' do
+ 
+     strings.each do |name, data|
+       it "should clean #{name} HTML" do
+-        @s.fragment(data[:html]).must_equal(data[:restricted])
++        _(@s.fragment(data[:html])).must_equal(data[:restricted])
+       end
+     end
+ 
+     protocols.each do |name, data|
+       it "should not allow #{name}" do
+-        @s.fragment(data[:html]).must_equal(data[:restricted])
++        _(@s.fragment(data[:html])).must_equal(data[:restricted])
+       end
+     end
+   end
+@@ -279,24 +279,24 @@ describe 'Sanitize::Transformers::CleanElement' do
+     end
+ 
+     it 'should not choke on valueless attributes' do
+-      @s.fragment('foo <a href>foo</a> bar')
++      _(@s.fragment('foo <a href>foo</a> bar'))
+         .must_equal 'foo <a href="" rel="nofollow">foo</a> bar'
+     end
+ 
+     it 'should downcase attribute names' do
+-      @s.fragment('<a HREF="javascript:alert(\'foo\')">bar</a>')
++      _(@s.fragment('<a HREF="javascript:alert(\'foo\')">bar</a>'))
+         .must_equal '<a rel="nofollow">bar</a>'
+     end
+ 
+     strings.each do |name, data|
+       it "should clean #{name} HTML" do
+-        @s.fragment(data[:html]).must_equal(data[:basic])
++        _(@s.fragment(data[:html])).must_equal(data[:basic])
+       end
+     end
+ 
+     protocols.each do |name, data|
+       it "should not allow #{name}" do
+-        @s.fragment(data[:html]).must_equal(data[:basic])
++        _(@s.fragment(data[:html])).must_equal(data[:basic])
+       end
+     end
+   end
+@@ -307,19 +307,19 @@ describe 'Sanitize::Transformers::CleanElement' do
+     end
+ 
+     it 'should encode special chars in attribute values' do
+-      @s.fragment('<a href="http://example.com"; title="<b>&eacute;xamples</b> & things">foo</a>')
++      _(@s.fragment('<a href="http://example.com"; title="<b>&eacute;xamples</b> & things">foo</a>'))
+         .must_equal '<a href="http://example.com"; title="<b>??xamples</b> &amp; things">foo</a>'
+     end
+ 
+     strings.each do |name, data|
+       it "should clean #{name} HTML" do
+-        @s.fragment(data[:html]).must_equal(data[:relaxed])
++        _(@s.fragment(data[:html])).must_equal(data[:relaxed])
+       end
+     end
+ 
+     protocols.each do |name, data|
+       it "should not allow #{name}" do
+-        @s.fragment(data[:html]).must_equal(data[:relaxed])
++        _(@s.fragment(data[:html])).must_equal(data[:relaxed])
+       end
+     end
+   end
+@@ -328,103 +328,103 @@ describe 'Sanitize::Transformers::CleanElement' do
+     it 'should allow attributes on all elements if allowlisted under :all' do
+       input = '<p class="foo">bar</p>'
+ 
+-      Sanitize.fragment(input).must_equal ' bar '
++      _(Sanitize.fragment(input)).must_equal ' bar '
+ 
+-      Sanitize.fragment(input, {
++      _(Sanitize.fragment(input, {
+         :elements   => ['p'],
+         :attributes => {:all => ['class']}
+-      }).must_equal input
++      })).must_equal input
+ 
+-      Sanitize.fragment(input, {
++      _(Sanitize.fragment(input, {
+         :elements   => ['p'],
+         :attributes => {'div' => ['class']}
+-      }).must_equal '<p>bar</p>'
++      })).must_equal '<p>bar</p>'
+ 
+-      Sanitize.fragment(input, {
++      _(Sanitize.fragment(input, {
+         :elements   => ['p'],
+         :attributes => {'p' => ['title'], :all => ['class']}
+-      }).must_equal input
++      })).must_equal input
+     end
+ 
+     it "should not allow relative URLs when relative URLs aren't allowlisted" do
+       input = '<a href="/foo/bar">Link</a>'
+ 
+-      Sanitize.fragment(input,
++      _(Sanitize.fragment(input,
+         :elements   => ['a'],
+         :attributes => {'a' => ['href']},
+         :protocols  => {'a' => {'href' => ['http']}}
+-      ).must_equal '<a>Link</a>'
++      )).must_equal '<a>Link</a>'
+     end
+ 
+     it 'should allow relative URLs containing colons when the colon is not in the first path segment' do
+       input = '<a href="/wiki/Special:Random">Random Page</a>'
+ 
+-      Sanitize.fragment(input, {
++      _(Sanitize.fragment(input, {
+         :elements   => ['a'],
+         :attributes => {'a' => ['href']},
+         :protocols  => {'a' => {'href' => [:relative]}}
+-      }).must_equal input
++      })).must_equal input
+     end
+ 
+     it 'should allow relative URLs containing colons when the colon is part of an anchor' do
+       input = '<a href="#fn:1">Footnote 1</a>'
+ 
+-      Sanitize.fragment(input, {
++      _(Sanitize.fragment(input, {
+         :elements   => ['a'],
+         :attributes => {'a' => ['href']},
+         :protocols  => {'a' => {'href' => [:relative]}}
+-      }).must_equal input
++      })).must_equal input
+ 
+       input = '<a href="somepage#fn:1">Footnote 1</a>'
+ 
+-      Sanitize.fragment(input, {
++      _(Sanitize.fragment(input, {
+         :elements   => ['a'],
+         :attributes => {'a' => ['href']},
+         :protocols  => {'a' => {'href' => [:relative]}}
+-      }).must_equal input
++      })).must_equal input
+     end
+ 
+     it 'should remove the contents of filtered nodes when :remove_contents is true' do
+-      Sanitize.fragment('foo bar <div>baz<span>quux</span></div>',
++      _(Sanitize.fragment('foo bar <div>baz<span>quux</span></div>',
+         :remove_contents => true
+-      ).must_equal 'foo bar   '
++      )).must_equal 'foo bar   '
+     end
+ 
+     it 'should remove the contents of specified nodes when :remove_contents is an Array or Set of element names as strings' do
+-      Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
++      _(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
+         :remove_contents => ['script', 'span']
+-      ).must_equal 'foo bar  baz hi '
++      )).must_equal 'foo bar  baz hi '
+ 
+-      Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
++      _(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
+         :remove_contents => Set.new(['script', 'span'])
+-      ).must_equal 'foo bar  baz hi '
++      )).must_equal 'foo bar  baz hi '
+     end
+ 
+     it 'should remove the contents of specified nodes when :remove_contents is an Array or Set of element names as symbols' do
+-      Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
++      _(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
+         :remove_contents => [:script, :span]
+-      ).must_equal 'foo bar  baz hi '
++      )).must_equal 'foo bar  baz hi '
+ 
+-      Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
++      _(Sanitize.fragment('foo bar <div>baz<span>quux</span> <b>hi</b><script>alert("hello!");</script></div>',
+         :remove_contents => Set.new([:script, :span])
+-      ).must_equal 'foo bar  baz hi '
++      )).must_equal 'foo bar  baz hi '
+     end
+ 
+     it 'should remove the contents of allowlisted iframes' do
+-      Sanitize.fragment('<iframe>hi <script>hello</script></iframe>',
++      _(Sanitize.fragment('<iframe>hi <script>hello</script></iframe>',
+         :elements => ['iframe']
+-      ).must_equal '<iframe></iframe>'
++      )).must_equal '<iframe></iframe>'
+     end
+ 
+     it 'should not allow arbitrary HTML5 data attributes by default' do
+-      Sanitize.fragment('<b data-foo="bar"></b>',
++      _(Sanitize.fragment('<b data-foo="bar"></b>',
+         :elements => ['b']
+-      ).must_equal '<b></b>'
++      )).must_equal '<b></b>'
+ 
+-      Sanitize.fragment('<b class="foo" data-foo="bar"></b>',
++      _(Sanitize.fragment('<b class="foo" data-foo="bar"></b>',
+         :attributes => {'b' => ['class']},
+         :elements   => ['b']
+-      ).must_equal '<b class="foo"></b>'
++      )).must_equal '<b class="foo"></b>'
+     end
+ 
+     it 'should allow arbitrary HTML5 data attributes when the :attributes config includes :data' do
+@@ -433,28 +433,28 @@ describe 'Sanitize::Transformers::CleanElement' do
+         :elements   => ['b']
+       )
+ 
+-      s.fragment('<b data-foo="valid" data-bar="valid"></b>')
++      _(s.fragment('<b data-foo="valid" data-bar="valid"></b>'))
+         .must_equal '<b data-foo="valid" data-bar="valid"></b>'
+ 
+-      s.fragment('<b data-="invalid"></b>')
++      _(s.fragment('<b data-="invalid"></b>'))
+         .must_equal '<b></b>'
+ 
+-      s.fragment('<b data-="invalid"></b>')
++      _(s.fragment('<b data-="invalid"></b>'))
+         .must_equal '<b></b>'
+ 
+-      s.fragment('<b data-xml="invalid"></b>')
++      _(s.fragment('<b data-xml="invalid"></b>'))
+         .must_equal '<b></b>'
+ 
+-      s.fragment('<b data-xmlfoo="invalid"></b>')
++      _(s.fragment('<b data-xmlfoo="invalid"></b>'))
+         .must_equal '<b></b>'
+ 
+-      s.fragment('<b data-f:oo="valid"></b>')
++      _(s.fragment('<b data-f:oo="valid"></b>'))
+         .must_equal '<b></b>'
+ 
+-      s.fragment('<b data-f/oo="partial"></b>')
++      _(s.fragment('<b data-f/oo="partial"></b>'))
+         .must_equal '<b data-f=""></b>' # Nokogiri quirk; not ideal, but harmless
+ 
+-      s.fragment('<b data-??foo="valid"></b>')
++      _(s.fragment('<b data-??foo="valid"></b>'))
+         .must_equal '<b></b>' # Another annoying Nokogiri quirk.
+     end
+ 
+@@ -467,78 +467,78 @@ describe 'Sanitize::Transformers::CleanElement' do
+         }
+       )
+ 
+-      s.fragment('<p>foo</p>').must_equal "\nfoo\n"
+-      s.fragment('<p>foo</p><p>bar</p>').must_equal "\nfoo\n\nbar\n"
+-      s.fragment('foo<div>bar</div>baz').must_equal "foo\nbar\nbaz"
+-      s.fragment('foo<br>bar<br>baz').must_equal "foo\nbar\nbaz"
++      _(s.fragment('<p>foo</p>')).must_equal "\nfoo\n"
++      _(s.fragment('<p>foo</p><p>bar</p>')).must_equal "\nfoo\n\nbar\n"
++      _(s.fragment('foo<div>bar</div>baz')).must_equal "foo\nbar\nbaz"
++      _(s.fragment('foo<br>bar<br>baz')).must_equal "foo\nbar\nbaz"
+     end
+ 
+     it 'should handle protocols correctly regardless of case' do
+       input = '<a href="hTTpS://foo.com/">Text</a>'
+ 
+-      Sanitize.fragment(input, {
++      _(Sanitize.fragment(input, {
+         :elements   => ['a'],
+         :attributes => {'a' => ['href']},
+         :protocols  => {'a' => {'href' => ['https']}}
+-      }).must_equal input
++      })).must_equal input
+ 
+       input = '<a href="mailto:some...@example.com?Subject=Hello">Text</a>'
+ 
+-      Sanitize.fragment(input, {
++      _(Sanitize.fragment(input, {
+         :elements   => ['a'],
+         :attributes => {'a' => ['href']},
+         :protocols  => {'a' => {'href' => ['https']}}
+-      }).must_equal "<a>Text</a>"
++      })).must_equal "<a>Text</a>"
+     end
+ 
+     it 'should sanitize protocols in data attributes even if data attributes are generically allowed' do
+       input = '<a data-url="mailto:some...@example.com">Text</a>'
+ 
+-      Sanitize.fragment(input, {
++      _(Sanitize.fragment(input, {
+         :elements => ['a'],
+         :attributes => {'a' => [:data]},
+         :protocols => {'a' => {'data-url' => ['https']}}
+-      }).must_equal "<a>Text</a>"
++      })).must_equal "<a>Text</a>"
+ 
+-      Sanitize.fragment(input, {
++      _(Sanitize.fragment(input, {
+         :elements => ['a'],
+         :attributes => {'a' => [:data]},
+         :protocols => {'a' => {'data-url' => ['mailto']}}
+-      }).must_equal input
++      })).must_equal input
+     end
+ 
+     it 'should prevent `<meta>` tags from being used to set a non-UTF-8 charset' do
+-      Sanitize.document('<html><head><meta charset="utf-8"></head><body>Howdy!</body></html>',
++      _(Sanitize.document('<html><head><meta charset="utf-8"></head><body>Howdy!</body></html>',
+         :elements   => %w[html head meta body],
+         :attributes => {'meta' => ['charset']}
+-      ).must_equal "<html><head><meta charset=\"utf-8\"></head><body>Howdy!</body></html>"
++      )).must_equal "<html><head><meta charset=\"utf-8\"></head><body>Howdy!</body></html>"
+ 
+-      Sanitize.document('<html><meta charset="utf-8">Howdy!</html>',
++      _(Sanitize.document('<html><meta charset="utf-8">Howdy!</html>',
+         :elements   => %w[html meta],
+         :attributes => {'meta' => ['charset']}
+-      ).must_equal "<html><meta charset=\"utf-8\">Howdy!</html>"
++      )).must_equal "<html><meta charset=\"utf-8\">Howdy!</html>"
+ 
+-      Sanitize.document('<html><meta charset="us-ascii">Howdy!</html>',
++      _(Sanitize.document('<html><meta charset="us-ascii">Howdy!</html>',
+         :elements   => %w[html meta],
+         :attributes => {'meta' => ['charset']}
+-      ).must_equal "<html><meta charset=\"utf-8\">Howdy!</html>"
++      )).must_equal "<html><meta charset=\"utf-8\">Howdy!</html>"
+ 
+-      Sanitize.document('<html><meta http-equiv="content-type" content=" text/html; charset=us-ascii">Howdy!</html>',
++      _(Sanitize.document('<html><meta http-equiv="content-type" content=" text/html; charset=us-ascii">Howdy!</html>',
+         :elements   => %w[html meta],
+         :attributes => {'meta' => %w[content http-equiv]}
+-      ).must_equal "<html><meta http-equiv=\"content-type\" content=\" text/html;charset=utf-8\">Howdy!</html>"
++      )).must_equal "<html><meta http-equiv=\"content-type\" content=\" text/html;charset=utf-8\">Howdy!</html>"
+ 
+-      Sanitize.document('<html><meta http-equiv="Content-Type" content="text/plain;charset = us-ascii">Howdy!</html>',
++      _(Sanitize.document('<html><meta http-equiv="Content-Type" content="text/plain;charset = us-ascii">Howdy!</html>',
+         :elements   => %w[html meta],
+         :attributes => {'meta' => %w[content http-equiv]}
+-      ).must_equal "<html><meta http-equiv=\"Content-Type\" content=\"text/plain;charset=utf-8\">Howdy!</html>"
++      )).must_equal "<html><meta http-equiv=\"Content-Type\" content=\"text/plain;charset=utf-8\">Howdy!</html>"
+     end
+ 
+     it 'should not modify `<meta>` tags that already set a UTF-8 charset' do
+-      Sanitize.document('<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"></head><body>Howdy!</body></html>',
++      _(Sanitize.document('<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"></head><body>Howdy!</body></html>',
+         :elements   => %w[html head meta body],
+         :attributes => {'meta' => %w[content http-equiv]}
+-      ).must_equal "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"></head><body>Howdy!</body></html>"
++      )).must_equal "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"></head><body>Howdy!</body></html>"
+     end
+ 
+   end
+diff --git a/test/test_config.rb b/test/test_config.rb
+index d3a81e31917b..33e7c7166da2 100644
+--- a/test/test_config.rb
++++ b/test/test_config.rb
+@@ -6,7 +6,7 @@ describe 'Config' do
+   parallelize_me!
+ 
+   def verify_deeply_frozen(config)
+-    config.must_be :frozen?
++    _(config).must_be :frozen?
+ 
+     if Hash === config
+       config.each_value {|v| verify_deeply_frozen(v) }
+@@ -27,7 +27,7 @@ describe 'Config' do
+       a = {:one => {:one_one => [0, '1', :a], :one_two => false, :one_three => Set.new([:a, :b, :c])}}
+       b = Sanitize::Config.freeze_config(a)
+ 
+-      b.must_be_same_as a
++      _(b).must_be_same_as a
+       verify_deeply_frozen a
+     end
+   end
+@@ -40,10 +40,10 @@ describe 'Config' do
+ 
+       c = Sanitize::Config.merge(a, b)
+ 
+-      c.wont_be_same_as a
+-      c.wont_be_same_as b
++      _(c).wont_be_same_as a
++      _(c).wont_be_same_as b
+ 
+-      c.must_equal(
++      _(c).must_equal(
+         :one => {
+           :one_one   => [0, '1', :a],
+           :one_two   => true,
+@@ -53,13 +53,13 @@ describe 'Config' do
+         :two => 2
+       )
+ 
+-      c[:one].wont_be_same_as a[:one]
+-      c[:one][:one_one].wont_be_same_as a[:one][:one_one]
++      _(c[:one]).wont_be_same_as a[:one]
++      _(c[:one][:one_one]).wont_be_same_as a[:one][:one_one]
+     end
+ 
+     it 'should raise an ArgumentError if either argument is not a Hash' do
+-      proc { Sanitize::Config.merge('foo', {}) }.must_raise ArgumentError
+-      proc { Sanitize::Config.merge({}, 'foo') }.must_raise ArgumentError
++      _(proc { Sanitize::Config.merge('foo', {}) }).must_raise ArgumentError
++      _(proc { Sanitize::Config.merge({}, 'foo') }).must_raise ArgumentError
+     end
+   end
+ end
+diff --git a/test/test_malicious_css.rb b/test/test_malicious_css.rb
+index a7cc75979165..52c9539992f0 100644
+--- a/test/test_malicious_css.rb
++++ b/test/test_malicious_css.rb
+@@ -16,27 +16,27 @@ describe 'Malicious CSS' do
+   end
+ 
+   it 'should not be possible to inject an expression by munging it with a comment' do
+-    @s.properties(%[width:expr/*XSS*/ession(alert('XSS'))]).
++    _(@s.properties(%[width:expr/*XSS*/ession(alert('XSS'))])).
+       must_equal ''
+ 
+-    @s.properties(%[width:ex/*XSS*//*/*/pression(alert("XSS"))]).
++    _(@s.properties(%[width:ex/*XSS*//*/*/pression(alert("XSS"))])).
+       must_equal ''
+   end
+ 
+   it 'should not be possible to inject an expression by munging it with a newline' do
+-    @s.properties(%[width:\nexpression(alert('XSS'));]).
++    _(@s.properties(%[width:\nexpression(alert('XSS'));])).
+       must_equal ''
+   end
+ 
+   it 'should not allow the javascript protocol' do
+-    @s.properties(%[background-image:url("javascript:alert('XSS')");]).
++    _(@s.properties(%[background-image:url("javascript:alert('XSS')");])).
+       must_equal ''
+ 
+-    Sanitize.fragment(%[<div style="background-image: url(&#1;javascript:alert('XSS'))">],
+-      Sanitize::Config::RELAXED).must_equal '<div></div>'
++    _(Sanitize.fragment(%[<div style="background-image: url(&#1;javascript:alert('XSS'))">],
++      Sanitize::Config::RELAXED)).must_equal '<div></div>'
+   end
+ 
+   it 'should not allow behaviors' do
+-    @s.properties(%[behavior: url(xss.htc);]).must_equal ''
++    _(@s.properties(%[behavior: url(xss.htc);])).must_equal ''
+   end
+ end
+diff --git a/test/test_malicious_html.rb b/test/test_malicious_html.rb
+index 39163b9926d6..d0fc57bbc2ad 100644
+--- a/test/test_malicious_html.rb
++++ b/test/test_malicious_html.rb
+@@ -17,111 +17,111 @@ describe 'Malicious HTML' do
+ 
+   describe 'comments' do
+     it 'should not allow script injection via conditional comments' do
+-      @s.fragment(%[<!--[if gte IE 4]>\n<script>alert('XSS');</script>\n<![endif]-->]).
++      _(@s.fragment(%[<!--[if gte IE 4]>\n<script>alert('XSS');</script>\n<![endif]-->])).
+         must_equal ''
+     end
+   end
+ 
+   describe 'interpolation (ERB, PHP, etc.)' do
+     it 'should escape ERB-style tags' do
+-      @s.fragment('<% naughty_ruby_code %>').
++      _(@s.fragment('<% naughty_ruby_code %>')).
+         must_equal '&lt;% naughty_ruby_code %&gt;'
+ 
+-      @s.fragment('<%= naughty_ruby_code %>').
++      _(@s.fragment('<%= naughty_ruby_code %>')).
+         must_equal '&lt;%= naughty_ruby_code %&gt;'
+     end
+ 
+     it 'should remove PHP-style tags' do
+-      @s.fragment('<? naughtyPHPCode(); ?>').
++      _(@s.fragment('<? naughtyPHPCode(); ?>')).
+         must_equal ''
+ 
+-      @s.fragment('<?= naughtyPHPCode(); ?>').
++      _(@s.fragment('<?= naughtyPHPCode(); ?>')).
+         must_equal ''
+     end
+   end
+ 
+   describe '<body>' do
+     it 'should not be possible to inject JS via a malformed event attribute' do
+-      @s.document('<html><head></head><body onload!#$%&()*~+-_.,:;?@[/|\\]^`=alert("XSS")></body></html>').
++      _(@s.document('<html><head></head><body onload!#$%&()*~+-_.,:;?@[/|\\]^`=alert("XSS")></body></html>')).
+         must_equal "<html><head></head><body></body></html>"
+     end
+   end
+ 
+   describe '<iframe>' do
+     it 'should not be possible to inject an iframe using an improperly closed tag' do
+-      @s.fragment(%[<iframe src=http://ha.ckers.org/scriptlet.html <]).
++      _(@s.fragment(%[<iframe src=http://ha.ckers.org/scriptlet.html <])).
+         must_equal ''
+     end
+   end
+ 
+   describe '<img>' do
+     it 'should not be possible to inject JS via an unquoted <img> src attribute' do
+-      @s.fragment("<img src=javascript:alert('XSS')>").must_equal '<img>'
++      _(@s.fragment("<img src=javascript:alert('XSS')>")).must_equal '<img>'
+     end
+ 
+     it 'should not be possible to inject JS using grave accents as <img> src delimiters' do
+-      @s.fragment("<img src=`javascript:alert('XSS')`>").must_equal '<img>'
++      _(@s.fragment("<img src=`javascript:alert('XSS')`>")).must_equal '<img>'
+     end
+ 
+     it 'should not be possible to inject <script> via a malformed <img> tag' do
+-      @s.fragment('<img """><script>alert("XSS")</script>">').
++      _(@s.fragment('<img """><script>alert("XSS")</script>">')).
+         must_equal '<img>"&gt;'
+     end
+ 
+     it 'should not be possible to inject protocol-based JS' do
+-      @s.fragment('<img src=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>').
++      _(@s.fragment('<img src=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>')).
+         must_equal '<img>'
+ 
+-      @s.fragment('<img src=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>').
++      _(@s.fragment('<img src=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>')).
+         must_equal '<img>'
+ 
+-      @s.fragment('<img src=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>').
++      _(@s.fragment('<img src=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>')).
+         must_equal '<img>'
+ 
+       # Encoded tab character.
+-      @s.fragment(%[<img src="jav&#x09;ascript:alert('XSS');">]).
++      _(@s.fragment(%[<img src="jav&#x09;ascript:alert('XSS');">])).
+         must_equal '<img>'
+ 
+       # Encoded newline.
+-      @s.fragment(%[<img src="jav&#x0A;ascript:alert('XSS');">]).
++      _(@s.fragment(%[<img src="jav&#x0A;ascript:alert('XSS');">])).
+         must_equal '<img>'
+ 
+       # Encoded carriage return.
+-      @s.fragment(%[<img src="jav&#x0D;ascript:alert('XSS');">]).
++      _(@s.fragment(%[<img src="jav&#x0D;ascript:alert('XSS');">])).
+         must_equal '<img>'
+ 
+       # Null byte.
+-      @s.fragment(%[<img src=java\0script:alert("XSS")>]).
++      _(@s.fragment(%[<img src=java\0script:alert("XSS")>])).
+         must_equal '<img>'
+ 
+       # Spaces plus meta char.
+-      @s.fragment(%[<img src=" &#14;  javascript:alert('XSS');">]).
++      _(@s.fragment(%[<img src=" &#14;  javascript:alert('XSS');">])).
+         must_equal '<img>'
+ 
+       # Mixed spaces and tabs.
+-      @s.fragment(%[<img src="j\na v\tascript://alert('XSS');">]).
++      _(@s.fragment(%[<img src="j\na v\tascript://alert('XSS');">])).
+         must_equal '<img>'
+     end
+ 
+     it 'should not be possible to inject protocol-based JS via whitespace' do
+-      @s.fragment(%[<img src="jav\tascript:alert('XSS');">]).
++      _(@s.fragment(%[<img src="jav\tascript:alert('XSS');">])).
+         must_equal '<img>'
+     end
+ 
+     it 'should not be possible to inject JS using a half-open <img> tag' do
+-      @s.fragment(%[<img src="javascript:alert('XSS')"]).
++      _(@s.fragment(%[<img src="javascript:alert('XSS')"])).
+         must_equal ''
+     end
+   end
+ 
+   describe '<script>' do
+     it 'should not be possible to inject <script> using a malformed non-alphanumeric tag name' do
+-      @s.fragment(%[<script/xss src="http://ha.ckers.org/xss.js";>alert(1)</script>]).
++      _(@s.fragment(%[<script/xss src="http://ha.ckers.org/xss.js";>alert(1)</script>])).
+         must_equal ''
+     end
+ 
+     it 'should not be possible to inject <script> via extraneous open brackets' do
+-      @s.fragment(%[<<script>alert("XSS");//<</script>]).
++      _(@s.fragment(%[<<script>alert("XSS");//<</script>])).
+         must_equal '&lt;'
+     end
+   end
+@@ -172,7 +172,7 @@ describe 'Malicious HTML' do
+ 
+           # This uses Nokogumbo's HTML-compliant serializer rather than
+           # libxml2's.
+-          @s.fragment(input).
++          _(@s.fragment(input)).
+             must_equal(%[<#{tag_name} #{attr_name}="examp<!--%22%20onmouseover=alert(1)>-->le.com">foo</#{tag_name}>])
+ 
+           # This uses the not-quite-standards-compliant libxml2 serializer via
+@@ -181,13 +181,13 @@ describe 'Malicious HTML' do
+           # https://github.com/sparklemotion/nokogiri/commit/4852e43cb6039e26d8c51af78621e539cbf46c5d
+           fragment = Nokogiri::HTML.fragment(input)
+           @s.node!(fragment)
+-          fragment.to_html.
++          _(fragment.to_html).
+             must_equal(%[<#{tag_name} #{attr_name}="examp&lt;!--%22%20onmouseover=alert(1)&gt;--&gt;le.com">foo</#{tag_name}>])
+         end
+ 
+         it 'should round-trip to the same output' do
+           output = @s.fragment(input)
+-          @s.fragment(output).must_equal(output)
++          _(@s.fragment(output)).must_equal(output)
+         end
+       end
+ 
+@@ -199,7 +199,7 @@ describe 'Malicious HTML' do
+ 
+           # This uses Nokogumbo's HTML-compliant serializer rather than
+           # libxml2's.
+-          @s.fragment(input).
++          _(@s.fragment(input)).
+             must_equal(%[<#{tag_name} #{attr_name}="examp<!--&quot; onmouseover=alert(1)>-->le.com">foo</#{tag_name}>])
+ 
+           # This uses the not-quite-standards-compliant libxml2 serializer via
+@@ -208,13 +208,13 @@ describe 'Malicious HTML' do
+           # https://github.com/sparklemotion/nokogiri/commit/4852e43cb6039e26d8c51af78621e539cbf46c5d
+           fragment = Nokogiri::HTML.fragment(input)
+           @s.node!(fragment)
+-          fragment.to_html.
++          _(fragment.to_html).
+             must_equal(%[<#{tag_name} #{attr_name}='examp&lt;!--" onmouseover=alert(1)&gt;--&gt;le.com'>foo</#{tag_name}>])
+         end
+ 
+         it 'should round-trip to the same output' do
+           output = @s.fragment(input)
+-          @s.fragment(output).must_equal(output)
++          _(@s.fragment(output)).must_equal(output)
+         end
+       end
+     end
+@@ -224,10 +224,10 @@ describe 'Malicious HTML' do
+   describe 'foreign content bypass in relaxed config' do
+     it 'prevents a sanitization bypass via carefully crafted foreign content' do
+       %w[iframe noembed noframes noscript plaintext script style xmp].each do |tag_name|
+-        @s.fragment(%[<math><#{tag_name}>/*&lt;/#{tag_name}&gt;&lt;img src onerror=alert(1)>*/]).
++        _(@s.fragment(%[<math><#{tag_name}>/*&lt;/#{tag_name}&gt;&lt;img src onerror=alert(1)>*/])).
+           must_equal ''
+ 
+-        @s.fragment(%[<svg><#{tag_name}>/*&lt;/#{tag_name}&gt;&lt;img src onerror=alert(1)>*/]).
++        _(@s.fragment(%[<svg><#{tag_name}>/*&lt;/#{tag_name}&gt;&lt;img src onerror=alert(1)>*/])).
+           must_equal ''
+       end
+     end
+diff --git a/test/test_parser.rb b/test/test_parser.rb
+index c22e38602222..dd68275ede98 100644
+--- a/test/test_parser.rb
++++ b/test/test_parser.rb
+@@ -6,26 +6,26 @@ describe 'Parser' do
+   parallelize_me!
+ 
+   it 'should translate valid entities into characters' do
+-    Sanitize.fragment("&apos;&eacute;&amp;").must_equal("'??&amp;")
++    _(Sanitize.fragment("&apos;&eacute;&amp;")).must_equal("'??&amp;")
+   end
+ 
+   it 'should translate orphaned ampersands into entities' do
+-    Sanitize.fragment('at&t').must_equal('at&amp;t')
++    _(Sanitize.fragment('at&t')).must_equal('at&amp;t')
+   end
+ 
+   it 'should not add newlines after tags when serializing a fragment' do
+-    Sanitize.fragment("<div>foo\n\n<p>bar</p><div>\nbaz</div></div><div>quux</div>", :elements => ['div', 'p'])
++    _(Sanitize.fragment("<div>foo\n\n<p>bar</p><div>\nbaz</div></div><div>quux</div>", :elements => ['div', 'p']))
+       .must_equal "<div>foo\n\n<p>bar</p><div>\nbaz</div></div><div>quux</div>"
+   end
+ 
+   it 'should not have the Nokogiri 1.4.2+ unterminated script/style element bug' do
+-    Sanitize.fragment('foo <script>bar').must_equal 'foo '
+-    Sanitize.fragment('foo <style>bar').must_equal 'foo '
++    _(Sanitize.fragment('foo <script>bar')).must_equal 'foo '
++    _(Sanitize.fragment('foo <style>bar')).must_equal 'foo '
+   end
+ 
+   it 'ambiguous non-tag brackets like "1 > 2 and 2 < 1" should be parsed correctly' do
+-    Sanitize.fragment('1 > 2 and 2 < 1').must_equal '1 &gt; 2 and 2 &lt; 1'
+-    Sanitize.fragment('OMG HAPPY BIRTHDAY! *<:-D').must_equal 'OMG HAPPY BIRTHDAY! *&lt;:-D'
++    _(Sanitize.fragment('1 > 2 and 2 < 1')).must_equal '1 &gt; 2 and 2 &lt; 1'
++    _(Sanitize.fragment('OMG HAPPY BIRTHDAY! *<:-D')).must_equal 'OMG HAPPY BIRTHDAY! *&lt;:-D'
+   end
+ 
+   describe 'when siblings are added after a node during traversal' do
+@@ -59,7 +59,7 @@ describe 'Parser' do
+       })
+ 
+       # All siblings should be traversed, and in the order added.
+-      siblings.must_equal [
++      _(siblings).must_equal [
+         "added_one_one_one",
+         "added_one_one",
+         "added_one_two",
+diff --git a/test/test_sanitize.rb b/test/test_sanitize.rb
+index 2f7c72d63aa5..d9b2789aacde 100644
+--- a/test/test_sanitize.rb
++++ b/test/test_sanitize.rb
+@@ -9,7 +9,7 @@ describe 'Sanitize' do
+       ]
+ 
+       Sanitize.new({ :transformers => transformers })
+-      transformers.length.must_equal(1)
++      _(transformers.length).must_equal(1)
+     end
+   end
+ 
+@@ -24,33 +24,33 @@ describe 'Sanitize' do
+       end
+ 
+       it 'should sanitize an HTML document' do
+-        @s.document('<!doctype html><html><b>Lo<!-- comment -->rem</b> <a href="pants" title="foo">ipsum</a> <a href="http://foo.com/";><strong>dolor</strong></a> sit<br/>amet <script>alert("hello world");</script></html>')
++        _(@s.document('<!doctype html><html><b>Lo<!-- comment -->rem</b> <a href="pants" title="foo">ipsum</a> <a href="http://foo.com/";><strong>dolor</strong></a> sit<br/>amet <script>alert("hello world");</script></html>'))
+           .must_equal "<html>Lorem ipsum dolor sit amet </html>"
+       end
+ 
+       it 'should not modify the input string' do
+         input = '<!DOCTYPE html><b>foo</b>'
+         @s.document(input)
+-        input.must_equal('<!DOCTYPE html><b>foo</b>')
++        _(input).must_equal('<!DOCTYPE html><b>foo</b>')
+       end
+ 
+       it 'should not choke on frozen documents' do
+-        @s.document('<!doctype html><html><b>foo</b>'.freeze).must_equal "<html>foo</html>"
++        _(@s.document('<!doctype html><html><b>foo</b>'.freeze)).must_equal "<html>foo</html>"
+       end
+ 
+       it 'should normalize newlines' do
+-        @s.document("a\r\n\n\r\r\r\nz").must_equal "<html>a\n\n\n\n\nz</html>"
++        _(@s.document("a\r\n\n\r\r\r\nz")).must_equal "<html>a\n\n\n\n\nz</html>"
+       end
+ 
+       it 'should strip control characters (except ASCII whitespace)' do
+         sample_control_chars = "\u0001\u0008\u000b\u000e\u001f\u007f\u009f"
+         whitespace = "\t\n\f\u0020"
+-        @s.document("a#{sample_control_chars}#{whitespace}z").must_equal "<html>a#{whitespace}z</html>"
++        _(@s.document("a#{sample_control_chars}#{whitespace}z")).must_equal "<html>a#{whitespace}z</html>"
+       end
+ 
+       it 'should strip non-characters' do
+         sample_non_chars = "\ufdd0\ufdef\ufffe\uffff\u{1fffe}\u{1ffff}\u{2fffe}\u{2ffff}\u{3fffe}\u{3ffff}\u{4fffe}\u{4ffff}\u{5fffe}\u{5ffff}\u{6fffe}\u{6ffff}\u{7fffe}\u{7ffff}\u{8fffe}\u{8ffff}\u{9fffe}\u{9ffff}\u{afffe}\u{affff}\u{bfffe}\u{bffff}\u{cfffe}\u{cffff}\u{dfffe}\u{dffff}\u{efffe}\u{effff}\u{ffffe}\u{fffff}\u{10fffe}\u{10ffff}"
+-        @s.document("a#{sample_non_chars}z").must_equal "<html>az</html>"
++        _(@s.document("a#{sample_non_chars}z")).must_equal "<html>az</html>"
+       end
+ 
+       describe 'when html body exceeds Nokogiri::Gumbo::DEFAULT_MAX_TREE_DEPTH' do
+@@ -71,7 +71,7 @@ describe 'Sanitize' do
+           end
+ 
+           it 'does not raise an ArgumentError exception' do
+-            @s.document(content).must_equal '<html>foo</html>'
++            _(@s.document(content)).must_equal '<html>foo</html>'
+           end
+         end
+       end
+@@ -79,40 +79,40 @@ describe 'Sanitize' do
+ 
+     describe '#fragment' do
+       it 'should sanitize an HTML fragment' do
+-        @s.fragment('<b>Lo<!-- comment -->rem</b> <a href="pants" title="foo">ipsum</a> <a href="http://foo.com/";><strong>dolor</strong></a> sit<br/>amet <script>alert("hello world");</script>')
++        _(@s.fragment('<b>Lo<!-- comment -->rem</b> <a href="pants" title="foo">ipsum</a> <a href="http://foo.com/";><strong>dolor</strong></a> sit<br/>amet <script>alert("hello world");</script>'))
+           .must_equal 'Lorem ipsum dolor sit amet '
+       end
+ 
+       it 'should not modify the input string' do
+         input = '<b>foo</b>'
+         @s.fragment(input)
+-        input.must_equal '<b>foo</b>'
++        _(input).must_equal '<b>foo</b>'
+       end
+ 
+       it 'should not choke on fragments containing <html> or <body>' do
+-        @s.fragment('<html><b>foo</b></html>').must_equal 'foo'
+-        @s.fragment('<body><b>foo</b></body>').must_equal 'foo'
+-        @s.fragment('<html><body><b>foo</b></body></html>').must_equal 'foo'
+-        @s.fragment('<!DOCTYPE html><html><body><b>foo</b></body></html>').must_equal 'foo'
++        _(@s.fragment('<html><b>foo</b></html>')).must_equal 'foo'
++        _(@s.fragment('<body><b>foo</b></body>')).must_equal 'foo'
++        _(@s.fragment('<html><body><b>foo</b></body></html>')).must_equal 'foo'
++        _(@s.fragment('<!DOCTYPE html><html><body><b>foo</b></body></html>')).must_equal 'foo'
+       end
+ 
+       it 'should not choke on frozen fragments' do
+-        @s.fragment('<b>foo</b>'.freeze).must_equal 'foo'
++        _(@s.fragment('<b>foo</b>'.freeze)).must_equal 'foo'
+       end
+ 
+       it 'should normalize newlines' do
+-        @s.fragment("a\r\n\n\r\r\r\nz").must_equal "a\n\n\n\n\nz"
++        _(@s.fragment("a\r\n\n\r\r\r\nz")).must_equal "a\n\n\n\n\nz"
+       end
+ 
+       it 'should strip control characters (except ASCII whitespace)' do
+         sample_control_chars = "\u0001\u0008\u000b\u000e\u001f\u007f\u009f"
+         whitespace = "\t\n\f\u0020"
+-        @s.fragment("a#{sample_control_chars}#{whitespace}z").must_equal "a#{whitespace}z"
++        _(@s.fragment("a#{sample_control_chars}#{whitespace}z")).must_equal "a#{whitespace}z"
+       end
+ 
+       it 'should strip non-characters' do
+         sample_non_chars = "\ufdd0\ufdef\ufffe\uffff\u{1fffe}\u{1ffff}\u{2fffe}\u{2ffff}\u{3fffe}\u{3ffff}\u{4fffe}\u{4ffff}\u{5fffe}\u{5ffff}\u{6fffe}\u{6ffff}\u{7fffe}\u{7ffff}\u{8fffe}\u{8ffff}\u{9fffe}\u{9ffff}\u{afffe}\u{affff}\u{bfffe}\u{bffff}\u{cfffe}\u{cffff}\u{dfffe}\u{dffff}\u{efffe}\u{effff}\u{ffffe}\u{fffff}\u{10fffe}\u{10ffff}"
+-        @s.fragment("a#{sample_non_chars}z").must_equal "az"
++        _(@s.fragment("a#{sample_non_chars}z")).must_equal "az"
+       end
+ 
+       describe 'when html body exceeds Nokogiri::Gumbo::DEFAULT_MAX_TREE_DEPTH' do
+@@ -133,7 +133,7 @@ describe 'Sanitize' do
+           end
+ 
+           it 'does not raise an ArgumentError exception' do
+-            @s.fragment(content).must_equal 'foo'
++            _(@s.fragment(content)).must_equal 'foo'
+           end
+         end
+       end
+@@ -147,13 +147,13 @@ describe 'Sanitize' do
+         doc.xpath('/html/body/node()').each {|node| frag << node }
+ 
+         @s.node!(frag)
+-        frag.to_html.must_equal 'Lorem ipsum dolor sit amet '
++        _(frag.to_html).must_equal 'Lorem ipsum dolor sit amet '
+       end
+ 
+       describe "when the given node is a document and <html> isn't allowlisted" do
+         it 'should raise a Sanitize::Error' do
+           doc = Nokogiri::HTML5.parse('foo')
+-          proc { @s.node!(doc) }.must_raise Sanitize::Error
++          _(proc { @s.node!(doc) }).must_raise Sanitize::Error
+         end
+       end
+     end
+@@ -163,7 +163,7 @@ describe 'Sanitize' do
+     describe '.document' do
+       it 'should sanitize an HTML document with the given config' do
+         html = '<!doctype html><html><b>Lo<!-- comment -->rem</b> <a href="pants" title="foo">ipsum</a> <a href="http://foo.com/";><strong>dolor</strong></a> sit<br/>amet <script>alert("hello world");</script></html>'
+-        Sanitize.document(html, :elements => ['html'])
++        _(Sanitize.document(html, :elements => ['html']))
+           .must_equal "<html>Lorem ipsum dolor sit amet </html>"
+       end
+     end
+@@ -171,7 +171,7 @@ describe 'Sanitize' do
+     describe '.fragment' do
+       it 'should sanitize an HTML fragment with the given config' do
+         html = '<b>Lo<!-- comment -->rem</b> <a href="pants" title="foo">ipsum</a> <a href="http://foo.com/";><strong>dolor</strong></a> sit<br/>amet <script>alert("hello world");</script>'
+-        Sanitize.fragment(html, :elements => ['strong'])
++        _(Sanitize.fragment(html, :elements => ['strong']))
+           .must_equal 'Lorem ipsum <strong>dolor</strong> sit amet '
+       end
+     end
+@@ -184,7 +184,7 @@ describe 'Sanitize' do
+         doc.xpath('/html/body/node()').each {|node| frag << node }
+ 
+         Sanitize.node!(frag, :elements => ['strong'])
+-        frag.to_html.must_equal 'Lorem ipsum <strong>dolor</strong> sit amet '
++        _(frag.to_html).must_equal 'Lorem ipsum <strong>dolor</strong> sit amet '
+       end
+     end
+   end
+diff --git a/test/test_sanitize_css.rb b/test/test_sanitize_css.rb
+index 8e825c921f7f..185b6503f52c 100644
+--- a/test/test_sanitize_css.rb
++++ b/test/test_sanitize_css.rb
+@@ -16,9 +16,9 @@ describe 'Sanitize::CSS' do
+       it 'should sanitize CSS properties' do
+         css = 'background: #fff; width: expression(alert("hi"));'
+ 
+-        @default.properties(css).must_equal ' '
+-        @relaxed.properties(css).must_equal 'background: #fff; '
+-        @custom.properties(css).must_equal 'background: #fff; '
++        _(@default.properties(css)).must_equal ' '
++        _(@relaxed.properties(css)).must_equal 'background: #fff; '
++        _(@custom.properties(css)).must_equal 'background: #fff; '
+       end
+ 
+       it 'should allow allowlisted URL protocols' do
+@@ -30,9 +30,9 @@ describe 'Sanitize::CSS' do
+           "background: url(https://example.com/https.jpg)",
+           "background: url('https://example.com/https.jpg')",
+         ].each do |css|
+-          @default.properties(css).must_equal ''
+-          @relaxed.properties(css).must_equal css
+-          @custom.properties(css).must_equal ''
++          _(@default.properties(css)).must_equal ''
++          _(@relaxed.properties(css)).must_equal css
++          _(@custom.properties(css)).must_equal ''
+         end
+       end
+ 
+@@ -46,18 +46,18 @@ describe 'Sanitize::CSS' do
+           "background: url('javas\\\ncript:alert(0)')",
+           "background: url('java\\0script:foo')"
+         ].each do |css|
+-          @default.properties(css).must_equal ''
+-          @relaxed.properties(css).must_equal ''
+-          @custom.properties(css).must_equal ''
++          _(@default.properties(css)).must_equal ''
++          _(@relaxed.properties(css)).must_equal ''
++          _(@custom.properties(css)).must_equal ''
+         end
+       end
+ 
+       it 'should not allow -moz-binding' do
+         css = "-moz-binding:url('http://ha.ckers.org/xssmoz.xml#xss')"
+ 
+-        @default.properties(css).must_equal ''
+-        @relaxed.properties(css).must_equal ''
+-        @custom.properties(css).must_equal ''
++        _(@default.properties(css)).must_equal ''
++        _(@relaxed.properties(css)).must_equal ''
++        _(@custom.properties(css)).must_equal ''
+       end
+ 
+       it 'should not allow expressions' do
+@@ -69,50 +69,50 @@ describe 'Sanitize::CSS' do
+           "xss:expression(alert(1))",
+           "height: foo(expression(alert(1)));"
+         ].each do |css|
+-          @default.properties(css).must_equal ''
+-          @relaxed.properties(css).must_equal ''
+-          @custom.properties(css).must_equal ''
++          _(@default.properties(css)).must_equal ''
++          _(@relaxed.properties(css)).must_equal ''
++          _(@custom.properties(css)).must_equal ''
+         end
+       end
+ 
+       it 'should not allow behaviors' do
+         css = "behavior: url(xss.htc);"
+ 
+-        @default.properties(css).must_equal ''
+-        @relaxed.properties(css).must_equal ''
+-        @custom.properties(css).must_equal ''
++        _(@default.properties(css)).must_equal ''
++        _(@relaxed.properties(css)).must_equal ''
++        _(@custom.properties(css)).must_equal ''
+       end
+ 
+       describe 'when :allow_comments is true' do
+         it 'should preserve comments' do
+-          @relaxed.properties('color: #fff; /* comment */ width: 100px;')
++          _(@relaxed.properties('color: #fff; /* comment */ width: 100px;'))
+             .must_equal 'color: #fff; /* comment */ width: 100px;'
+ 
+-          @relaxed.properties("color: #fff; /* \n\ncomment */ width: 100px;")
++          _(@relaxed.properties("color: #fff; /* \n\ncomment */ width: 100px;"))
+             .must_equal "color: #fff; /* \n\ncomment */ width: 100px;"
+         end
+       end
+ 
+       describe 'when :allow_comments is false' do
+         it 'should strip comments' do
+-          @custom.properties('color: #fff; /* comment */ width: 100px;')
++          _(@custom.properties('color: #fff; /* comment */ width: 100px;'))
+             .must_equal 'color: #fff;  width: 100px;'
+ 
+-          @custom.properties("color: #fff; /* \n\ncomment */ width: 100px;")
++          _(@custom.properties("color: #fff; /* \n\ncomment */ width: 100px;"))
+             .must_equal 'color: #fff;  width: 100px;'
+         end
+       end
+ 
+       describe 'when :allow_hacks is true' do
+         it 'should allow common CSS hacks' do
+-          @relaxed.properties('_border: 1px solid #fff; *width: 10px')
++          _(@relaxed.properties('_border: 1px solid #fff; *width: 10px'))
+             .must_equal '_border: 1px solid #fff; *width: 10px'
+         end
+       end
+ 
+       describe 'when :allow_hacks is false' do
+         it 'should not allow common CSS hacks' do
+-          @custom.properties('_border: 1px solid #fff; *width: 10px')
++          _(@custom.properties('_border: 1px solid #fff; *width: 10px'))
+             .must_equal ' '
+         end
+       end
+@@ -131,14 +131,14 @@ describe 'Sanitize::CSS' do
+           }
+         ].strip
+ 
+-        @default.stylesheet(css).strip.must_equal %[
++        _(@default.stylesheet(css).strip).must_equal %[
+           .foo {  }
+           #bar {  }
+         ].strip
+ 
+-        @relaxed.stylesheet(css).must_equal css
++        _(@relaxed.stylesheet(css)).must_equal css
+ 
+-        @custom.stylesheet(css).strip.must_equal %[
++        _(@custom.stylesheet(css).strip).must_equal %[
+           .foo { color: #fff; }
+           #bar {  }
+         ].strip
+@@ -146,34 +146,34 @@ describe 'Sanitize::CSS' do
+ 
+       describe 'when :allow_comments is true' do
+         it 'should preserve comments' do
+-          @relaxed.stylesheet('.foo { color: #fff; /* comment */ width: 100px; }')
++          _(@relaxed.stylesheet('.foo { color: #fff; /* comment */ width: 100px; }'))
+             .must_equal '.foo { color: #fff; /* comment */ width: 100px; }'
+ 
+-          @relaxed.stylesheet(".foo { color: #fff; /* \n\ncomment */ width: 100px; }")
++          _(@relaxed.stylesheet(".foo { color: #fff; /* \n\ncomment */ width: 100px; }"))
+             .must_equal ".foo { color: #fff; /* \n\ncomment */ width: 100px; }"
+         end
+       end
+ 
+       describe 'when :allow_comments is false' do
+         it 'should strip comments' do
+-          @custom.stylesheet('.foo { color: #fff; /* comment */ width: 100px; }')
++          _(@custom.stylesheet('.foo { color: #fff; /* comment */ width: 100px; }'))
+             .must_equal '.foo { color: #fff;  width: 100px; }'
+ 
+-          @custom.stylesheet(".foo { color: #fff; /* \n\ncomment */ width: 100px; }")
++          _(@custom.stylesheet(".foo { color: #fff; /* \n\ncomment */ width: 100px; }"))
+             .must_equal '.foo { color: #fff;  width: 100px; }'
+         end
+       end
+ 
+       describe 'when :allow_hacks is true' do
+         it 'should allow common CSS hacks' do
+-          @relaxed.stylesheet('.foo { _border: 1px solid #fff; *width: 10px }')
++          _(@relaxed.stylesheet('.foo { _border: 1px solid #fff; *width: 10px }'))
+             .must_equal '.foo { _border: 1px solid #fff; *width: 10px }'
+         end
+       end
+ 
+       describe 'when :allow_hacks is false' do
+         it 'should not allow common CSS hacks' do
+-          @custom.stylesheet('.foo { _border: 1px solid #fff; *width: 10px }')
++          _(@custom.stylesheet('.foo { _border: 1px solid #fff; *width: 10px }'))
+             .must_equal '.foo {  }'
+         end
+       end
+@@ -185,9 +185,9 @@ describe 'Sanitize::CSS' do
+           ".foo { background: #fff; font: 16pt 'Comic Sans MS'; }\n" <<
+           "#bar { top: 125px; background: green; }")
+ 
+-        @custom.tree!(tree).must_be_same_as tree
++        _(@custom.tree!(tree)).must_be_same_as tree
+ 
+-        Crass::Parser.stringify(tree).must_equal String.new("\n") <<
++        _(Crass::Parser.stringify(tree)).must_equal String.new("\n") <<
+             ".foo { background: #fff;  }\n" <<
+             "#bar {  background: green; }"
+       end
+@@ -199,9 +199,9 @@ describe 'Sanitize::CSS' do
+       it 'should sanitize CSS properties with the given config' do
+         css = 'background: #fff; width: expression(alert("hi"));'
+ 
+-        Sanitize::CSS.properties(css).must_equal ' '
+-        Sanitize::CSS.properties(css, Sanitize::Config::RELAXED[:css]).must_equal 'background: #fff; '
+-        Sanitize::CSS.properties(css, :properties => %w[background color width]).must_equal 'background: #fff; '
++        _(Sanitize::CSS.properties(css)).must_equal ' '
++        _(Sanitize::CSS.properties(css, Sanitize::Config::RELAXED[:css])).must_equal 'background: #fff; '
++        _(Sanitize::CSS.properties(css, :properties => %w[background color width])).must_equal 'background: #fff; '
+       end
+     end
+ 
+@@ -218,14 +218,14 @@ describe 'Sanitize::CSS' do
+           }
+         ].strip
+ 
+-        Sanitize::CSS.stylesheet(css).strip.must_equal %[
++        _(Sanitize::CSS.stylesheet(css).strip).must_equal %[
+           .foo {  }
+           #bar {  }
+         ].strip
+ 
+-        Sanitize::CSS.stylesheet(css, Sanitize::Config::RELAXED[:css]).must_equal css
++        _(Sanitize::CSS.stylesheet(css, Sanitize::Config::RELAXED[:css])).must_equal css
+ 
+-        Sanitize::CSS.stylesheet(css, :properties => %w[background color width]).strip.must_equal %[
++        _(Sanitize::CSS.stylesheet(css, :properties => %w[background color width]).strip).must_equal %[
+           .foo { color: #fff; }
+           #bar {  }
+         ].strip
+@@ -238,9 +238,9 @@ describe 'Sanitize::CSS' do
+           ".foo { background: #fff; font: 16pt 'Comic Sans MS'; }\n" <<
+           "#bar { top: 125px; background: green; }")
+ 
+-        Sanitize::CSS.tree!(tree, :properties => %w[background color width]).must_be_same_as tree
++        _(Sanitize::CSS.tree!(tree, :properties => %w[background color width])).must_be_same_as tree
+ 
+-        Crass::Parser.stringify(tree).must_equal String.new("\n") <<
++        _(Crass::Parser.stringify(tree)).must_equal String.new("\n") <<
+             ".foo { background: #fff;  }\n" <<
+             "#bar {  background: green; }"
+       end
+@@ -256,7 +256,7 @@ describe 'Sanitize::CSS' do
+     # https://github.com/rgrove/sanitize/issues/121
+     it 'should parse the contents of @media rules properly' do
+       css = '@media { p[class="center"] { text-align: center; }}'
+-      @relaxed.stylesheet(css).must_equal css
++      _(@relaxed.stylesheet(css)).must_equal css
+ 
+       css = %[
+         @media (max-width: 720px) {
+@@ -269,7 +269,7 @@ describe 'Sanitize::CSS' do
+         }
+       ].strip
+ 
+-      @relaxed.stylesheet(css).must_equal %[
++      _(@relaxed.stylesheet(css)).must_equal %[
+         @media (max-width: 720px) {
+           p.foo > .bar { float: right;  }
+           #baz { color: green; }
+@@ -303,7 +303,7 @@ describe 'Sanitize::CSS' do
+         }
+       ].strip
+ 
+-      @relaxed.stylesheet(css).must_equal css
++      _(@relaxed.stylesheet(css)).must_equal css
+     end
+ 
+     describe ":at_rules" do
+@@ -314,7 +314,7 @@ describe 'Sanitize::CSS' do
+           .foo { color: green; }
+         ].strip
+ 
+-        @relaxed.stylesheet(css).strip.must_equal %[
++        _(@relaxed.stylesheet(css).strip).must_equal %[
+           .foo { color: green; }
+         ].strip
+       end
+@@ -333,7 +333,7 @@ describe 'Sanitize::CSS' do
+             .foo { color: green; }
+           ].strip
+ 
+-          @scss.stylesheet(css).must_equal %[
++          _(@scss.stylesheet(css)).must_equal %[
+             @charset 'utf-8';
+             @import url('foo.css');
+             .foo { color: green; }
+@@ -347,7 +347,7 @@ describe 'Sanitize::CSS' do
+             .foo { color: green; }
+           ].strip
+ 
+-          @scss.stylesheet(css).strip.must_equal %[
++          _(@scss.stylesheet(css).strip).must_equal %[
+             .foo { color: green; }
+           ].strip
+         end
+@@ -367,7 +367,7 @@ describe 'Sanitize::CSS' do
+               @import url('https://somesite.com/something.css');
+             ].strip
+ 
+-            @scss.stylesheet(css).strip.must_equal %[
++            _(@scss.stylesheet(css).strip).must_equal %[
+               @import url('https://somesite.com/something.css');
+             ].strip
+           end
+@@ -388,7 +388,7 @@ describe 'Sanitize::CSS' do
+               @import url('https://fonts.googleapis.com/css?family=Indie+Flower');
+             ].strip
+ 
+-            @scss.stylesheet(css).strip.must_equal %[
++            _(@scss.stylesheet(css).strip).must_equal %[
+               @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
+               @import url('https://fonts.googleapis.com/css?family=Indie+Flower');
+             ].strip
+@@ -401,7 +401,7 @@ describe 'Sanitize::CSS' do
+               @import url('https://nastysite.com/nasty_hax0r.css');
+             ].strip
+ 
+-            @scss.stylesheet(css).strip.must_equal %[
++            _(@scss.stylesheet(css).strip).must_equal %[
+               @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
+             ].strip
+           end
+@@ -413,7 +413,7 @@ describe 'Sanitize::CSS' do
+               @import url('');
+             ].strip
+ 
+-            @scss.stylesheet(css).strip.must_equal %[
++            _(@scss.stylesheet(css).strip).must_equal %[
+               @import 'https://fonts.googleapis.com/css?family=Indie+Flower';
+             ].strip
+           end
+diff --git a/test/test_transformers.rb b/test/test_transformers.rb
+index 54ee2984ace6..580e340b067f 100644
+--- a/test/test_transformers.rb
++++ b/test/test_transformers.rb
+@@ -11,14 +11,14 @@ describe 'Transformers' do
+       :transformers => lambda {|env|
+         return unless env[:node].element?
+ 
+-        env[:config][:foo].must_equal :bar
+-        env[:is_allowlisted].must_equal false
+-        env[:is_whitelisted].must_equal env[:is_allowlisted]
+-        env[:node].must_be_kind_of Nokogiri::XML::Node
+-        env[:node_name].must_equal 'span'
+-        env[:node_allowlist].must_be_kind_of Set
+-        env[:node_allowlist].must_be_empty
+-        env[:node_whitelist].must_equal env[:node_allowlist]
++        _(env[:config][:foo]).must_equal :bar
++        _(env[:is_allowlisted]).must_equal false
++        _(env[:is_whitelisted]).must_equal env[:is_allowlisted]
++        _(env[:node]).must_be_kind_of Nokogiri::XML::Node
++        _(env[:node_name]).must_equal 'span'
++        _(env[:node_allowlist]).must_be_kind_of Set
++        _(env[:node_allowlist]).must_be_empty
++        _(env[:node_whitelist]).must_equal env[:node_allowlist]
+       }
+     )
+   end
+@@ -30,7 +30,7 @@ describe 'Transformers' do
+       :transformers => proc {|env| nodes << env[:node_name] }
+     )
+ 
+-    nodes.must_equal %w[
++    _(nodes).must_equal %w[
+       #document-fragment div text text text comment script text
+     ]
+   end
+@@ -42,25 +42,25 @@ describe 'Transformers' do
+       :transformers => proc {|env| nodes << env[:node_name] if env[:node].element? }
+     )
+ 
+-    nodes.must_equal %w[div span strong b p]
++    _(nodes).must_equal %w[div span strong b p]
+   end
+ 
+   it 'should allowlist nodes in the node allowlist' do
+-    Sanitize.fragment('<div class="foo">foo</div><span>bar</span>',
++    _(Sanitize.fragment('<div class="foo">foo</div><span>bar</span>',
+       :transformers => [
+         proc {|env|
+           {:node_allowlist => [env[:node]]} if env[:node_name] == 'div'
+         },
+ 
+         proc {|env|
+-          env[:is_allowlisted].must_equal false unless env[:node_name] == 'div'
+-          env[:is_allowlisted].must_equal true if env[:node_name] == 'div'
+-          env[:node_allowlist].must_include env[:node] if env[:node_name] == 'div'
+-          env[:is_whitelisted].must_equal env[:is_allowlisted]
+-          env[:node_whitelist].must_equal env[:node_allowlist]
++          _(env[:is_allowlisted]).must_equal false unless env[:node_name] == 'div'
++          _(env[:is_allowlisted]).must_equal true if env[:node_name] == 'div'
++          _(env[:node_allowlist]).must_include env[:node] if env[:node_name] == 'div'
++          _(env[:is_whitelisted]).must_equal env[:is_allowlisted]
++          _(env[:node_whitelist]).must_equal env[:node_allowlist]
+         }
+       ]
+-    ).must_equal '<div class="foo">foo</div>bar'
++    )).must_equal '<div class="foo">foo</div>bar'
+   end
+ 
+   it 'should clear the node allowlist after each fragment' do
+@@ -73,19 +73,19 @@ describe 'Transformers' do
+     Sanitize.fragment('<div>foo</div>',
+       :transformers => proc {|env|
+         called = true
+-        env[:is_allowlisted].must_equal false
+-        env[:is_whitelisted].must_equal env[:is_allowlisted]
+-        env[:node_allowlist].must_be_empty
+-        env[:node_whitelist].must_equal env[:node_allowlist]
++        _(env[:is_allowlisted]).must_equal false
++        _(env[:is_whitelisted]).must_equal env[:is_allowlisted]
++        _(env[:node_allowlist]).must_be_empty
++        _(env[:node_whitelist]).must_equal env[:node_allowlist]
+       }
+     )
+ 
+-    called.must_equal true
++    _(called).must_equal true
+   end
+ 
+   it 'should accept a method transformer' do
+     def transformer(env); end
+-    Sanitize.fragment('<div>foo</div>', :transformers => method(:transformer))
++    _(Sanitize.fragment('<div>foo</div>', :transformers => method(:transformer)))
+       .must_equal(' foo ')
+   end
+ 
+@@ -114,32 +114,32 @@ describe 'Transformers' do
+ 
+     it 'should allow images with relative URLs' do
+       input = '<img src="/foo/bar.jpg">'
+-      @s.fragment(input).must_equal(input)
++      _(@s.fragment(input)).must_equal(input)
+     end
+ 
+     it 'should allow images at the example.com domain' do
+       input = '<img src="http://example.com/foo/bar.jpg";>'
+-      @s.fragment(input).must_equal(input)
++      _(@s.fragment(input)).must_equal(input)
+ 
+       input = '<img src="https://example.com/foo/bar.jpg";>'
+-      @s.fragment(input).must_equal(input)
++      _(@s.fragment(input)).must_equal(input)
+ 
+       input = '<img src="//example.com/foo/bar.jpg">'
+-      @s.fragment(input).must_equal(input)
++      _(@s.fragment(input)).must_equal(input)
+     end
+ 
+     it 'should not allow images at other domains' do
+       input = '<img src="http://evil.com/foo/bar.jpg";>'
+-      @s.fragment(input).must_equal('')
++      _(@s.fragment(input)).must_equal('')
+ 
+       input = '<img src="https://evil.com/foo/bar.jpg";>'
+-      @s.fragment(input).must_equal('')
++      _(@s.fragment(input)).must_equal('')
+ 
+       input = '<img src="//evil.com/foo/bar.jpg">'
+-      @s.fragment(input).must_equal('')
++      _(@s.fragment(input)).must_equal('')
+ 
+       input = '<img src="http://subdomain.example.com/foo/bar.jpg";>'
+-      @s.fragment(input).must_equal('')
++      _(@s.fragment(input)).must_equal('')
+     end
+   end
+ 
+@@ -177,35 +177,35 @@ describe 'Transformers' do
+     it 'should allow HTTP YouTube video embeds' do
+       input = '<iframe width="420" height="315" src="http://www.youtube.com/embed/QH2-TGUlwu4"; frameborder="0" allowfullscreen bogus="bogus"><script>alert()</script></iframe>'
+ 
+-      Sanitize.fragment(input, :transformers => youtube_transformer)
++      _(Sanitize.fragment(input, :transformers => youtube_transformer))
+         .must_equal '<iframe width="420" height="315" src="http://www.youtube.com/embed/QH2-TGUlwu4"; frameborder="0" allowfullscreen=""></iframe>'
+     end
+ 
+     it 'should allow HTTPS YouTube video embeds' do
+       input = '<iframe width="420" height="315" src="https://www.youtube.com/embed/QH2-TGUlwu4"; frameborder="0" allowfullscreen bogus="bogus"><script>alert()</script></iframe>'
+ 
+-      Sanitize.fragment(input, :transformers => youtube_transformer)
++      _(Sanitize.fragment(input, :transformers => youtube_transformer))
+         .must_equal '<iframe width="420" height="315" src="https://www.youtube.com/embed/QH2-TGUlwu4"; frameborder="0" allowfullscreen=""></iframe>'
+     end
+ 
+     it 'should allow protocol-relative YouTube video embeds' do
+       input = '<iframe width="420" height="315" src="//www.youtube.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen bogus="bogus"><script>alert()</script></iframe>'
+ 
+-      Sanitize.fragment(input, :transformers => youtube_transformer)
++      _(Sanitize.fragment(input, :transformers => youtube_transformer))
+         .must_equal '<iframe width="420" height="315" src="//www.youtube.com/embed/QH2-TGUlwu4" frameborder="0" allowfullscreen=""></iframe>'
+     end
+ 
+     it 'should allow privacy-enhanced YouTube video embeds' do
+       input = '<iframe width="420" height="315" src="https://www.youtube-nocookie.com/embed/QH2-TGUlwu4"; frameborder="0" allowfullscreen bogus="bogus"><script>alert()</script></iframe>'
+ 
+-      Sanitize.fragment(input, :transformers => youtube_transformer)
++      _(Sanitize.fragment(input, :transformers => youtube_transformer))
+         .must_equal '<iframe width="420" height="315" src="https://www.youtube-nocookie.com/embed/QH2-TGUlwu4"; frameborder="0" allowfullscreen=""></iframe>'
+     end
+ 
+     it 'should not allow non-YouTube video embeds' do
+       input = '<iframe width="420" height="315" src="http://www.fake-youtube.com/embed/QH2-TGUlwu4"; frameborder="0" allowfullscreen></iframe>'
+ 
+-      Sanitize.fragment(input, :transformers => youtube_transformer)
++      _(Sanitize.fragment(input, :transformers => youtube_transformer))
+         .must_equal('')
+     end
+   end
+@@ -223,7 +223,7 @@ describe 'Transformers' do
+     it 'should allow the <b> tag to be changed to a <strong> tag' do
+       input = '<b>text</b>'
+ 
+-      Sanitize.fragment(input, :elements => ['strong'], :transformers => b_to_strong_tag_transformer)
++      _(Sanitize.fragment(input, :elements => ['strong'], :transformers => b_to_strong_tag_transformer))
+         .must_equal '<strong>text</strong>'
+     end
+   end
+-- 
+2.39.1
+
diff -Nru ruby-sanitize-6.0.0/debian/patches/series ruby-sanitize-6.0.0/debian/patches/series
--- ruby-sanitize-6.0.0/debian/patches/series	2022-01-27 20:51:53.000000000 +0100
+++ ruby-sanitize-6.0.0/debian/patches/series	2023-02-20 20:27:46.000000000 +0100
@@ -1 +1,4 @@
 no-relative-path.patch
+Update-tests-to-remove-deprecated-minitest-must_be.patch
+Forcibly-escape-content-in-unescaped-text-elements-i.patch
+Always-remove-noscript-elements.patch

Reply via email to