我想这很常见,这是一个已经解决的问题,但是对于Loofah和Nokogiri而言,我还没有找到解决方案。
我正在使用Loofah,一个包装Nokogiri的HTML scrubber库,用于擦除一些HTML文本以供显示。但是,该文本有时会发生在诸如<
和>
字符之间的电子邮件地址之类的内容中,例如< foo@domain.com >
。 Loofah正在考虑将其作为HTML或XML标签,并将其从文本中删除。
有没有办法防止这种情况发生,同时还要做好擦除实际标签的工作?
编辑:这是一个失败的测试用例:
require 'test/unit'
require 'test/unit/ui/console/testrunner'
require 'nokogiri'
MAGICAL_REGEXP = /<([^(?:\/|!\-\-)].*)>/
def filter_html(content)
# Current approach in a gist: We capture content enclosed in angle brackets.
# Then, we check if the excerpt right after the opening bracket is a valid HTML
# tag. If it's not, we substitute the matched content (which is the captured
# content enclosed in angle brackets) for the captured content enclosed in
# the HTML entities for the angle brackets. This does not work with nested
# HTML tags, since regular expressions are not meant for this.
content.to_s.gsub(MAGICAL_REGEXP) do |excerpt|
capture = $1
Nokogiri::HTML::ElementDescription[capture.split(/[<> ]/).first] ? excerpt : "<#{capture}>"
end
end
class HTMLTest < Test::Unit::TestCase
def setup
@raw_html = <<-EOS
<html>
<foo@bar.baz>
<p><foo@<b class="highlight">bar</b>.baz></p>
<p>
<foo@<b class="highlight">bar</b>.baz>
</p>
< don't erase this >
</html>
EOS
@filtered_html = <<-EOS
<html>
<foo@bar.baz>
<p><foo@<b class="highlight">bar</b>.baz></p>
<p>
<foo@<b class="highlight">bar</b>.baz>
</p>
< don't erase this >
</html>
EOS
end
def test_filter_html
assert_equal(@filtered_html, filter_html(@raw_html))
end
end
# Can you make this test pass?
Test::Unit::UI::Console::TestRunner.run(HTMLTest)
我们目前正在使用一些非常邪恶的正则表达式hackery来尝试实现这一目标,但正如上面的评论所述,它不适用于非嵌套内部标签“嵌套”。我们实际上也希望保留<b class="highlight">
元素。
以下示例不使用Loofah,但应用程序本身也在其他地方使用,因此在此处添加它并不困难。我们只是不确定应该使用哪些配置选项,如果有的话。
答案 0 :(得分:2)
由于主要问题是HTML实体尖括号中包含的HTML标签 - 完全被Nokogiri破坏 - 我们通过删除上述HTML标签,转义非HTML标签尖括号然后放入HTML标签回来了。这听起来有点hackish,但它的工作完美。我们的第一个目标是转发尖括号中的电子邮件地址,但这种方法(据称)适用于任何类型的文本。
# Does not run on ruby 1.9
require 'test/unit'
require 'test/unit/ui/console/testrunner'
require 'nokogiri'
require 'active_support/secure_random'
def filter_html(content)
# Used to mark highlighted words.
random_hex = SecureRandom.hex(6)
# Remove highlighting.
highlighted_terms = []
without_highlighting = content.to_s.gsub(/<b class="highlight">(.*?)<\/b>/) do |match|
highlighted_terms << $1
"highlight-#{random_hex}:#{$1}"
end
# Escape non-HTML angle brackets.
escaped_content = without_highlighting.to_s.gsub(/<(?:\s*\/)?([^!\-\-].*?)>/) do |excerpt|
capture = $1
tag = capture.split(/[^a-zA-Z1-6]/).reject(&:empty?).first
!!Nokogiri::HTML::ElementDescription[tag] ? excerpt : "<#{capture}>"
end
# Add highlighting back.
highlighted_terms.uniq.each do |term|
escaped_content.gsub!(/highlight-#{random_hex}:(#{term})/) do |match|
"<b class=\"highlight\">#{$1}</b>"
end
end
escaped_content
end
class HTMLTest < Test::Unit::TestCase
def setup
@raw_html = <<-EOS
<html>
<foo@bar.baz>
<p><foo@<b class="highlight">bar</b>.baz></p>
<p>
<foo@<b class="highlight">bar</b>.baz>
</p>
< don't erase this >
</html>
EOS
@filtered_html = <<-EOS
<html>
<foo@bar.baz>
<p><foo@<b class="highlight">bar</b>.baz></p>
<p>
<foo@<b class="highlight">bar</b>.baz>
</p>
< don't erase this >
</html>
EOS
end
def test_filter_html
assert_equal(@filtered_html, filter_html(@raw_html))
end
end
# It passes!
Test::Unit::UI::Console::TestRunner.run(HTMLTest)