LISTING 1

<!DOCTYPE xsl:stylesheet  [
  <!ENTITY apos   "'">
  <!ENTITY nbsp   " ">
  <!ENTITY copy   "©">
  <!ENTITY reg    "®">
  <!ENTITY trade  "™">
  <!ENTITY mdash  "—">
  <!ENTITY ldquo  "“">
  <!ENTITY rdquo  "”"> 
  <!ENTITY pound  "£">
  <!ENTITY yen    "¥">
  <!ENTITY euro   "€">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="no" omit-xml-declaration="yes" />
  
  <xsl:variable name="lcase" select="'abcdefghijklmnopqrstuvwxyz'" />
  <xsl:variable name="ucase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
  <xsl:variable name="tags"  select="',head,meta,script,frame,frameset,iframe,style,'" />
  
  <xsl:template match="text()">
    <xsl:value-of select="." disable-output-escaping="no" />
  </xsl:template>

  <xsl:template match="comment()">
    <xsl:comment><xsl:value-of select="." /></xsl:comment>
  </xsl:template>
  
  <xsl:template match="*">
    <xsl:copy>
      <xsl:copy-of select="@*[ 
        starts-with(translate(local-name(),$ucase,$lcase),'on')=false()
        and starts-with(translate(normalize-space(.),$ucase,$lcase),'javascript:')=false()]" />
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="*[contains($tags,concat(',',translate(local-name(),$ucase,$lcase),','))=true()]" />
  
  <xsl:template match="*[contains(',html,body,',concat(',',translate(local-name(),$ucase,$lcase),','))]">
    <xsl:apply-templates select="./node()" />
  </xsl:template>
  
  <xsl:template match="*[starts-with(translate(local-name(),$ucase,$lcase),'cf')=true()]">
    <xsl:apply-templates select="./node()" />
  </xsl:template>
</xsl:stylesheet>


LISTING 2

<cfcomponent displayname="HtmlGuardian">
  <cfset variables.filter = "">
  
  <cffunction name="init" access="public" output="false">
    <cfargument name="filter" type="string" required="true" hint="XSL">
    <cfset variables.filter = arguments.filter>
    <cfreturn this>
  </cffunction>
  
  <cffunction name="parseXHTML" access="private" output="false"
  hint="converts user provided XHTML content into an XML document object">
    <cfargument name="input" type="string" required="true">
    <cfxml variable="arguments.xml" casesensitive="false">
      <cfoutput>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
		"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        <html>
        <body>
        #REReplaceNoCase(trim(arguments.input),"<\?xml[^>]*\?>","","ONE")#
        </body>
        </html>
      </cfoutput>
    </cfxml>
    <cfreturn arguments.xml>
  </cffunction>
  
  <cffunction name="inputIsXHTML" access="public" 
  output="false" returntype="boolean"
  hint="tests a user provided XHTML content string for validness">
    <cfargument name="input" type="string" required="true">
    <cfif not len(trim(input))><cfreturn true></cfif>
    <cftry>
      <cfset parseXHTML(input)><cfreturn true>
      <cfcatch><cfreturn false></cfcatch>
    </cftry>
  </cffunction>
  
  <cffunction name="filterXHTMLInput" access="public" 
  output="false" returntype="string" 
  hint="removes potentially malicious tags from user provided XHTML content">
    <cfargument name="input" type="string" required="true">
    
    <cfif not len(trim(variables.filter)) or not len(trim(input))>
      <cfreturn input>
    </cfif>
    
    <cftry>
      <cfset arguments.input = parseXHTML(input)>
      <cfreturn XMLTransform(arguments.input,trim(variables.filter))>
      <cfcatch type="any">
        <cfreturn "">
      </cfcatch>
    </cftry>
  </cffunction>
</cfcomponent>
.