# Samizdat HTML validation # # Copyright (c) 2002-2005 Dmitry Borodaenko # # This program is free software. # You can distribute/modify this program under the terms of # the GNU General Public License version 2 or later. # # vim: et sw=2 sts=2 ts=8 tw=0 require 'cgi' require 'yaml' require 'rexml/document' $TIDYLIB = 'libtidy.so' require 'tidy' # use (") instead of (') in XML attributes, escape both of them # module REXML class Attribute def to_string %{#@expanded_name="#{to_s().gsub(/"/, '"').gsub(/'/, ''')}"} end end end # module REXML module Samizdat class Sanitize def initialize(xhtml) @xhtml = xhtml end attr_reader :xhtml # compare elements and attributes with xhtml.yaml # def sanitize_element(xml, filter=@xhtml) if not filter.keys.include?(xml.name) xml.document.delete_element(xml) return end if xml.has_attributes? attrs = filter['_common'].merge((filter[xml.name] or {})) xml.attributes.each_attribute do |a| xml.delete_attribute(a.name) unless attrs[a.name] === a.to_s # todo: sanitize CSS in style="" attributes end end if xml.has_elements? xml.elements.each {|e| sanitize_element(e) } end end # return sanitized HTML # def sanitize(html, filter=@xhtml, fragment=true) xhtml = Tidy.open(:output_xhtml => true, :literal_attributes => true, :tidy_mark => false, :wrap => 0, :char_encoding => 'utf8' ) {|tidy| tidy.clean(html.to_s.untaint) } xhtml.taint begin xml = REXML::Document.new(xhtml).root xml = xml.elements['//html/body'] if fragment # work around tidy rescue REXML::ParseException raise RuntimeError, "Invalid HTML detected: " + CGI::escapeHTML($!.continued_exception.to_s.gsub!(/\n.*/, '')) end sanitize_element(xml, filter) html = '' if fragment xml.each {|child| child.write(html, -1, false, true) } else xml.write(html, -1, false, true) end html end end end # module Samizdat