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. + + [](http://badge.fury.io/rb/sanitize) + [](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><img src=x onerror=alert(1)> '>]) ++ ) ++ 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}><img src=x onerror=alert(1)></#{name}></math>", ++ @s.fragment("<math><#{name}><img src=x onerror=alert(1)></#{name}>") ++ ) ++ end ++ ++ it "forcibly escapes text content inside `<#{name}>` in an SVG namespace" do ++ assert_equal( ++ "<svg><#{name}><img src=x onerror=alert(1)></#{name}></svg>", ++ @s.fragment("<svg><#{name}><img src=x onerror=alert(1)></#{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}><img src=x onerror=alert(1)></#{name}>") ++ ) ++ end ++ ++ it "removes text content inside `<#{name}>` in an SVG namespace" do ++ assert_equal( ++ "<svg><#{name}></#{name}></svg>", ++ @s.fragment("<svg><#{name}><img src=x onerror=alert(1)></#{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}><img src=x onerror=alert(1)></#{name}>") ++ ) ++ end ++ ++ it "removes `<#{name}>` elements in an SVG namespace" do ++ assert_equal( ++ '<svg></svg>', ++ @s.fragment("<svg><#{name}><img src=x onerror=alert(1)></#{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 --> -->bar' +- @s.fragment("foo <div <!-- comment -->>bar</div>").must_equal 'foo <div>>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 --> -->bar' ++ _(@s.fragment("foo <div <!-- comment -->>bar</div>")).must_equal 'foo <div>>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 <!-- <!-- <!-- --> --> -->bar' +- @s.fragment("foo <div <!-- comment -->>bar</div>").must_equal 'foo <div>>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 <!-- <!-- <!-- --> --> -->bar' ++ _(@s.fragment("foo <div <!-- comment -->>bar</div>")).must_equal 'foo <div>>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 '<' + +- Sanitize.fragment('< script <>> alert("<xss>");</script>') ++ _(Sanitize.fragment('< script <>> alert("<xss>");</script>')) + .must_equal '< script <>> 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>éxamples</b> & things">foo</a>') ++ _(@s.fragment('<a href="http://example.com" title="<b>éxamples</b> & things">foo</a>')) + .must_equal '<a href="http://example.com" title="<b>??xamples</b> & 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(javascript:alert('XSS'))">], +- Sanitize::Config::RELAXED).must_equal '<div></div>' ++ _(Sanitize.fragment(%[<div style="background-image: url(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 '<% naughty_ruby_code %>' + +- @s.fragment('<%= naughty_ruby_code %>'). ++ _(@s.fragment('<%= naughty_ruby_code %>')). + must_equal '<%= naughty_ruby_code %>' + 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>">' + end + + it 'should not be possible to inject protocol-based JS' do +- @s.fragment('<img src=javascript:alert('XSS')>'). ++ _(@s.fragment('<img src=javascript:alert('XSS')>')). + must_equal '<img>' + +- @s.fragment('<img src=javascript:alert('XSS')>'). ++ _(@s.fragment('<img src=javascript:alert('XSS')>')). + must_equal '<img>' + +- @s.fragment('<img src=javascript:alert('XSS')>'). ++ _(@s.fragment('<img src=javascript:alert('XSS')>')). + must_equal '<img>' + + # Encoded tab character. +- @s.fragment(%[<img src="jav	ascript:alert('XSS');">]). ++ _(@s.fragment(%[<img src="jav	ascript:alert('XSS');">])). + must_equal '<img>' + + # Encoded newline. +- @s.fragment(%[<img src="jav
ascript:alert('XSS');">]). ++ _(@s.fragment(%[<img src="jav
ascript:alert('XSS');">])). + must_equal '<img>' + + # Encoded carriage return. +- @s.fragment(%[<img src="jav
ascript:alert('XSS');">]). ++ _(@s.fragment(%[<img src="jav
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="  javascript:alert('XSS');">]). ++ _(@s.fragment(%[<img src="  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 '<' + 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<!--%22%20onmouseover=alert(1)>-->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<!--" 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<!--" onmouseover=alert(1)>-->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}>/*</#{tag_name}><img src onerror=alert(1)>*/]). ++ _(@s.fragment(%[<math><#{tag_name}>/*</#{tag_name}><img src onerror=alert(1)>*/])). + must_equal '' + +- @s.fragment(%[<svg><#{tag_name}>/*</#{tag_name}><img src onerror=alert(1)>*/]). ++ _(@s.fragment(%[<svg><#{tag_name}>/*</#{tag_name}><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("'é&").must_equal("'??&") ++ _(Sanitize.fragment("'é&")).must_equal("'??&") + end + + it 'should translate orphaned ampersands into entities' do +- Sanitize.fragment('at&t').must_equal('at&t') ++ _(Sanitize.fragment('at&t')).must_equal('at&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 > 2 and 2 < 1' +- Sanitize.fragment('OMG HAPPY BIRTHDAY! *<:-D').must_equal 'OMG HAPPY BIRTHDAY! *<:-D' ++ _(Sanitize.fragment('1 > 2 and 2 < 1')).must_equal '1 > 2 and 2 < 1' ++ _(Sanitize.fragment('OMG HAPPY BIRTHDAY! *<:-D')).must_equal 'OMG HAPPY BIRTHDAY! *<:-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