[Erp5-report] r25842 - in /erp5/trunk/products/PortalTransforms: ./ transforms/

nobody at svn.erp5.org nobody at svn.erp5.org
Tue Mar 3 21:44:37 CET 2009


Author: kazuhiko
Date: Tue Mar  3 21:44:36 2009
New Revision: 25842

URL: http://svn.erp5.org?rev=25842&view=rev
Log:
1.4.1-nexedi - 2009-03-03
=========================

  * Allowed the abbr, acronym, var, dfn, samp, address, bdo, thead, tfoot,
    col, and colgroup tags by default, since they are harmless, valid XHTML
    and shouldn't be filtered. Fixes:
    http://dev.plone.org/plone/ticket/6712 and
    http://dev.plone.org/plone/ticket/7251
    [limi] (backport from 1.5.5-final)

  * Add another XSS fix from for handling extraneous brackets.
  [dunny] (backport from 1.5.3-final)

  * Add XSS fixes from Anton Stonor to safe_html transform.
  [alecm, stonor] (backport from 1.5.3-final)

  * casting to int is evil without previous check of the type. so we assume as
    in CMFPlone just zero for non-int-castable values.
    [jensens] (backport from 1.5.0-a1)

  * the values in the safe_html valid tag dictionary can become strings when
    modifying them via the ZMI. Explicitly convert them to integers before
    testing their value.
    [wichert] (backport from 1.5.0-a1)

Modified:
    erp5/trunk/products/PortalTransforms/HISTORY.txt
    erp5/trunk/products/PortalTransforms/transforms/safe_html.py
    erp5/trunk/products/PortalTransforms/utils.py

Modified: erp5/trunk/products/PortalTransforms/HISTORY.txt
URL: http://svn.erp5.org/erp5/trunk/products/PortalTransforms/HISTORY.txt?rev=25842&r1=25841&r2=25842&view=diff
==============================================================================
--- erp5/trunk/products/PortalTransforms/HISTORY.txt [utf8] (original)
+++ erp5/trunk/products/PortalTransforms/HISTORY.txt [utf8] Tue Mar  3 21:44:36 2009
@@ -1,10 +1,35 @@
+1.4.1-nexedi - 2009-03-03
+=========================
+
+  * Allowed the abbr, acronym, var, dfn, samp, address, bdo, thead, tfoot,
+    col, and colgroup tags by default, since they are harmless, valid XHTML
+    and shouldn't be filtered. Fixes:
+    http://dev.plone.org/plone/ticket/6712 and
+    http://dev.plone.org/plone/ticket/7251
+    [limi] (backport from 1.5.5-final)
+
+  * Add another XSS fix from for handling extraneous brackets.
+  [dunny] (backport from 1.5.3-final)
+
+  * Add XSS fixes from Anton Stonor to safe_html transform.
+  [alecm, stonor] (backport from 1.5.3-final)
+
+  * casting to int is evil without previous check of the type. so we assume as
+    in CMFPlone just zero for non-int-castable values.
+    [jensens] (backport from 1.5.0-a1)
+
+  * the values in the safe_html valid tag dictionary can become strings when
+    modifying them via the ZMI. Explicitly convert them to integers before
+    testing their value.
+    [wichert] (backport from 1.5.0-a1)
+
 1.4.0-nexedi - 2008-10-24
-========================
+=========================
 
   * fixed an infinite loop bug.
 
   * remove PortalTransforms/configure.zcml that is not compatible with
-    Zope-2.8's five.
+    Zope-2.8's Five (1.0.2).
 
   * let the user configure 'initial_header_level' (cf
     'rest-header-level' directive).
@@ -16,7 +41,7 @@
     transform tool will be copied and this transform will be removed
     from copied one.
 
-1.4.0-final - 2006-06-16
+1.4.1-final - 2006-09-08
 ========================
 
   * Shut down a noisy logging message to DEBUG level.

Modified: erp5/trunk/products/PortalTransforms/transforms/safe_html.py
URL: http://svn.erp5.org/erp5/trunk/products/PortalTransforms/transforms/safe_html.py?rev=25842&r1=25841&r2=25842&view=diff
==============================================================================
--- erp5/trunk/products/PortalTransforms/transforms/safe_html.py [utf8] (original)
+++ erp5/trunk/products/PortalTransforms/transforms/safe_html.py [utf8] Tue Mar  3 21:44:36 2009
@@ -1,5 +1,7 @@
 import logging
 from sgmllib import SGMLParser
+import re
+from cgi import escape
 
 from Products.PortalTransforms.interfaces import itransform
 from Products.PortalTransforms.utils import log
@@ -8,23 +10,62 @@
 from Products.CMFDefault.utils import SimpleHTMLParser
 from Products.CMFDefault.utils import VALID_TAGS
 from Products.CMFDefault.utils import NASTY_TAGS
+from Products.PortalTransforms.utils import safeToInt
 
 # tag mapping: tag -> short or long tag
 VALID_TAGS = VALID_TAGS.copy()
 NASTY_TAGS = NASTY_TAGS.copy()
 
-# add some tags to allowed types. This should be fixed in CMFDefault
+# add some tags to allowed types. These should be backported to CMFDefault.
 VALID_TAGS['ins'] = 1
 VALID_TAGS['del'] = 1
 VALID_TAGS['q'] = 1
-VALID_TAGS['map']=1
-VALID_TAGS['area']=1
+VALID_TAGS['map'] = 1
+VALID_TAGS['area'] = 1
+VALID_TAGS['abbr'] = 1
+VALID_TAGS['acronym'] = 1
+VALID_TAGS['var'] = 1
+VALID_TAGS['dfn'] = 1
+VALID_TAGS['samp'] = 1
+VALID_TAGS['address'] = 1
+VALID_TAGS['bdo'] = 1
+VALID_TAGS['thead'] = 1
+VALID_TAGS['tfoot'] = 1
+VALID_TAGS['col'] = 1
+VALID_TAGS['colgroup'] = 1
 
 msg_pat = """
 <div class="system-message">
 <p class="system-message-title">System message: %s</p>
 %s</d>
 """
+
+def hasScript(s):
+   """ Dig out evil Java/VB script inside an HTML attribute """
+
+   # look for "script" and "expression"
+   javascript_pattern = re.compile("([\s\n]*?s[\s\n]*?c[\s\n]*?r[\s\n]*?i[\s\n]*?p[\s\n]*?t[\s\n]*?:)|([\s\n]*?e[\s\n]*?x[\s\n]*?p[\s\n]*?r[\s\n]*?e[\s\n]*?s[\s\n]*?s[\s\n]*?i[\s\n]*?o[\s\n]*?n)", re.DOTALL|re.IGNORECASE)
+   s = decode_htmlentities(s)
+   return javascript_pattern.findall(s)
+
+def decode_htmlentities(s):
+   """ XSS code can be hidden with htmlentities """
+
+   entity_pattern = re.compile("&#(?P<htmlentity>x?\w+)?;?")
+   s = entity_pattern.sub(decode_htmlentity,s)
+   return s
+
+def decode_htmlentity(m):
+   entity_value = m.groupdict()['htmlentity']
+   if entity_value.lower().startswith('x'):
+      try:
+          return chr(int('0'+entity_value,16))
+      except ValueError:
+          return entity_value
+   try:
+      return chr(int(entity_value))
+   except ValueError:
+      return entity_value
 
 class StrippingParser(SGMLParser):
     """Pass only allowed tags;  raise exception for known-bad.
@@ -47,7 +88,7 @@
     def handle_data(self, data):
         if self.suppress: return
         if data:
-            self.result.append(data)
+            self.result.append(escape(data))
 
     def handle_charref(self, name):
         if self.suppress: return
@@ -74,23 +115,24 @@
         """
 
         if self.suppress: return
-        #if self.remove_javascript and tag == script and :
 
         if self.valid.has_key(tag):
             self.result.append('<' + tag)
 
+            remove_script = getattr(self,'remove_javascript',True)
+
             for k, v in attrs:
-                if self.remove_javascript and k.strip().lower().startswith('on'):
+                if remove_script and k.strip().lower().startswith('on'):
                     if not self.raise_error: continue
-                    else: raise IllegalHTML, 'Javascript event "%s" not allowed.' % k
-                elif self.remove_javascript and v.strip().lower().startswith('javascript:' ):
+                    else: raise IllegalHTML, 'Script event "%s" not allowed.' % k
+                elif remove_script and hasScript(v):
                     if not self.raise_error: continue
-                    else: raise IllegalHTML, 'Javascript URI "%s" not allowed.' % v
+                    else: raise IllegalHTML, 'Script URI "%s" not allowed.' % v
                 else:
                     self.result.append(' %s="%s"' % (k, v))
 
             #UNUSED endTag = '</%s>' % tag
-            if self.valid.get(tag):
+            if safeToInt(self.valid.get(tag)):
                 self.result.append('>')
             else:
                 self.result.append(' />')
@@ -103,10 +145,10 @@
             pass
 
     def unknown_endtag(self, tag):
-        if self.nasty.has_key(tag):
+        if self.nasty.has_key(tag) and not self.valid.has_key(tag):
             self.suppress = False
         if self.suppress: return
-        if self.valid.get(tag):
+        if safeToInt(self.valid.get(tag)):
             self.result.append('</%s>' % tag)
             #remTag = '</%s>' % tag
 
@@ -127,13 +169,19 @@
 
 class SafeHTML:
     """Simple transform which uses CMFDefault functions to
-    clean potentially bad tags.
-    Tags must explicit be allowed in valid_tags to pass. Only the tags
-    themself are removed, not their contents. If tags are
-    removed and in nasty_tags, they are removed with
-    all of their contents.
-    Be aware that you may need to clean the cache to let a Object
-    call the transform again.
+    clean potentially bad tags.   
+
+    Tags must explicit be allowed in valid_tags to pass. Only
+    the tags themself are removed, not their contents. If tags
+    are removed and in nasty_tags, they are removed with
+    all of their contents.         
+    
+    Objects will not be transformed again with changed settings.
+    You need to clear the cache by e.g.
+    1.) restarting your zope or
+    2.) empty the zodb-cache via ZMI -> Control_Panel
+        -> Database Management -> main || other_used_database
+        -> Flush Cache.
     """
 
     __implements__ = itransform

Modified: erp5/trunk/products/PortalTransforms/utils.py
URL: http://svn.erp5.org/erp5/trunk/products/PortalTransforms/utils.py?rev=25842&r1=25841&r2=25842&view=diff
==============================================================================
--- erp5/trunk/products/PortalTransforms/utils.py [utf8] (original)
+++ erp5/trunk/products/PortalTransforms/utils.py [utf8] Tue Mar  3 21:44:36 2009
@@ -28,3 +28,12 @@
 import os.path
 _www = os.path.join(os.path.dirname(__file__), 'www')
 skins_dir = None
+
+def safeToInt(value):
+    """Convert value to integer or just return 0 if we can't"""
+    try:
+        return int(value)
+    except ValueError:
+        return 0
+    except TypeError:
+        return 0




More information about the Erp5-report mailing list