Commit 22206337 authored by Martijn Pieters's avatar Martijn Pieters

Big change

- Make DTML automatically html quote data indirectly taken from REQUEST
  which contain a '<'. Make sure (almost) all string operation preserve the
  taint on this data.

- Fix exceptions that use REQUEST data; quote the data.

- Don't let form and cookie values mask the REQUEST computed values such as
  URL0 and BASE1.
parent 3657c47f
......@@ -77,9 +77,17 @@ Zope Changes
- FileLibrary and GuestBook example applications gave anonymous
users the Manager proxy role when uploading files - a potential
vulnerability on production servers.
vulnerability on production servers.
- Exceptions that use untrusted information from a REQUEST object in
the exception message now html-quote that information.
Features Added
- <dtml-var name> and &dtml.-name; will now automatically HTML-quote
unsafe data taken implictly from the REQUEST object. Data taken
explicitly from the REQUEST object is not affected, as well as any
other data not originating from REQUEST.
- ZCatalog index management ui is now integrated into ZCatalog rather
than being a subobject managment screen with different tabs.
......
......@@ -21,6 +21,7 @@ import ExtensionClass, Acquisition
from Permission import pname
from Owned import UnownableOwner
from Globals import InitializeClass
from cgi import escape
class RoleManager:
def manage_getPermissionMapping(self):
......@@ -64,7 +65,7 @@ class RoleManager:
raise 'Permission mapping error', (
"""Attempted to map a permission to a permission, %s,
that is not valid. This should never happen. (Waaa).
""" % p)
""" % escape(p))
setPermissionMapping(name, wrapper, p)
......@@ -118,7 +119,7 @@ class PM(ExtensionClass.Base):
# We want to make sure that any non-explicitly set methods are
# private!
if name.startswith('_') and name.endswith("_Permission"): return ''
raise AttributeError, name
raise AttributeError, escape(name)
PermissionMapper=PM
......
......@@ -12,7 +12,7 @@
##############################################################################
"""Access control support"""
__version__='$Revision: 1.53 $'[11:-2]
__version__='$Revision: 1.54 $'[11:-2]
from Globals import DTMLFile, MessageDialog, Dictionary
......@@ -20,6 +20,7 @@ from Acquisition import Implicit, Acquired, aq_get
import Globals, ExtensionClass, PermissionMapping, Products
from Permission import Permission
from App.Common import aq_base
from cgi import escape
ListType=type([])
......@@ -171,7 +172,8 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager):
return
raise 'Invalid Permission', (
"The permission <em>%s</em> is invalid." % permission_to_manage)
"The permission <em>%s</em> is invalid." %
escape(permission_to_manage))
_normal_manage_access=DTMLFile('dtml/access', globals())
......@@ -244,7 +246,7 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager):
valid_roles)
raise 'Invalid Permission', (
"The permission <em>%s</em> is invalid." % permission)
"The permission <em>%s</em> is invalid." % escape(permission))
def acquiredRolesAreUsedBy(self, permission):
"used by management screen"
......@@ -256,7 +258,7 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager):
return type(roles) is ListType and 'CHECKED' or ''
raise 'Invalid Permission', (
"The permission <em>%s</em> is invalid." % permission)
"The permission <em>%s</em> is invalid." % escape(permission))
# Local roles support
......
......@@ -38,6 +38,7 @@ import Globals, OFS.Folder, OFS.SimpleItem, os, Acquisition, Products
import re, zlib, Globals, cPickle, marshal, rotor
import ZClasses, ZClasses.ZClass, AccessControl.Owned
from urllib import quote
from cgi import escape
from OFS.Folder import Folder
from Factory import Factory
......@@ -254,14 +255,14 @@ class Product(Folder, PermissionManager):
"Product Distributions"
def __bobo_traverse__(self, REQUEST, name):
if name[-7:] != '.tar.gz': raise 'Invalid Name', name
if name[-7:] != '.tar.gz': raise 'Invalid Name', escape(name)
l=name.find('-')
id, version = name[:l], name[l+1:-7]
product=self.aq_parent
if product.id==id and product.version==version:
return Distribution(product)
raise 'Invalid version or product id', name
raise 'Invalid version or product id', escape(name)
Distributions=Distributions()
......
......@@ -142,6 +142,8 @@ class DTMLFile(Bindings, Explicit, ClassicHTMLFile):
# We're first, so get the REQUEST.
try:
req = self.aq_acquire('REQUEST')
if hasattr(req, 'taintWrapper'):
req = req.taintWrapper()
except: pass
bound_data['REQUEST'] = req
ns.this = bound_data['context']
......
......@@ -10,7 +10,7 @@
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"$Id: DT_String.py,v 1.49 2001/11/28 15:50:55 matt Exp $"
"$Id: DT_String.py,v 1.50 2002/08/01 16:00:39 mj Exp $"
import thread,re,exceptions,os
......@@ -404,6 +404,7 @@ class String:
# print '============================================================'
if mapping is None: mapping = {}
if hasattr(mapping, 'taintWrapper'): mapping = mapping.taintWrapper()
if not hasattr(self,'_v_cooked'):
try: changed=self.__changed__()
......
......@@ -10,8 +10,8 @@
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''$Id: DT_Util.py,v 1.86 2002/03/27 10:14:02 htrd Exp $'''
__version__='$Revision: 1.86 $'[11:-2]
'''$Id: DT_Util.py,v 1.87 2002/08/01 16:00:39 mj Exp $'''
__version__='$Revision: 1.87 $'[11:-2]
import re, os
from html_quote import html_quote, ustr # for import by other modules, dont remove!
......@@ -67,6 +67,49 @@ if LIMITED_BUILTINS:
else:
d[name] = f
try:
# Wrap the string module so it can deal with TaintedString strings.
from ZPublisher.TaintedString import TaintedString
from types import FunctionType, BuiltinFunctionType, StringType
import string
class StringModuleWrapper:
def __getattr__(self, key):
attr = getattr(string, key)
if (isinstance(attr, FunctionType) or
isinstance(attr, BuiltinFunctionType)):
return StringFunctionWrapper(attr)
else:
return attr
class StringFunctionWrapper:
def __init__(self, method):
self._method = method
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
d['string'] = StringModuleWrapper()
except ImportError:
# Use the string module already defined in RestrictedPython.Utilities
pass
# The functions below are meant to bind to the TemplateDict.
_marker = [] # Create a new marker object.
......
......@@ -145,8 +145,8 @@ Evaluating expressions without rendering results
''' # '
__rcs_id__='$Id: DT_Var.py,v 1.53 2002/05/21 14:41:41 andreasjung Exp $'
__version__='$Revision: 1.53 $'[11:-2]
__rcs_id__='$Id: DT_Var.py,v 1.54 2002/08/01 16:00:39 mj Exp $'
__version__='$Revision: 1.54 $'[11:-2]
from DT_Util import parse_params, name_param, str, ustr
import os, string, re, sys
......@@ -155,6 +155,7 @@ from cgi import escape
from html_quote import html_quote # for import by other modules, dont remove!
from types import StringType
from Acquisition import aq_base
from ZPublisher.TaintedString import TaintedString
class Var:
name='var'
......@@ -232,9 +233,19 @@ class Var:
if hasattr(val, fmt):
val = _get(val, fmt)()
elif special_formats.has_key(fmt):
val = special_formats[fmt](val, name, md)
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: val = 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]
......@@ -247,17 +258,40 @@ class Var:
if hasattr(val, fmt):
val = _get(val, fmt)()
elif special_formats.has_key(fmt):
val = special_formats[fmt](val, name, md)
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: val = 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': val=ustr(val)
else: val = ('%'+self.fmt) % (val,)
if fmt=='s':
# Keep tainted strings as tainted strings here.
if not isinstance(val, TaintedString):
val=str(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: val=f(val)
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']
......@@ -274,6 +308,9 @@ class Var:
else: l='...'
val=val+l
if isinstance(val, TaintedString):
val = val.quoted()
return val
__call__=render
......@@ -298,6 +335,9 @@ def url_quote_plus(v, name='(Unknown name)', md={}):
def newline_to_br(v, name='(Unknown name)', md={}):
# Unsafe data is explicitly quoted here; we don't expect this to be HTML
# quoted later on anyway.
if isinstance(v, TaintedString): v = v.quoted()
v=str(v)
if v.find('\r') >= 0: v=''.join(v.split('\r'))
if v.find('\n') >= 0: v='<br />\n'.join(v.split('\n'))
......@@ -368,7 +408,7 @@ def sql_quote(v, name='(Unknown name)', md={}):
This is needed to securely insert values into sql
string literals in templates that generate sql.
"""
if v.find("'") >= 0: return "''".join(v.split("'"))
if v.find("'") >= 0: return v.replace("'", "''")
return v
special_formats={
......@@ -389,7 +429,7 @@ special_formats={
}
def spacify(val):
if val.find('_') >= 0: val=" ".join(val.split('_'))
if val.find('_') >= 0: val=val.replace('_', ' ')
return val
modifiers=(html_quote, url_quote, url_quote_plus, newline_to_br,
......
......@@ -12,7 +12,7 @@
****************************************************************************/
static char cDocumentTemplate_module_documentation[] =
""
"\n$Id: cDocumentTemplate.c,v 1.46 2002/05/09 13:19:17 htrd Exp $"
"\n$Id: cDocumentTemplate.c,v 1.47 2002/08/01 16:00:39 mj Exp $"
;
#include "ExtensionClass.h"
......@@ -22,7 +22,7 @@ static PyObject *py___call__, *py___roles__, *py_AUTHENTICATED_USER;
static PyObject *py_hasRole, *py__proxy_roles, *py_Unauthorized;
static PyObject *py_Unauthorized_fmt, *py_guarded_getattr;
static PyObject *py__push, *py__pop, *py_aq_base, *py_renderNS;
static PyObject *py___class__, *html_quote, *ustr;
static PyObject *py___class__, *html_quote, *ustr, *untaint_name;
/* ----------------------------------------------------- */
......@@ -665,6 +665,7 @@ render_blocks_(PyObject *blocks, PyObject *rendered,
{
PyObject *block, *t, *args;
int l, i, k=0, append;
int skip_html_quote = 0;
if ((l=PyList_Size(blocks)) < 0) return -1;
for (i=0; i < l; i++)
......@@ -689,6 +690,23 @@ render_blocks_(PyObject *blocks, PyObject *rendered,
if (t == NULL) return -1;
if (! ( PyString_Check(t) || PyUnicode_Check(t) ) )
{
/* This might be a TaintedString object */
PyObject *untaintmethod = NULL;
untaintmethod = PyObject_GetAttr(t, untaint_name);
if (untaintmethod) {
/* Quote it */
UNLESS_ASSIGN(t,
PyObject_CallObject(untaintmethod, NULL)) return -1;
skip_html_quote = 1;
} else PyErr_Clear();
Py_XDECREF(untaintmethod);
}
if (! ( PyString_Check(t) || PyUnicode_Check(t) ) )
{
args = PyTuple_New(1);
......@@ -700,9 +718,9 @@ render_blocks_(PyObject *blocks, PyObject *rendered,
UNLESS(t) return -1;
}
if (PyTuple_GET_SIZE(block) == 3) /* html_quote */
if (skip_html_quote == 0 && PyTuple_GET_SIZE(block) == 3)
/* html_quote */
{
int skip_html_quote;
if (PyString_Check(t))
{
if (strchr(PyString_AS_STRING(t), '&') ||
......@@ -961,6 +979,7 @@ initcDocumentTemplate(void)
UNLESS(py_isDocTemp=PyString_FromString("isDocTemp")) return;
UNLESS(py_renderNS=PyString_FromString("__render_with_namespace__")) return;
UNLESS(py_blocks=PyString_FromString("blocks")) return;
UNLESS(untaint_name=PyString_FromString("__untaint__")) return;
UNLESS(py_acquire=PyString_FromString("aq_acquire")) return;
UNLESS(py___call__=PyString_FromString("__call__")) return;
UNLESS(py___roles__=PyString_FromString("__roles__")) return;
......
......@@ -12,9 +12,9 @@
##############################################################################
__doc__="""Object Manager
$Id: ObjectManager.py,v 1.155 2002/07/09 15:14:51 zigg Exp $"""
$Id: ObjectManager.py,v 1.156 2002/08/01 16:00:39 mj Exp $"""
__version__='$Revision: 1.155 $'[11:-2]
__version__='$Revision: 1.156 $'[11:-2]
import App.Management, Acquisition, Globals, CopySupport, Products
import os, App.FactoryDispatcher, re, Products
......@@ -34,6 +34,8 @@ import App.Common
from AccessControl import getSecurityManager
from zLOG import LOG, ERROR
import sys,fnmatch,copy
from cgi import escape
from types import StringType, UnicodeType
import XMLExportImport
customImporters={
......@@ -51,11 +53,12 @@ def checkValidId(self, id, allow_dup=0):
# check_valid_id() will be called again later with allow_dup
# set to false before the object is added.
if not id or (type(id) != type('')):
if not id or not isinstance(id, StringType):
if isinstance(id, UnicodeType): id = escape(id)
raise BadRequestException, ('Empty or invalid id specified', id)
if bad_id(id) is not None:
raise BadRequestException, (
'The id "%s" contains characters illegal in URLs.' % id)
'The id "%s" contains characters illegal in URLs.' % escape(id))
if id[0]=='_': raise BadRequestException, (
'The id "%s" is invalid - it begins with an underscore.' % id)
if id[:3]=='aq_': raise BadRequestException, (
......@@ -434,13 +437,13 @@ class ObjectManager(
for n in ids:
if n in p:
return MessageDialog(title='Not Deletable',
message='<EM>%s</EM> cannot be deleted.' % n,
message='<EM>%s</EM> cannot be deleted.' % escape(n),
action ='./manage_main',)
while ids:
id=ids[-1]
v=self._getOb(id, self)
if v is self:
raise 'BadRequest', '%s does not exist' % ids[-1]
raise 'BadRequest', '%s does not exist' % escape(ids[-1])
self._delObject(id)
del ids[-1]
if REQUEST is not None:
......@@ -511,7 +514,7 @@ class ObjectManager(
"""Import an object from a file"""
dirname, file=os.path.split(file)
if dirname:
raise BadRequestException, 'Invalid file name %s' % file
raise BadRequestException, 'Invalid file name %s' % escape(file)
instance_home = INSTANCE_HOME
zope_home = ZOPE_HOME
......@@ -521,7 +524,7 @@ class ObjectManager(
if os.path.exists(filepath):
break
else:
raise BadRequestException, 'File does not exist: %s' % file
raise BadRequestException, 'File does not exist: %s' % escape(file)
self._importObjectFromFile(filepath, verify=not not REQUEST,
set_owner=set_owner)
......
......@@ -12,7 +12,7 @@
##############################################################################
"""Property management"""
__version__='$Revision: 1.44 $'[11:-2]
__version__='$Revision: 1.45 $'[11:-2]
import ExtensionClass, Globals
import ZDOM
......@@ -21,6 +21,7 @@ from ZPublisher.Converters import type_converters
from Globals import DTMLFile, MessageDialog
from Acquisition import Implicit, aq_base
from Globals import Persistent
from cgi import escape
......@@ -121,7 +122,7 @@ class PropertyManager(ExtensionClass.Base, ZDOM.ElementWithAttributes):
def valid_property_id(self, id):
if not id or id[:1]=='_' or (id[:3]=='aq_') \
or (' ' in id) or hasattr(aq_base(self), id):
or (' ' in id) or hasattr(aq_base(self), id) or escape(id) != id:
return 0
return 1
......@@ -188,7 +189,7 @@ class PropertyManager(ExtensionClass.Base, ZDOM.ElementWithAttributes):
# the value to the type of the existing property.
self._wrapperCheck(value)
if not self.hasProperty(id):
raise 'Bad Request', 'The property %s does not exist' % id
raise 'Bad Request', 'The property %s does not exist' % escape(id)
if type(value)==type(''):
proptype=self.getPropertyType(id) or 'string'
if type_converters.has_key(proptype):
......@@ -197,7 +198,7 @@ class PropertyManager(ExtensionClass.Base, ZDOM.ElementWithAttributes):
def _delProperty(self, id):
if not self.hasProperty(id):
raise ValueError, 'The property %s does not exist' % id
raise ValueError, 'The property %s does not exist' % escape(id)
delattr(self,id)
self._properties=tuple(filter(lambda i, n=id: i['id'] != n,
self._properties))
......@@ -281,7 +282,7 @@ class PropertyManager(ExtensionClass.Base, ZDOM.ElementWithAttributes):
for name, value in props.items():
if self.hasProperty(name):
if not 'w' in propdict[name].get('mode', 'wd'):
raise 'BadRequest', '%s cannot be changed' % name
raise 'BadRequest', '%s cannot be changed' % escape(name)
self._updateProperty(name, value)
if REQUEST:
message="Saved changes."
......@@ -324,7 +325,7 @@ class PropertyManager(ExtensionClass.Base, ZDOM.ElementWithAttributes):
for id in ids:
if not hasattr(aq_base(self), id):
raise 'BadRequest', (
'The property <em>%s</em> does not exist' % id)
'The property <em>%s</em> does not exist' % escape(id))
if (not 'd' in propdict[id].get('mode', 'wd')) or (id in nd):
return MessageDialog(
title ='Cannot delete %s' % id,
......
......@@ -12,7 +12,7 @@
##############################################################################
"""Property sheets"""
__version__='$Revision: 1.84 $'[11:-2]
__version__='$Revision: 1.85 $'[11:-2]
import time, App.Management, Globals
from webdav.WriteLockInterface import WriteLockInterface
......@@ -26,6 +26,7 @@ from Globals import Persistent
from Traversable import Traversable
from Acquisition import aq_base
from AccessControl import getSecurityManager
from cgi import escape
class View(App.Management.Tabs, Base):
"""A view of an object, typically used for management purposes
......@@ -141,7 +142,7 @@ class PropertySheet(Traversable, Persistent, Implicit):
def valid_property_id(self, id):
if not id or id[:1]=='_' or (id[:3]=='aq_') \
or (' ' in id):
or (' ' in id) or escape(id) != id:
return 0
return 1
......@@ -180,7 +181,7 @@ class PropertySheet(Traversable, Persistent, Implicit):
# systems.
self._wrapperCheck(value)
if not self.valid_property_id(id):
raise 'Bad Request', 'Invalid property id, %s.' % id
raise 'Bad Request', 'Invalid property id, %s.' % escape(id)
if not self.property_extensible_schema__():
raise 'Bad Request', (
......@@ -190,7 +191,8 @@ class PropertySheet(Traversable, Persistent, Implicit):
if hasattr(aq_base(self),id):
if not (id=='title' and not self.__dict__.has_key(id)):
raise 'Bad Request', (
'Invalid property id, <em>%s</em>. It is in use.' % id)
'Invalid property id, <em>%s</em>. It is in use.' %
escape(id))
if meta is None: meta={}
prop={'id':id, 'type':type, 'meta':meta}
pself._properties=pself._properties+(prop,)
......@@ -211,10 +213,10 @@ class PropertySheet(Traversable, Persistent, Implicit):
# it will used to _replace_ the properties meta data.
self._wrapperCheck(value)
if not self.hasProperty(id):
raise 'Bad Request', 'The property %s does not exist.' % id
raise 'Bad Request', 'The property %s does not exist.' % escape(id)
propinfo=self.propertyInfo(id)
if not 'w' in propinfo.get('mode', 'wd'):
raise 'Bad Request', '%s cannot be changed.' % id
raise 'Bad Request', '%s cannot be changed.' % escape(id)
if type(value)==type(''):
proptype=propinfo.get('type', 'string')
if type_converters.has_key(proptype):
......@@ -232,13 +234,13 @@ class PropertySheet(Traversable, Persistent, Implicit):
# Delete the property with the given id. If a property with the
# given id does not exist, a ValueError is raised.
if not self.hasProperty(id):
raise 'Bad Request', 'The property %s does not exist.' % id
raise 'Bad Request', 'The property %s does not exist.' % escape(id)
vself=self.v_self()
if hasattr(vself, '_reserved_names'):
nd=vself._reserved_names
else: nd=()
if (not 'd' in self.propertyInfo(id).get('mode', 'wd')) or (id in nd):
raise 'Bad Request', '%s cannot be deleted.' % id
raise 'Bad Request', '%s cannot be deleted.' % escape(id)
delattr(vself, id)
pself=self.p_self()
pself._properties=tuple(filter(lambda i, n=id: i['id'] != n,
......@@ -262,7 +264,7 @@ class PropertySheet(Traversable, Persistent, Implicit):
# Return a mapping containing property meta-data
for p in self._propertyMap():
if p['id']==id: return p
raise ValueError, 'The property %s does not exist.' % id
raise ValueError, 'The property %s does not exist.' % escape(id)
def _propertyMap(self):
# Return a tuple of mappings, giving meta-data for properties.
......@@ -418,7 +420,7 @@ class PropertySheet(Traversable, Persistent, Implicit):
for name, value in props.items():
if self.hasProperty(name):
if not 'w' in propdict[name].get('mode', 'wd'):
raise 'BadRequest', '%s cannot be changed' % name
raise 'BadRequest', '%s cannot be changed' % escape(name)
self._updateProperty(name, value)
if REQUEST is not None:
return MessageDialog(
......@@ -487,13 +489,13 @@ class DAVProperties(Virtual, PropertySheet, View):
return getattr(self, method)()
def _setProperty(self, id, value, type='string', meta=None):
raise ValueError, '%s cannot be set.' % id
raise ValueError, '%s cannot be set.' % escape(id)
def _updateProperty(self, id, value):
raise ValueError, '%s cannot be updated.' % id
raise ValueError, '%s cannot be updated.' % escape(id)
def _delProperty(self, id):
raise ValueError, '%s cannot be deleted.' % id
raise ValueError, '%s cannot be deleted.' % escape(id)
def _propertyMap(self):
# Only use getlastmodified if returns a value
......
......@@ -18,6 +18,7 @@ from Acquisition import Acquired
import Persistence
from thread import allocate_lock
from zLOG import LOG, WARNING
from cgi import escape
broken_klasses={}
broken_klasses_lock = allocate_lock()
......@@ -42,7 +43,7 @@ class BrokenClass(Acquisition.Explicit, SimpleItem.Item,
def __getattr__(self, name):
if name[:3]=='_p_':
return BrokenClass.inheritedAttribute('__getattr__')(self, name)
raise AttributeError, name
raise AttributeError, escape(name)
manage=manage_main=Globals.DTMLFile('dtml/brokenEdit',globals())
manage_workspace=manage
......
......@@ -14,7 +14,7 @@
"""Text Index
"""
__version__ = '$Revision: 1.32 $'[11:-2]
__version__ = '$Revision: 1.33 $'[11:-2]
import re
......@@ -35,6 +35,7 @@ from BTrees.IIBTree import difference, weightedIntersection
from Lexicon import Lexicon
from types import *
from cgi import escape
class Op:
def __init__(self, name):
......@@ -482,7 +483,7 @@ class TextIndex(Persistent, Implicit, SimpleItem):
query_operator = operator_dict.get(qop)
if query_operator is None:
raise exceptions.RuntimeError, ("Invalid operator '%s' "
"for a TextIndex" % qop)
"for a TextIndex" % escape(qop))
r = None
for key in record.keys:
......
......@@ -13,7 +13,7 @@
"""Simple column indices"""
__version__='$Revision: 1.12 $'[11:-2]
__version__='$Revision: 1.13 $'[11:-2]
from Globals import Persistent
from Acquisition import Implicit
......@@ -30,6 +30,7 @@ import BTrees.Length
from Products.PluginIndexes.common.util import parseIndexRequest
import sys
from cgi import escape
_marker = []
......@@ -330,7 +331,7 @@ class UnIndex(Persistent, Implicit, SimpleItem):
# experimental code for specifing the operator
operator = record.get('operator',self.useOperator)
if not operator in self.operators :
raise RuntimeError,"operator not valid: %s" % operator
raise RuntimeError,"operator not valid: %s" % escape(operator)
# depending on the operator we use intersection or union
if operator=="or": set_func = union
......
......@@ -11,7 +11,7 @@
#
############################################################################
__version__='$Revision: 1.10 $'[11:-2]
__version__='$Revision: 1.11 $'[11:-2]
import Globals
from Persistence import Persistent
from ZODB import TimeStamp
......@@ -26,12 +26,13 @@ import SessionInterfaces
from SessionPermissions import *
from common import DEBUG
import os, time, random, string, binascii, sys, re
from cgi import escape
b64_trans = string.maketrans('+/', '-.')
b64_untrans = string.maketrans('-.', '+/')
badidnamecharsin = re.compile('[\?&;, ]').search
badcookiecharsin = re.compile('[;, ]').search
badidnamecharsin = re.compile('[\?&;,<> ]').search
badcookiecharsin = re.compile('[;,<>& ]').search
twodotsin = re.compile('(\w*\.){2,}').search
_marker = []
......@@ -119,7 +120,8 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
# somebody screwed with the REQUEST instance during
# this request.
raise BrowserIdManagerErr, (
'Ill-formed browserid in REQUEST.browser_id_: %s' % bid
'Ill-formed browserid in REQUEST.browser_id_: %s' %
escape(bid)
)
return bid
# fall through & ck id namespaces if bid is not in request.
......@@ -235,7 +237,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
def setBrowserIdName(self, k):
""" sets browser id name string """
if not (type(k) is type('') and k and not badidnamecharsin(k)):
raise BrowserIdManagerErr, 'Bad id name string %s' % repr(k)
raise BrowserIdManagerErr, 'Bad id name string %s' % escape(repr(k))
self.browserid_name = k
security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdName')
......@@ -309,7 +311,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
def setCookiePath(self, path=''):
""" sets cookie 'path' element for id cookie """
if not (type(path) is type('') and not badcookiecharsin(path)):
raise BrowserIdManagerErr, 'Bad cookie path %s' % repr(path)
raise BrowserIdManagerErr, 'Bad cookie path %s' % escape(repr(path))
self.cookie_path = path
security.declareProtected(ACCESS_CONTENTS_PERM, 'getCookiePath')
......@@ -323,7 +325,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
if type(days) not in (type(1), type(1.0)):
raise BrowserIdManagerErr,(
'Bad cookie lifetime in days %s (requires integer value)'
% repr(days)
% escape(repr(days))
)
self.cookie_life_days = int(days)
......@@ -337,7 +339,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
""" sets cookie 'domain' element for id cookie """
if type(domain) is not type(''):
raise BrowserIdManagerErr, (
'Cookie domain must be string: %s' % repr(domain)
'Cookie domain must be string: %s' % escape(repr(domain))
)
if not domain:
self.cookie_domain = ''
......@@ -346,11 +348,11 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
raise BrowserIdManagerErr, (
'Cookie domain must contain at least two dots (e.g. '
'".zope.org" or "www.zope.org") or it must be left blank. : '
'%s' % `domain`
'%s' % escape(`domain`)
)
if badcookiecharsin(domain):
raise BrowserIdManagerErr, (
'Bad characters in cookie domain %s' % `domain`
'Bad characters in cookie domain %s' % escape(`domain`)
)
self.cookie_domain = domain
......
......@@ -13,10 +13,10 @@
"""
Transient Object Container Class ('timeslice'-based design).
$Id: Transience.py,v 1.25 2002/06/21 01:51:43 chrism Exp $
$Id: Transience.py,v 1.26 2002/08/01 16:00:41 mj Exp $
"""
__version__='$Revision: 1.25 $'[11:-2]
__version__='$Revision: 1.26 $'[11:-2]
import Globals
from Globals import HTMLFile
......@@ -42,6 +42,7 @@ from TransientObject import TransientObject
import thread
import ThreadLock
import Queue
from cgi import escape
_marker = []
......@@ -324,14 +325,14 @@ class TransientObjectContainer(SimpleItem):
def _setTimeout(self, timeout_mins):
if type(timeout_mins) is not type(1):
raise TypeError, (timeout_mins, "Must be integer")
raise TypeError, (escape(`timeout_mins`), "Must be integer")
self._timeout_secs = t_secs = timeout_mins * 60
# timeout_slices == fewest number of timeslices that's >= t_secs
self._timeout_slices=int(math.ceil(float(t_secs)/self._period))
def _setLimit(self, limit):
if type(limit) is not type(1):
raise TypeError, (limit, "Must be integer")
raise TypeError, (escape(`limit`), "Must be integer")
self._limit = limit
security.declareProtected(MGMT_SCREEN_PERM, 'nudge')
......
......@@ -10,10 +10,11 @@
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__version__='$Revision: 1.16 $'[11:-2]
__version__='$Revision: 1.17 $'[11:-2]
import re
from types import ListType, TupleType, UnicodeType
from cgi import escape
def field2string(v):
if hasattr(v,'read'): return v.read()
......@@ -53,7 +54,7 @@ def field2int(v):
try: return int(v)
except ValueError:
raise ValueError, (
"An integer was expected in the value '%s'" % v
"An integer was expected in the value '%s'" % escape(v)
)
raise ValueError, 'Empty entry when <strong>integer</strong> expected'
......@@ -65,7 +66,8 @@ def field2float(v):
try: return float(v)
except ValueError:
raise ValueError, (
"A floating-point number was expected in the value '%s'" % v
"A floating-point number was expected in the value '%s'" %
escape(v)
)
raise ValueError, (
'Empty entry when <strong>floating-point number</strong> expected')
......@@ -81,7 +83,7 @@ def field2long(v):
try: return long(v)
except ValueError:
raise ValueError, (
"A long integer was expected in the value '%s'" % v
"A long integer was expected in the value '%s'" % escape(v)
)
raise ValueError, 'Empty entry when <strong>integer</strong> expected'
......@@ -100,7 +102,11 @@ def field2lines(v):
def field2date(v):
from DateTime import DateTime
v = field2string(v)
return DateTime(v)
try:
v = DateTime(v)
except DateTime.SyntaxError, e:
raise DateTime.SyntaxError, escape(e)
return v
def field2boolean(v):
return not not v
......
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__version__='$Revision: 1.1 $'[11:-2]
from cgi import escape
# TaintedStrings hold potentially dangerous untrusted data; anything that could
# possibly hold HTML is considered dangerous. DTML code will use the quoted
# value of this tring, and raised exceptions in Zope will use the repr()
# conversion.
class TaintedString:
def __init__(self, value):
self._value = value
def __str__(self):
return self._value
def __repr__(self):
return repr(self.quoted())
def __cmp__(self, o):
return cmp(self._value, o)
def __hash__(self):
return hash(self._value)
def __len__(self):
return len(self._value)
def __getitem__(self, index):
v = self._value[index]
if '<' in v:
v = self.__class__(v)
return v
def __getslice__(self, i, j):
i = max(i, 0)
j = max(j, 0)
v = self._value[i:j]
if '<' in v:
v = self.__class__(v)
return v
def __add__(self, o):
return self.__class__(self._value + o)
def __radd__(self, o):
return self.__class__(o + self._value)
def __mul__(self, o):
return self.__class__(self._value * o)
def __rmul__(self, o):
return self.__class__(o * self._value)
def __mod__(self, o):
return self.__class__(self._value % o)
def __int__(self):
return int(self._value)
def __float__(self):
return float(self._value)
def __long__(self):
return long(self._value)
def __getstate__(self):
# If an object tries to store a TaintedString, it obviously wasn't aware
# that it was playing with untrusted data. Complain acordingly.
raise SystemError("A TaintedString cannot be pickled. Code that "
"caused this TaintedString to be stored should be more careful "
"with untrusted data from the REQUEST.")
def __getattr__(self, a):
# for string methods support other than those defined below
return getattr(self._value, a)
# Python 2.2 only.
def decode(self, *args):
return self.__class__(self._value.decode(*args))
def encode(self, *args):
return self.__class__(self._value.encode(*args))
def expandtabs(self, *args):
return self.__class__(self._value.expandtabs(*args))
def replace(self, *args):
v = self._value.replace(*args)
if '<' in v:
v = self.__class__(v)
return v
def split(self, *args):
r = self._value.split(*args)
return map(lambda v, c=self.__class__: '<' in v and c(v) or v, r)
def splitlines(self, *args):
r = self._value.splitlines(*args)
return map(lambda v, c=self.__class__: '<' in v and c(v) or v, r)
def translate(self, *args):
v = self._value.translate(*args)
if '<' in v:
v = self.__class__(v)
return v
def quoted(self):
return escape(self._value, 1)
# As called by cDocumentTemplate
__untaint__ = quoted
def createSimpleWrapper(func):
return lambda s, f=func: s.__class__(getattr(s._value, f)())
def createOneArgWrapper(func):
return lambda s, a, f=func: s.__class__(getattr(s._value, f)(a))
simpleWrappedMethods = \
"capitalize lower lstrip rstrip strip swapcase title upper".split()
oneArgWrappedMethods = "center join ljust rjust".split()
for f in simpleWrappedMethods:
setattr(TaintedString, f, createSimpleWrapper(f))
for f in oneArgWrappedMethods:
setattr(TaintedString, f, createOneArgWrapper(f))
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
class TestTaintedString(unittest.TestCase):
def setUp(self):
self.unquoted = '<test attr="&">'
self.quoted = '&lt;test attr=&quot;&amp;&quot;&gt;'
self.tainted = self._getClass()(self.unquoted)
def _getClass(self):
from ZPublisher.TaintedString import TaintedString
return TaintedString
def testStr(self):
self.assertEquals(str(self.tainted), self.unquoted)
def testRepr(self):
self.assertEquals(repr(self.tainted), repr(self.quoted))
def testCmp(self):
self.assertEquals(cmp(self.tainted, self.unquoted), 0)
self.assertEquals(cmp(self.tainted, 'a'), -1)
self.assertEquals(cmp(self.tainted, '.'), 1)
def testHash(self):
hash = {}
hash[self.tainted] = self.quoted
hash[self.unquoted] = self.unquoted
self.assertEquals(hash[self.tainted], self.unquoted)
def testLen(self):
self.assertEquals(len(self.tainted), len(self.unquoted))
def testGetItem(self):
self.assert_(isinstance(self.tainted[0], self._getClass()))
self.assertEquals(self.tainted[0], '<')
self.failIf(isinstance(self.tainted[-1], self._getClass()))
self.assertEquals(self.tainted[-1], '>')
def testGetSlice(self):
self.assert_(isinstance(self.tainted[0:1], self._getClass()))
self.assertEquals(self.tainted[0:1], '<')
self.failIf(isinstance(self.tainted[1:], self._getClass()))
self.assertEquals(self.tainted[1:], self.unquoted[1:])
def testConcat(self):
self.assert_(isinstance(self.tainted + 'test', self._getClass()))
self.assertEquals(self.tainted + 'test', self.unquoted + 'test')
self.assert_(isinstance('test' + self.tainted, self._getClass()))
self.assertEquals('test' + self.tainted, 'test' + self.unquoted)
def testMultiply(self):
self.assert_(isinstance(2 * self.tainted, self._getClass()))
self.assertEquals(2 * self.tainted, 2 * self.unquoted)
self.assert_(isinstance(self.tainted * 2, self._getClass()))
self.assertEquals(self.tainted * 2, self.unquoted * 2)
def testInterpolate(self):
tainted = self._getClass()('<%s>')
self.assert_(isinstance(tainted % 'foo', self._getClass()))
self.assertEquals(tainted % 'foo', '<foo>')
tainted = self._getClass()('<%s attr="%s">')
self.assert_(isinstance(tainted % ('foo', 'bar'), self._getClass()))
self.assertEquals(tainted % ('foo', 'bar'), '<foo attr="bar">')
def testStringMethods(self):
simple = "capitalize isalpha isdigit islower isspace istitle isupper" \
" lower lstrip rstrip strip swapcase upper".split()
returnsTainted = "capitalize lower lstrip rstrip strip swapcase upper"
returnsTainted = returnsTainted.split()
unquoted = '\tThis is a test '
tainted = self._getClass()(unquoted)
for f in simple:
v = getattr(tainted, f)()
self.assertEquals(v, getattr(unquoted, f)())
if f in returnsTainted:
self.assert_(isinstance(v, self._getClass()))
else:
self.failIf(isinstance(v, self._getClass()))
justify = "center ljust rjust".split()
for f in justify:
v = getattr(tainted, f)(30)
self.assertEquals(v, getattr(unquoted, f)(30))
self.assert_(isinstance(v, self._getClass()))
searches = "find index rfind rindex endswith startswith".split()
searchraises = "index rindex".split()
for f in searches:
v = getattr(tainted, f)('test')
self.assertEquals(v, getattr(unquoted, f)('test'))
if f in searchraises:
self.assertRaises(ValueError, getattr(tainted, f), 'nada')
self.assertEquals(tainted.count('test', 1, -1),
unquoted.count('test', 1, -1))
self.assertEquals(tainted.encode(), unquoted.encode())
self.assert_(isinstance(tainted.encode(), self._getClass()))
self.assertEquals(tainted.expandtabs(10),
unquoted.expandtabs(10))
self.assert_(isinstance(tainted.expandtabs(), self._getClass()))
self.assertEquals(tainted.replace('test', 'spam'),
unquoted.replace('test', 'spam'))
self.assert_(isinstance(tainted.replace('test', '<'), self._getClass()))
self.failIf(isinstance(tainted.replace('test', 'spam'),
self._getClass()))
self.assertEquals(tainted.split(), unquoted.split())
for part in self._getClass()('< < <').split():
self.assert_(isinstance(part, self._getClass()))
for part in tainted.split():
self.failIf(isinstance(part, self._getClass()))
multiline = 'test\n<tainted>'
lines = self._getClass()(multiline).split()
self.assertEquals(lines, multiline.split())
self.assert_(isinstance(lines[1], self._getClass()))
self.failIf(isinstance(lines[0], self._getClass()))
transtable = ''.join(map(chr, range(256)))
self.assertEquals(tainted.translate(transtable),
unquoted.translate(transtable))
self.assert_(isinstance(self._getClass()('<').translate(transtable),
self._getClass()))
self.failIf(isinstance(self._getClass()('<').translate(transtable, '<'),
self._getClass()))
def testQuoted(self):
self.assertEquals(self.tainted.quoted(), self.quoted)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestTaintedString, 'test'))
return suite
def main():
unittest.TextTestRunner().run(test_suite())
def debug():
test_suite().debug()
def pdebug():
import pdb
pdb.run('debug()')
if __name__=='__main__':
if len(sys.argv) > 1:
globals()[sys.argv[1]]()
else:
main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment