changeset 533:1f9926e7f5b5

span macro: add (hopefully safe) style support, let through only properties on whitelist
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Tue, 22 Jun 2010 16:49:38 +0200
parents 942f9b55773e
children 7bcfe0b3823a
files data/plugin/macro/span.py
diffstat 1 files changed, 70 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/data/plugin/macro/span.py	Tue Jun 22 14:21:59 2010 +0200
+++ b/data/plugin/macro/span.py	Tue Jun 22 16:49:38 2010 +0200
@@ -3,9 +3,14 @@
     MoinMoin - span generating macro
 
     Supported attrs: css_class, id, lang, dir, title
+    
+    Partially supported attrs: style - only safe stuff on whitelist (with
+                               SUPPORT_STYLE_ATTR = True), other stuff
+                               can contain javascript, XSS danger!
+                               If you don't need style, consider using
+                               SUPPORT_STYLE_ATTR = False for better safety.
 
-    Unsupported attrs: style - can contain javascript, XSS danger
-                       event attrs - same reason
+    Unsupported attrs: event attrs - unsafe, can contain javascript, XSS danger
                        align - deprecated by the W3C (use css classes)
 
     Usage:
@@ -13,15 +18,72 @@
     <<span(css_class=red)>>same as above<<span>>
     <<span(id=foobar)>>some text in a span with id foobar<<span>>
     <<span(title="read this!")>>some text with a mouseover title<<span>>
+    <<span(style="color: red; font: 20pt sans-serif;")>>20pt sans-serif red<<span>>
 
     @copyright: 2010 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
 
+SUPPORT_STYLE_ATTR = True # True should be safe, False is safer :)
+
 Dependencies = []
 
 from MoinMoin.wikiutil import required_arg
 
+def make_style_safe(style):
+    """ make html 'style' attribute value safe """
+    # whitelist of safe style attributes, taken from:
+    # http://validator.w3.org/feed/docs/warning/DangerousStyleAttr.html
+    whitelist = ("azimuth,background,background-color,border,border-bottom,"
+                 "border-bottom-color,border-bottom-style,border-bottom-width,"
+                 "border-collapse,border-color,border-left,border-left-color,"
+                 "border-left-style,border-left-width,border-right,border-right-color,"
+                 "border-right-style,border-right-width,border-spacing,border-style,"
+                 "border-top,border-top-color,border-top-style,border-top-width,"
+                 "border-width,clear,color,cursor,direction,display,elevation,"
+                 "float,font,font-family,font-size,font-style,font-variant,"
+                 "font-weight,height,letter-spacing,line-height,margin,"
+                 "margin-bottom,margin-left,margin-right,margin-top,overflow,"
+                 "padding,padding-bottom,padding-left,padding-right,padding-top,"
+                 "pause,pause-after,pause-before,pitch,pitch-range,richness,"
+                 "speak,speak-header,speak-numeral,speak-punctuation,speech-rate,"
+                 "stress,text-align,text-decoration,text-indent,unicode-bidi,"
+                 "vertical-align,voice-family,volume,white-space,width").split(',')
+
+    def style_split(style):
+        """
+        split style into a list of declarations,
+        split the declarations into property, value tuples,
+        remove all surrounding whitespace
+        """
+        decls = [decl.split(u':', 1) for decl in style.split(u';')]
+        # remove spaces from propery and value
+        result = []
+        for decl in decls:
+            if len(decl) == 2:
+                result.append((decl[0].strip(), decl[1].strip()))
+        return result
+
+    def style_join(decls):
+        """
+        join a list of prop, value tuples into a style declaration
+        """
+        decls = [u'%s: %s' % (prop, val) for prop, val in decls]
+        style = u'; '.join(decls)
+        return style
+
+    def decl_filter(decls):
+        """
+        filter a list of prop, value tuples, only let whitelisted props through
+        """
+        return [(prop, val) for prop, val in decls if prop in whitelist]
+
+    decls = style_split(style)
+    decls = decl_filter(decls)
+    style = style_join(decls)
+    return style
+
+
 def macro_span(macro,
                # first the stuff we can directly give to span formatter:
                css_class=u'',
@@ -31,8 +93,7 @@
                title=u'',
                # deprecated by W3C:
                #align=u'',
-               # dangerous, see docstring:
-               #style=u'',
+               style=u'',
               ):
     attrs = {}
     for key, value in [
@@ -40,10 +101,14 @@
         ('lang', lang), ('dir', dir),
         ('title', title),
         #('align', align),
-        #('style', style),
         ]:
         if value:
             attrs[key] = value
+
+    if SUPPORT_STYLE_ATTR:
+        if style:
+            attrs['style'] = make_style_safe(style)
+
     span = macro.formatter.span
     if attrs:
         return span(True, **attrs)