[Zope-Checkins] CVS: Zope/lib/python/DocumentTemplate - DT_String.py:1.49.16.1 DT_Util.py:1.85.16.1 DT_Var.py:1.49.16.1 cDocumentTemplate.c:1.42.16.1
Martijn Pieters
mj@zope.com
Thu, 1 Aug 2002 12:01:28 -0400
Update of /cvs-repository/Zope/lib/python/DocumentTemplate
In directory cvs.zope.org:/tmp/cvs-serv9310/lib/python/DocumentTemplate
Modified Files:
Tag: Zope-2_5-branch
DT_String.py DT_Util.py DT_Var.py cDocumentTemplate.c
Log Message:
Big change, merge from trunk.
- 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.
=== Zope/lib/python/DocumentTemplate/DT_String.py 1.49 => 1.49.16.1 ===
# print '============================================================'
if mapping is None: mapping = {}
+ if hasattr(mapping, 'taintWrapper'): mapping = mapping.taintWrapper()
if not hasattr(self,'_v_cooked'):
try: changed=self.__changed__()
=== Zope/lib/python/DocumentTemplate/DT_Util.py 1.85 => 1.85.16.1 ===
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.
=== Zope/lib/python/DocumentTemplate/DT_Var.py 1.49 => 1.49.16.1 ===
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 @@
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 @@
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=str(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 @@
else: l='...'
val=val+l
+ if isinstance(val, TaintedString):
+ val = val.quoted()
+
return val
__call__=render
@@ -298,6 +335,9 @@
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'))
@@ -366,7 +406,7 @@
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={
@@ -387,7 +427,7 @@
}
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,
=== Zope/lib/python/DocumentTemplate/cDocumentTemplate.c 1.42 => 1.42.16.1 ===
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;
+static PyObject *py___class__, *html_quote, *untaint_name;
/* ----------------------------------------------------- */
@@ -663,6 +663,7 @@
{
PyObject *block, *t;
int l, i, k=0, append;
+ int skip_quote = 0;
if ((l=PyList_Size(blocks)) < 0) return -1;
for (i=0; i < l; i++)
@@ -685,13 +686,34 @@
if (PyString_Check(t)) t=PyObject_GetItem(md, t);
else t=PyObject_CallObject(t, mda);
- if (t == NULL || (! PyString_Check(t)))
+ if (t == NULL) return -1;
+
+ skip_quote = 0;
+
+ if (!PyString_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_quote = 1;
+
+ } else PyErr_Clear();
+
+ Py_XDECREF(untaintmethod);
+ }
+
+ if (!PyString_Check(t))
{
if (t) ASSIGN(t, PyObject_Str(t));
UNLESS(t) return -1;
}
- if (PyString_Check(t)
+ if (skip_quote == 0 && PyString_Check(t)
&& PyTuple_GET_SIZE(block) == 3) /* html_quote */
{
if (strchr(PyString_AS_STRING(t), '&')
@@ -878,6 +900,7 @@
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;