Changeset a30779 in indico


Ignore:
Timestamp:
10/08/10 15:55:20 (3 years ago)
Author:
Jose Benito <jose.benito.gonzalez@…>
Branches:
master, burotel, hello-world-walkthrough, ipv6, new-webex, v0.97-series, v0.98-series, v0.98.2, v0.98.3, v0.98b1, v0.98b2, v0.99, b8c30da8ebdbdcbd675a873997cc3e95f567de49, 36509dd327b5670bb394f5ec070c14653b4c9c19
Children:
a9e8ac
Parents:
203b37
git-author:
Leszek Syroka <leszek.marek.syroka@…> (06/04/10 15:39:42)
git-committer:
Jose Benito <jose.benito.gonzalez@…> (10/08/10 15:55:20)
Message:

[FIX] Sanity module

  • client and server side input parsing integration
  • merged with branch fix#376-inline-minute-no-iframes
  • both parser are using the same whitelists
  • logic of client side parser changed to be coherent with server side parser
  • parsers whitelists and current sanitization level were put to vars.js.tpl file to the dictionary 'Security'
  • whitelists content updated
  • CSS keywords filtering added
  • protocol whitelist and url check added
  • fix#395
Location:
indico
Files:
6 edited
1 moved

Legend:

Unmodified
Added
Removed
  • indico/MaKaC/common/security.py

    rbdd862 ra30779  
    2323 
    2424from MaKaC.errors import MaKaCError, htmlScriptError, htmlForbiddenTag 
    25 from MaKaC.webinterface.common.tools import scriptDetection, escape_html, restrictedHTML 
     25from MaKaC.webinterface.common.tools import escape_html, restrictedHTML 
    2626 
    2727""" 
     
    7272                        params[param][i] = escape_html(item) 
    7373 
     74##    elif level == 1: 
     75##        #level 1 or default 
     76##        #raise error if script or style detected 
     77##        ret = None 
     78##        for param in params.keys(): 
     79##            if isinstance(params[param], str): 
     80##                ret = scriptDetection(params[param]) 
     81##                if not restrictedHTML(params[param], level): 
     82##                    raise htmlForbiddenTag(params[param]) 
     83##            elif isinstance(params[param], list): 
     84##                for item in params[param]: 
     85##                    if isinstance(item, str): 
     86##                        ret = scriptDetection(item) 
     87##                        if ret: 
     88##                            raise htmlScriptError(item) 
     89##                        if not restrictedHTML(item, level): 
     90##                            raise htmlForbiddenTag(item) 
     91##            if ret: 
     92##                raise htmlScriptError(params[param]) 
     93## 
     94##    elif level == 2: 
     95##        #raise error if script but style accepted 
     96##        ret = None 
     97##        for param in params.keys(): 
     98##            if isinstance(params[param], str): 
     99##                ret = scriptDetection(params[param], allowStyle=True) 
     100##                if ret: 
     101##                    raise htmlScriptError(params[param]) 
     102##                ret = restrictedHTML(params[param], level) 
     103##                if not ret: 
     104##                    raise htmlForbiddenTag(params[param]) 
     105##            elif isinstance(params[param], list): 
     106##                for item in params[param]: 
     107##                    if isinstance(item, str): 
     108##                        ret = scriptDetection(item, allowStyle=True) 
     109##                        if ret: 
     110##                            raise htmlScriptError(item) 
     111##                        ret = restrictedHTML(item, level) 
     112##                        if not ret: 
     113##                            raise htmlForbiddenTag(item) 
     114 
    74115    # raise error if form or iframe tags are used 
    75116    elif level == 1: 
    76117        #level 1 or default 
    77118        #raise error if script or style detected 
    78         ret = None 
    79119        for param in params.keys(): 
    80120            if isinstance(params[param], str): 
    81                 ret = scriptDetection(params[param]) 
    82                 if not restrictedHTML(params[param]): 
     121                if not restrictedHTML(params[param], level): 
    83122                    raise htmlForbiddenTag(params[param]) 
    84123            elif isinstance(params[param], list): 
    85124                for item in params[param]: 
    86125                    if isinstance(item, str): 
    87                         ret = scriptDetection(item) 
    88                         if ret: 
    89                             raise htmlScriptError(item) 
    90                         if not restrictedHTML(item): 
     126                        if not restrictedHTML(item, level): 
    91127                            raise htmlForbiddenTag(item) 
    92             if ret: 
    93                 raise htmlScriptError(params[param]) 
    94128 
    95129    elif level == 2: 
    96130        #raise error if script but style accepted 
    97         ret = None 
    98131        for param in params.keys(): 
    99132            if isinstance(params[param], str): 
    100                 ret = scriptDetection(params[param], allowStyle=True) 
    101                 if ret: 
    102                     raise htmlScriptError(params[param]) 
    103                 ret = restrictedHTML(params[param]) 
    104                 if not ret: 
     133                if not restrictedHTML(params[param], level): 
    105134                    raise htmlForbiddenTag(params[param]) 
    106135            elif isinstance(params[param], list): 
    107136                for item in params[param]: 
    108137                    if isinstance(item, str): 
    109                         ret = scriptDetection(item, allowStyle=True) 
    110                         if ret: 
    111                             raise htmlScriptError(item) 
    112                         ret = restrictedHTML(item) 
    113                         if not ret: 
     138                        if not restrictedHTML(params[param], level): 
    114139                            raise htmlForbiddenTag(item) 
    115140 
  • indico/MaKaC/webinterface/common/tools.py

    rb25817 ra30779  
    1919## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 
    2020from MaKaC.common.logger import Logger 
     21from HTMLParser import HTMLParser, HTMLParseError 
     22from xml.sax.saxutils import  unescape 
    2123 
    2224import re 
    23  
    24  
    2525 
    2626allowedTags = ["a","abbr","acronym","address","area", 
     
    3838               "p","pre", 
    3939               "q", 
    40                "s","samp","small","span","strike","strong","style","sub","sup", 
     40               "s","samp","small","span","strike","strong","sub","sup", 
    4141               "table","tbody","td","tfoot","th","thead","tr","tt", 
    4242               "u","ul", 
    4343               "var"] 
    4444 
    45 notAllowedTags = [ "applet", 
    46                    "base", "basefont", "button", 
    47                    "form", "frame", "frameset", 
    48                    "head", 
    49                    "iframe", "input", "isindex", 
    50                    "label", "link", 
    51                    "meta", 
    52                    "noframe", "noscript", 
    53                    "object", "optgroup", "option" 
    54                    "param", 
    55                    "script", "select", 
    56                    "textarea", 
    57                    "title", "embed"] 
    58  
     45allowedAttrs = ["align", "abbr", "alt", 
     46                "border", "bgcolor", 
     47                "class", "cellpadding", "color", "char", "charoff", "cite", "clear", "colspan", "compact", 
     48                "dir", "disabled", "face", 
     49                "href", "height", "headers","hreflang","hspace", 
     50                "id", "ismap", 
     51                "lang", 
     52                "name", "noshade", "nowrap", 
     53                "rel", "rev", "rowspan", "rules", 
     54                "size", "scope", "shape", "span","start", "summary", 
     55                "title", "tabindex", "type", 
     56                "valign", "value", "vspace", 
     57                "width"] 
     58 
     59allowedCssProperties = [ "background-color", "border-top-color", "border-top-style", "border-top-width", 
     60                         "border-top", "border-right-color", "border-right-style",  "border-right-width", 
     61                         "border-right", "border-bottom-color", "border-bottom-style", "border-bottom-width", 
     62                         "border-bottom", "border-left-color", "border-left-style", "border-left-width", 
     63                         "border-left", "border-color", "border-style", "border-width", "border", "bottom", 
     64                         "border-collapse", "border-spacing", 
     65                         "color", "clear", "clip", "caption-side", 
     66                         "display", "direction", 
     67                         "empty-cells", 
     68                         "float","font-size","font-family","font-style","font","font-variant","font-weight", 
     69                         "font-size-adjust", "font-stretch", 
     70                         "height", 
     71                         "left", "list-style-type", "list-style-position", "line-height", "letter-spacing", 
     72                         "marker-offset","margin","margin-left","margin-right","margin-top","margin-bottom","max-height", 
     73                         "min-height","max-width", "min-width", "marks", 
     74                         "overflow", "outline-color", "outline-style", "outline-width", "outline", "orphans", 
     75                         "position", "padding-top", "padding-right", "padding-bottom", "padding-left", "padding", 
     76                         "page", "page-break-after", "page-break-before", "page-break-inside", 
     77                         "quotes", 
     78                         "right", 
     79                         "size", 
     80                         "text-align", "top", "table-layout", "text-decoration", "text-indent", "text-shadow", 
     81                         "text-transform", 
     82                         "unicode-bidi", 
     83                         "visibility", "vertical-align", 
     84                         "width", "widows", "white-space", "word-spacing", "word-wrap", 
     85                         "z-index"] 
     86 
     87allowedCssKeywords = ['auto', 'aqua', 
     88                      'black', 'block', 'blue', 'bold', 'both', 'bolder', 'bottom', 'brown', 
     89                      'center', 'collapse', 
     90                      'dashed', 'dotted', 
     91                      'fuchsia', 
     92                      'gray', 'green', 
     93                      '!important', 'italic', 'inherit', 
     94                      'left', 'lime', 'large', 'larger', 'length', 'lighter', 
     95                      'maroon', 'medium', 'middle', 
     96                      'none', 'navy', 'normal', 'nowrap', 
     97                      'olive', 'oblique', 
     98                      'pointer', 'purple', 
     99                      'red', 'right', 
     100                      'small', 'smaller', 'solid', 'silver', 
     101                      'teal', 'top', 'transparent', 
     102                      'underline', 
     103                      'white', 
     104                      'x-small', 'xx-small', 'x-large', 'xx-large', 
     105                      'yellow'] 
     106 
     107allowedProtocols = [    'afs', 'aim', 
     108                        'callto', 
     109                        'feed', 'ftp', 
     110                        'http', 'https', 
     111                        'irc', 
     112                        'mailto', 
     113                        'news', 
     114                        'gopher', 
     115                        'nntp', 
     116                        'rsync', 'rtsp', 
     117                        'ssh', 'sftp', 
     118                        'tag', 'telnet', 
     119                        'urn', 
     120                        'webcal', 
     121                        'xmpp' ] 
     122 
     123urlProperties = ['action', 
     124                 'cite', 
     125                 'href', 
     126                 'longdesc', 
     127                 'src', 
     128                 'xlink:href', 'xml:base'] 
     129## 
     130##notAllowedTags = [ "applet", 
     131##                   "base", "basefont", "button", 
     132##                   "embed", 
     133##                   "form", "frame", "frameset", 
     134##                   "head", 
     135##                   "iframe", "input", "isindex", 
     136##                   "label", "link", 
     137##                   "meta", 
     138##                   "noframe", "noscript", 
     139##                   "object", "optgroup", "option" 
     140##                   "param", 
     141##                   "script", "select", 
     142##                   "textarea", 
     143##                   "title"] 
     144## 
    59145 
    60146# Generate the regular expression objects to found the not allowed tags 
    61147tagSearch = re.compile("< *[^<^>^ ]+",re.IGNORECASE|re.DOTALL) 
    62148 
    63 scriptStr = "\s*(s|&#115;|&#83;)"\ 
    64             "\s*(c|&#99;|&#67;)"\ 
    65             "\s*(r|&#114;|&#82;)"\ 
    66             "\s*(i|&#105;|&#73;)"\ 
    67             "\s*(p|&#112;|&#80;)"\ 
    68             "\s*(t|&#112;|&#84)" 
    69 script = re.compile("< *%s"%scriptStr,re.IGNORECASE|re.DOTALL) 
    70  
    71 iframeStr = "\s*(i|&#105;|&#73;)"\ 
    72             "\s*(f|&#102;|&#70;)"\ 
    73             "\s*(r|&#114;|&#82;)"\ 
    74             "\s*(a|&#97;|&#65;)"\ 
    75             "\s*(m|&#109;|&#77;)"\ 
    76             "\s*(e|&#101;|&#69;)" 
    77 iframe = re.compile("< *%s"%iframeStr,re.IGNORECASE|re.DOTALL) 
    78  
    79 formStr = "\s*(f|&#102;|&#70;)"\ 
    80           "\s*(o|&#111;|&#79;)"\ 
    81           "\s*(r|&#114;|&#82;)"\ 
    82           "\s*(m|&#109;|&#77;)" 
    83 form = re.compile("< *%s"%formStr,re.IGNORECASE|re.DOTALL) 
    84  
    85 onList = ["onblur","onchange","onclick","ondblclick","onerror","onfocus", 
    86           "onkeydown","onkeypress","onkeyup","onload","onmousedown","onmousemove", 
    87           "onmouseout","onmouseover","onmouseup","onreset","onresize","onselect", 
    88           "onsubmit","onunload"] 
    89  
    90 on = re.compile("<[^>]*(%s)"%"|".join(onList), re.IGNORECASE|re.DOTALL) 
    91  
    92 style = re.compile("<[^>]*style") 
    93  
    94 def scriptDetection(txt, allowStyle=False): 
    95     #search for "<script> tag 
    96     if script.findall(txt): 
    97         return True 
    98     #search for javascript event manager 
    99     if on.findall(txt): 
    100         return True 
    101     if not allowStyle: 
    102         #search for style 
    103         if style.findall(txt): 
    104             return True 
    105     return False 
     149##scriptStr = "\s*(s|&#115;|&#83;)"\ 
     150##            "\s*(c|&#99;|&#67;)"\ 
     151##            "\s*(r|&#114;|&#82;)"\ 
     152##            "\s*(i|&#105;|&#73;)"\ 
     153##            "\s*(p|&#112;|&#80;)"\ 
     154##            "\s*(t|&#112;|&#84)" 
     155##script = re.compile("< *%s"%scriptStr,re.IGNORECASE|re.DOTALL) 
     156## 
     157##iframeStr = "\s*(i|&#105;|&#73;)"\ 
     158##            "\s*(f|&#102;|&#70;)"\ 
     159##            "\s*(r|&#114;|&#82;)"\ 
     160##            "\s*(a|&#97;|&#65;)"\ 
     161##            "\s*(m|&#109;|&#77;)"\ 
     162##            "\s*(e|&#101;|&#69;)" 
     163##iframe = re.compile("< *%s"%iframeStr,re.IGNORECASE|re.DOTALL) 
     164## 
     165##formStr = "\s*(f|&#102;|&#70;)"\ 
     166##          "\s*(o|&#111;|&#79;)"\ 
     167##          "\s*(r|&#114;|&#82;)"\ 
     168##          "\s*(m|&#109;|&#77;)" 
     169##form = re.compile("< *%s"%formStr,re.IGNORECASE|re.DOTALL) 
     170## 
     171##onList = ["onblur","onchange","onclick","ondblclick","onerror","onfocus", 
     172##          "onkeydown","onkeypress","onkeyup","onload","onmousedown","onmousemove", 
     173##          "onmouseout","onmouseover","onmouseup","onreset","onresize","onselect", 
     174##          "onsubmit","onunload"] 
     175## 
     176##on = re.compile("<[^>]*(%s)"%"|".join(onList), re.IGNORECASE|re.DOTALL) 
     177## 
     178##style = re.compile("<[^>]*style") 
     179 
     180##def scriptDetection(txt, allowStyle=False): 
     181##    #search for "<script> tag 
     182##    if script.findall(txt): 
     183##        return True 
     184##    #search for javascript event manager 
     185##    if on.findall(txt): 
     186##        return True 
     187##    if not allowStyle: 
     188##        #search for style 
     189##        if style.findall(txt): 
     190##            return True 
     191##    return False 
    106192 
    107193##def restrictedHTML(txt): 
     
    112198##    return True 
    113199 
    114 def restrictedHTML(txt): 
    115     #use a black list 
    116     notFound = True 
    117     for tag in tagSearch.findall(txt): 
    118         tag = tag[1:].strip() 
    119         if tag: 
    120             if tag[0] == "/": 
    121                 tag = tag[1:].strip() 
    122             if tag: 
    123                 if tag.lower() in notAllowedTags: 
    124                     notFound = False 
    125     return notFound 
     200##def restrictedHTML(txt): 
     201##    #use a black list 
     202##    notFound = True 
     203##    for tag in tagSearch.findall(txt): 
     204##        tag = tag[1:].strip() 
     205##        if tag: 
     206##            if tag[0] == "/": 
     207##                tag = tag[1:].strip() 
     208##            if tag: 
     209##                if tag.lower() in notAllowedTags: 
     210##                    notFound = False 
     211##    return notFound 
    126212 
    127213##def restrictedHTML(txt): 
     
    135221##    return True 
    136222 
     223 
     224class HarmfulHTMLException: 
     225 
     226    def __init__(self, msg, pos = 0): 
     227        self.msg = 'Potentially harmful HTML: "%s" found at %s' % (msg, pos) 
     228 
     229class RestrictedHTMLParser( HTMLParser ): 
     230 
     231    _defaultSanitizationLevel = 1 
     232 
     233    def __init__( self, sanitizationLevel = _defaultSanitizationLevel): 
     234        HTMLParser.__init__(self) 
     235        if sanitizationLevel not in range(0,3): 
     236            sanitizationLevel = self._defaultSanitizationLevel 
     237        self._sanitizationLevel = sanitizationLevel 
     238 
     239    def getSanitizationLevel(self): 
     240        return self._sanitizationLevel 
     241 
     242    def setSanitizationLevel(self, sanitizationLevel): 
     243        if sanitizationLevel not in range(0,3): 
     244            sanitizationLevel = self._defaultSanitizationLevel 
     245        self._sanitizationLevel = sanitizationLevel 
     246 
     247    def handle_inlineStyle(self, style): 
     248        # disallow urls 
     249        style = re.compile('url\s*\(\s*[^\s)]+?\s*\)\s*').sub(' ', style) 
     250        # gauntlet 
     251        if not re.match("""^([\-:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$""", style): raise HarmfulHTMLException(style, self.getpos()) 
     252        if not re.match("^\s*([-\w]+\s*:[^:;]*(;\s*|$))*$", style): raise HarmfulHTMLException(style, self.getpos()) 
     253        for prop, value in re.findall("([-\w]+)\s*:\s*([^:;]*)", style): 
     254            if not value: continue 
     255            if prop.lower() in allowedCssProperties or \ 
     256               prop.split('-')[0].lower() in ['background', 'border', 'margin', 'padding']: 
     257                for keyword in value.split(): 
     258                    if not keyword.lower() in allowedCssKeywords and \ 
     259                        not re.match("^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\-?\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$", keyword): 
     260                        raise HarmfulHTMLException(style, self.getpos()) 
     261            else: 
     262                raise HarmfulHTMLException(style, self.getpos()) 
     263 
     264    def handle_starttag(self, tag, attrs): 
     265        if tag not in allowedTags: 
     266            raise HarmfulHTMLException( tag, self.getpos() ) 
     267        for attr in attrs: 
     268            if self.getSanitizationLevel() >= 2 and attr[0] == 'style': 
     269                self.handle_inlineStyle(attr[1]) 
     270            elif attr[0] not in allowedAttrs: 
     271                raise HarmfulHTMLException(attr[0], self.getpos()) 
     272            elif attr[0] in urlProperties: 
     273                val_unescaped = re.sub("[`\000-\040\177-\240\s]+", '', unescape(attr[1])).lower() 
     274                #remove replacement characters from unescaped characters 
     275                val_unescaped = val_unescaped.replace(u"\ufffd", "") 
     276                if (re.match("^[a-z0-9][-+.a-z0-9]*:", val_unescaped) and 
     277                    (val_unescaped.split(':')[0] not in 
     278                     allowedProtocols)): 
     279                    raise HarmfulHTMLException(val_unescaped.split(':')[0], self.getpos()) 
     280 
     281 
     282def restrictedHTML(txt, sanitizationLevel): 
     283    try: 
     284        parser = RestrictedHTMLParser(sanitizationLevel) 
     285        parser.feed(txt) 
     286        parser.close() 
     287    except (HarmfulHTMLException, HTMLParseError): 
     288        return False 
     289    return True 
    137290 
    138291def hasTags(s): 
  • indico/MaKaC/webinterface/tpls/js/vars.js.tpl

    r6b4ff5 ra30779  
    44<% from MaKaC.rb_location import Location %> 
    55<% import simplejson %> 
     6<% import MaKaC.webinterface.common.tools as securityTools %> 
    67<%! 
    78config = Config.getInstance() 
     
    141142        Locations: <%= jsonEncode(locationList) %> 
    142143    }, 
     144 
     145    Security:{ 
     146        allowedTags: "<%= ",".join(securityTools.allowedTags) %>", 
     147        allowedAttributes: "<%= ",".join(securityTools.allowedAttrs) %>", 
     148        allowedCssKeywords: "<%= ",".join(securityTools.allowedCssKeywords) %>", 
     149        allowedCssProperties: "<%= ",".join(securityTools.allowedCssProperties) %>", 
     150        allowedProtocols: "<%= ",".join(securityTools.allowedProtocols) %>", 
     151        urlProperties: "<%= ",".join(securityTools.urlProperties) %>", 
     152        sanitizationLevel: <%= Config.getInstance().getSanitizationLevel() %> 
     153    }, 
     154 
    143155    Settings: { 
    144156        ExtAuthenticators: <%= extAuths %>, 
  • indico/htdocs/js/indico/Common/Loader.js

    r3cc6b6 ra30779  
    11include(ScriptRoot + "indico/Common/TimezoneSelector.js"); 
    22include(ScriptRoot + "indico/Common/IntelligentSearchBox.js"); 
     3include(ScriptRoot + "indico/Common/htmlparser.js"); 
  • indico/htdocs/js/indico/Common/htmlparser.js

    r203b37 ra30779  
    1414 * 
    1515 * // or to escape harmful html and scripts 
    16  * escapeHarmfulHTML(htmlString, params); 
     16 * escapeHarmfulHTML(htmlString, sanitizationLevel, params); 
    1717 * 
    1818 * // or to get an XML string: 
     
    100100                        }); 
    101101 
    102                         if ( self.handler.start ) 
    103                             self.handler.start( tagName, attrs, unary ); 
     102                        self.handler.start( tagName, attrs, unary ); 
    104103                    } 
    105104                }; 
     
    255254        }); 
    256255 
    257 //Regular Expressions for parsing properties and values 
    258 var whitespaces = /\s*/; 
    259 var propertyName = /[\w-]+/; 
    260 var propertyPattern = new RegExp( whitespaces.source + propertyName.source + whitespaces.source + /:/.source ); 
    261  
    262 var valueElementName = /#?[\w\/\:\.]+%?/; 
    263 var valueElementEnding = /(((\(?\)\s*;)|[)(,;\s]))?/; 
    264 const valueElement = new RegExp(whitespaces.source + valueElementName.source + whitespaces.source + valueElementEnding.source); 
    265  
    266 var singleValue = /\s*(\w+%?)\s*/ 
    267 var multipleValues = /((\s*[\w]+\s*%?,?)+)/; 
    268 var link = /(\s*\w+:\/\/[\w\/\.]+\s*)/; 
    269 var color = /(\s*#(\w){6}\s*)/; 
    270 var valuePatternName = new RegExp(color.source + "|(" + singleValue.source + "(\\s*\\((" + multipleValues.source + "|" + link.source +")\\)\\s*)?)"); 
    271 var valuePatternLoop = new RegExp("(" + whitespaces.source + valuePatternName.source + whitespaces.source + ")+"); 
    272 var valuePattern = new RegExp(valuePatternLoop.source + /;?/.source); 
    273  
    274256//Default parameters 
    275257 
    276258//Properties which can appear in a CSS 
    277 var propertyWhitelist = makeMap("background-color,border-top-color,border-top-style,border-top-width," + 
    278                                 "border-top,border-right-color,border-right-style,border-right-width," + 
    279                                 "border-right,border-bottom-color,border-bottom-style,border-bottom-width," + 
    280                                 "border-bottom,border-left-color,border-left-style,border-left-width," + 
    281                                 "border-left,border-color,border-style,border-width,border,bottom," + 
    282                                 "border-collapse,border-spacing," + 
    283                                 "color,clear,clip,caption-side," + 
    284                                 "display,direction," + 
    285                                 "empty-cells," + 
    286                                 "float,font-size,font-family,font-style,font,font-variant,font-weight," + 
    287                                 "font-size-adjust,font-stretch," + 
    288                                 "height," + 
    289                                 "left,list-style-type,list-style-position,line-height,letter-spacing," + 
    290                                 "marker-offset,margin,margin-left,margin-rigth,margin-top,margin-bottom,max-height," + 
    291                                 "min-height,max-width,min-width,marks," + 
    292                                 "overflow,outline-color,outline-style,outline-width,outline,orphans," + 
    293                                 "position,padding-top,padding-right,padding-bottom,padding-left,padding," + 
    294                                 "page,page-break-after,page-break-before,page-break-inside," + 
    295                                 "quotes," + 
    296                                 "right," + 
    297                                 "size," + 
    298                                 "text-align,top,table-layout,text-decoration,text-indent,text-shadow," + 
    299                                 "text-transform," + 
    300                                 "unicode-bidi," + 
    301                                 "visibility,vertical-align," + 
    302                                 "width,widows,white-space,word-spacing,word-wrap," + 
    303                                 "z-index"); 
    304  
    305 //Values which cannot appear in a CSS 
    306 var valueBlacklist = makeMap(""); 
     259var propertyWhitelist = makeMap( Indico.Security.allowedCssProperties ); 
     260 
     261//Keywords which can appear in a CSS 
     262var keywordWhitelist = makeMap( Indico.Security.allowedCssKeywords ); 
    307263 
    308264type("inlineCSSParser",[], 
    309265        { 
    310266            parse: function(){ 
    311  
    312267                var result = ""; 
    313                 var property; 
    314                 var values; 
    315268                var security = 0; 
    316269 
    317                 while (this.css.replace(/\s+/g,"")){ 
    318  
    319                     property = this.getProperty(); 
    320                     values = this.getValues(); 
    321  
    322                     if(this.propertyWhitelist[property]) 
    323                     { 
    324                         result += property + ":"; 
    325                         for(var i = 0; i < values.length; ++i) 
    326                             if(!this.valueBlacklist[values[i]]) 
    327                                 result += " " + values[i]; 
    328                             else 
    329                                 security = 1; 
    330                     } 
    331                     else 
    332                         security = 1; 
     270                // disallow urls 
     271                this.css = this.css.replace(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ') 
     272                // gauntlet 
     273                if (!(/^([\-:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$/).test(this.css)) 
     274                    throw "Parse Error: " + this.css; 
     275                if (!(/^\s*([-\w]+\s*:[^:;]*(;\s*|$))*$/).test(this.css)) 
     276                    throw "Parse Error: " + this.css; 
     277 
     278                parts = this.css.split(/;/g); 
     279                for( i in parts){ 
     280                    if( parts[i].replace(/\s/g,'') != ""){ 
     281                        property = parts[i].split(/:/g)[0].replace(/\s/g,''); 
     282                        values = parts[i].split(/:/g)[1].split(/\s/g); 
     283                        value = "" 
     284                        for( j in values){ 
     285                           if( values[j].replace(/\s/g,'')) 
     286                               if((/^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\-?\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/).test(values[j].replace(/\s/g,'')) || 
     287                               this.keywordWhitelist[values[j].replace(/\s/g,'')]) 
     288                                   value += " " + values[j].replace(/\s/g,'') 
     289                               else 
     290                                   security = 1; 
     291                        } 
     292                        if (this.propertyWhitelist[property] && value){ 
     293                            result += property + ':' + value + ';'; 
     294                        } 
     295                        else{ 
     296                            security = 1; 
     297                        } 
     298                    } 
    333299                } 
     300 
    334301                return [result, security]; 
    335             }, 
    336  
    337             getProperty: function(){ 
    338                 var property = this.css.match(this.propertyPattern); 
    339                 if(property){ 
    340                     this.css = this.css.substring(property[0].length); 
    341                     return property[0].substring(0, property[0].length - 1).replace(/\s+/g,""); 
    342302                } 
    343                 throw "Parse Error: " + this.css; 
    344             }, 
    345  
    346             getValues: function(){ 
    347                 var values = this.css.match(this.valuePattern); 
    348                 var valuesTable = []; 
    349                 var tmp; 
    350                 if(values) { 
    351                     this.css = this.css.substring(values[0].length); 
    352                     while(values[0] != ""){ 
    353                         tmp = values[0].match(this.valueElement); 
    354                         values[0] = values[0].substring(tmp[0].length); 
    355                         valuesTable.push(tmp[0].replace(/\s+/g,"")); 
    356                     } 
    357                     return valuesTable; 
    358  
    359                 } 
    360                 throw "Parse Error: " + this.css; 
    361             } 
     303 
    362304        }, 
    363305        /**Cleans text from not proper css properties and values. 
     
    365307         * @param (dictionary) params Parser parameters. If not set defaults are used. Take a look at the beginning of the type. 
    366308         *                            params.propertyWhitelist - accepted list of properties. 
    367          *                            params.valueBlacklist - list of forbidden values. 
     309         *                            params.keywordWhitelistt - list of allowed css keywords. 
    368310         * @returns (tuple(string, security)) First element of the tuple is parsed text. Second one indicates which actions was done during parsing: 
    369311         *                                    0 - removing removing not necessary whitespace or empty values. 
     
    372314        function(css, params){ 
    373315            this.css = css; 
    374  
    375             // Regular Expressions for parsing properties and values 
    376             this.propertyPattern = propertyPattern; 
    377  
    378             this.valueElement = valueElement; 
    379  
    380             this.valuePattern = valuePattern; 
    381  
    382             //Properties which can appear in a CSS 
    383316            this.propertyWhitelist = params && params.propertyWhitelist ? params.propertyWhitelist : propertyWhitelist; 
    384             //Values which cannot appear in a CSS 
    385             this.valueBlacklist = params && params.valueBlacklist ? params.valueBlacklist : valueBlacklist; 
     317            this.keywordWhitelist = params && params.keywordWhitelist ? params.keywordWhitelist : keywordWhitelist; 
    386318        }); 
    387319 
    388320 
    389 var defaultTagWhitelist = makeMap(  "a,abbr,acronym,address,area," + 
    390                                     "b,bdo,big,blockquote,br," + 
    391                                     "caption,center,cite,code,col,colgroup," + 
    392                                     "dd,del,dir,div,dfn,dl,dt," + 
    393                                     "em," + 
    394                                     "fieldset,font," + 
    395                                     "h1,h2,h3,h4,h5,h6,hr," + 
    396                                     "i,img,ins," + 
    397                                     "kbd," + 
    398                                     "legend,li," + 
    399                                     "map,menu," + 
    400                                     "ol," + 
    401                                     "p,pre," + 
    402                                     "q," + 
    403                                     "s,samp,small,span,strike,strong,sub,sup," + 
    404                                     "table,tbody,td,tfoot,th,thead,tr,tt," + 
    405                                     "u,ul," + 
    406                                     "var"); 
    407  
    408 var defaultAttribWhitelist = makeMap("align,abbr,alt," + 
    409                                      "border,bgcolor," + 
    410                                      "class,cellpadding,color,char,charoff,cite,clear,colspan,compact," + 
    411                                      "dir,disabled,face," + 
    412                                      "href,height,headers,hreflang,hspace," + 
    413                                      "id,ismap," + 
    414                                      "lang," + 
    415                                       "name,noshade,nowrap," + 
    416                                      "rel,rev,rowspan,rules," + 
    417                                      "size,scope,shape,span,start,summary," + 
    418                                      "title,tabindex,type," + 
    419                                      "valign,value,vspace," + 
    420                                      "width"); 
     321var defaultTagWhitelist = makeMap( Indico.Security.allowedTags ); 
     322 
     323var defaultAttribWhitelist = makeMap( Indico.Security.allowedAttributes ); 
     324 
     325var defaultAllowedProtocols = makeMap( Indico.Security.allowedProtocols ); 
     326 
     327var urlProperties = makeMap( Indico.Security.urlProperties ) 
    421328 
    422329/**Cleans text from scripts, styles and potentialy harmful html. 
    423330 * @param (String) html Text to be parsed. 
     331  * @param sanitizationLevel Current indico sanitization level. If 0 or 3 text is not parsed at all. If 1 inline css script are deleted. If 2 text is parsed normally. 
    424332 * @param (dictionary) params Parser parameters. For further description see 'params' in HTMLParser and inlineCSSParser types. 
    425333 *                     params.tagWhitelist String containing tags to be accepted by parser in lowercase separated by coma(without whitespace). If not defined defualts is used. 
    426334 *                     params.attribWhitelist String containing attributes to be accepted by parser in lowercase separated by coma(without whitespace). If not defined defualts is used. 
     335 *                     params.allowedProtocols String contains allowed protocols. 
    427336 *                     params.strict If true while parsing style fails exception is thrown. False by default. 
    428337 * @returns (tuple(string, security)) First element of the tuple is parsed text. Second one indicates which actions was done during parsing: 
     
    431340 *                                    2 - removing scripts, objects, applets, embed etc. 
    432341 **/ 
    433 function escapeHarmfulHTML( html, params ) { 
    434  
    435     var self = this; 
    436     var strict = params && params.strict || false; 
    437  
    438     //Result string 
    439     var results = ""; 
    440  
    441     //Tags which can occur in the string. Other tags are omitted. 
    442     var tagWhitelist = params && params.tagWhitelist ? params.tagWhitelist : defaultTagWhitelist; 
    443  
    444     //Attributes allowed. 
    445     var attribWhitelist = params && params.attribWhitelist ? params.attribWhitelist : defaultAttribWhitelist; 
    446  
    447     var security = 0; 
    448  
    449     parser = new HTMLParser(html, { 
    450         start: function( tag, attrs, unary ) { 
    451         if( tagWhitelist[tag] ) { 
    452             results += "<" + tag; 
    453  
    454             for ( var i = 0; i < attrs.length; i++ ) 
    455                 if(attribWhitelist[ attrs[i].name ] || unary && attrs[i].name == '/') { 
    456                     if(attrs[i].name == "style") { 
     342function escapeHarmfulHTML( html, sanitizationLevel, params ) { 
     343 
     344    sanitizationLevel = sanitizationLevel == undefined ? Indico.Security.sanitizationLevel : sanitizationLevel; 
     345 
     346    if( sanitizationLevel == 1 || sanitizationLevel == 2  ) 
     347    { 
     348        var self = this; 
     349        var strict = params && params.strict || false; 
     350 
     351        //Result string 
     352        var results = ""; 
     353 
     354        //Tags which can occur in the string. Other tags are omitted. 
     355        var tagWhitelist = params && params.tagWhitelist ? params.tagWhitelist : defaultTagWhitelist; 
     356 
     357        //Attributes allowed. 
     358        var attribWhitelist = params && params.attribWhitelist ? params.attribWhitelist : defaultAttribWhitelist; 
     359 
     360        //Protocols allowed 
     361        var allowedProtocols = params && params.allowedProtocols ? params.allowedProtocols : defaultAllowedProtocols; 
     362 
     363        var security = 0; 
     364 
     365        parser = new HTMLParser(html, { 
     366            start: function( tag, attrs, unary ) { 
     367            if( tagWhitelist[tag] ) { 
     368                results += "<" + tag; 
     369 
     370                for ( var i = 0; i < attrs.length; i++ ) 
     371                    if(attrs[i].name == "style" && sanitizationLevel == 2) { 
    457372                        try{ 
    458373                            var cssParser = new inlineCSSParser(attrs[i].escaped, params); 
     
    461376                        catch(error){ 
    462377                            if(typeof error == "string" && error.indexOf("Parse Error") != -1 && !strict) 
    463                                 var tuple = ["",-1]; 
     378                                var tuple = ["",1]; 
    464379                            else 
    465380                                throw error; 
    466381                        } 
    467                         attrs[i].escaped = tuple[0]; 
     382                        if(tuple[0] != '') 
     383                            results += " " + attrs[i].name + '="' + tuple[0] + '"'; 
    468384                        security = max(security, tuple[1]); 
    469385                    } 
    470                     if(attrs[i].escaped) 
    471                         results += " " + attrs[i].name + '="' + attrs[i].escaped + '"'; 
    472                 } 
    473                 else 
    474                     security = max(security, 1); 
    475  
    476             results += (unary ? "/" : "") + ">"; 
     386                    else if(attribWhitelist[ attrs[i].name ] || unary && attrs[i].name == '/') { 
     387                        if(urlProperties[attrs[i].name]){ 
     388                            attrs[i].escaped = attrs[i].escaped.replace(/[`\000-\040\177-\240\s]+/g, ''); 
     389                            attrs[i].escaped = attrs[i].escaped.replace(/\ufffd/g, "") 
     390                            if (/^[a-z0-9][-+.a-z0-9]*:/.test(attrs[i].escaped) && !allowedProtocols[attrs[i].escaped.split(':')[0]]) { 
     391                                security = 1; 
     392                                continue; 
     393                            } 
     394                        } 
     395                        if( attrs[i].escaped != '') 
     396                            results += " " + attrs[i].name + '="' + attrs[i].escaped + '"'; 
     397                    } 
     398                    else 
     399                        security = max(security, 1); 
     400 
     401                results += (unary ? "/" : "") + ">"; 
     402            } 
     403            else 
     404                security = max(1, security); 
     405        }, 
     406        end: function( tag ) { 
     407            if(tagWhitelist[tag]) 
     408                results += "</" + tag + ">"; 
     409            else 
     410                security = max(1, security); 
     411        }, 
     412        chars: function( text ) { 
     413            results += text; 
     414        }, 
     415        comment: function( text ) { 
     416           // results += "<!--" + text + "-->"; 
     417        }, 
     418        escape: function(text, tag) { 
     419            security = max(2, security); 
    477420        } 
    478         else 
    479             security = max(1, security); 
    480     }, 
    481     end: function( tag ) { 
    482         if(tagWhitelist[tag]) 
    483             results += "</" + tag + ">"; 
    484         else 
    485             security = max(1, security); 
    486     }, 
    487     chars: function( text ) { 
    488         results += text; 
    489     }, 
    490     comment: function( text ) { 
    491        // results += "<!--" + text + "-->"; 
    492     }, 
    493     escape: function(text, tag) { 
    494         security = max(2, security); 
     421        }, params); 
     422 
     423        parser.parse(); 
     424 
     425        return [results, security]; 
    495426    } 
    496     }, params); 
    497  
    498     parser.parse(); 
    499  
    500     return [results, security]; 
     427    else 
     428        return [html,0]; 
    501429}; 
    502430 
  • indico/htdocs/js/indico/Legacy/Dialogs.js

    r203b37 ra30779  
    663663                           } 
    664664 
    665                            var popup = new ConfirmPopup("Warning!", Html.div({style: {width:pixels(300), textAlign:'justify'}},"Your minutes contains some potentially harmful " + security + ", which cannot be stored in the database. Clean the text manually or use the automatic Indico cleaner."), cleaningFunction); 
     665                           var popup = new ConfirmPopup($T("Warning!"), Html.div({style: {width:pixels(300), textAlign:'justify'}},$T("Your minutes contains some potentially harmful " + security + ", which cannot be stored in the database. Clean the text manually or use the automatic Indico cleaner.")), cleaningFunction); 
    666666                           popup.draw = function(){ 
    667667                               var self = this; 
     
    691691                       if(typeof error == "string" && error.indexOf("Parse Error") != -1){ 
    692692                           closeMinutes = false; 
    693                            var popup = new WarningPopup("Warning!", "Format of your minutes is invalid. Please check the syntax."); 
     693                           var popup = new WarningPopup($T("Warning!"), $T("Format of your minutes is invalid. Please check the syntax.")); 
    694694                           popup.open(); 
    695695                       } 
  • indico/htdocs/js/indico/Legacy/Loader.js

    r1113bb ra30779  
    22include(ScriptRoot + "indico/Legacy/Dialogs.js"); 
    33include(ScriptRoot + "indico/Legacy/Util.js"); 
    4 include(ScriptRoot + "indico/Legacy/htmlparser.js"); 
    5  
Note: See TracChangeset for help on using the changeset viewer.