[Zope-Checkins] CVS: Zope/lib/python/DocumentTemplate - DT_String.py:1.50 DT_Util.py:1.87 DT_Var.py:1.54 cDocumentTemplate.c:1.47
Martijn Pieters
mj@zope.com
Thu, 1 Aug 2002 12:01:10 -0400
Update of /cvs-repository/Zope/lib/python/DocumentTemplate
In directory cvs.zope.org:/tmp/cvs-serv9325/lib/python/DocumentTemplate
Modified Files:
DT_String.py DT_Util.py DT_Var.py cDocumentTemplate.c
Log Message:
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.
=== Zope/lib/python/DocumentTemplate/DT_String.py 1.49 => 1.50 ===
# 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.86 => 1.87 ===
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.53 => 1.54 ===
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=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 @@
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'))
@@ -368,7 +408,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={
@@ -389,7 +429,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.46 => 1.47 ===
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 @@
{
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 @@
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 @@
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 @@
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;