def __call__(self, *args, **kw): tainted = 0 args = list(args) for i in range(len(args)): if isinstance(args[i], TaintedString): tainted = 1 args[i] = str(args[i]) for k, v in kw.items(): if isinstance(v, TaintedString): tainted = 1 kw[k] = str(v) args = tuple(args) retval = self._method(*args, **kw) if tainted and isinstance(retval, StringType) and '<' in retval: retval = TaintedString(retval) return retval
def render(self, md): args = self.args have_arg = args.has_key name = self.__name__ val = self.expr if val is None: if md.has_key(name): if have_arg('url'): val = md.getitem(name, 0) val = val.absolute_url() else: val = md[name] else: if have_arg('missing'): return args['missing'] else: raise KeyError, name else: val = val.eval(md) if have_arg('url'): val = val.absolute_url() __traceback_info__ = name, val, args if have_arg('null') and not val and val != 0: # check for null (false but not zero, including None, [], '') return args['null'] # handle special formats defined using fmt= first if have_arg('fmt'): _get = getattr(md, 'guarded_getattr', None) if _get is None: _get = getattr fmt = args['fmt'] if have_arg('null') and not val and val != 0: try: if hasattr(val, fmt): val = _get(val, fmt)() elif special_formats.has_key(fmt): if fmt == 'html-quote' and \ isinstance(val, TaintedString): # TaintedStrings will be quoted by default, don't # double quote. pass else: val = special_formats[fmt](val, name, md) elif fmt == '': val = '' else: if isinstance(val, TaintedString): val = TaintedString(fmt % val) else: val = fmt % val except: t, v = sys.exc_type, sys.exc_value if hasattr(sys, 'exc_info'): t, v = sys.exc_info()[:2] if val is None or not str(val): return args['null'] raise t, v else: # We duplicate the code here to avoid exception handler # which tends to screw up stack or leak if hasattr(val, fmt): val = _get(val, fmt)() elif special_formats.has_key(fmt): if fmt == 'html-quote' and \ isinstance(val, TaintedString): # TaintedStrings will be quoted by default, don't # double quote. pass else: val = special_formats[fmt](val, name, md) elif fmt == '': val = '' else: if isinstance(val, TaintedString): val = TaintedString(fmt % val) else: val = fmt % val # finally, pump it through the actual string format... fmt = self.fmt if fmt == 's': # Keep tainted strings as tainted strings here. if not isinstance(val, TaintedString): val = ustr(val) else: # Keep tainted strings as tainted strings here. wastainted = 0 if isinstance(val, TaintedString): wastainted = 1 val = ('%' + self.fmt) % (val, ) if wastainted and '<' in val: val = TaintedString(val) # next, look for upper, lower, etc for f in self.modifiers: if f.__name__ == 'html_quote' and isinstance(val, TaintedString): # TaintedStrings will be quoted by default, don't double quote. continue val = f(val) if have_arg('size'): size = args['size'] try: size = int(size) except: raise ValueError, ( '''a <code>size</code> attribute was used in a <code>var</code> tag with a non-integer value.''') if len(val) > size: val = val[:size] l = val.rfind(' ') if l > size / 2: val = val[:l + 1] if have_arg('etc'): l = args['etc'] else: l = '...' val = val + l if isinstance(val, TaintedString): val = val.quoted() return val
def processInputs( self, # "static" variables that we want to be local for speed SEQUENCE=1, DEFAULT=2, RECORD=4, RECORDS=8, REC=12, # RECORD | RECORDS EMPTY=16, CONVERTED=32, hasattr=hasattr, getattr=getattr, setattr=setattr, search_type=re.compile('(:[a-zA-Z][-a-zA-Z0-9_]+|\\.[xy])$').search, ): """Process request inputs We need to delay input parsing so that it is done under publisher control for error handling purposes. """ response = self.response environ = self.environ method = environ.get('REQUEST_METHOD','GET') if method != 'GET': fp = self.stdin else: fp = None form = self.form other = self.other taintedform = self.taintedform # If 'QUERY_STRING' is not present in environ # FieldStorage will try to get it from sys.argv[1] # which is not what we need. if 'QUERY_STRING' not in environ: environ['QUERY_STRING'] = '' meth = None fs = ZopeFieldStorage(fp=fp,environ=environ,keep_blank_values=1) if not hasattr(fs,'list') or fs.list is None: # XXX begin monkeypatch contentType = None if fs.headers.has_key('content-type'): contentType = fs.headers['content-type'] # cut off a possible charset definition if contentType.find(';') >= 0: contentType = contentType[0:contentType.find(';')] # Hm, maybe it's an SOAP # SOAP 1.1 has HTTP SOAPAction Field # SOAP 1.2 has Content-Type application/soap+xml # # found a Content-Type of text/xml-SOAP on a Microsoft page # this page points 3 HTTP-Fields for SOAP Requests: # MethodName, InterfaceName (opt) and MessageCall # if (environ.has_key('HTTP_SOAPACTION') or (contentType in ['application/soap+xml', 'text/xml', 'text/xml-SOAP']) and fs.value.find('SOAP-ENV:Body') > 0): global soap if soap is None: import soap fp.seek(0) directlyProvides(self, ISOAPRequest) sparser = soap.SOAPParser(fp.read()) meth = sparser.method self.args = sparser.parse() response = soap.SOAPResponse(response) response._soap11 = environ.has_key('HTTP_SOAPACTION') response._soap12 = (contentType == 'application/soap+xml') response._contentType = contentType response._method = meth response._error_format = 'text/xml' other['RESPONSE'] = self.response = response other['REQUEST_METHOD'] = method self.maybe_webdav_client = 0 # XXX end monkeypatch # Stash XML request for interpretation by a SOAP-aware view other['SOAPXML'] = fs.value # Hm, maybe it's an XML-RPC elif ('content-type' in fs.headers and 'text/xml' in fs.headers['content-type'] and method == 'POST'): # Ye haaa, XML-RPC! global xmlrpc if xmlrpc is None: from ZPublisher import xmlrpc meth, self.args = xmlrpc.parse_input(fs.value) response = xmlrpc.response(response) other['RESPONSE'] = self.response = response self.maybe_webdav_client = 0 else: self._file = fs.file else: fslist = fs.list tuple_items = {} lt = type([]) CGI_name = isCGI_NAMEs defaults = {} tainteddefaults = {} converter = None for item in fslist: isFileUpload = 0 key = item.name if (hasattr(item,'file') and hasattr(item,'filename') and hasattr(item,'headers')): if (item.file and (item.filename is not None # RFC 1867 says that all fields get a content-type. # or 'content-type' in map(lower, item.headers.keys()) )): item = FileUpload(item) isFileUpload = 1 else: item = item.value flags = 0 character_encoding = '' # Variables for potentially unsafe values. tainted = None converter_type = None # Loop through the different types and set # the appropriate flags # We'll search from the back to the front. # We'll do the search in two steps. First, we'll # do a string search, and then we'll check it with # a re search. l = key.rfind(':') if l >= 0: mo = search_type(key,l) if mo: l = mo.start(0) else: l = -1 while l >= 0: type_name = key[l+1:] key = key[:l] c = get_converter(type_name, None) if c is not None: converter = c converter_type = type_name flags = flags | CONVERTED elif type_name == 'list': flags = flags | SEQUENCE elif type_name == 'tuple': tuple_items[key] = 1 flags = flags | SEQUENCE elif (type_name == 'method' or type_name == 'action'): if l: meth = key else: meth = item elif (type_name == 'default_method' or type_name == \ 'default_action'): if not meth: if l: meth = key else: meth = item elif type_name == 'default': flags = flags | DEFAULT elif type_name == 'record': flags = flags | RECORD elif type_name == 'records': flags = flags | RECORDS elif type_name == 'ignore_empty': if not item: flags = flags | EMPTY elif has_codec(type_name): character_encoding = type_name l = key.rfind(':') if l < 0: break mo = search_type(key,l) if mo: l = mo.start(0) else: l = -1 # Filter out special names from form: if key in CGI_name or key[:5] == 'HTTP_': continue # If the key is tainted, mark it so as well. tainted_key = key if '<' in key: tainted_key = TaintedString(key) if flags: # skip over empty fields if flags & EMPTY: continue #Split the key and its attribute if flags & REC: key = key.split(".") key, attr = ".".join(key[:-1]), key[-1] # Update the tainted_key if necessary tainted_key = key if '<' in key: tainted_key = TaintedString(key) # Attributes cannot hold a <. if '<' in attr: raise ValueError( "%s is not a valid record attribute name" % escape(attr)) # defer conversion if flags & CONVERTED: try: if character_encoding: # We have a string with a specified character # encoding. This gets passed to the converter # either as unicode, if it can handle it, or # crunched back down to latin-1 if it can not. item = unicode(item,character_encoding) if hasattr(converter,'convert_unicode'): item = converter.convert_unicode(item) else: item = converter( item.encode(default_encoding)) else: item = converter(item) # Flag potentially unsafe values if converter_type in ('string', 'required', 'text', 'ustring', 'utext'): if not isFileUpload and '<' in item: tainted = TaintedString(item) elif converter_type in ('tokens', 'lines', 'utokens', 'ulines'): is_tainted = 0 tainted = item[:] for i in range(len(tainted)): if '<' in tainted[i]: is_tainted = 1 tainted[i] = TaintedString(tainted[i]) if not is_tainted: tainted = None except: if (not item and not (flags & DEFAULT) and key in defaults): item = defaults[key] if flags & RECORD: item = getattr(item,attr) if flags & RECORDS: item = getattr(item[-1], attr) if tainted_key in tainteddefaults: tainted = tainteddefaults[tainted_key] if flags & RECORD: tainted = getattr(tainted, attr) if flags & RECORDS: tainted = getattr(tainted[-1], attr) else: raise elif not isFileUpload and '<' in item: # Flag potentially unsafe values tainted = TaintedString(item) # If the key is tainted, we need to store stuff in the # tainted dict as well, even if the value is safe. if '<' in tainted_key and tainted is None: tainted = item #Determine which dictionary to use if flags & DEFAULT: mapping_object = defaults tainted_mapping = tainteddefaults else: mapping_object = form tainted_mapping = taintedform #Insert in dictionary if key in mapping_object: if flags & RECORDS: #Get the list and the last record #in the list. reclist is mutable. reclist = mapping_object[key] x = reclist[-1] if tainted: # Store a tainted copy as well if tainted_key not in tainted_mapping: tainted_mapping[tainted_key] = deepcopy( reclist) treclist = tainted_mapping[tainted_key] lastrecord = treclist[-1] if not hasattr(lastrecord, attr): if flags & SEQUENCE: tainted = [tainted] setattr(lastrecord, attr, tainted) else: if flags & SEQUENCE: getattr(lastrecord, attr).append(tainted) else: newrec = record() setattr(newrec, attr, tainted) treclist.append(newrec) elif tainted_key in tainted_mapping: # If we already put a tainted value into this # recordset, we need to make sure the whole # recordset is built. treclist = tainted_mapping[tainted_key] lastrecord = treclist[-1] copyitem = item if not hasattr(lastrecord, attr): if flags & SEQUENCE: copyitem = [copyitem] setattr(lastrecord, attr, copyitem) else: if flags & SEQUENCE: getattr(lastrecord, attr).append(copyitem) else: newrec = record() setattr(newrec, attr, copyitem) treclist.append(newrec) if not hasattr(x,attr): #If the attribute does not #exist, setit if flags & SEQUENCE: item = [item] setattr(x,attr,item) else: if flags & SEQUENCE: # If the attribute is a # sequence, append the item # to the existing attribute y = getattr(x, attr) y.append(item) setattr(x, attr, y) else: # Create a new record and add # it to the list n = record() setattr(n,attr,item) mapping_object[key].append(n) elif flags & RECORD: b = mapping_object[key] if flags & SEQUENCE: item = [item] if not hasattr(b, attr): # if it does not have the # attribute, set it setattr(b, attr, item) else: # it has the attribute so # append the item to it setattr(b, attr, getattr(b, attr) + item) else: # it is not a sequence so # set the attribute setattr(b, attr, item) # Store a tainted copy as well if necessary if tainted: if tainted_key not in tainted_mapping: tainted_mapping[tainted_key] = deepcopy( mapping_object[key]) b = tainted_mapping[tainted_key] if flags & SEQUENCE: seq = getattr(b, attr, []) seq.append(tainted) setattr(b, attr, seq) else: setattr(b, attr, tainted) elif tainted_key in tainted_mapping: # If we already put a tainted value into this # record, we need to make sure the whole record # is built. b = tainted_mapping[tainted_key] if flags & SEQUENCE: seq = getattr(b, attr, []) seq.append(item) setattr(b, attr, seq) else: setattr(b, attr, item) else: # it is not a record or list of records found = mapping_object[key] if tainted: # Store a tainted version if necessary if tainted_key not in tainted_mapping: copied = deepcopy(found) if isinstance(copied, lt): tainted_mapping[tainted_key] = copied else: tainted_mapping[tainted_key] = [copied] tainted_mapping[tainted_key].append(tainted) elif tainted_key in tainted_mapping: # We may already have encountered a tainted # value for this key, and the tainted_mapping # needs to hold all the values. tfound = tainted_mapping[tainted_key] if isinstance(tfound, lt): tainted_mapping[tainted_key].append(item) else: tainted_mapping[tainted_key] = [tfound, item] if type(found) is lt: found.append(item) else: found = [found,item] mapping_object[key] = found else: # The dictionary does not have the key if flags & RECORDS: # Create a new record, set its attribute # and put it in the dictionary as a list a = record() if flags & SEQUENCE: item = [item] setattr(a,attr,item) mapping_object[key] = [a] if tainted: # Store a tainted copy if necessary a = record() if flags & SEQUENCE: tainted = [tainted] setattr(a, attr, tainted) tainted_mapping[tainted_key] = [a] elif flags & RECORD: # Create a new record, set its attribute # and put it in the dictionary if flags & SEQUENCE: item = [item] r = mapping_object[key] = record() setattr(r,attr,item) if tainted: # Store a tainted copy if necessary if flags & SEQUENCE: tainted = [tainted] r = tainted_mapping[tainted_key] = record() setattr(r, attr, tainted) else: # it is not a record or list of records if flags & SEQUENCE: item = [item] mapping_object[key] = item if tainted: # Store a tainted copy if necessary if flags & SEQUENCE: tainted = [tainted] tainted_mapping[tainted_key] = tainted else: # This branch is for case when no type was specified. mapping_object = form if not isFileUpload and '<' in item: tainted = TaintedString(item) elif '<' in key: tainted = item #Insert in dictionary if key in mapping_object: # it is not a record or list of records found = mapping_object[key] if tainted: # Store a tainted version if necessary if tainted_key not in taintedform: copied = deepcopy(found) if isinstance(copied, lt): taintedform[tainted_key] = copied else: taintedform[tainted_key] = [copied] elif not isinstance(taintedform[tainted_key], lt): taintedform[tainted_key] = [ taintedform[tainted_key]] taintedform[tainted_key].append(tainted) elif tainted_key in taintedform: # We may already have encountered a tainted value # for this key, and the taintedform needs to hold # all the values. tfound = taintedform[tainted_key] if isinstance(tfound, lt): taintedform[tainted_key].append(item) else: taintedform[tainted_key] = [tfound, item] if type(found) is lt: found.append(item) else: found = [found,item] mapping_object[key] = found else: mapping_object[key] = item if tainted: taintedform[tainted_key] = tainted #insert defaults into form dictionary if defaults: for key, value in defaults.items(): tainted_key = key if '<' in key: tainted_key = TaintedString(key) if key not in form: # if the form does not have the key, # set the default form[key] = value if tainted_key in tainteddefaults: taintedform[tainted_key] = \ tainteddefaults[tainted_key] else: #The form has the key tdefault = tainteddefaults.get(tainted_key, value) if isinstance(value, record): # if the key is mapped to a record, get the # record r = form[key] # First deal with tainted defaults. if tainted_key in taintedform: tainted = taintedform[tainted_key] for k, v in tdefault.__dict__.items(): if not hasattr(tainted, k): setattr(tainted, k, v) elif tainted_key in tainteddefaults: # Find out if any of the tainted default # attributes needs to be copied over. missesdefault = 0 for k, v in tdefault.__dict__.items(): if not hasattr(r, k): missesdefault = 1 break if missesdefault: tainted = deepcopy(r) for k, v in tdefault.__dict__.items(): if not hasattr(tainted, k): setattr(tainted, k, v) taintedform[tainted_key] = tainted for k, v in value.__dict__.items(): # loop through the attributes and value # in the default dictionary if not hasattr(r, k): # if the form dictionary doesn't have # the attribute, set it to the default setattr(r,k,v) form[key] = r elif isinstance(value, lt): # the default value is a list l = form[key] if not isinstance(l, lt): l = [l] # First deal with tainted copies if tainted_key in taintedform: tainted = taintedform[tainted_key] if not isinstance(tainted, lt): tainted = [tainted] for defitem in tdefault: if isinstance(defitem, record): for k, v in defitem.__dict__.items(): for origitem in tainted: if not hasattr(origitem, k): setattr(origitem, k, v) else: if not defitem in tainted: tainted.append(defitem) taintedform[tainted_key] = tainted elif tainted_key in tainteddefaults: missesdefault = 0 for defitem in tdefault: if isinstance(defitem, record): try: for k, v in \ defitem.__dict__.items(): for origitem in l: if not hasattr( origitem, k): missesdefault = 1 raise NestedLoopExit except NestedLoopExit: break else: if not defitem in l: missesdefault = 1 break if missesdefault: tainted = deepcopy(l) for defitem in tdefault: if isinstance(defitem, record): for k, v in ( defitem.__dict__.items()): for origitem in tainted: if not hasattr( origitem, k): setattr(origitem, k, v) else: if not defitem in tainted: tainted.append(defitem) taintedform[tainted_key] = tainted for x in value: # for each x in the list if isinstance(x, record): # if the x is a record for k, v in x.__dict__.items(): # loop through each # attribute and value in # the record for y in l: # loop through each # record in the form # list if it doesn't # have the attributes # in the default # dictionary, set them if not hasattr(y, k): setattr(y, k, v) else: # x is not a record if not x in l: l.append(x) form[key] = l else: # The form has the key, the key is not mapped # to a record or sequence so do nothing pass # Convert to tuples if tuple_items: for key in tuple_items.keys(): # Split the key and get the attr k = key.split( ".") k,attr = '.'.join(k[:-1]), k[-1] a = attr new = '' # remove any type_names in the attr while not a =='': a = a.split( ":") a,new = ':'.join(a[:-1]), a[-1] attr = new if k in form: # If the form has the split key get its value tainted_split_key = k if '<' in k: tainted_split_key = TaintedString(k) item =form[k] if isinstance(item, record): # if the value is mapped to a record, check if it # has the attribute, if it has it, convert it to # a tuple and set it if hasattr(item,attr): value = tuple(getattr(item,attr)) setattr(item,attr,value) else: # It is mapped to a list of records for x in item: # loop through the records if hasattr(x, attr): # If the record has the attribute # convert it to a tuple and set it value = tuple(getattr(x,attr)) setattr(x,attr,value) # Do the same for the tainted counterpart if tainted_split_key in taintedform: tainted = taintedform[tainted_split_key] if isinstance(item, record): seq = tuple(getattr(tainted, attr)) setattr(tainted, attr, seq) else: for trec in tainted: if hasattr(trec, attr): seq = getattr(trec, attr) seq = tuple(seq) setattr(trec, attr, seq) else: # the form does not have the split key tainted_key = key if '<' in key: tainted_key = TaintedString(key) if key in form: # if it has the original key, get the item # convert it to a tuple item = form[key] item = tuple(form[key]) form[key] = item if tainted_key in taintedform: tainted = tuple(taintedform[tainted_key]) taintedform[tainted_key] = tainted if meth: if 'PATH_INFO' in environ: path = environ['PATH_INFO'] while path[-1:] == '/': path = path[:-1] else: path = '' other['PATH_INFO'] = path = "%s/%s" % (path,meth) self._hacked_path = 1
def render(self, md): args=self.args have_arg=args.has_key name=self.__name__ val=self.expr if val is None: if md.has_key(name): if have_arg('url'): val=md.getitem(name,0) val=val.absolute_url() else: val = md[name] else: if have_arg('missing'): return args['missing'] else: raise KeyError, name else: val=val.eval(md) if have_arg('url'): val=val.absolute_url() __traceback_info__=name, val, args if have_arg('null') and not val and val != 0: # check for null (false but not zero, including None, [], '') return args['null'] # handle special formats defined using fmt= first if have_arg('fmt'): _get = getattr(md, 'guarded_getattr', None) if _get is None: _get = getattr fmt=args['fmt'] if have_arg('null') and not val and val != 0: try: if hasattr(val, fmt): val = _get(val, fmt)() elif special_formats.has_key(fmt): if fmt == 'html-quote' and \ isinstance(val, TaintedString): # TaintedStrings will be quoted by default, don't # double quote. pass else: val = special_formats[fmt](val, name, md) elif fmt=='': val='' else: if isinstance(val, TaintedString): val = TaintedString(fmt % val) else: val = fmt % val except: t, v= sys.exc_type, sys.exc_value if hasattr(sys, 'exc_info'): t, v = sys.exc_info()[:2] if val is None or not str(val): return args['null'] raise t, v else: # We duplicate the code here to avoid exception handler # which tends to screw up stack or leak if hasattr(val, fmt): val = _get(val, fmt)() elif special_formats.has_key(fmt): if fmt == 'html-quote' and \ isinstance(val, TaintedString): # TaintedStrings will be quoted by default, don't # double quote. pass else: val = special_formats[fmt](val, name, md) elif fmt=='': val='' else: if isinstance(val, TaintedString): val = TaintedString(fmt % val) else: val = fmt % val # finally, pump it through the actual string format... fmt=self.fmt if fmt=='s': # Keep tainted strings as tainted strings here. if not isinstance(val, TaintedString): val=ustr(val) else: # Keep tainted strings as tainted strings here. wastainted = 0 if isinstance(val, TaintedString): wastainted = 1 val = ('%'+self.fmt) % (val,) if wastainted and '<' in val: val = TaintedString(val) # next, look for upper, lower, etc for f in self.modifiers: if f.__name__ == 'html_quote' and isinstance(val, TaintedString): # TaintedStrings will be quoted by default, don't double quote. continue val=f(val) if have_arg('size'): size=args['size'] try: size=int(size) except: raise ValueError,( '''a <code>size</code> attribute was used in a <code>var</code> tag with a non-integer value.''') if len(val) > size: val=val[:size] l=val.rfind(' ') if l > size/2: val=val[:l+1] if have_arg('etc'): l=args['etc'] else: l='...' val=val+l if isinstance(val, TaintedString): val = val.quoted() return val