Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rubygem-loofah for openSUSE:Factory checked in at 2026-09-10 11:48:05 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-loofah (Old) and /work/SRC/openSUSE:Factory/.rubygem-loofah.new.1265 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-loofah" Thu Sep 10 11:48:05 2026 rev:29 rq:1376640 version:2.25.2 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-loofah/rubygem-loofah.changes 2026-03-29 20:01:36.007676176 +0200 +++ /work/SRC/openSUSE:Factory/.rubygem-loofah.new.1265/rubygem-loofah.changes 2026-09-10 11:51:11.464086746 +0200 @@ -1,0 +2,26 @@ +Wed Sep 9 06:54:59 UTC 2026 - Johannes Segitz <[email protected]> + +- update to 2.25.2 + * Ensure Loofah::HTML5::Scrub.allowed_uri? recognizes numeric character + references without semicolons (e.g. javascript:alert(1)), which browsers + decode and execute, and rejects schemes split by them. See + GHSA-5qhf-9phg-95m2. @flavorjones + * Ensure Loofah::HTML5::Scrub.allowed_uri? recognizes the named character + references 	 and 
, which CGI.unescapeHTML does not decode and + browsers strip from URIs, and rejects schemes split by them (e.g. + java	script:alert(1)). See GHSA-8whx-365g-h9vv. @flavorjones + * Ensure that both href and xlink:href attributes on SVG elements like use + are restricted to local (same-document) references. Previously only + xlink:href was restricted, allowing the SVG 2 href attribute to reference + external documents. See GHSA-9wjq-cp2p-hrgf. @flavorjones + * Harden data: URI mediatype parsing in Loofah::HTML5::Scrub.allowed_uri?. + The mediatype is now parsed following the WHATWG data: URL spec and RFC + 2397 instead of simply being split on a colon. A data: URI with an omitted + or malformed mediatype is now treated as text/plain and allowed, and one + without the required comma is now rejected. #305 @flavorjones + * Remove feed from the default set of allowed protocols + * Ensure Loofah::HTML5::Scrub.allowed_uri? recognizes unescaped whitespace + entities and rejects schemas containing them. See GHSA-46fp-8f5p-pf2m. #302 + @flavorjones + +------------------------------------------------------------------- Old: ---- loofah-2.25.0.gem New: ---- loofah-2.25.2.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-loofah.spec ++++++ --- /var/tmp/diff_new_pack.4Gx4I7/_old 2026-09-10 11:51:12.206117866 +0200 +++ /var/tmp/diff_new_pack.4Gx4I7/_new 2026-09-10 11:51:12.208117950 +0200 @@ -24,7 +24,7 @@ # Name: rubygem-loofah -Version: 2.25.0 +Version: 2.25.2 Release: 0 %define mod_name loofah %define mod_full_name %{mod_name}-%{version} ++++++ loofah-2.25.0.gem -> loofah-2.25.2.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CHANGELOG.md new/CHANGELOG.md --- old/CHANGELOG.md 1980-01-02 01:00:00.000000000 +0100 +++ new/CHANGELOG.md 1980-01-02 01:00:00.000000000 +0100 @@ -1,5 +1,24 @@ # Changelog +## 2.25.2 / 2026-07-15 + +### Security + +* Ensure `Loofah::HTML5::Scrub.allowed_uri?` recognizes numeric character references without semicolons (e.g. `javascript:alert(1)`), which browsers decode and execute, and rejects schemes split by them. See [GHSA-5qhf-9phg-95m2](https://github.com/flavorjones/loofah/security/advisories/GHSA-5qhf-9phg-95m2). @flavorjones +* Ensure `Loofah::HTML5::Scrub.allowed_uri?` recognizes the named character references `	` and `
`, which `CGI.unescapeHTML` does not decode and browsers strip from URIs, and rejects schemes split by them (e.g. `java	script:alert(1)`). See [GHSA-8whx-365g-h9vv](https://github.com/flavorjones/loofah/security/advisories/GHSA-8whx-365g-h9vv). @flavorjones +* Ensure that both `href` and `xlink:href` attributes on SVG elements like `use` are restricted to local (same-document) references. Previously only `xlink:href` was restricted, allowing the SVG 2 `href` attribute to reference external documents. See [GHSA-9wjq-cp2p-hrgf](https://github.com/flavorjones/loofah/security/advisories/GHSA-9wjq-cp2p-hrgf). @flavorjones + +### Improved + +* Harden `data:` URI mediatype parsing in `Loofah::HTML5::Scrub.allowed_uri?`. The mediatype is now parsed following the [WHATWG data: URL spec](https://fetch.spec.whatwg.org/#data-urls) and [RFC 2397](https://www.rfc-editor.org/rfc/rfc2397) instead of simply being split on a colon. A `data:` URI with an omitted or malformed mediatype is now treated as `text/plain` and allowed, and one without the required comma is now rejected. #305 @flavorjones +* Remove `feed` from the default set of allowed protocols. The [feed URI scheme](https://en.wikipedia.org/wiki/Feed_URI_scheme) was never accepted as a standard protocol, and no major browser supports it. Removing it reduces the attack surface particularly for non-browser contexts. #304 @flavorjones +* Remove a vestigial `p` alternative from `Loofah::HTML5::SafeList::PROTOCOL_SEPARATOR`. This appears to be an ancient typo dating back to pre-extraction Rails circa 2007. #305 @flavorjones + + +## 2.25.1 / 2026-03-17 + +* Ensure `Loofah::HTML5::Scrub.allowed_uri?` recognizes unescaped whitespace entities and rejects schemas containing them. See [GHSA-46fp-8f5p-pf2m](https://github.com/flavorjones/loofah/security/advisories/GHSA-46fp-8f5p-pf2m). #302 @flavorjones + ## 2.25.0 / 2025-12-15 * Extract `Loofah::HTML5::Scrub.allowed_uri?` which operates on a string. Previously this logic was coupled to the parsed tree in `.scrub_uri_attribute`. #300 @flavorjones diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/SECURITY.md new/SECURITY.md --- old/SECURITY.md 1980-01-02 01:00:00.000000000 +0100 +++ new/SECURITY.md 1980-01-02 01:00:00.000000000 +0100 @@ -2,7 +2,7 @@ The Loofah core contributors take security very seriously and investigate all reported vulnerabilities. -If you would like to report a vulnerablity or have a security concern regarding Loofah, please [report it via HackerOne](https://hackerone.com/loofah/reports/new). +If you would like to report a vulnerablity or have a security concern regarding Loofah, please [report it via Github](https://github.com/flavorjones/loofah/security). Your report will be acknowledged within 24 hours, and you'll receive a more detailed response within 72 hours indicating next steps in handling your report. Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/loofah/html5/safelist.rb new/lib/loofah/html5/safelist.rb --- old/lib/loofah/html5/safelist.rb 1980-01-02 01:00:00.000000000 +0100 +++ new/lib/loofah/html5/safelist.rb 1980-01-02 01:00:00.000000000 +0100 @@ -605,6 +605,11 @@ "stroke", ]) + SVG_HREF_ATTRIBUTES = Set.new([ + "xlink:href", + "href", + ]) + SVG_ALLOW_LOCAL_HREF = Set.new([ "altGlyph", "animate", @@ -978,7 +983,7 @@ "stroke-opacity", ]) - PROTOCOL_SEPARATOR = /:|(�*58)|(p)|(�*3a)|(%|%)3A/i + PROTOCOL_SEPARATOR = /:|(�*58)|(�*3a)|(%|%)3A/i ACCEPTABLE_PROTOCOLS = Set.new([ "afs", @@ -987,7 +992,6 @@ "data", "ed2k", "fax", - "feed", "ftp", "gopher", "http", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/loofah/html5/scrub.rb new/lib/loofah/html5/scrub.rb --- old/lib/loofah/html5/scrub.rb 1980-01-02 01:00:00.000000000 +0100 +++ new/lib/loofah/html5/scrub.rb 1980-01-02 01:00:00.000000000 +0100 @@ -14,7 +14,43 @@ CSS_WHITESPACE = " " CSS_PROPERTY_STRING_WITHOUT_EMBEDDED_QUOTES = /\A(["'])?[^"']+\1\z/ DATA_ATTRIBUTE_NAME = /\Adata-[\w-]+\z/ - URI_PROTOCOL_REGEX = /\A[a-z][a-z0-9+\-.]*:/ # RFC 3986 + + # Decimal (`:`) or hexadecimal (`:`) form, with or without the trailing semicolon that + # CGI.unescapeHTML requires but browsers do not. + NUMERIC_CHARACTER_REFERENCE = /&#(x[0-9a-f]+|[0-9]+);?/i + + # A scheme (RFC 3986) followed by a protocol separator. The separator must recognize the same + # encoded-colon forms as PROTOCOL_SEPARATOR, otherwise a scheme split by an encoded colon (for + # example "javascript:alert(1)") would not be recognized as having a scheme and would skip + # protocol validation. + URI_PROTOCOL_REGEX = /\A[a-z][a-z0-9+\-.]*#{SafeList::PROTOCOL_SEPARATOR}/ + + # Matches a valid MIME type "essence" (type "/" subtype, no parameters), used to + # decide whether a data: URI mediatype is well-formed; a non-match is not a valid + # MIME type, which the data: URL processor treats as text/plain. Specs: + # + # https://mimesniff.spec.whatwg.org/#valid-mime-type + # https://mimesniff.spec.whatwg.org/#mime-type-essence + # https://mimesniff.spec.whatwg.org/#http-token-code-point + # + # The character class below is the HTTP token set (tchar) from RFC 9110 section + # 5.6.2, https://www.rfc-editor.org/rfc/rfc9110#name-tokens : + # + # tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" + # / "_" / "`" / "|" / "~" / DIGIT / ALPHA + # + # ALPHA is written a-z, not a-zA-Z, because allowed_uri? downcases the input first. + DATA_URI_MEDIATYPE = %r{ + \A + [a-z0-9!\#$%&'*+\-.^_`|~]+ # type: 1*tchar + / # "/" is not a tchar, so it is the sole delimiter + [a-z0-9!\#$%&'*+\-.^_`|~]+ # subtype: 1*tchar + \z + }x + + # HTML5 named character references for whitespace that browsers strip from + # URIs. CGI.unescapeHTML does not decode these, so they are handled explicitly. + WHITESPACE_CHARACTER_REFERENCES = /&(Tab|NewLine);/ class << self def allowed_element?(element_name) @@ -48,7 +84,7 @@ end next unless SafeList::SVG_ALLOW_LOCAL_HREF.include?(node.name) && - attr_name == "xlink:href" && + SafeList::SVG_HREF_ATTRIBUTES.include?(attr_name) && attr_node.value =~ /^\s*[^#\s].*/m attr_node.remove @@ -141,26 +177,52 @@ attr_node.value = values.join(" ") end - # Returns true if the given URI string is safe, false otherwise. - # This method can be used to validate URI attribute values without - # requiring a Nokogiri DOM node. + # Returns true if the given URI string is safe, false otherwise. This method can be used to + # validate URI attribute values without requiring a Nokogiri DOM node. def allowed_uri?(uri_string) - # this logic lifted nearly verbatim from HTML5 sanitization - val_unescaped = CGI.unescapeHTML(uri_string.gsub(CONTROL_CHARACTERS, "")).gsub(":", ":").downcase - if URI_PROTOCOL_REGEX.match?(val_unescaped) - protocol = val_unescaped.split(SafeList::PROTOCOL_SEPARATOR)[0] + # CGI.unescapeHTML decodes numeric references only when they carry a trailing semicolon, so + # also decode the semicolon-less ones, which browsers still decode and execute. Normalizing + # more aggressively than a browser only rejects more, which is safe. Control characters are + # stripped both before and after decoding, since decoding can produce them. That strip must + # precede WHITESPACE_CHARACTER_REFERENCES: removing a control character can reveal a named + # whitespace reference. + uri_string = decode_numeric_character_references(CGI.unescapeHTML(uri_string.gsub(CONTROL_CHARACTERS, ""))) + uri_string.gsub!(CONTROL_CHARACTERS, "") + uri_string.gsub!(WHITESPACE_CHARACTER_REFERENCES, "") + uri_string.gsub!(":", ":") + uri_string.downcase! + if URI_PROTOCOL_REGEX.match?(uri_string) + protocol = uri_string.split(SafeList::PROTOCOL_SEPARATOR)[0] return false unless SafeList::ALLOWED_PROTOCOLS.include?(protocol) if protocol == "data" # permit only allowed data mediatypes - mediatype = val_unescaped.split(SafeList::PROTOCOL_SEPARATOR)[1] - mediatype, _ = mediatype.split(/[;,]/)[0..1] if mediatype - return false if mediatype && !SafeList::ALLOWED_URI_DATA_MEDIATYPES.include?(mediatype) + return false unless SafeList::ALLOWED_URI_DATA_MEDIATYPES.include?(data_uri_mediatype(uri_string)) end end true end + def decode_numeric_character_references(string) + string.gsub(NUMERIC_CHARACTER_REFERENCE) do |reference| + digits = ::Regexp.last_match(1) + hexadecimal = digits.start_with?("x", "X") + digits = digits[1..-1] if hexadecimal + significant_digits = digits.sub(/\A0+/, "") + + # The largest code point is U+10FFFF: 7 decimal or 6 hexadecimal significant digits. + # Anything longer is out of range; skip it without building a large integer from it. + next reference if significant_digits.length > (hexadecimal ? 6 : 7) + + codepoint = significant_digits.to_i(hexadecimal ? 16 : 10) + begin + codepoint.chr(Encoding::UTF_8) + rescue RangeError + reference + end + end + end + def scrub_uri_attribute(attr_node) if allowed_uri?(attr_node.value) false @@ -235,6 +297,20 @@ string end end + + private + + # Returns the mediatype of a data: URI per RFC 2397, or nil when the + # required comma is absent. allowed_uri? entity-decodes, downcases, and + # strips control characters before calling this. An omitted or malformed + # mediatype resolves to "text/plain", matching the WHATWG data: URL processor. + def data_uri_mediatype(uri_string) + metadata, comma, _data = uri_string.delete_prefix("data:").partition(",") + return nil if comma.empty? + + mediatype = metadata.delete_suffix(";base64").split(";", 2).first.to_s.strip + mediatype.match?(DATA_URI_MEDIATYPE) ? mediatype : "text/plain" + end end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/loofah/version.rb new/lib/loofah/version.rb --- old/lib/loofah/version.rb 1980-01-02 01:00:00.000000000 +0100 +++ new/lib/loofah/version.rb 1980-01-02 01:00:00.000000000 +0100 @@ -2,5 +2,5 @@ module Loofah # The version of Loofah you are using - VERSION = "2.25.0" + VERSION = "2.25.2" end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 1980-01-02 01:00:00.000000000 +0100 +++ new/metadata 1980-01-02 01:00:00.000000000 +0100 @@ -1,7 +1,7 @@ --- !ruby/object:Gem::Specification name: loofah version: !ruby/object:Gem::Version - version: 2.25.0 + version: 2.25.2 platform: ruby authors: - Mike Dalessio @@ -96,7 +96,7 @@ - !ruby/object:Gem::Version version: '0' requirements: [] -rubygems_version: 3.6.9 +rubygems_version: 4.0.10 specification_version: 4 summary: Loofah is a general library for manipulating and transforming HTML/XML documents and fragments, built on top of Nokogiri.
