Skip to content

Instantly share code, notes, and snippets.

@JordanMilne
Created October 21, 2014 03:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JordanMilne/ac8833b9a6941a17f89c to your computer and use it in GitHub Desktop.
Save JordanMilne/ac8833b9a6941a17f89c to your computer and use it in GitHub Desktop.
pyenv-compatible patch for pytaint against python 2.7.5, see https://github.com/felixgr/pytaint
From 2aea5633663dd0b91d55a80a17ad22edfb23731f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Gr=C3=B6bert?= <groebert@google.com>
Date: Mon, 7 Oct 2013 15:44:48 +0200
Subject: [PATCH 1/9] pytaint patch
---
Include/Python.h | 3 +
Include/meritobject.h | 28 +
Include/pyerrors.h | 1 +
Include/stringobject.h | 53 +-
Include/taintobject.h | 125 ++
Include/unicodeobject.h | 61 +-
Lib/taint.py | 927 +++++++++++++
Lib/test/exception_hierarchy.txt | 1 +
Lib/test/tainttestdata/config1.json | 32 +
Lib/test/tainttestdata/config2.json | 10 +
Lib/test/tainttestdata/config3.json | 7 +
Lib/test/tainttestdata/config4.json | 5 +
Lib/test/tainttestdata/config_broken_big.json | 47 +
test/tainttestdata/config_broken_small.json | 5 +
Lib/test/tainttestdata/mockmodule.py | 13 +
Lib/test/test_sys.py | 6 +-
Lib/test/test_taint.py | 1458 ++++++++++++++++++++
Lib/test/test_taintmodule.py | 982 +++++++++++++
Lib/test/test_taintunicode.py | 1294 +++++++++++++++++
Makefile.pre.in | 2 +
Objects/exceptions.c | 7 +
Objects/meritobject.c | 180 +++
.../Objects/stringlib/string_format.h | 63 +-
Objects/stringobject.c | 929 ++++++++++---
Objects/taintobject.c | 336 +++++
Objects/unicodeobject.c | 756 ++++++++--
Python/bltinmodule.c | 6 +
Python/pythonrun.c | 2 +
28 files changed, 7043 insertions(+), 296 deletions(-)
create mode 100644 Include/meritobject.h
create mode 100644 Include/taintobject.h
create mode 100644 Lib/taint.py
create mode 100644 Lib/test/tainttestdata/config1.json
create mode 100644 Lib/test/tainttestdata/config2.json
create mode 100644 Lib/test/tainttestdata/config3.json
create mode 100644 Lib/test/tainttestdata/config4.json
create mode 100644 Lib/test/tainttestdata/config_broken_big.json
create mode 100644 Lib/test/tainttestdata/config_broken_small.json
create mode 100644 Lib/test/tainttestdata/mockmodule.py
create mode 100644 Lib/test/test_taint.py
create mode 100644 Lib/test/test_taintmodule.py
create mode 100644 Lib/test/test_taintunicode.py
create mode 100644 Objects/meritobject.c
create mode 100644 Objects/taintobject.c
diff --git a/Include/Python.h b/Include/Python.h
index 775412b..1af4e23 100644
--- a/Include/Python.h
+++ b/Include/Python.h
@@ -117,6 +117,9 @@
#include "warnings.h"
#include "weakrefobject.h"
+#include "taintobject.h"
+#include "meritobject.h"
+
#include "codecs.h"
#include "pyerrors.h"
diff --git a/Include/meritobject.h b/Include/meritobject.h
new file mode 100644
index 0000000..3844587
--- /dev/null
+++ b/Include/meritobject.h
@@ -0,0 +1,28 @@
+#ifndef Py_MERITOBJECT_H
+#define Py_MERITOBJECT_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+PyAPI_DATA(PyTypeObject) PyMerit_MeritType;
+PyAPI_DATA(PyObject*) _PyMerit_FullPropagation;
+PyAPI_DATA(PyObject*) _PyMerit_PartialPropagation;
+PyAPI_DATA(PyObject*) _PyMerit_NonePropagation;
+
+#define PyMerit_FULL_PROPAGATION(m) ( \
+ PyObject_GetAttrString(m, "propagation") == \
+ (PyObject *)_PyMerit_FullPropagation)
+#define PyMerit_PARTIAL_PROPAGATION(m) ( \
+ PyObject_GetAttrString(m, "propagation") == \
+ (PyObject *)_PyMerit_PartialPropagation)
+#define PyMerit_NONE_PROPAGATION(m) ( \
+ PyObject_GetAttrString(m, "propagation") == \
+ (PyObject *)_PyMerit_NonePropagation)
+
+PyAPI_FUNC(void) _PyTaint_Init(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_MERITOBJECT_H */
diff --git a/Include/pyerrors.h b/Include/pyerrors.h
index dbe3bfa..92d9dd3 100644
--- a/Include/pyerrors.h
+++ b/Include/pyerrors.h
@@ -145,6 +145,7 @@ PyAPI_DATA(PyObject *) PyExc_TabError;
PyAPI_DATA(PyObject *) PyExc_ReferenceError;
PyAPI_DATA(PyObject *) PyExc_SystemError;
PyAPI_DATA(PyObject *) PyExc_SystemExit;
+PyAPI_DATA(PyObject *) PyExc_TaintError;
PyAPI_DATA(PyObject *) PyExc_TypeError;
PyAPI_DATA(PyObject *) PyExc_UnboundLocalError;
PyAPI_DATA(PyObject *) PyExc_UnicodeError;
diff --git a/Include/stringobject.h b/Include/stringobject.h
index 18b5b41..26876e8 100644
--- a/Include/stringobject.h
+++ b/Include/stringobject.h
@@ -8,6 +8,7 @@ extern "C" {
#endif
#include <stdarg.h>
+#include "taintobject.h"
/*
Type PyStringObject represents a character string. An extra zero byte is
@@ -34,6 +35,7 @@ functions should be applied to nil objects.
typedef struct {
PyObject_VAR_HEAD
+ PyTaintObject *ob_merits;
long ob_shash;
int ob_sstate;
char ob_sval[1];
@@ -45,6 +47,8 @@ typedef struct {
* ob_sstate != 0 iff the string object is in stringobject.c's
* 'interned' dictionary; in this case the two references
* from 'interned' to this object are *not counted* in ob_refcnt.
+ * ob_merits is NULL for untainted strings, otherwise it is a tuple of
+ * string's merits
*/
} PyStringObject;
@@ -75,7 +79,7 @@ PyAPI_FUNC(int) _PyString_Eq(PyObject *, PyObject*);
PyAPI_FUNC(PyObject *) PyString_Format(PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) _PyString_FormatLong(PyObject*, int, int,
int, char**, int*);
-PyAPI_FUNC(PyObject *) PyString_DecodeEscape(const char *, Py_ssize_t,
+PyAPI_FUNC(PyObject *) PyString_DecodeEscape(const char *, Py_ssize_t,
const char *, Py_ssize_t,
const char *);
@@ -84,12 +88,45 @@ PyAPI_FUNC(void) PyString_InternImmortal(PyObject **);
PyAPI_FUNC(PyObject *) PyString_InternFromString(const char *);
PyAPI_FUNC(void) _Py_ReleaseInternedStrings(void);
+PyAPI_FUNC(PyObject *)
+PyString_FromStringAndSizeSameMerits(const char *, Py_ssize_t,
+ PyTaintObject *);
+PyAPI_FUNC(PyObject *) PyString_FromStringAndSizeNoIntern(const char *,
+ Py_ssize_t);
+PyAPI_FUNC(int) _PyString_BinaryTaintPropagateInPlace(
+ register PyStringObject *, register PyStringObject *,
+ register PyStringObject *);
+PyAPI_FUNC(int) _PyString_CopyTaint(register PyStringObject *,
+ register PyStringObject *);
+PyAPI_FUNC(int) _PyString_PropagateTaintInPlace(register PyStringObject *,
+ register PyStringObject *);
+PyAPI_FUNC(PyObject*) PyString_AssignTaint(PyStringObject *, PyTaintObject *);
+
+/* Macro which assigns source's taint object to target. It assumes that source
+ is tainted and target is not (ie. is a new string with NULL ob_merits). */
+#define _PyString_COPY_TAINT_REFERENCE(target, source) \
+do { \
+ assert(!PyString_CHECK_TAINTED(target));\
+ assert(PyString_CHECK_TAINTED(source));\
+ assert(target->ob_refcnt == 1);\
+ ((PyStringObject*)target)->ob_merits = \
+ ((PyStringObject*)source)->ob_merits; \
+ Py_INCREF(((PyStringObject*)target)->ob_merits); \
+} while(0);
+
/* Use only if you know it's a string */
#define PyString_CHECK_INTERNED(op) (((PyStringObject *)(op))->ob_sstate)
+#define PyString_CHECK_TAINTED(op) (((PyStringObject *)(op))->ob_merits != NULL)
/* Macro, trading safety for speed */
#define PyString_AS_STRING(op) (((PyStringObject *)(op))->ob_sval)
#define PyString_GET_SIZE(op) Py_SIZE(op)
+#define PyString_GET_MERITS(op) (((PyStringObject *)(op))->ob_merits)
+#define PyString_ASSIGN_MERITS(x, t) \
+do {\
+ (((PyStringObject*)x)->ob_merits = t);\
+ Py_XINCREF(t);\
+} while(0);\
/* _PyString_Join(sep, x) is like sep.join(x). sep must be PyStringObject*,
x must be an iterable object. */
@@ -107,7 +144,7 @@ PyAPI_FUNC(PyObject*) PyString_Decode(
const char *errors /* error handling */
);
-/* Encodes a char buffer of the given size and returns a
+/* Encodes a char buffer of the given size and returns a
Python object. */
PyAPI_FUNC(PyObject*) PyString_Encode(
@@ -117,7 +154,7 @@ PyAPI_FUNC(PyObject*) PyString_Encode(
const char *errors /* error handling */
);
-/* Encodes a string object and returns the result as Python
+/* Encodes a string object and returns the result as Python
object. */
PyAPI_FUNC(PyObject*) PyString_AsEncodedObject(
@@ -127,8 +164,8 @@ PyAPI_FUNC(PyObject*) PyString_AsEncodedObject(
);
/* Encodes a string object and returns the result as Python string
- object.
-
+ object.
+
If the codec returns an Unicode object, the object is converted
back to a string using the default encoding.
@@ -140,7 +177,7 @@ PyAPI_FUNC(PyObject*) PyString_AsEncodedString(
const char *errors /* error handling */
);
-/* Decodes a string object and returns the result as Python
+/* Decodes a string object and returns the result as Python
object. */
PyAPI_FUNC(PyObject*) PyString_AsDecodedObject(
@@ -150,8 +187,8 @@ PyAPI_FUNC(PyObject*) PyString_AsDecodedObject(
);
/* Decodes a string object and returns the result as Python string
- object.
-
+ object.
+
If the codec returns an Unicode object, the object is converted
back to a string using the default encoding.
diff --git a/Include/taintobject.h b/Include/taintobject.h
new file mode 100644
index 0000000..8a2e81c
--- /dev/null
+++ b/Include/taintobject.h
@@ -0,0 +1,125 @@
+#ifndef Py_TAINTOBJECT_H
+#define Py_TAINTOBJECT_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct { PyObject_VAR_HEAD } PyTaintObject;
+
+/* Create an object representing taint with no merits (ie. empty tuple).
+ Returns NULL on failure.
+*/
+
+PyAPI_FUNC(PyTaintObject*) PyTaint_EmptyMerits();
+
+/* Return a new taint object having all the merits of old one and also
+ new_merit. Returns NULL on failure.
+*/
+
+PyAPI_FUNC(PyTaintObject*) _PyTaint_AddMerit(PyTaintObject *taint,
+ PyObject *new_merit);
+
+/* Extract taint from taintable object - ie. string or unicode. Since it should
+ be only used internally, Py_FatalError is raised when an invalid object is
+ passed. Returns borrowed reference.
+ */
+PyTaintObject*
+_PyTaint_GetFromObject(PyObject *obj);
+
+/* Checks if object is a valid merit object with a valid propagation strategy.
+
+ Returns -1 on failure, 1 on success. */
+int
+_PyTaint_ValidMerit(PyObject *obj);
+
+/* Apply taint propagation rules to a and b and store result in r.
+
+ Returns -1 on failure, 1 on success. */
+PyAPI_FUNC(int) PyTaint_PropagationResult(
+ PyTaintObject **r,
+ PyTaintObject *a,
+ PyTaintObject *b
+ );
+
+/* Apply taint propagation rules to target and source and store result in
+ target. This function creates a new PyTaintObject instead of modifying
+ old one.
+
+ Returns -1 on failure, 1 on success. The reference to original target is
+ stolen; a new object is created and its ownership is transferred to the
+ caller. */
+int
+PyTaint_PropagateTo(PyTaintObject **target,
+ PyTaintObject *source);
+
+/* For taintable object obj, return object with the same contents as obj and
+ passed taint value. Steals reference to obj when succesful.
+
+ Returns NULL on failure. If obj is not taintable (ie. not a unicode nor
+ string), a TypeError is raised.
+ */
+PyAPI_FUNC(PyObject*)
+PyTaint_AssignToObject(PyObject *obj, PyTaintObject *taint);
+
+/* Returns 1 if given object is taintable, 0 otherwise. Recognized taintable
+ objects are stringobject and unicode.
+ */
+PyAPI_FUNC(int)
+PyTaint_IsTaintable(PyObject *obj);
+
+/* --- Macros -------------------------------------------------------------- */
+#define PyTaint_IS_CLEAN(x) (((PyTaintObject*)x == NULL))
+
+/* --- Collection tainting ------------------------------------------------- */
+
+/* Assign source taint object to each element in target list, modifying them.
+ It is assumed that all elements of the list are stringobjects with NULL
+ ob_merits. In case any of the elements is shared (interned or have a refcount
+ bigger than one), it is replaced with a new copy of itself.
+
+ This function doesn't check validity of its arguments.
+
+ Returns 1 on success, -1 on failure (ie. when it failed when attempting to
+ copy one of its items).
+*/
+PyAPI_FUNC(int) _PyTaint_TaintStringListItems(
+ PyObject *target,
+ PyTaintObject *source
+ );
+
+/* Create a copy of tuple target in which every element's taint is a copy of
+ source. It is assumed that target is a tuple of non tainted string objects.
+ In case any of the strings is shared (interned or have a refcount bigger
+ than one), a new copy of the string is created (otherwise, an inplace
+ tainting is done).
+
+ This function steals reference to target and doesn't check validity of its
+ arguments.
+
+ Returns tuple with tainted items on success, NULL on failure (ie. when it
+ failed when attempting to create a new tuple/string).
+*/
+PyAPI_FUNC(PyObject*) _PyTaint_TaintStringTupleItems(
+ PyObject *target,
+ PyTaintObject *source
+ );
+
+/* Works the same as _PyTaint_TaintStringTupleItems, except that assumes that
+ elements of the tuple are unicode objects. */
+
+PyAPI_FUNC(PyObject*) _PyTaint_TaintUnicodeTupleItems(
+ PyObject *target,
+ PyTaintObject *source
+ );
+
+/* Works the same as _PyTaint_TaintStringListItems, except that assumes that
+ elements of the list are unicode objects. */
+PyAPI_FUNC(int) _PyTaint_TaintUnicodeListItems(
+ PyObject *target,
+ PyTaintObject *source
+ );
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_STRINGOBJECT_H */
diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h
index 9ab724a..893eb8a 100644
--- a/Include/unicodeobject.h
+++ b/Include/unicodeobject.h
@@ -2,6 +2,7 @@
#define Py_UNICODEOBJECT_H
#include <stdarg.h>
+#include "taintobject.h"
/*
@@ -406,6 +407,13 @@ typedef PY_UNICODE_TYPE Py_UNICODE;
((*((string)->str + (offset) + (substring)->length-1) == *((substring)->str + (substring)->length-1))) && \
!memcmp((string)->str + (offset), (substring)->str, (substring)->length*sizeof(Py_UNICODE)))
+/* Assumes that x is not tainted */
+#define PyUnicode_ASSIGN_MERITS(x, t) \
+do {\
+ (((PyUnicodeObject*)x)->merits = t);\
+ Py_XINCREF(t);\
+} while(0);\
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -420,6 +428,7 @@ typedef struct {
PyObject *defenc; /* (Default) Encoded version as Python
string, or NULL; this is used for
implementing the buffer protocol */
+ PyTaintObject *merits;
} PyUnicodeObject;
PyAPI_DATA(PyTypeObject) PyUnicode_Type;
@@ -427,6 +436,9 @@ PyAPI_DATA(PyTypeObject) PyUnicode_Type;
#define PyUnicode_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_UNICODE_SUBCLASS)
#define PyUnicode_CheckExact(op) (Py_TYPE(op) == &PyUnicode_Type)
+#define PyUnicode_CHECK_TAINTED(op) \
+ (((PyUnicodeObject *)(op))->merits != NULL)
+#define PyUnicode_IS_LATIN_CHAR(str) (str[0] < 256U)
/* Fast access macros */
#define PyUnicode_GET_SIZE(op) \
@@ -437,6 +449,15 @@ PyAPI_DATA(PyTypeObject) PyUnicode_Type;
(((PyUnicodeObject *)(op))->str)
#define PyUnicode_AS_DATA(op) \
((const char *)((PyUnicodeObject *)(op))->str)
+#define PyUnicode_GET_MERITS(op) \
+ (((PyUnicodeObject *)(op))->merits)
+
+#define PyUnicode_IS_SHARED(op) \
+ (((PyUnicodeObject*)op) == unicode_empty || \
+ (((PyUnicodeObject*)op)->length == 1 && \
+ PyUnicode_IS_LATIN_CHAR(((PyUnicodeObject*)op)->str) && \
+ unicode_latin1[((PyUnicodeObject*)op)->str[0]] == \
+ ((PyUnicodeObject*)op)))
/* --- Constants ---------------------------------------------------------- */
@@ -466,6 +487,23 @@ PyAPI_FUNC(PyObject*) PyUnicode_FromUnicode(
Py_ssize_t size /* size of buffer */
);
+/* Similar to PyUnicode_FromUnicode, except it does not allow for sharing of
+ short strings. */
+PyAPI_FUNC(PyObject*) PyUnicode_FromUnicodeNoSharing(
+ const Py_UNICODE *u, /* Unicode buffer */
+ Py_ssize_t size /* size of buffer */
+ );
+
+/* Works the same as PyUnicode_FromUnicode when null merits are passed.
+ Otherwise it creates a non-shared string with given merits. The new object
+ owns a new reference to merits. */
+PyAPI_FUNC(PyObject*) PyUnicode_FromUnicodeSameMerits(
+ const Py_UNICODE *u, /* Unicode buffer */
+ Py_ssize_t size, /* size of buffer */
+ PyTaintObject *merits /* taint value of new string */
+ );
+
+
/* Similar to PyUnicode_FromUnicode(), but u points to Latin-1 encoded bytes */
PyAPI_FUNC(PyObject*) PyUnicode_FromStringAndSize(
const char *u, /* char buffer */
@@ -553,6 +591,24 @@ PyAPI_FUNC(PyObject*) PyUnicode_FromObject(
register PyObject *obj /* Object */
);
+/* Copy pointer to taint object from source to target. Target's original taint
+ object is decref'ed, and source's increfed (no new objects are created). */
+void _PyUnicode_CopyTaint(PyUnicodeObject *target,
+ PyUnicodeObject *source);
+
+/* When passed a unicode object returns 1 when it is passed, 0 otherwise. If
+ passed object is not a unicode object, sets ValueError and returns -1. */
+int
+PyUnicode_IsShared(PyUnicodeObject *u);
+
+/* PyUnicode_AssigntTaint will return unicode object with same contents as u
+ and taint value taint. The return value may be either the same object as u
+ or a new unicodeobject.
+
+ Steals reference to u. Returns NULL on failure. */
+PyObject*
+PyUnicode_AssignTaint(PyUnicodeObject *u, PyTaintObject *taint);
+
PyAPI_FUNC(PyObject *) PyUnicode_FromFormatV(const char*, va_list);
PyAPI_FUNC(PyObject *) PyUnicode_FromFormat(const char*, ...);
@@ -1065,13 +1121,16 @@ PyAPI_FUNC(PyObject*) PyUnicode_EncodeCharmap(
ordinals (ones which cause a LookupError) are left untouched and
are copied as-is.
+ This function steals a reference to passed taintobject (either when
+ succesful or on failure).
*/
PyAPI_FUNC(PyObject *) PyUnicode_TranslateCharmap(
const Py_UNICODE *data, /* Unicode char buffer */
Py_ssize_t length, /* Number of Py_UNICODE chars to encode */
PyObject *table, /* Translate table */
- const char *errors /* error handling */
+ const char *errors, /* error handling */
+ PyTaintObject *taint /* taint object to propagate changes with */
);
#ifdef MS_WIN32
diff --git a/Lib/taint.py b/Lib/taint.py
new file mode 100644
index 0000000..9a921bd
--- /dev/null
+++ b/Lib/taint.py
@@ -0,0 +1,927 @@
+# Copyright 2013 Google Inc. All Rights Reserved.
+#
+
+"""Taint module - utilities and patching for taint tracking."""
+
+
+import json
+import itertools
+import sys
+import types
+import re
+from inspect import currentframe, getouterframes
+from contextlib import contextmanager
+from functools import wraps
+
+__author__ = "Marcin Fatyga"
+
+
+def source(func):
+ """Turn function f into a taint source.
+
+ Given a function returning a taintable object (either string/unicode or
+ a collection of them), a new function returning a tainted string will be
+ produced. The __name__ and __doc__ attributes of new function will be the
+ same as in the old one. Supported collections are builtins - list, tuple,
+ set, frozenset and dictionary (note - when tainting a dictionary, only
+ values are tainted; keys are not modified). Collections are tainted
+ recursively.
+
+ Args:
+ f: Function returning a string or collections of strings.
+
+ Returns:
+ string: A tainted string or string collection.
+
+ """
+
+ if not callable(func):
+ return _taint_object(func)
+
+ @wraps(func)
+ def inner(*args, **kwargs):
+ result = func(*args, **kwargs)
+ return _taint_object(result)
+
+ return inner
+
+
+def _patch_sources(config, frame):
+ """Create all taint sources specified in config for a given frame."""
+
+ for s in config.get(u"sources", ()):
+ namespace, target = _get_namespace_and_target(s, frame)
+ apply_patch(namespace, target, source)
+
+
+def cleaner(merit):
+ """Create decorator to turn function into a taint cleaner for the merit."""
+
+ def inner_cleaner(func):
+ """Turn function f into a cleaner.
+
+ Make the return of value function safe for operations requiring given
+ merit.
+
+ Args:
+ f: function which will be a cleaner
+
+ Returns:
+ function: a cleaner for given merit
+ """
+
+ @wraps(func)
+ def inner(*args, **kwargs):
+ r = func(*args, **kwargs)
+ return r._cleanfor(merit)
+
+ return inner
+
+ return inner_cleaner
+
+
+def _patch_cleaners(config, frame):
+ """Create all cleaners specified in config for a given frame."""
+ if not u"cleaners" in config:
+ return
+ merit = None
+ for clean in config.get(u"cleaners", ()):
+ if type(clean) == dict:
+ merit = _get_merit(clean[u"merit"], frame)
+ else:
+ namespace, target = _get_namespace_and_target(clean, frame)
+ apply_patch(namespace, target, cleaner(merit))
+
+
+def sink(merit):
+ """Create decorator to turn function into sensitive sink."""
+
+ def inner_sink(func):
+ """Turn function f into a sink.
+
+ Make the argument function sensitive for operations with given merit.
+
+ Args:
+ f: Function which will be a sink.
+
+ Returns:
+ function: A sink sensitive for given merit.
+
+ """
+
+ @wraps(func)
+ def inner(*args, **kwargs):
+ taint_violations = []
+ for arg in args:
+ if (isinstance(arg, types.StringTypes) and
+ not arg.isclean(merit)):
+ taint_violations.append(arg)
+ for kwarg_value in kwargs.itervalues():
+ if (isinstance(kwarg_value, types.StringTypes) and
+ not kwarg_value.isclean(merit)):
+ taint_violations.append(kwarg_value)
+
+ if taint_violations:
+ message = ("Following arguments have no merit {}:\n"
+ .format(merit))
+ message += "\n".join(taint_violations)
+ raise TaintError(message)
+
+ return func(*args, **kwargs)
+
+ return inner
+
+ return inner_sink
+
+
+def _complex_sink(args_merits, kwargs_merits):
+ """Create decorator to turn function into sensitive sink (with different
+ taint checks for each argument)."""
+
+ def inner_sink(func):
+ """Turn function f into a sink.
+
+ Make the argument function sensitive for operations with merit m.
+
+ Args:
+ f: function which will be a sink
+
+ Returns:
+ function: a sink sensitive for merit m
+ """
+ def check(argument, merits):
+ if argument is None:
+ return
+ elif isinstance(argument, types.StringTypes):
+ for m in merits:
+ if not argument.isclean(m):
+ raise TaintError("Object \"{}\" has no merit {}."
+ .format(argument, m))
+ elif isinstance(argument, types.DictType):
+ for a in argument.itervalues():
+ check(a, merits)
+ elif (isinstance(argument, types.ListType) or
+ isinstance(argument, types.TupleType) or
+ isinstance(argument, frozenset) or
+ isinstance(argument, set)):
+ for a in argument:
+ check(a, merits)
+
+ @wraps(func)
+ def inner(*args, **kwargs):
+ for arg, merit in zip(args, args_merits):
+ check(arg, merit)
+
+ for kwarg_value in kwargs.keys():
+ check(kwargs[kwarg_value], kwargs_merits[kwarg_value])
+
+ return func(*args, **kwargs)
+
+ return inner
+
+ return inner_sink
+
+
+def _patch_sinks(config, frame):
+ """Create all taint sinks specified in config for a given frame."""
+ merit = None
+ for snk in config.get(u"sinks", ()):
+ if type(snk) == dict:
+ if u"merit" in snk:
+ merit = _get_merit(snk[u"merit"], frame)
+ continue
+ else:
+ for (sink_name, sink_specs) in snk.items():
+ args = _preprocess_complex_args(sink_specs[u"args"],
+ frame)
+ kwargs = _preprocess_complex_kwargs(sink_specs[u"kwargs"],
+ frame)
+ patch = _complex_sink(args, kwargs)
+ namespace, target = _get_namespace_and_target(sink_name,
+ frame)
+ else:
+ patch = sink(merit)
+ namespace, target = _get_namespace_and_target(snk, frame)
+ if not merit:
+ raise ValueError(("Malformed config file - expected "
+ "merit, got \"%s\"") % snk)
+
+ apply_patch(namespace, target, patch)
+
+
+def _patch_propagators(config, frame):
+ """Create all taint propagators specified in config for a given frame."""
+
+ for prop in config.get(u"propagators", ()):
+ namespace, target = _get_namespace_and_target(prop, frame)
+ apply_patch(namespace, target, propagator)
+
+
+def propagator(obj):
+ """ Decorator for turning function or a class into a taint propagator. """
+
+ if isinstance(obj, types.FunctionType):
+ return _proxy_function(obj)
+ else: # silently assume it's a class
+ return _proxy_class(obj)
+
+
+def apply_patch(namespace, target_name, action):
+ """Patch a function or method to give it taint tracking capabilities.
+
+ Args:
+ namespace: Namespace in which function/method exists.
+ target_name: The name of concerned function/method.
+ action: Decorator providing taint tracking capabilities, which will be
+ applied to the target.
+
+ Returns:
+ None
+ """
+ target = getattr(namespace, target_name)
+
+ # namespace can either be a class or module, so we will patch either
+ # function or method
+ if isinstance(namespace, types.TypeType):
+ if isinstance(target, types.TypeType): # a class
+ patched = action(target)
+ elif isinstance(target, types.FunctionType):
+ patched = staticmethod(action(getattr(namespace, target_name)))
+ elif target.im_class == type: # class method
+ patched = classmethod(action(target.im_func))
+ else: # instance method
+ patched = action(target)
+ else: # regular function
+ # namespace is module, so target is a function
+ patched = action(target)
+
+ setattr(namespace, target_name, patched)
+
+
+def _get_namespace_and_target(full_name, frame):
+ """Get namespace in which an object exists (and its name relative to that
+ namespace) based on its current frame and name relative to current
+ namespace.
+
+ Args:
+ full_name: Relative name of the object.
+ frame: Current frame.
+
+ Returns:
+ (namespace, relative_name): tuple such that:
+ - namespace: object's namespace
+ - relative_name: object's name relative to namespace
+
+ """
+
+ namespace = sys.modules[frame.f_globals["__name__"]]
+ traversal = full_name.split(".")
+ target = traversal[-1]
+
+ if len(traversal) > 1:
+ namespace = getattr(namespace, traversal[0])
+ else:
+ return namespace, target
+
+ for t in traversal[1:-1]:
+ namespace = getattr(namespace, t)
+
+ return namespace, target
+
+
+def _get_merit(merit_name, frame):
+ """Get merit object from given frame by name."""
+ namespace, target = _get_namespace_and_target(merit_name, frame)
+ return getattr(namespace, target)
+
+
+def _load_config(config_handle):
+ """Load config from file object."""
+ return json.load(config_handle)
+
+
+def _preprocess_check(check_spec, frame):
+ """ Extract information about check from check_spec and based on the frame prepare
+ list of merits to check against.
+
+ Args:
+ check_spec - either a string (when no checks are required) or a
+ dictionary with exactly one key being the name of argument, and a
+ value being either a merit name, or list of merits names.
+ frame - a frame from which merits to check against will be extracted
+
+ Returns:
+ a list (perhaps empty) of merit objects to check against
+
+ """
+ if type(check_spec) != dict:
+ return [] # no checks
+ else:
+ merits = check_spec.values()[0]
+ if type(merits) == list:
+ return [_get_merit(m, frame) for m in merits]
+ else:
+ return [_get_merit(merits, frame)]
+
+
+def _preprocess_complex_args(sink_args, frame):
+ """ Prepare merit checks for each of args of complex sink by extracting
+ correct merit from given frame. """
+ return [_preprocess_check(a, frame) for a in sink_args]
+
+
+def _preprocess_complex_kwargs(sink_kwargs, frame):
+ """ Prepare merit checks for each of kwargs of complex sink by extracting
+ correct merit from given frame.
+
+ Args:
+ sink_kwargs - list of merit checks for keyword arguments -
+ each item is a merit check specification (as described by
+ _preprocess_check)
+ frame - a frame from which merits to check against will be extracted
+
+ Returns:
+ a dictionary in which keys are names of kwargs, and values are lists
+ (perhaps empty) of merit objects to check against
+ """
+
+ res = {}
+ for k in sink_kwargs:
+ checks = _preprocess_check(k, frame)
+ name = k if type(k) != dict else k.keys()[0]
+ res[name] = checks
+
+ return res
+
+
+class Validator(object):
+ """ Convenience class for validating json configurations. """
+ def __init__(self, config):
+ self.config = config
+ self.supported_fields = [u"sources", u"cleaners", u"sinks", u"options"]
+ self.supported_options = [u"propagate_re", u"taint_files"]
+ self.errors = []
+ self.warnings = []
+
+ def validate(self):
+ if not self.validate_fields():
+ return self.warnings, self.errors
+
+ self.validate_options()
+ self.validate_cleaners()
+ self.validate_sinks()
+
+ return self.warnings, self.errors
+
+ def err(self, message):
+ self.errors.append(message)
+
+ def warn(self, message):
+ self.warnings.append(message)
+
+ def _check_list(self, key, name):
+ if key in self.config:
+ if not isinstance(self.config[key], types.ListType):
+ self.err("Malformed {} in the taint config (expected a"
+ " list, not {})".format(key, type(self.config[key])))
+
+ def _check_merit(self, obj):
+ if not isinstance(obj, types.DictType):
+ return False
+ if not u"merit" in obj.keys():
+ return False
+ if not isinstance(obj[u"merit"], types.StringTypes):
+ return False
+ if len(obj) > 1:
+ self.err("Malformed merit description {}".format(obj))
+ return True
+
+ def _check_patchable(self, obj):
+ if not isinstance(obj, types.StringTypes):
+ return False
+ return re.match("^([_a-zA-Z]\w*)(\.[_a-zA-Z]\w*)*$", obj)
+
+ def _check_complex(self, obj):
+ if not isinstance(obj, types.DictType):
+ return False
+ if len(obj) != 1:
+ return False
+ specs = obj.values()[0]
+ name = obj.keys()[0]
+
+ for x in specs.keys():
+ if x not in (u"args", u"kwargs"):
+ self.warn("Unexpected key in {}: {}.".format(name, x))
+
+ args, kwargs = [], []
+ if u"args" in specs:
+ args = specs[u"args"]
+ if not isinstance(args, types.ListType):
+ self.err("Malformed args (expected list) for complex sink {}."
+ .format(name))
+ return False
+
+ if u"kwargs" in specs:
+ kwargs = specs[u"kwargs"]
+ if not isinstance(kwargs, types.ListType):
+ self.err("Malformed kwargs (expected list) for complex sink"
+ " {}.".format(name))
+ return False
+
+ for arg in itertools.chain(args, kwargs):
+ if (isinstance(arg, types.StringTypes) and
+ self._check_identifier(arg)):
+ continue
+ if not isinstance(arg, types.DictType):
+ self.err("Malformed (keyword or positional) argument {}"
+ " in complex sink {}.".format(arg, name))
+ return False
+ if len(arg) != 1:
+ self.err("Malformed (keyword or positional) argument {}"
+ " in complex sink {}.".format(arg, name))
+ return False
+ merits = arg.values()[0]
+ if isinstance(merits, types.ListType):
+ if not all([isinstance(m, types.StringTypes) and
+ self._check_identifier(m)
+ for m in merits]):
+ self.err("Malformed merits {} for argument {}"
+ " in complex sink {}.".format(merits,
+ arg.keys()[0], name))
+ return False
+ continue
+
+ if (isinstance(merits, types.StringTypes) and
+ self._check_identifier(merits)):
+ continue
+ self.err("Malformed merits {} for argument {} in complex sink {}."
+ .format(merits, arg.keys()[0], name))
+ return False
+ return True
+
+ def _check_identifier(self, identifier):
+ return re.match(r'^[_a-zA-Z]\w*$', identifier)
+
+ def validate_fields(self):
+ everything = set(self.config.keys())
+ expected = set(self.supported_fields)
+
+ if not everything <= expected: # ie. check against unexpected options
+ unexpected = sorted(everything - expected)
+ self.warn("Unexpected fields in config: {}."
+ .format(", ".join(unexpected)))
+
+ for field in self.supported_fields:
+ self._check_list(field, field)
+
+ return not self.errors
+
+ def validate_cleaners(self):
+ first_merit = False
+ last_merit = None # indicates if previous element of config was a merit
+ for c in self.config.get(u"cleaners", ()):
+ if self._check_merit(c):
+ if last_merit:
+ self.warn("No cleaners specified for merit {}."
+ .format(last_merit))
+ else:
+ first_merit = True
+ last_merit = c[u"merit"]
+ elif self._check_patchable(c):
+ if not first_merit:
+ self.err("No merit specified for cleaner {}.".format(c))
+ last_merit = None
+ else:
+ self.err("Unexpected object in cleaners: {}.".format(c))
+
+ # cleaners should not end with merit
+ if last_merit:
+ self.warn("No cleaners specified for merit {}.".format(last_merit))
+
+ def validate_options(self):
+ if u"options" not in self.config:
+ return
+
+ supported_options = set(["propagate_re", "taint_files"])
+ unexpected = set(self.config[u"options"]) - set(self.supported_options)
+
+ if unexpected:
+ self.warn("Unexpected options: {}"
+ .format(", ".join(sorted(unexpected))))
+
+
+ def validate_sinks(self):
+ last_merit = None
+ previous_object = None
+
+ for s in self.config.get(u"sinks", ()):
+ if self._check_merit(s):
+ if previous_object == "merit":
+ self.warn("No sinks specified for merit {}."
+ .format(last_merit))
+ last_merit = s[u"merit"]
+ previous_object = "merit"
+ elif self._check_patchable(s):
+ if not last_merit:
+ self.err("No merit specified for sink {}.".format(s))
+ elif previous_object == "complex":
+ self.warn("Config may be confusing - simple sink {} is"
+ " preceded by a complex sink, not simple sink or"
+ " merit.".format(s))
+ previous_object = "patchable"
+ elif self._check_complex(s):
+ if previous_object == "merit" and last_merit:
+ self.warn("Config may be confusing - complex sink {}"
+ " preceded by a merit {}, not another sink."
+ .format(s.keys()[0], last_merit))
+ previous_object = "complex"
+ else:
+ self.err("Unexpected object in sinks: {}.".format(s))
+
+ if last_merit:
+ self.warn("No sinks specified for merit {}.".format(last_merit))
+
+
+def _apply_options(config, current_frame):
+ """ Apply global taint options to the application. Currently supported
+ options are:
+ - propagate_re - add taint propagation to regular expressions
+ - taint_files - make fileobjects returned by open tainted
+ """
+
+ for option in config.get(u"options", ()):
+ if option == u"propagate_re":
+ _patch_re()
+ elif option == u"taint_files":
+ _patch_file_handles()
+
+
+def _patch_re():
+ """ Patch regular expressions for taint propagation. Taint will be
+ propagated from arguments of re modules functions, like re.search into both
+ compiled re objects and re matches. """
+
+ import sre_compile, re
+
+ propagators = [
+ "match",
+ "search",
+ "sub",
+ "subn",
+ "split",
+ "findall",
+ "finditer",
+ "escape",
+ "compile"
+ ]
+
+ for p in propagators:
+ setattr(re, p, _proxy_function(getattr(re, p)))
+
+ # patch compilation to make caching taint aware
+ def _compile(*key):
+ # internal: compile pattern
+ taint = _get_taint(key[0])
+ if taint is not None: # can't hash the set
+ taint = tuple(taint)
+ cachekey = (type(key[0]), key, taint)
+ p = re._cache.get(cachekey)
+ if p is not None:
+ return p
+ pattern, flags = key
+ if isinstance(pattern, re._pattern_type):
+ if flags:
+ raise ValueError("Cannot process flags argument with"
+ " a compiled pattern")
+ return pattern
+ if not sre_compile.isstring(pattern):
+ raise TypeError("first argument must be string or compiled"
+ " pattern")
+
+ p = sre_compile.compile(pattern, flags)
+
+ if len(re._cache) >= re._MAXCACHE:
+ re._cache.clear()
+ re._cache[cachekey] = p
+ return p
+
+ setattr(re, "_compile", _compile)
+
+
+def _patch_file_handles():
+ """ Proxy fileobjects for taint propagation. Every fileobject created by
+ open will return tainted contents. """
+ globals()["open"] = _proxy_function(open, tainted=True)
+
+
+def enable(config_name="PLUMBING"):
+ """Enable taint tracking in module.
+
+ Monkey patches everything given in config file.
+
+ Args:
+ config_name: Name of the config file.
+ """
+
+ with open(config_name) as config_handle:
+ config = _load_config(config_handle)
+
+ current_frame = getouterframes(currentframe())[1][0]
+
+ _apply_options(config, current_frame)
+ _patch_sources(config, current_frame)
+ _patch_sinks(config, current_frame)
+ _patch_cleaners(config, current_frame)
+ _patch_propagators(config, current_frame)
+
+
+# Context managers.
+# Disable pylint warning: "undefined variable: Merit" (it is a new builtin,
+# pylint is not aware of it yet):
+# pylint: disable=E0602 class _PropagationContext(object):
+
+@contextmanager
+def unsafePropagationFull(merit):
+ propagation = merit.propagation
+ merit.propagation = Merit.FullPropagation
+ yield
+ merit.propagation = propagation
+
+@contextmanager
+def unsafePropagationPartial(merit):
+ propagation = merit.propagation
+ merit.propagation = Merit.PartialPropagation
+ yield
+ merit.propagation = propagation
+
+@contextmanager
+def unsafePropagationNone(merit):
+ propagation = merit.propagation
+ merit.propagation = Merit.NonePropagation
+ yield
+ merit.propagation = propagation
+
+
+# TODO(marcinf) some merits (choose propagation for them)
+class SecretMerit(Merit): pass
+class PickleMerit(Merit): pass
+class ShellMerit(Merit): pass
+class XSSMerit(Merit): pass
+class SQLiMerit(Merit): pass
+
+
+class Taintable(object):
+ """ Base class for taint propagator proxies. """
+
+ # special cases
+ def __iter__(self):
+ obj = object.__getattribute__(self, "__obj")
+ return _taint_attr(object.__getattribute__(obj, "__iter__"),
+ object.__getattribute__(self, "__taint"))()
+
+ def next(self):
+ obj = object.__getattribute__(self, "__obj")
+ return _taint_attr(object.__getattribute__(obj, "next"),
+ object.__getattribute__(self, "__taint"))()
+
+ def __str__(self):
+ return str(object.__getattribute__(self, "__obj"))
+
+ def __repr__(self):
+ return repr(object.__getattribute__(self, "__obj"))
+
+ def __unicode__(self):
+ return unicode(object.__getattribute__(self, "__obj"))
+
+ def __nonzero__(self):
+ return bool(object.__getattribute__(self, "__obj"))
+
+ # attributes
+ def __getattribute__(self, attr):
+ return _taint_attr(getattr(object.__getattribute__(self, "__obj"),
+ attr),
+ object.__getattribute__(self, "__taint"))
+
+ # TODO it is also possible to propagate taint when setting
+ # perhaps an object which taint is modified by setting attributes may be a
+ # good idea in future.
+
+ def __setattr__(self, attr, value):
+ setattr(object.__getattribute__(self, "__obj"), attr, value)
+
+ def __delattr__(self, attr):
+ delattr(object.__getattribute__(self, "__obj"), attr)
+
+# magic methods to create for taint proxies
+_magic_methods = ["__lt__", "__le__", "__eq__", "__ne__", "__gt__", "__ge__",
+ "__cmp__", "__rcmp__", "__hash__", "__get__",
+ "__set__", "__delete__", "__isinstancecheck__",
+ "__subclasshook__", "__call__", "__len__", "__getitem__",
+ "__setitem__", "__delitem__", "__reversed__",
+ "__contains__", "__getslice__", "__setslice__",
+ "__delslice__", "__add__", "__sub__", "__mul__",
+ "__floordiv__", "__mod__", "__divmod__", "__pow__",
+ "__lshift__", "__rshift__", "__and__", "__xor__", "__or__",
+ "__div__", "__truediv__", "__radd__", "__rsub__",
+ "__rmul__", "__rdiv__", "__rtruediv__", "__rfloordiv__",
+ "__rmod__", "__rdivmod__", "__rpow__", "__rlshift__",
+ "__rrshift__", "__rand__", "__rxor__", "__ror__",
+ "__iadd__", "__isub__", "__imul__", "__idiv__",
+ "__itruediv__"]
+
+
+def _proxy_class(kls):
+ """ Create a proxy class for kls which propagates taint, but otherwise
+ behaves the same. """
+
+ class MC(type):
+ """ Metaclass for the taint propagating proxy. """
+ def __repr__(self):
+ return repr(kls) + " (tainted)"
+
+ def __str__(self):
+ return str(kls) + " (tainted)"
+
+ def __unicode__(self):
+ return unicode(kls) + " (tainted)"
+
+ def __new__(mcs, name, bases, dct):
+ for m in _magic_methods:
+ if hasattr(kls, m):
+ def _tainted_meth(self, *arg, **kwargs):
+ taint = _collect_taint(list(args) + kwargs.values() +
+ [object.__getattribute__(obj, "__taint")])
+ return _taint_object(getattr(kls, m), taint)
+ dct[m] = getattr(kls, m)
+ return type.__new__(mcs, "%s" % kls.__name__, bases, dct)
+
+ class Propagator(Taintable):
+ __metaclass__ = MC
+
+ def __init__(self, *args, **kwargs):
+ object.__setattr__(self, "__obj", kls(*args, **kwargs))
+ object.__setattr__(self, "__cls", kls)
+ taint = _collect_taint(list(args) + kwargs.values())
+ object.__setattr__(self, "__taint", taint)
+
+
+ return Propagator
+
+class Propagator(Taintable):
+ def __new__(cls, obj, taint):
+ dct = {}
+ for m in _magic_methods:
+ if hasattr(cls, m):
+ def _tainted_meth(self, *arg, **kwargs):
+ taint = _collect_taint(list(args) + kwargs.values() +
+ [object.__getattribute__(obj, "__taint")])
+ return _taint_object(getattr(kls, m), taint)
+
+ cls_tainted = type("%s" % cls.__name__, cls.__mro__, dct)
+ return object.__new__(cls_tainted)
+
+ def __init__(self, obj, taint):
+ object.__setattr__(self, "__obj", obj)
+ object.__setattr__(self, "__taint", taint)
+
+
+def _propagator_wrapper(obj, taint):
+ """ Default wrapper for propagating taint over arbitrary object. For objects
+ of types: NoneType, int, float, bool, long, the same object is returned. For
+ objects of other types, given object is wrapped in the Propagator. """
+ if type(obj) in [types.NoneType, types.IntType, types.FloatType,
+ types.BooleanType, types.LongType]:
+ return obj
+ else:
+ return Propagator(obj, taint)
+
+
+def _proxy_function(func, tainted=False):
+ """ Decorate a function func so that it will return a tainted object.
+ Type of decorated function return value depends on func's return value. If
+ it is a:
+ - string/unicode - they will be tainted by their builtin mechanisms
+ - builtin collection of taintable objects - each object of collection
+ will be tainted (for dictionaries, only values are tainted, keys are not
+ modified)
+ - other object - it will proxied by Propagator class
+
+ The taint value will be either:
+ - when tainted is false (default) - result of taint propagation between
+ func's arguments
+ - when tainted is true - always tainted with no merits
+ """
+
+ @wraps(func)
+ def inner(*args, **kwargs):
+ taint = _collect_taint(list(args) + kwargs.values())
+ if tainted:
+ taint = _propagate(taint, tuple())
+ res = func(*args, **kwargs)
+
+ return _taint_object(res, taint)
+
+ return inner
+
+
+# Taint utilities
+#
+#
+# TODO similar utilities are provided by taintobject.c . Probably a Python
+# wrapper around it would be better idea than redefining them again here.
+
+def _get_taint(obj):
+ """ Extract taint from taintable object - either string/unicode with builtin
+ taint or an object proxied inside taint propagator. """
+ if isinstance(obj, types.StringTypes):
+ return obj._merits()
+ elif isinstance(obj, Taintable):
+ return object.__getattribute__(obj, "__taint")
+ return None
+
+
+def _collect_taint(objects):
+ """ Return result of taint propagation across objects from given list. """
+ if not objects:
+ return None
+
+ taint = _get_taint(objects[0])
+ for o in objects[1:]:
+ taint = _propagate(taint, _get_taint(o))
+ return taint
+
+def _propagate(a, b):
+ """ Using taint propagation semantics, propagate merits between a and b.
+ Both a and b represent taint, being either a collection of merits (perhaps
+ empty) or None (when representing "taint" of untainted object)."""
+
+
+ # both clean
+ if a is None and b is None:
+ return None
+
+ # both tainted
+ if a is not None and b is not None:
+ return set(merit for merit in set(a) & set(b)
+ if merit.propagation != Merit.NonePropagation)
+
+ # one is tainted, other clean
+ if a is None:
+ source = b
+ else:
+ source = a
+ source = set(source)
+
+ return set(merit for merit in source
+ if merit.propagation == Merit.FullPropagation)
+
+def _taint_object(obj, taint=(), taint_wrapper=_propagator_wrapper):
+ """ Taint arbitrary object with the taint. For string/unicode objects, their
+ builtin taint mechanisms will be used. For builtin collections, each item
+ will be tainted recursively with this function (for dicts, only values are
+ tainted). For other objects, taint_wrapper will be used to give them taint
+ propagating capabilities (default taint_wrapper is the Propagator proxy
+ class).
+
+ Args:
+ - obj - object to taint
+ - taint - None (when object should actually remain clean) or collection of
+ merits (perhaps empty, if the object should be tainted with no merits)
+ defaults to empty tuple
+ - taint_wrapper - a callable to wrap untaintable objects with taint
+ propagation - defaults to Propagator
+
+ """
+
+ if isinstance(obj, types.StringTypes):
+ if taint is None:
+ return obj
+ obj = obj.taint()
+ for merit in taint:
+ obj = obj._cleanfor(merit)
+ return obj
+ elif type(obj) is types.ListType:
+ return [_taint_object(o, taint) for o in obj]
+ elif type(obj) is types.TupleType:
+ return tuple(_taint_object(o, taint) for o in obj)
+ elif type(obj) is types.DictType:
+ return {k: _taint_object(v, taint) for (k, v) in obj.iteritems()}
+ elif type(obj) is set:
+ return set(_taint_object(o, taint) for o in obj)
+ elif type(obj) is frozenset:
+ return frozenset(_taint_object(o, taint) for o in obj)
+ else:
+ return taint_wrapper(obj, taint)
+
+def _taint_attr(attr, taint):
+ if callable(attr):
+ def f(*args, **kwargs):
+ res = attr(*args, **kwargs)
+ new_taint = _collect_taint(list(args) + kwargs.values())
+ new_taint = _propagate(new_taint, taint)
+ return _taint_object(res, new_taint)
+ return f
+ else:
+ return _taint_object(attr, taint)
+
+
diff --git a/Lib/test/exception_hierarchy.txt b/Lib/test/exception_hierarchy.txt
index 82b6ddf..ea4a6d3 100644
--- a/Lib/test/exception_hierarchy.txt
+++ b/Lib/test/exception_hierarchy.txt
@@ -32,6 +32,7 @@ BaseException
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
+ | +-- TaintError
| +-- TypeError
| +-- ValueError
| +-- UnicodeError
diff --git a/Lib/test/tainttestdata/config1.json b/Lib/test/tainttestdata/config1.json
new file mode 100644
index 0000000..d995564
--- /dev/null
+++ b/Lib/test/tainttestdata/config1.json
@@ -0,0 +1,32 @@
+{
+ "cleaners": [
+ {"merit": "MeritFull"},
+ "SimplePatcherTest.InnerClass.static_cleaner",
+ "SimplePatcherTest.InnerClass.instance_cleaner",
+ "SimplePatcherTest.InnerClass.class_cleaner",
+ "toplevel_cleaner",
+ {"merit": "MeritNone"},
+ "toplevel_cleaner",
+ "SimplePatcherTest.InnerClass.instance_cleaner"],
+ "sinks": [
+ {"merit": "MeritFull"},
+ "SimplePatcherTest.InnerClass.static_sink",
+ "SimplePatcherTest.InnerClass.class_sink",
+ "SimplePatcherTest.InnerClass.instance_sink",
+ "toplevel_sink",
+ {"SimplePatcherTest.InnerClass.complex_sink": {
+ "args": [
+ "self",
+ {"a": "MeritFull"},
+ "b",
+ {"c": ["MeritFull", "MeritNone"]}],
+ "kwargs": [
+ "e",
+ {"d": ["MeritNone", "MeritFull"]},
+ {"f": ["MeritNone", "MeritFull"]}]}}],
+ "sources": [
+ "SimplePatcherTest.InnerClass.static_source",
+ "SimplePatcherTest.InnerClass.instance_source",
+ "SimplePatcherTest.InnerClass.class_source",
+ "toplevel_source"]
+}
diff --git a/Lib/test/tainttestdata/config2.json b/Lib/test/tainttestdata/config2.json
new file mode 100644
index 0000000..36bd443
--- /dev/null
+++ b/Lib/test/tainttestdata/config2.json
@@ -0,0 +1,10 @@
+{
+ "cleaners": [
+ {"merit": "MockModule.MeritX"},
+ "MockModule.MockClass.instance_cleaner"],
+ "sinks": [
+ {"merit": "MockModule.MeritX"},
+ "MockModule.MockClass.instance_sink"],
+ "sources": [
+ "MockModule.MockClass.instance_source"]
+}
diff --git a/Lib/test/tainttestdata/config3.json b/Lib/test/tainttestdata/config3.json
new file mode 100644
index 0000000..0701c80
--- /dev/null
+++ b/Lib/test/tainttestdata/config3.json
@@ -0,0 +1,7 @@
+{
+ "propagators" :
+ [
+ "PropagatorTest.Person",
+ "toplevel_propagator"
+ ]
+}
diff --git a/Lib/test/tainttestdata/config4.json b/Lib/test/tainttestdata/config4.json
new file mode 100644
index 0000000..a318da5
--- /dev/null
+++ b/Lib/test/tainttestdata/config4.json
@@ -0,0 +1,5 @@
+{
+ "options":
+ ["propagate_re"]
+}
+
diff --git a/Lib/test/tainttestdata/config_broken_big.json b/Lib/test/tainttestdata/config_broken_big.json
new file mode 100644
index 0000000..49e774a
--- /dev/null
+++ b/Lib/test/tainttestdata/config_broken_big.json
@@ -0,0 +1,47 @@
+{
+ "foo": [],
+ "bar": [],
+ "baz": [],
+ "cleaners": [
+ "cleaner_f",
+ {"merit": "MeritA"},
+ {"merit": "MeritB"},
+ "toplevel_cleaner",
+ [],
+ {"merit": "MeritC"}],
+ "sinks": [
+ "function_f",
+ {"merit": "MeritD"},
+ {"merit": "MeritE"},
+ {"complex_sink": {
+ "args": []}},
+ "function_g",
+ {"complex_sink_2": {
+ "foobar": [],
+ "args": []}},
+ {"complex_sink_3": {
+ "args": "zxcv"}},
+ {"complex_sink_4": {
+ "kwargs": "zxcv"}},
+ {"complex_sink_5": {
+ "kwargs": [["qwer"]]}},
+ {"complex_sink_6": {
+ "kwargs": [{}]}},
+ {"complex_sink_7": {
+ "kwargs": [{"aaa": 1234}]}},
+ {"complex_sink_8": {
+ "kwargs": [{"bbb": [5]}]}},
+ ["unexpected", "object"],
+ {"merit": "MeritF"}
+ ],
+ "sources": [
+ "SimplePatcherTest.InnerClass.static_source",
+ "SimplePatcherTest.InnerClass.instance_source",
+ "SimplePatcherTest.InnerClass.class_source",
+ "toplevel_source"],
+ "options": [
+ "propagate_re",
+ "foo",
+ "bar"
+ ]
+}
diff --git a/Lib/test/tainttestdata/config_broken_small.json b/Lib/test/tainttestdata/config_broken_small.json
new file mode 100644
index 0000000..a402aa2
--- /dev/null
+++ b/Lib/test/tainttestdata/config_broken_small.json
@@ -0,0 +1,5 @@
+{
+ "sinks": "broken",
+ "sources": "broken",
+ "cleaners": "broken"
+}
diff --git a/Lib/test/tainttestdata/mockmodule.py b/Lib/test/tainttestdata/mockmodule.py
new file mode 100644
index 0000000..4d040d3
--- /dev/null
+++ b/Lib/test/tainttestdata/mockmodule.py
@@ -0,0 +1,13 @@
+""" Test data for taint module patcher. """
+
+class MockClass(object):
+ def instance_source(self):
+ return "abc"
+
+ def instance_cleaner(self, s):
+ return s
+
+ def instance_sink(self, s):
+ return True
+
+class MeritX(Merit): pass
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index fbea655..886d36a 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -704,8 +704,8 @@ class SizeofTest(unittest.TestCase):
check(slice(1), size('3P'))
# str
vh = test.test_support._vheader
- check('', struct.calcsize(vh + 'lic'))
- check('abc', struct.calcsize(vh + 'lic') + 3)
+ check('', struct.calcsize(vh + 'Plic'))
+ check('abc', struct.calcsize(vh + 'Plic') + 3)
# super
check(super(int), size('3P'))
# tuple
@@ -731,7 +731,7 @@ class SizeofTest(unittest.TestCase):
# we need to test for both sizes, because we don't know if the string
# has been cached
for s in samples:
- check(s, size('PPlP') + usize * (len(s) + 1))
+ check(s, size('PPPlP') + usize * (len(s) + 1))
# weakref
import weakref
check(weakref.ref(int), size('2Pl2P'))
diff --git a/Lib/test/test_taint.py b/Lib/test/test_taint.py
new file mode 100644
index 0000000..3af47af
--- /dev/null
+++ b/Lib/test/test_taint.py
@@ -0,0 +1,1458 @@
+import unittest, string
+import sys
+from test import test_support, string_tests
+
+
+class MeritFull(Merit):
+ propagation = Merit.FullPropagation
+
+class MeritFull2(Merit):
+ propagation = Merit.FullPropagation
+
+class MeritPartial(Merit):
+ propagation = Merit.PartialPropagation
+
+class MeritPartial2(Merit):
+ propagation = Merit.PartialPropagation
+
+class MeritNone(Merit): # defaults to NonePropagation
+ pass
+
+class MeritNone2(Merit):
+ propagation = Merit.NonePropagation
+
+class AbstractTaintTest(unittest.TestCase):
+ def assertTainted(self, object):
+ self.assertTrue(object.istainted())
+ self.assertFalse(object.isclean())
+
+ def assertClean(self, object):
+ self.assertTrue(object.isclean())
+ self.assertFalse(object.istainted())
+
+ def assertMerits(self, object, merits):
+ if merits == None or object._merits() == None:
+ self.assertEqual(object._merits(), None)
+ self.assertEqual(merits, None)
+ self.assertClean(object)
+ else:
+ self.assertEqual(object._merits(), set(merits))
+
+class TaintTest(AbstractTaintTest):
+ def test_taint(self):
+ t = 't'.taint()
+ self.assertTainted(t)
+
+ tt = t.taint()
+ self.assertTainted(tt)
+
+ ttt = 'a longer string that will be tainted'.taint()
+ self.assertTainted(ttt)
+
+ u = 'u'
+ self.assertClean(u)
+
+ self.assertEqual('x', 'x'.taint())
+ self.assertEqual('a loooooooooooooooooooonger string', \
+ 'a loooooooooooooooooooonger string'.taint())
+
+ def test_taint_exception(self):
+ try:
+ with self.assertRaises(TaintError):
+ raise TaintError
+ except NameError:
+ self.fail("TaintError is not defined.")
+
+ def test_interning(self):
+ t = 'ttttt'.taint()
+ it = intern('ttttt')
+ jt = intern('ttttt')
+ u1 = intern('uuuuu')
+ u2 = intern('uuuuu')
+ self.assertRaises(TypeError, intern, t)
+
+ self.assertEqual(it, t)
+ self.assertIsNot(it, t)
+ self.assertEqual(it, jt)
+ self.assertIs(it, jt)
+ self.assertEqual(it, jt.taint())
+ self.assertIsNot(it, jt.taint())
+
+ u1.taint()
+ # u1 is interned and tainting it will return a
+ # non-interned copy
+ self.assertClean(u1)
+ self.assertClean(u2)
+
+
+class MeritsTest(AbstractTaintTest):
+ def test_propagate(self):
+ t = 'ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ s = 'sssss'
+ s_full = t._cleanfor(MeritFull2)._cleanfor(MeritFull)
+ s_part = t._cleanfor(MeritPartial2)._cleanfor(MeritPartial)
+ s_none = t._cleanfor(MeritNone2)._cleanfor(MeritNone)
+ s_all = t._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+
+ self.assertMerits(t._propagate(t_full), [MeritFull])
+ self.assertMerits(t._propagate(t_part), [MeritPartial])
+ self.assertMerits(t._propagate(t_none), [MeritNone])
+ self.assertMerits(t._propagate(t_all),
+ [MeritFull, MeritPartial, MeritNone])
+ self.assertMerits(t_part._propagate(s_full), [MeritFull, MeritFull2])
+ self.assertMerits(t_full._propagate(s_part), [MeritPartial,
+ MeritPartial2])
+ self.assertMerits(t_none._propagate(s_none), [MeritNone, MeritNone2])
+ self.assertMerits(t_all._propagate(s_all),
+ [MeritFull2, MeritPartial2, MeritNone2])
+
+ def test_listing_merits(self):
+ t = 'ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ t_full2 = t._cleanfor(MeritFull)._cleanfor(MeritFull)
+ t_part2 = t._cleanfor(MeritPartial)._cleanfor(MeritPartial)
+ t_none2 = t._cleanfor(MeritNone)._cleanfor(MeritNone)
+ t_all2 = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+ t_full3 = t._cleanfor(MeritFull2)._cleanfor(MeritFull)
+ t_part3 = t._cleanfor(MeritPartial2)._cleanfor(MeritPartial)
+ t_none3 = t._cleanfor(MeritNone2)._cleanfor(MeritNone)
+ t_all3 = t._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+
+ s = 'abcdef'
+ s_full = s._cleanfor(MeritFull)
+ s_part = s._cleanfor(MeritNone)
+ s_none = s._cleanfor(MeritNone)
+ s_all = s._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ u = 'uuuuu'
+ self.assertMerits(t, [])
+ self.assertMerits(s, None)
+ self.assertMerits(u, None)
+ self.assertMerits(t_full, [MeritFull])
+ self.assertMerits(t_part, [MeritPartial])
+ self.assertMerits(t_none, [MeritNone])
+ self.assertMerits(t_all, [MeritNone, MeritFull, MeritPartial])
+
+ self.assertMerits(t_full2, [MeritFull])
+ self.assertMerits(t_part2, [MeritPartial])
+ self.assertMerits(t_none2, [MeritNone])
+ self.assertMerits(t_all, [MeritNone, MeritFull, MeritPartial])
+
+ self.assertMerits(t_full3, [MeritFull, MeritFull2])
+ self.assertMerits(t_part3, [MeritPartial, MeritPartial2])
+ self.assertMerits(t_none3, [MeritNone, MeritNone2])
+
+ def test_full_propagation(self):
+ # this tests propagation semantics, not the _propagate method
+ t = 'ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ t_full2 = t._cleanfor(MeritFull)._cleanfor(MeritFull)
+ t_part2 = t._cleanfor(MeritPartial)._cleanfor(MeritPartial)
+ t_none2 = t._cleanfor(MeritNone)._cleanfor(MeritNone)
+ t_all2 = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+ t_full3 = t._cleanfor(MeritFull2)._cleanfor(MeritFull)
+ t_part3 = t._cleanfor(MeritPartial2)._cleanfor(MeritPartial)
+ t_none3 = t._cleanfor(MeritNone2)._cleanfor(MeritNone)
+ t_all3 = t._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+ t_all4 = t_all2._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+
+ s = 'abcdef'
+ s_full = s._cleanfor(MeritFull)
+ s_part = s._cleanfor(MeritNone)
+ s_none = s._cleanfor(MeritNone)
+ s_all = s._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ u = 'uuuuu'
+
+ self.assertMerits(u + s, None)
+
+ self.assertMerits(u + t_full, [MeritFull])
+ self.assertMerits(u + s_full, [MeritFull])
+ self.assertMerits(u + t_all, [MeritFull])
+ self.assertMerits(u + t_full2, [MeritFull])
+ self.assertMerits(u + t_all2, [MeritFull])
+ self.assertMerits(u + t_full3, [MeritFull, MeritFull2])
+ self.assertMerits(u + t_all3, [MeritFull2])
+ self.assertMerits(u + t_all4, [MeritFull2, MeritFull])
+
+ self.assertMerits(s + t_full, [MeritFull])
+ self.assertMerits(s + s_full, [MeritFull])
+ self.assertMerits(s + t_all, [MeritFull])
+ self.assertMerits(s + t_full2, [MeritFull])
+ self.assertMerits(s + t_all2, [MeritFull])
+ self.assertMerits(s + t_full3, [MeritFull, MeritFull2])
+ self.assertMerits(s + t_all3, [MeritFull2])
+ self.assertMerits(s + t_all4, [MeritFull2, MeritFull])
+
+ self.assertMerits(t_full + t_full2, [MeritFull])
+ self.assertMerits(t_full + t_full3, [MeritFull])
+ self.assertMerits(t_full + t_all, [MeritFull])
+ self.assertMerits(t_full + t_all2, [MeritFull])
+ self.assertMerits(t_full + t_all3, [])
+
+ self.assertMerits(t_full3 + t_full2, [MeritFull])
+ self.assertMerits(t_full3 + t_all3, [MeritFull2])
+ self.assertMerits(t_full3 + t_all4, [MeritFull2, MeritFull])
+ self.assertMerits(t_full3 + t_all, [MeritFull])
+
+ self.assertMerits(t_all + t_all2, [MeritFull, MeritPartial])
+ self.assertMerits(t_all + t_all3, [])
+ self.assertMerits(t_all2 + t_all3, [])
+ self.assertMerits(t_all4 + t_all3, [MeritFull2, MeritPartial2])
+ self.assertMerits(t_all4 + t_all, [MeritFull, MeritPartial])
+ self.assertMerits(t_all4 + t_all2, [MeritFull, MeritPartial])
+
+ def test_partial_propagation(self):
+ # this tests propagation semantics, not the _propagate method
+ t = 'ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ t_full2 = t._cleanfor(MeritFull)._cleanfor(MeritFull)
+ t_part2 = t._cleanfor(MeritPartial)._cleanfor(MeritPartial)
+ t_none2 = t._cleanfor(MeritNone)._cleanfor(MeritNone)
+ t_all2 = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+ t_full3 = t._cleanfor(MeritFull2)._cleanfor(MeritFull)
+ t_part3 = t._cleanfor(MeritPartial2)._cleanfor(MeritPartial)
+ t_none3 = t._cleanfor(MeritNone2)._cleanfor(MeritNone)
+ t_all3 = t._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+ t_all4 = t_all2._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+
+ s = 'abcdef'
+ s_full = s._cleanfor(MeritFull)
+ s_part = s._cleanfor(MeritNone)
+ s_none = s._cleanfor(MeritNone)
+ s_all = s._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ u = 'uuuuu'
+
+ self.assertMerits(u + s, None)
+
+ self.assertMerits(u + t_part, [])
+ self.assertMerits(u + s_part, [])
+ self.assertMerits(u + t_part2, [])
+ self.assertMerits(u + t_part3, [])
+ self.assertMerits(u + t_all, [MeritFull])
+ self.assertMerits(u + t_all2, [MeritFull])
+ self.assertMerits(u + t_all3, [MeritFull2])
+ self.assertMerits(u + t_all4, [MeritFull2, MeritFull])
+
+ self.assertMerits(s + t_part, [])
+ self.assertMerits(s + s_part, [])
+ self.assertMerits(s + t_part2, [])
+ self.assertMerits(s + t_part3, [])
+ self.assertMerits(s + t_all, [MeritFull])
+ self.assertMerits(s + t_all2, [MeritFull])
+ self.assertMerits(s + t_all3, [MeritFull2])
+ self.assertMerits(s + t_all4, [MeritFull2, MeritFull])
+
+ self.assertMerits(t_part + t_part2, [MeritPartial])
+ self.assertMerits(t_part + t_part3, [MeritPartial])
+ self.assertMerits(t_part + t_all, [MeritPartial])
+ self.assertMerits(t_part + t_all2, [MeritPartial])
+ self.assertMerits(t_part + t_all3, [])
+
+ self.assertMerits(t_part3 + t_part2, [MeritPartial])
+ self.assertMerits(t_part3 + t_all3, [MeritPartial2])
+ self.assertMerits(t_part3 + t_all4, [MeritPartial2, MeritPartial])
+ self.assertMerits(t_part3 + t_all, [MeritPartial])
+
+ self.assertMerits(t_all + t_all2, [MeritFull, MeritPartial])
+ self.assertMerits(t_all + t_all3, [])
+ self.assertMerits(t_all2 + t_all3, [])
+ self.assertMerits(t_all4 + t_all3, [MeritFull2, MeritPartial2])
+ self.assertMerits(t_all4 + t_all, [MeritFull, MeritPartial])
+ self.assertMerits(t_all4 + t_all2, [MeritFull, MeritPartial])
+
+ def test_none_propagation(self):
+ # this tests propagation semantics, not the _propagate method
+ t = 'ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ t_full2 = t._cleanfor(MeritFull)._cleanfor(MeritFull)
+ t_part2 = t._cleanfor(MeritPartial)._cleanfor(MeritPartial)
+ t_none2 = t._cleanfor(MeritNone)._cleanfor(MeritNone)
+ t_all2 = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+ t_full3 = t._cleanfor(MeritFull2)._cleanfor(MeritFull)
+ t_part3 = t._cleanfor(MeritPartial2)._cleanfor(MeritPartial)
+ t_none3 = t._cleanfor(MeritNone2)._cleanfor(MeritNone)
+ t_all3 = t._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+ t_all4 = t_all2._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+
+ s = 'abcdef'
+ s_full = s._cleanfor(MeritFull)
+ s_part = s._cleanfor(MeritNone)
+ s_none = s._cleanfor(MeritNone)
+ s_all = s._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ u = 'uuuuu'
+
+ words = [t, t_full, t_part, t_none, t_all, t_full2, t_part2, t_none2,
+ t_all2, t_full3, t_part3, t_none3, t_all3, t_all4, s, s_full,
+ s_part, s_none, s_all, u]
+
+ for w in words:
+ for v in words:
+ m = (v + w)._merits()
+ if m == None:
+ self.assertClean(w)
+ self.assertClean(v)
+ continue
+ self.assertNotIn(MeritNone, m)
+ self.assertNotIn(MeritNone2, m)
+
+class UnaryStringOperationTest(AbstractTaintTest):
+ """ Test string methods which use only one string argument - ie. where
+ taint is just copied from the argument to result. """
+
+ def test_repeat(self):
+ self.assertTainted('abcd'.taint() * 0)
+ self.assertTainted(''.taint() * 100)
+ self.assertTainted('ABCD asdf'.taint() * 15)
+ self.assertTainted('i am very long'.taint() * 10000)
+
+ self.assertTainted('abcd'._cleanfor(MeritFull) * 0)
+ self.assertTainted(''._cleanfor(MeritFull) * 100)
+ self.assertTainted('ABCD asdf'._cleanfor(MeritFull) * 15)
+ self.assertTainted('i am very long'._cleanfor(MeritFull) * 10000)
+
+ self.assertTainted('abcd'._cleanfor(MeritPartial) * 0)
+ self.assertTainted(''._cleanfor(MeritPartial) * 100)
+ self.assertTainted('ABCD asdf'._cleanfor(MeritPartial) * 15)
+ self.assertTainted('i am very long'._cleanfor(MeritPartial) * 10000)
+
+ self.assertTainted('abcd'._cleanfor(MeritNone) * 0)
+ self.assertTainted(''._cleanfor(MeritNone) * 100)
+ self.assertTainted('ABCD asdf'._cleanfor(MeritNone) * 15)
+ self.assertTainted('i am very long'._cleanfor(MeritNone) * 10000)
+
+ self.assertClean('abcd' * 0)
+ self.assertClean('' * 100)
+ self.assertClean('ABCD' * 5)
+ self.assertClean('a very long string' * 10000)
+
+ def test_item(self):
+ u = 'aaaa'
+ t = 'aaaa'.taint()
+ c = 'aaaa'._cleanfor(MeritFull)
+ self.assertClean(u[1])
+ self.assertClean(u[2])
+ self.assertClean(u[1])
+ self.assertClean(u[0])
+
+ self.assertTainted(t[0])
+ self.assertTainted(t[-1])
+ self.assertTainted(t[2])
+ self.assertTainted(t[-1])
+
+ self.assertTainted(c[0])
+ self.assertTainted(c[-2])
+ self.assertTainted(c[1])
+ self.assertTainted(c[2])
+
+ def test_slice(self):
+ u = 'aaaaaaaaa'
+ t = 'ttttttttt'.taint()
+ c = 'ccccccccc'._cleanfor(MeritFull)
+
+ self.assertClean(u[1:])
+ self.assertClean(u[-1:])
+ self.assertClean(u[2:5])
+ self.assertClean(u[2:])
+ self.assertClean(u[:-2])
+ self.assertClean(u[1:5])
+ self.assertTainted(t[2:])
+ self.assertTainted(t[-1:])
+ self.assertTainted(t[3:10])
+ self.assertTainted(t[2:6])
+ self.assertTainted(t[:6])
+ self.assertTainted(t[:0])
+
+ self.assertTainted(c[1:])
+ self.assertTainted(c[-1:])
+ self.assertTainted(c[:0])
+ self.assertTainted(c[-1:])
+ self.assertTainted(c[4:3])
+
+ def test_subscript(self):
+ u = 'aaaaaaaaa'
+ t = 'ttttttttt'.taint()
+ c = 'ccccccccc'._cleanfor(MeritFull)
+
+ self.assertClean(u[1::1])
+ self.assertClean(u[-1::1])
+ self.assertClean(u[2:5:1])
+ self.assertClean(u[1:9:2])
+ self.assertClean(u[8:1:-3])
+ self.assertClean(u[::-1])
+ self.assertClean(u[::-2])
+ self.assertClean(u[2::1])
+ self.assertClean(u[:-2:1])
+ self.assertClean(u[1:5:1])
+ self.assertClean(u[1:7:2])
+ self.assertClean(u[-1::-2])
+ self.assertClean(u[-1::2])
+ self.assertTainted(t[2::1])
+ self.assertTainted(t[-1::1])
+ self.assertTainted(t[3:10:1])
+ self.assertTainted(t[3:10:3])
+ self.assertTainted(t[9:1:-1])
+ self.assertTainted(t[2:6:1])
+ self.assertTainted(t[:6:1])
+ self.assertTainted(t[::3])
+ self.assertTainted(t[::-1])
+ self.assertTainted(t[-1::-1])
+ self.assertTainted(t[:0:1])
+
+ self.assertTainted(c[1::1])
+ self.assertTainted(c[-1::1])
+ self.assertTainted(c[:0:1])
+ self.assertTainted(c[1:9:2])
+ self.assertTainted(c[-1::1])
+ self.assertTainted(c[-1:-7:-2])
+ self.assertTainted(c[4:3:1])
+
+ def test_lower(self):
+ self.assertTainted('abcd'.taint().lower())
+ self.assertTainted('aBCd 123'._cleanfor(MeritFull).lower())
+ self.assertTainted('ABCD'.taint()._cleanfor(MeritNone).lower())
+ self.assertTainted('ABCD XYZ'.taint().lower())
+ self.assertTainted(''.taint().lower())
+ self.assertTainted('1 3 \n\n'.taint().lower())
+
+ self.assertClean('abcd'.lower())
+ self.assertClean('aBCd 123'.lower())
+ self.assertClean('ABCD'.lower())
+ self.assertClean('ABCD XYZ'.lower())
+ self.assertClean(''.lower())
+ self.assertClean('1 3 \n\n'.lower())
+
+ def test_upper(self):
+ self.assertTainted('abcd'.taint().upper())
+ self.assertTainted('aBCd 123'._cleanfor(MeritFull).upper())
+ self.assertTainted('ABCD'.taint()._cleanfor(MeritNone).upper())
+ self.assertTainted('ABCD XYZ'.taint().upper())
+ self.assertTainted(''.taint().upper())
+ self.assertTainted('1 3 \n\n'.taint().upper())
+
+ self.assertClean('abcd'.upper())
+ self.assertClean('aBCd 123'.upper())
+ self.assertClean('ABCD'.upper())
+ self.assertClean('ABCD XYZ'.upper())
+ self.assertClean(''.upper())
+ self.assertClean('1 3 \n\n'.upper())
+
+ def test_title(self):
+ self.assertTainted('abcd'.taint().title())
+ self.assertTainted('aBCd 123'._cleanfor(MeritFull).title())
+ self.assertTainted('ABCD'.taint()._cleanfor(MeritNone).title())
+ self.assertTainted('ABCD XYZ'.taint().title())
+ self.assertTainted(''.taint().title())
+ self.assertTainted('1 3 \n\n'.taint().title())
+
+ self.assertClean('abcd'.title())
+ self.assertClean('aBCd 123'.title())
+ self.assertClean('ABCD'.title())
+ self.assertClean('ABCD XYZ'.title())
+ self.assertClean(''.title())
+ self.assertClean('1 3 \n\n'.title())
+
+ def test_capitalize(self):
+ self.assertTainted('abcd'.taint().title())
+ self.assertTainted('aBCd qwer asafd'._cleanfor(MeritFull).title())
+ self.assertTainted('ABCD'.taint()._cleanfor(MeritNone).title())
+ self.assertTainted('ABCD XYZ'.taint().title())
+ self.assertTainted(''.taint().title())
+ self.assertTainted('asdf zxcv \n hjkl\n'.taint().title())
+
+ self.assertClean('abcd'.title())
+ self.assertClean('aBCd 123'.title())
+ self.assertClean('ABCD'.title())
+ self.assertClean('ABCD XYZ HJKL'.title())
+ self.assertClean(''.title())
+ self.assertClean('1 3 \n\n'.title())
+
+ def test_zfill(self):
+ self.assertTainted('12'.taint().zfill(10))
+ self.assertTainted('+1234'.taint().zfill(10))
+ self.assertTainted('-1234'.taint().zfill(2))
+ self.assertTainted(''.taint().zfill(10))
+ self.assertTainted('400400'.taint().zfill(3))
+ self.assertTainted('123.432'.taint().zfill(10))
+
+ self.assertTainted('23400000'._cleanfor(MeritNone).zfill(100))
+ self.assertTainted('34434234'._cleanfor(MeritNone).zfill(3))
+ self.assertTainted('-123234234'._cleanfor(MeritPartial).zfill(100))
+ self.assertTainted('-999342'._cleanfor(MeritPartial).zfill(3))
+ self.assertTainted('345555.4663'._cleanfor(MeritFull).zfill(100))
+ self.assertTainted('3456765.466654'.\
+ _cleanfor(MeritFull).zfill(3))
+
+ self.assertClean('234'.zfill(2))
+ self.assertClean('-1453'.zfill(20))
+ self.assertClean('1345.3345'.zfill(2))
+ self.assertClean('6456.34354'.zfill(20))
+ self.assertClean('-9999.5345'.zfill(2))
+ self.assertClean('-1000.11234'.zfill(20))
+
+ self.assertTainted(''.taint().zfill(1))
+ self.assertClean('')
+
+ def test_expandtabs(self):
+ self.assertTainted(''.taint().expandtabs())
+ self.assertTainted('\t'.taint().expandtabs())
+ self.assertTainted('abcd \t qwer'.taint().expandtabs())
+ self.assertTainted('\t\tABCD'.taint().expandtabs())
+ self.assertTainted('ABCD\tXYZ'.taint().expandtabs())
+ self.assertTainted('asdf\t123@:#$L zxcv \t\t hjkl\n'.\
+ taint().expandtabs())
+
+ self.assertTainted(''._cleanfor(MeritFull).expandtabs())
+ self.assertTainted('\t'._cleanfor(MeritFull).expandtabs())
+ self.assertTainted('abcd \t qwer'._cleanfor(MeritNone).expandtabs())
+ self.assertTainted('\t\tABCD'._cleanfor(MeritNone).expandtabs())
+ self.assertTainted('ABCD\tXYZ'._cleanfor(MeritPartial).expandtabs())
+ self.assertTainted('asdf\t123@:#$L zxcv \t\t hjkl\n'.\
+ _cleanfor(MeritPartial).expandtabs())
+
+ self.assertClean(''.expandtabs())
+ self.assertClean('\t'.expandtabs())
+ self.assertClean('abcd \t qwer'.expandtabs())
+ self.assertClean('\t\tABCD'.expandtabs())
+ self.assertClean('ABCD\tXYZ'.expandtabs())
+ self.assertClean('asdf\t123@:#$L zxcv \t\t hjkl\n'.expandtabs())
+
+ def test_swapcase(self):
+ self.assertTainted('abcd'.taint().swapcase())
+ self.assertTainted('aBCd 123'._cleanfor(MeritFull).swapcase())
+ self.assertTainted('ABCD'.taint()._cleanfor(MeritNone).swapcase())
+ self.assertTainted('ABcd xyZ'.taint().swapcase())
+ self.assertTainted(''.taint().swapcase())
+ self.assertTainted('1 3 \n\n'.taint().swapcase())
+
+ self.assertClean('abcd'.swapcase())
+ self.assertClean('aBCd 123'.swapcase())
+ self.assertClean('aBCD'.swapcase())
+ self.assertClean('Abcd Xyz'.swapcase())
+ self.assertClean(''.swapcase())
+ self.assertClean('1 3 \n\n'.swapcase())
+
+ def test_coding(self):
+ ab = '\x41\x42'
+ aa = '\xaa\xaa'
+
+ self.assertClean(ab.decode())
+ self.assertTainted(ab.taint().decode())
+ self.assertMerits(ab._cleanfor(MeritFull).decode(),
+ [MeritFull])
+ self.assertMerits(ab._cleanfor(MeritPartial).decode(),
+ [MeritPartial])
+ self.assertMerits(ab._cleanfor(MeritNone)._cleanfor(MeritFull).decode(),
+ [MeritFull, MeritNone])
+
+ self.assertClean(aa.decode(errors='ignore'))
+ self.assertClean(aa.decode(errors='replace'))
+ self.assertTainted(aa.taint().decode(errors='ignore'))
+ self.assertMerits(aa._cleanfor(MeritFull).decode(errors='replace'),
+ [MeritFull])
+ self.assertMerits(aa._cleanfor(MeritPartial).decode(errors='ignore'),
+ [MeritPartial])
+ self.assertMerits(aa._cleanfor(MeritNone)._cleanfor(MeritFull).\
+ decode(errors='replace'),
+ [MeritFull, MeritNone])
+
+ self.assertClean(ab.encode())
+ self.assertTainted(ab.taint().encode())
+ self.assertMerits(ab._cleanfor(MeritFull).encode(),
+ [MeritFull])
+ self.assertMerits(ab._cleanfor(MeritPartial).encode(),
+ [MeritPartial])
+ self.assertMerits(ab._cleanfor(MeritNone)._cleanfor(MeritFull).encode(),
+ [MeritFull, MeritNone])
+
+
+
+
+
+class VariadicStringOperationTest(AbstractTaintTest):
+ """ Test string operations that take more than one argument and where
+ the propagation semantics is applied. """
+ def test_concatenation(self):
+ a = 'aaa'.taint()
+ b = 'bbb'.taint()
+ u = 'ccc'
+ c = a + b
+ d = a + u
+ e = u + a
+ f = u + u
+ self.assertTainted(c)
+ self.assertTainted(d)
+ self.assertTainted(e)
+ self.assertClean(f)
+
+ def test_join(self):
+ t = 'ttttt'.taint()
+ u = 'uuuuu'
+ a = 'aaaaa'._cleanfor(MeritFull)
+ b = 'bbbbb'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone)
+
+ self.assertTainted(t.join([]))
+ self.assertTainted(a.join([]))
+ self.assertTainted(b.join([]))
+ self.assertTainted(c.join([]))
+ self.assertClean(u.join([]))
+
+ self.assertTainted(t.join(['a']))
+ self.assertTainted(a.join(['a']))
+ self.assertTainted(b.join(['a']))
+ self.assertTainted(c.join(['a']))
+ self.assertClean(u.join(['a']))
+
+ self.assertTainted(t.join(['a', '']))
+ self.assertTainted(a.join(['', 'a']))
+ self.assertTainted(b.join(['a', '']))
+ self.assertTainted(c.join(['', 'a', '']))
+ self.assertClean(u.join(['a', '']))
+
+ self.assertTainted(t.join(['']))
+ self.assertTainted(a.join(['', '']))
+ self.assertTainted(c.join(['', '', '']))
+ self.assertClean(u.join(['', '', '', '', '']))
+ self.assertTainted(u.join(['', ''.taint(), '', '', '', '']))
+ self.assertTainted(u.join([''._cleanfor(MeritFull), '', '', '']))
+ self.assertTainted(u.join(['', '', t]))
+
+ self.assertTainted(t.join(['a', 'xx']))
+ self.assertTainted(t.join(['aaaaaaaaaaaa']))
+ self.assertTainted(a.join(['b', 'axxxk']))
+ self.assertTainted(b.join(['a', 'aa', 'f', 'g', 'h', 'r']))
+ self.assertTainted(c.join(['c', 'afff', 'dddd']))
+ self.assertClean(u.join(['aaaa']))
+ self.assertClean(u.join(['aa', 'bb', 'cc', 'd']))
+ self.assertTainted(u.join(['aa'.taint(), 'bb', 'cc', 'd']))
+ self.assertTainted(u.join(['aa', 'bb'._cleanfor(MeritFull),\
+ 'cc'._cleanfor(MeritNone), 'd']))
+
+ def test_split(self):
+ t = 't t t tt tt'.taint()
+ u = 'u uu uuu uuuu u'
+ a = 'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone)\
+ ._cleanfor(MeritPartial)
+ ss = ' '
+
+ [self.assertTainted(x) for x in t.split()]
+ [self.assertTainted(x) for x in a.split()]
+ [self.assertTainted(x) for x in b.split()]
+ [self.assertTainted(x) for x in c.split()]
+ [self.assertClean(x) for x in u.split()]
+
+ [self.assertTainted(x) for x in t.split(' ')]
+ [self.assertTainted(x) for x in a.split(' ')]
+ [self.assertTainted(x) for x in b.split(' ')]
+ [self.assertTainted(x) for x in c.split(' ')]
+ [self.assertClean(x) for x in u.split(' ')]
+
+ [self.assertTainted(x) for x in t.split(' '.taint())]
+ [self.assertTainted(x) for x in a.split(' '.taint())]
+ [self.assertTainted(x) for x in b.split(' '.taint())]
+ [self.assertTainted(x) for x in c.split(' '.taint())]
+ [self.assertTainted(x) for x in u.split(' '.taint())]
+
+ [self.assertTainted(x) for x in t.split(ss)]
+ [self.assertTainted(x) for x in a.split(ss)]
+ [self.assertTainted(x) for x in b.split(ss)]
+ [self.assertTainted(x) for x in c.split(ss)]
+ [self.assertClean(x) for x in u.split(ss)]
+
+ [self.assertMerits(x, [MeritPartial]) for x in \
+ c.rpartition(' '._cleanfor(MeritPartial))]
+
+ def test_rsplit(self):
+ t = 't t t tt tt'.taint()
+ u = 'u uu uuu uuuu u'
+ a = 'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone).\
+ _cleanfor(MeritPartial)
+ ss = ' '
+
+ [self.assertTainted(x) for x in t.rsplit()]
+ [self.assertTainted(x) for x in a.rsplit()]
+ [self.assertTainted(x) for x in b.rsplit()]
+ [self.assertTainted(x) for x in c.rsplit()]
+ [self.assertClean(x) for x in u.rsplit()]
+
+ [self.assertTainted(x) for x in t.rsplit(' ')]
+ [self.assertTainted(x) for x in a.rsplit(' ')]
+ [self.assertTainted(x) for x in b.rsplit(' ')]
+ [self.assertTainted(x) for x in c.rsplit(' ')]
+ [self.assertClean(x) for x in u.rsplit(' ')]
+
+ [self.assertTainted(x) for x in t.rsplit(' '.taint())]
+ [self.assertTainted(x) for x in a.rsplit(' '.taint())]
+ [self.assertTainted(x) for x in b.rsplit(' '.taint())]
+ [self.assertTainted(x) for x in c.rsplit(' '.taint())]
+ [self.assertTainted(x) for x in u.rsplit(' '.taint())]
+
+ [self.assertTainted(x) for x in t.rsplit(ss)]
+ [self.assertTainted(x) for x in a.rsplit(ss)]
+ [self.assertTainted(x) for x in b.rsplit(ss)]
+ [self.assertTainted(x) for x in c.rsplit(ss)]
+ [self.assertClean(x) for x in u.rsplit(ss)]
+
+ [self.assertMerits(x, [MeritPartial]) for x in \
+ c.rpartition(' '._cleanfor(MeritPartial))]
+
+ def test_splitlines(self):
+ t = 't \n t\n t tt \n\n\n tt'.taint()
+ u = '\nu uu n\n\n \n uuuu u'
+ a = '\n\na \n aa aa a \n\n a'._cleanfor(MeritFull)
+ b = 'b bbb\n bb \n\nb'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone).\
+ _cleanfor(MeritPartial)
+
+ [self.assertTainted(x) for x in t.splitlines()]
+ [self.assertTainted(x) for x in a.splitlines()]
+ [self.assertTainted(x) for x in b.splitlines()]
+ [self.assertTainted(x) for x in c.splitlines()]
+ [self.assertClean(x) for x in u.splitlines()]
+
+ [self.assertTainted(x) for x in t.splitlines()]
+ [self.assertTainted(x) for x in a.splitlines()]
+ [self.assertTainted(x) for x in b.splitlines()]
+ [self.assertTainted(x) for x in c.splitlines()]
+ [self.assertClean(x) for x in u.splitlines()]
+
+ [self.assertTainted(x) for x in t.splitlines()]
+ [self.assertTainted(x) for x in a.splitlines()]
+ [self.assertTainted(x) for x in b.splitlines()]
+ [self.assertTainted(x) for x in c.splitlines()]
+ [self.assertClean(x) for x in u.splitlines()]
+
+ [self.assertTainted(x) for x in t.splitlines()]
+ [self.assertTainted(x) for x in a.splitlines()]
+ [self.assertTainted(x) for x in b.splitlines()]
+ [self.assertTainted(x) for x in c.splitlines()]
+ [self.assertClean(x) for x in u.splitlines()]
+
+ def test_rpartition(self):
+ t = 't t t tt tt'.taint()
+ u = 'u uu uuu uuuu u'
+ a = 'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone).\
+ _cleanfor(MeritPartial)
+ ss = ' '
+ tt = ' '.taint()
+
+ [self.assertTainted(x) for x in t.rpartition(' ')]
+ [self.assertTainted(x) for x in a.rpartition(' ')]
+ [self.assertTainted(x) for x in b.rpartition(' ')]
+ [self.assertTainted(x) for x in c.rpartition(' ')]
+ [self.assertClean(x) for x in u.rpartition(' ')]
+
+ [self.assertTainted(x) for x in t.rpartition(' '.taint())]
+ [self.assertTainted(x) for x in a.rpartition(' '.taint())]
+ [self.assertTainted(x) for x in b.rpartition(' '.taint())]
+ [self.assertTainted(x) for x in c.rpartition(' '.taint())]
+ [self.assertTainted(x) for x in u.rpartition(' '.taint())]
+
+ [self.assertTainted(x) for x in t.rpartition(ss)]
+ [self.assertTainted(x) for x in a.rpartition(ss)]
+ [self.assertTainted(x) for x in b.rpartition(ss)]
+ [self.assertTainted(x) for x in c.rpartition(ss)]
+ [self.assertClean(x) for x in u.rpartition(ss)]
+
+ [self.assertTainted(x) for x in t.rpartition(tt)]
+ [self.assertTainted(x) for x in a.rpartition(tt)]
+ [self.assertTainted(x) for x in b.rpartition(tt)]
+ [self.assertTainted(x) for x in c.rpartition(tt)]
+ [self.assertTainted(x) for x in u.rpartition(tt)]
+
+ [self.assertMerits(x, [MeritPartial]) for x in \
+ c.rpartition(' '._cleanfor(MeritPartial))]
+
+ def test_partition(self):
+ t = 't t t tt tt'.taint()
+ u = 'u uu uuu uuuu u'
+ a = 'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone).\
+ _cleanfor(MeritPartial)
+ ss = ' '
+ tt = ' '.taint()
+
+ [self.assertTainted(x) for x in t.partition(' ')]
+ [self.assertTainted(x) for x in a.partition(' ')]
+ [self.assertTainted(x) for x in b.partition(' ')]
+ [self.assertTainted(x) for x in c.partition(' ')]
+ [self.assertClean(x) for x in u.partition(' ')]
+
+ [self.assertTainted(x) for x in t.partition(' '.taint())]
+ [self.assertTainted(x) for x in a.partition(' '.taint())]
+ [self.assertTainted(x) for x in b.partition(' '.taint())]
+ [self.assertTainted(x) for x in c.partition(' '.taint())]
+ [self.assertTainted(x) for x in u.partition(' '.taint())]
+
+ [self.assertTainted(x) for x in t.partition(ss)]
+ [self.assertTainted(x) for x in a.partition(ss)]
+ [self.assertTainted(x) for x in b.partition(ss)]
+ [self.assertTainted(x) for x in c.partition(ss)]
+ [self.assertClean(x) for x in u.partition(ss)]
+
+ [self.assertTainted(x) for x in t.partition(tt)]
+ [self.assertTainted(x) for x in a.partition(tt)]
+ [self.assertTainted(x) for x in b.partition(tt)]
+ [self.assertTainted(x) for x in c.partition(tt)]
+ [self.assertTainted(x) for x in u.partition(tt)]
+
+ [self.assertMerits(x, [MeritPartial]) for x in \
+ c.partition(' '._cleanfor(MeritPartial))]
+
+ def test_strip(self):
+ t = ' t t t tt tt'.taint()
+ u = 'u uu uuu uuuu u'
+ a = 'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone)
+
+ x = 'xy'
+ y = 'xy'.taint()
+ z = 'xy'._cleanfor(MeritPartial)
+
+ self.assertTainted(t.strip())
+ self.assertTainted(a.strip())
+ self.assertTainted(b.strip())
+ self.assertTainted(c.strip())
+ self.assertClean(u.strip())
+
+ self.assertTainted(t.strip(x))
+ self.assertTainted(a.strip(x))
+ self.assertTainted(b.strip(x))
+ self.assertTainted(c.strip(x))
+ self.assertClean(u.strip(x))
+
+ self.assertTainted(t.strip(y))
+ self.assertTainted(a.strip(y))
+ self.assertTainted(b.strip(y))
+ self.assertTainted(c.strip(y))
+ self.assertTainted(u.strip(y))
+
+ self.assertTainted(t.strip(z))
+ self.assertTainted(a.strip(z))
+ self.assertTainted(b.strip(z))
+ self.assertTainted(c.strip(z))
+ self.assertTainted(u.strip(z))
+
+ def test_rstrip(self):
+ t = ' t t t tt tt'.taint()
+ u = 'u uu uuu uuuu u'
+ a = 'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone)
+
+ x = 'xy'
+ y = 'xy'.taint()
+ z = 'xy'._cleanfor(MeritPartial)
+
+ self.assertTainted(t.strip())
+ self.assertTainted(a.strip())
+ self.assertTainted(b.strip())
+ self.assertTainted(c.strip())
+ self.assertClean(u.strip())
+
+ self.assertTainted(t.strip(x))
+ self.assertTainted(a.strip(x))
+ self.assertTainted(b.strip(x))
+ self.assertTainted(c.strip(x))
+ self.assertClean(u.strip(x))
+
+ self.assertTainted(t.strip(y))
+ self.assertTainted(a.strip(y))
+ self.assertTainted(b.strip(y))
+ self.assertTainted(c.strip(y))
+ self.assertTainted(u.strip(y))
+
+ self.assertTainted(t.strip(z))
+ self.assertTainted(a.strip(z))
+ self.assertTainted(b.strip(z))
+ self.assertTainted(c.strip(z))
+ self.assertTainted(u.strip(z))
+
+ def test_lstrip(self):
+ t = ' t t t tt tt'.taint()
+ u = 'u uu uuu uuuu u'
+ a = 'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone)
+
+ x = 'xy'
+ y = 'xy'.taint()
+ z = 'xy'._cleanfor(MeritPartial)
+
+ self.assertTainted(t.strip())
+ self.assertTainted(a.strip())
+ self.assertTainted(b.strip())
+ self.assertTainted(c.strip())
+ self.assertClean(u.strip())
+
+ self.assertTainted(t.strip(x))
+ self.assertTainted(a.strip(x))
+ self.assertTainted(b.strip(x))
+ self.assertTainted(c.strip(x))
+ self.assertClean(u.strip(x))
+
+ self.assertTainted(t.strip(y))
+ self.assertTainted(a.strip(y))
+ self.assertTainted(b.strip(y))
+ self.assertTainted(c.strip(y))
+ self.assertTainted(u.strip(y))
+
+ self.assertTainted(t.strip(z))
+ self.assertTainted(a.strip(z))
+ self.assertTainted(b.strip(z))
+ self.assertTainted(c.strip(z))
+ self.assertTainted(u.strip(z))
+
+ def test_ljust(self):
+ t = ' t t t tt tt'.taint()
+ t2 = ''.taint()
+ u = 'u uu uuu uuuu u'
+ u2 = ''
+ a = 'a aa aa a a'._cleanfor(MeritFull)
+ b = u._cleanfor(MeritFull)._cleanfor(MeritNone)
+
+ x = '-'
+ y = '-'.taint()
+ z = '-'._cleanfor(MeritPartial)
+
+ self.assertTainted(t.ljust(20))
+ self.assertTainted(t2.ljust(20))
+ self.assertTainted(a.ljust(20))
+ self.assertTainted(b.ljust(20))
+ self.assertClean(u.ljust(20))
+ self.assertClean(u2.ljust(20))
+
+ self.assertTainted(t.ljust(0, x))
+ self.assertTainted(t2.ljust(0, x))
+ self.assertTainted(a.ljust(0, x))
+ self.assertTainted(b.ljust(0, x))
+ self.assertClean(u.ljust(0, x))
+ self.assertClean(u2.ljust(0, x))
+
+ self.assertTainted(t.ljust(30, x))
+ self.assertTainted(t2.ljust(30, x))
+ self.assertTainted(a.ljust(30, x))
+ self.assertTainted(b.ljust(30, x))
+ self.assertClean(u.ljust(30, x))
+ self.assertClean(u2.ljust(30, x))
+
+ self.assertTainted(t.ljust(0, y))
+ self.assertTainted(t2.ljust(0, y))
+ self.assertTainted(a.ljust(0, y))
+ self.assertTainted(b.ljust(0, y))
+ self.assertTainted(u.ljust(0, y))
+ self.assertTainted(u2.ljust(0, y))
+
+ self.assertTainted(t.ljust(30, y))
+ self.assertTainted(t2.ljust(30, y))
+ self.assertTainted(a.ljust(30, y))
+ self.assertTainted(b.ljust(30, y))
+ self.assertTainted(u.ljust(30, y))
+ self.assertTainted(u2.ljust(30, y))
+
+ self.assertTainted(t.ljust(0, z))
+ self.assertTainted(t2.ljust(0, z))
+ self.assertTainted(a.ljust(0, z))
+ self.assertTainted(b.ljust(0, z))
+ self.assertTainted(u.ljust(0, z))
+ self.assertTainted(u2.ljust(0, z))
+
+ self.assertTainted(t.ljust(30, z))
+ self.assertTainted(t2.ljust(30, z))
+ self.assertTainted(a.ljust(30, z))
+ self.assertTainted(b.ljust(30, z))
+ self.assertTainted(u.ljust(30, z))
+ self.assertTainted(u2.ljust(30, z))
+
+ # check if interning is not broken
+ self.assertTainted('u'.ljust(0, 'x'.taint()))
+ self.assertClean('u')
+ self.assertTainted('u'.taint().ljust(20, 'x'))
+ self.assertClean('u')
+ self.assertTainted('u'.ljust(0, 'x'.taint()))
+ self.assertClean('u')
+ self.assertTainted('u'.taint().ljust(20, 'x'))
+ self.assertClean('u')
+
+ def test_rjust(self):
+ t = ' t t t tt tt'.taint()
+ t2 = ''.taint()
+ u = 'u uu uuu uuuu u'
+ u2 = ''
+ a = 'a aa aa a a'._cleanfor(MeritFull)
+ b = u._cleanfor(MeritFull)._cleanfor(MeritNone)
+
+ x = '-'
+ y = '-'.taint()
+ z = '-'._cleanfor(MeritPartial)
+
+ self.assertTainted(t.rjust(20))
+ self.assertTainted(t2.rjust(20))
+ self.assertTainted(a.rjust(20))
+ self.assertTainted(b.rjust(20))
+ self.assertClean(u.rjust(20))
+ self.assertClean(u2.rjust(20))
+
+ self.assertTainted(t.rjust(0, x))
+ self.assertTainted(t2.rjust(0, x))
+ self.assertTainted(a.rjust(0, x))
+ self.assertTainted(b.rjust(0, x))
+ self.assertClean(u.rjust(0, x))
+ self.assertClean(u2.rjust(0, x))
+
+ self.assertTainted(t.rjust(30, x))
+ self.assertTainted(t2.rjust(30, x))
+ self.assertTainted(a.rjust(30, x))
+ self.assertTainted(b.rjust(30, x))
+ self.assertClean(u.rjust(30, x))
+ self.assertClean(u2.rjust(30, x))
+
+ self.assertTainted(t.rjust(0, y))
+ self.assertTainted(t2.rjust(0, y))
+ self.assertTainted(a.rjust(0, y))
+ self.assertTainted(b.rjust(0, y))
+ self.assertTainted(u.rjust(0, y))
+ self.assertTainted(u2.rjust(0, y))
+
+ self.assertTainted(t.rjust(30, y))
+ self.assertTainted(t2.rjust(30, y))
+ self.assertTainted(a.rjust(30, y))
+ self.assertTainted(b.rjust(30, y))
+ self.assertTainted(u.rjust(30, y))
+ self.assertTainted(u2.rjust(30, y))
+
+ self.assertTainted(t.rjust(0, z))
+ self.assertTainted(t2.rjust(0, z))
+ self.assertTainted(a.rjust(0, z))
+ self.assertTainted(b.rjust(0, z))
+ self.assertTainted(u.rjust(0, z))
+ self.assertTainted(u2.rjust(0, z))
+
+ self.assertTainted(t.rjust(30, z))
+ self.assertTainted(t2.rjust(30, z))
+ self.assertTainted(a.rjust(30, z))
+ self.assertTainted(b.rjust(30, z))
+ self.assertTainted(u.rjust(30, z))
+ self.assertTainted(u2.rjust(30, z))
+
+ # check if interning is not broken
+ self.assertTainted('u'.rjust(0, 'x'.taint()))
+ self.assertClean('u')
+ self.assertTainted('u'.taint().rjust(20, 'x'))
+ self.assertClean('u')
+ self.assertTainted('u'.rjust(0, 'x'.taint()))
+ self.assertClean('u')
+ self.assertTainted('u'.taint().rjust(20, 'x'))
+ self.assertClean('u')
+
+ def test_center(self):
+ t = ' t t t tt tt'.taint()
+ t2 = ''.taint()
+ u = 'u uu uuu uuuu u'
+ u2 = ''
+ a = 'a aa aa a a'._cleanfor(MeritFull)
+ b = u._cleanfor(MeritFull)._cleanfor(MeritNone)
+
+ x = '-'
+ y = '-'.taint()
+ z = '-'._cleanfor(MeritPartial)
+
+ self.assertTainted(t.center(20))
+ self.assertTainted(t2.center(20))
+ self.assertTainted(a.center(20))
+ self.assertTainted(b.center(20))
+ self.assertClean(u.center(20))
+ self.assertClean(u2.center(20))
+
+ self.assertTainted(t.center(0, x))
+ self.assertTainted(t2.center(0, x))
+ self.assertTainted(a.center(0, x))
+ self.assertTainted(b.center(0, x))
+ self.assertClean(u.center(0, x))
+ self.assertClean(u2.center(0, x))
+
+ self.assertTainted(t.center(30, x))
+ self.assertTainted(t2.center(30, x))
+ self.assertTainted(a.center(30, x))
+ self.assertTainted(b.center(30, x))
+ self.assertClean(u.center(30, x))
+ self.assertClean(u2.center(30, x))
+
+ self.assertTainted(t.center(0, y))
+ self.assertTainted(t2.center(0, y))
+ self.assertTainted(a.center(0, y))
+ self.assertTainted(b.center(0, y))
+ self.assertTainted(u.center(0, y))
+ self.assertTainted(u2.center(0, y))
+
+ self.assertTainted(t.center(30, y))
+ self.assertTainted(t2.center(30, y))
+ self.assertTainted(a.center(30, y))
+ self.assertTainted(b.center(30, y))
+ self.assertTainted(u.center(30, y))
+ self.assertTainted(u2.center(30, y))
+
+ self.assertTainted(t.center(0, z))
+ self.assertTainted(t2.center(0, z))
+ self.assertTainted(a.center(0, z))
+ self.assertTainted(b.center(0, z))
+ self.assertTainted(u.center(0, z))
+ self.assertTainted(u2.center(0, z))
+
+ self.assertTainted(t.center(30, z))
+ self.assertTainted(t2.center(30, z))
+ self.assertTainted(a.center(30, z))
+ self.assertTainted(b.center(30, z))
+ self.assertTainted(u.center(30, z))
+ self.assertTainted(u2.center(30, z))
+
+ # check if interning is not broken
+ self.assertTainted('u'.center(0, 'x'.taint()))
+ self.assertClean('u')
+ self.assertTainted('u'.taint().center(20, 'x'))
+ self.assertClean('u')
+ self.assertTainted('u'.center(0, 'x'.taint()))
+ self.assertClean('u')
+ self.assertTainted('u'.taint().center(20, 'x'))
+ self.assertClean('u')
+
+ def test_replace(self):
+ s = 'abc def def def'
+ a = 'def'
+ b = 'xyz'
+ st = s.taint()
+ at = s.taint()
+ bt = s.taint()
+
+ self.assertClean(s.replace(a, b))
+ self.assertTainted(st.replace(a, b))
+ self.assertTainted(s.replace(at, b))
+ self.assertTainted(st.replace(at, b))
+ self.assertTainted(s.replace(a, bt))
+ self.assertTainted(st.replace(a, bt))
+ self.assertTainted(s.replace(at, bt))
+ self.assertTainted(st.replace(at, bt))
+
+ def test_format_operator(self):
+ # test formatting using the % operator
+ t = 'ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ fmt = "%s %s %s %%"
+ fmt_taint = fmt.taint()
+ fmt_full = fmt_taint._cleanfor(MeritFull)
+ fmt_part = fmt_taint._cleanfor(MeritPartial)
+ fmt_none = fmt_taint._cleanfor(MeritNone)
+ fmt_all = fmt_full._cleanfor(MeritPartial)._cleanfor(MeritNone)
+
+ self.assertClean(fmt % ('a', 'b', 'c'))
+ self.assertTainted(fmt_taint % ('a', 'b', 'c'))
+ self.assertTainted(fmt_taint % (t, t, t))
+ self.assertTainted(fmt % ('a', 'b', t))
+ self.assertTainted(fmt % (t, 'b', t))
+ self.assertTainted(fmt % ('a', t, 'b'))
+
+ self.assertMerits(fmt % (t, 'a', t_full), [])
+ self.assertMerits(fmt % ('b', 'a', t_full), [MeritFull])
+ self.assertMerits(fmt % (t_all , t_full, 'a'), [MeritFull])
+ self.assertMerits(fmt_taint % (t, 'a', t_full), [])
+ self.assertMerits(fmt_taint % ('b', 'a', t_full), [])
+ self.assertMerits(fmt_taint % (t_all , t_full, 'a'), [])
+
+ self.assertMerits(fmt_full % (t_all , t_full, 'a'), [MeritFull])
+ self.assertMerits(fmt_all % (t_all , t_full, 'a'), [MeritFull])
+ self.assertMerits(fmt_part % (t_part , t_all, t_all), [MeritPartial])
+ self.assertMerits(fmt_all % (t_all, t_part, t_all), [MeritPartial])
+ self.assertMerits(fmt_none % (t_none , t_all, 'c'), [])
+ self.assertMerits(fmt_all % (t_all, t_none, t_all), [])
+
+ def test_format_method(self):
+ # test formatting using the format method
+ t = 'ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ fmt = "{} {} {} {{}}"
+ fmt_taint = fmt.taint()
+ fmt_full = fmt_taint._cleanfor(MeritFull)
+ fmt_part = fmt_taint._cleanfor(MeritPartial)
+ fmt_none = fmt_taint._cleanfor(MeritNone)
+ fmt_all = fmt_full._cleanfor(MeritPartial)._cleanfor(MeritNone)
+
+ self.assertClean(fmt.format('a', 'b', 'c'))
+ # TODO(marcinf) specification says that result of below operation
+ # should be tainted. However, since the last argument ('d') is not
+ # interpolated into format string, it is clean. Change the docs
+ # accordingly to mention that taint is propagated only across the
+ # relevant arguments.
+ self.assertClean(fmt.format('a', 'b', 'c', 'd'.taint()))
+ self.assertTainted(fmt_taint.format('a', 'b', 'c'))
+ self.assertTainted(fmt_taint.format(t, t, t))
+ self.assertTainted(fmt.format('a', 'b', t))
+ self.assertTainted(fmt.format(t, 'b', t))
+ self.assertTainted(fmt.format('a', t, 'b'))
+
+ self.assertMerits(fmt.format(t, 'a', t_full), [])
+ self.assertMerits(fmt.format('b', 'a', t_full), [MeritFull])
+ self.assertMerits(fmt.format(t_all , t_full, 'a'), [MeritFull])
+ self.assertMerits(fmt_taint.format(t, 'a', t_full), [])
+ self.assertMerits(fmt_taint.format('b', 'a', t_full), [])
+ self.assertMerits(fmt_taint.format(t_all , t_full, 'a'), [])
+
+ self.assertMerits(fmt_full.format(t_all , t_full, 'a'), [MeritFull])
+ self.assertMerits(fmt_all.format(t_all , t_full, 'a'), [MeritFull])
+ self.assertMerits(fmt_part.format(t_part , t_all, t_all),
+ [MeritPartial])
+ self.assertMerits(fmt_all.format(t_all, t_part, t_all),
+ [MeritPartial])
+ self.assertMerits(fmt_none.format(t_none , t_all, 'c'), [])
+ self.assertMerits(fmt_all.format(t_all, t_none, t_all), [])
+
+ fmt = "{2} {0} {1} {{}}"
+ fmt_taint = fmt.taint()
+ fmt_full = fmt_taint._cleanfor(MeritFull)
+ fmt_part = fmt_taint._cleanfor(MeritPartial)
+ fmt_none = fmt_taint._cleanfor(MeritNone)
+ fmt_all = fmt_full._cleanfor(MeritPartial)._cleanfor(MeritNone)
+
+ self.assertClean(fmt.format('a', 'b', 'c'))
+ self.assertClean(fmt.format('a', 'b', 'c', 'd'.taint()))
+ self.assertTainted(fmt_taint.format('a', 'b', 'c'))
+ self.assertTainted(fmt_taint.format(t, t, t))
+ self.assertTainted(fmt.format('a', 'b', t))
+ self.assertTainted(fmt.format(t, 'b', t))
+ self.assertTainted(fmt.format('a', t, 'b'))
+
+ self.assertMerits(fmt.format(t, 'a', t_full), [])
+ self.assertMerits(fmt.format('b', 'a', t_full), [MeritFull])
+ self.assertMerits(fmt.format(t_all , t_full, 'a'), [MeritFull])
+ self.assertMerits(fmt_taint.format(t, 'a', t_full), [])
+ self.assertMerits(fmt_taint.format('b', 'a', t_full), [])
+ self.assertMerits(fmt_taint.format(t_all , t_full, 'a'), [])
+
+ self.assertMerits(fmt_full.format(t_all , t_full, 'a'), [MeritFull])
+ self.assertMerits(fmt_all.format(t_all , t_full, 'a'), [MeritFull])
+ self.assertMerits(fmt_part.format(t_part , t_all, t_all),
+ [MeritPartial])
+ self.assertMerits(fmt_all.format(t_all, t_part, t_all),
+ [MeritPartial])
+ self.assertMerits(fmt_none.format(t_none , t_all, 'c'), [])
+ self.assertMerits(fmt_all.format(t_all, t_none, t_all), [])
+
+ fmt = "{x} {y[0]} {z.t} {{}}"
+ fmt_taint = fmt.taint()
+ fmt_full = fmt_taint._cleanfor(MeritFull)
+ fmt_part = fmt_taint._cleanfor(MeritPartial)
+ fmt_none = fmt_taint._cleanfor(MeritNone)
+ fmt_all = fmt_full._cleanfor(MeritPartial)._cleanfor(MeritNone)
+
+ def pack(x):
+ """ Create a dummy object d satisfying d.t == x. This is for
+ testing formatting string with objects' attributes. """
+ return type('zt', (), {'t': x})
+
+ self.assertClean(fmt.format(x='a', y=['b'], z=pack('c')))
+ self.assertClean(fmt.format(x='a', y=['b'], z=pack('c'),
+ t='d'.taint()))
+ self.assertTainted(fmt_taint.format(x='a', y=['b'], z=pack('c')))
+ self.assertTainted(fmt_taint.format(x=t, y=[t], z=pack(t)))
+ self.assertTainted(fmt.format(x='a', y=['b'], z=pack(t)))
+ self.assertTainted(fmt.format(x=t, y=['b'], z=pack(t)))
+ self.assertTainted(fmt.format(x='a', y=[t], z=pack('b')))
+
+ self.assertMerits(fmt.format(x=t, y=['a'], z=pack(t_full)),
+ [])
+ self.assertMerits(fmt.format(x='b', y=['a'], z=pack(t_full)),
+ [MeritFull])
+ self.assertMerits(fmt.format(x=t_all , y=[t_full], z=pack('a')),
+ [MeritFull])
+ self.assertMerits(fmt_taint.format(x=t, y=['a'], z=pack(t_full)),
+ [])
+ self.assertMerits(fmt_taint.format(x='b', y=['a'], z=pack(t_full)),
+ [])
+ self.assertMerits(fmt_taint.format(x=t_all , y=[t_full], z=pack('a')),
+ [])
+
+ self.assertMerits(fmt_full.format(x=t_all , y=[t_full], z=pack('a')),
+ [MeritFull])
+ self.assertMerits(fmt_all.format(x=t_all , y=[t_full], z=pack('a')),
+ [MeritFull])
+ self.assertMerits(fmt_part.format(x=t_part , y=[t_all], z=pack(t_all)),
+ [MeritPartial])
+ self.assertMerits(fmt_all.format(x=t_all, y=[t_part], z=pack(t_all)),
+ [MeritPartial])
+ self.assertMerits(fmt_none.format(x=t_none , y=[t_all], z=pack('c')),
+ [])
+ self.assertMerits(fmt_all.format(x=t_all, y=[t_none], z=pack(t_all)),
+ [])
+
+ nested = "{0:{a}}"
+ self.assertMerits(nested.taint().format('t', a='s'), [])
+ self.assertMerits(nested._cleanfor(MeritFull).format('t', a='s'),
+ [MeritFull])
+ self.assertMerits(nested._cleanfor(MeritPartial).format('t', a='s'),
+ [])
+ self.assertMerits(nested._cleanfor(MeritPartial).format(
+ t_part, a='s'._cleanfor(MeritPartial)),
+ [MeritPartial])
+ self.assertMerits(nested._cleanfor(MeritNone).format(
+ t_none, a='s'._cleanfor(MeritNone)),
+ [])
+
+
+
+ def test_translate(self):
+ t = string.maketrans('ab', 'ba')
+ d1 = 'bcd'
+ d2 = 'ijk'
+ s1 = 'abcdef'
+ s2 = 'ghijkl'
+ s0 = 'a'
+
+ self.assertTainted(s1.taint().translate(t, d1))
+ self.assertTainted(s2.taint().translate(t, d1))
+ self.assertTainted(s1.translate(t.taint(), d2))
+ self.assertTainted(s2.translate(t, d2.taint()))
+ self.assertTainted(s1.taint().translate(t.taint()))
+ self.assertTainted(s2.translate(t.taint()))
+
+ self.assertTainted(s1.taint().translate(t, d1))
+ self.assertTainted(s2.taint().translate(t, d1))
+ self.assertTainted(s1.translate(t.taint(), d2))
+ self.assertTainted(s2.translate(t, d2.taint()))
+ self.assertTainted(s1.taint().translate(t.taint()))
+ self.assertTainted(s2.translate(t.taint()))
+ self.assertTainted(s0.translate(t.taint()))
+ self.assertTainted(s0.taint().translate(t))
+ self.assertTainted(s0.translate(t, d2.taint()))
+ self.assertTainted(s0.taint().translate(t, d2))
+
+ self.assertMerits(s1._cleanfor(MeritFull).translate(t, d1),
+ [MeritFull])
+ self.assertMerits(s2._cleanfor(MeritFull).translate(t, d1),
+ [MeritFull])
+ self.assertMerits(s1._cleanfor(MeritPartial).translate(t, d1),
+ [])
+ self.assertMerits(s2._cleanfor(MeritPartial).translate(t, d1),
+ [])
+ self.assertMerits(s1._cleanfor(MeritPartial)
+ .translate(t._cleanfor(MeritPartial),
+ d1._cleanfor(MeritPartial)),
+ [MeritPartial])
+ self.assertMerits(s2._cleanfor(MeritPartial)
+ .translate(t._cleanfor(MeritPartial),
+ d1._cleanfor(MeritPartial)),
+ [MeritPartial])
+ self.assertMerits(s1._cleanfor(MeritNone)
+ .translate(t._cleanfor(MeritNone),
+ d1._cleanfor(MeritNone)),
+ [])
+ self.assertMerits(s2._cleanfor(MeritNone)
+ .translate(t._cleanfor(MeritNone),
+ d1._cleanfor(MeritNone)),
+ [])
+
+ self.assertMerits(s1._cleanfor(MeritFull).translate(t, d2),
+ [MeritFull])
+ self.assertMerits(s2._cleanfor(MeritFull).translate(t, d2),
+ [MeritFull])
+ self.assertMerits(s1._cleanfor(MeritPartial).translate(t, d2),
+ [])
+ self.assertMerits(s2._cleanfor(MeritPartial).translate(t, d2),
+ [])
+ self.assertMerits(s1._cleanfor(MeritPartial)
+ .translate(t._cleanfor(MeritPartial),
+ d2._cleanfor(MeritPartial)),
+ [MeritPartial])
+ self.assertMerits(s2._cleanfor(MeritPartial)
+ .translate(t._cleanfor(MeritPartial),
+ d2._cleanfor(MeritPartial)),
+ [MeritPartial])
+ self.assertMerits(s1._cleanfor(MeritNone)
+ .translate(t._cleanfor(MeritNone),
+ d2._cleanfor(MeritNone)),
+ [])
+ self.assertMerits(s2._cleanfor(MeritNone)
+ .translate(t._cleanfor(MeritNone),
+ d2._cleanfor(MeritNone)),
+ [])
+
+ self.assertMerits(s0._cleanfor(MeritFull)
+ .translate(t._cleanfor(MeritFull)), [MeritFull])
+ self.assertMerits(s0._cleanfor(MeritFull).translate(t), [MeritFull])
+ self.assertMerits(s0.translate(t, d2._cleanfor(MeritPartial)), [])
+ self.assertMerits(s0._cleanfor(MeritNone)
+ .translate(t._cleanfor(MeritNone),
+ d2._cleanfor(MeritNone)), [])
+
+ self.assertClean(s1.translate(t, d1))
+ self.assertClean(s2.translate(t, d1))
+ self.assertClean(s1.translate(t, d2))
+ self.assertClean(s2.translate(t, d2))
+ self.assertClean(s1.translate(t))
+ self.assertClean(s2.translate(t))
+ self.assertClean(s0.translate(t))
+
+def test_main():
+ test_support.run_unittest(TaintTest, MeritsTest, UnaryStringOperationTest,
+ VariadicStringOperationTest)
diff --git a/Lib/test/test_taintmodule.py b/Lib/test/test_taintmodule.py
new file mode 100644
index 0000000..6e93ce1
--- /dev/null
+++ b/Lib/test/test_taintmodule.py
@@ -0,0 +1,982 @@
+import unittest
+import itertools
+import taint
+import json
+import imp
+import re
+from test import test_support
+
+CONFIG_1 = test_support.findfile('config1.json', subdir='tainttestdata')
+CONFIG_2 = test_support.findfile('config2.json', subdir='tainttestdata')
+CONFIG_3 = test_support.findfile('config3.json', subdir='tainttestdata')
+CONFIG_4 = test_support.findfile('config4.json', subdir='tainttestdata')
+
+BROKEN_SMALL = test_support.findfile('config_broken_small.json',
+ subdir='tainttestdata')
+BROKEN_BIG = test_support.findfile('config_broken_big.json',
+ subdir='tainttestdata')
+
+MOCK_MODULE_FILE = test_support.findfile('mockmodule.py',
+ subdir='tainttestdata')
+MockModule = imp.load_source('MockModule', MOCK_MODULE_FILE)
+
+class MeritFull(Merit):
+ propagation = Merit.FullPropagation
+
+class MeritPart(Merit):
+ propagation = Merit.PartialPropagation
+
+class MeritNone(Merit):
+ propagation = Merit.NonePropagation
+
+class AbstractTaintTest(unittest.TestCase):
+ def assertTainted(self, object):
+ self.assertTrue(object.istainted())
+ self.assertFalse(object.isclean())
+
+ def assertClean(self, object):
+ self.assertTrue(object.isclean())
+ self.assertFalse(object.istainted())
+
+ def assertMerits(self, object, merits):
+ if merits == None or object._merits() == None:
+ self.assertEqual(object._merits(), None)
+ self.assertEqual(merits, None)
+ self.assertClean(object)
+ else:
+ self.assertEqual(object._merits(), set(merits))
+
+ def assertTaintedAll(self, object):
+ for o in object:
+ self.assertTainted(o)
+
+ def assertCleanAll(self, object):
+ for o in object:
+ self.assertClean(o)
+
+ def assertMeritsAll(self, object, merits):
+ for o in object:
+ self.assertMerits(o, merits)
+
+ def assertMeritsAllGroups(self, object, merits):
+ for o in object:
+ self.assertMeritsAll(o.groupdict().values(), merits)
+
+# Though decorators are not encouraged way of using this module, the patching
+# module works by decorating functions specified in config, so it makes sense
+# to have (and test them) anyway.
+
+class DecoratorTest(AbstractTaintTest):
+ def test_simple_functions(self):
+ @taint.source
+ def src():
+ return "abc"
+
+ @taint.source
+ def src2():
+ return ["abc", "def", "xyz"]
+
+ @taint.source
+ def src3():
+ return ("abc", "def", ("xyz", "tw"))
+
+ @taint.source
+ def src4():
+ return {1: ["aaa", "bbb"],
+ 3: ("ccc", "ddd")}
+
+ @taint.cleaner(MeritFull)
+ def cln(s):
+ return s
+
+ @taint.sink(MeritFull)
+ def snk(s):
+ return True
+
+ def flatten(l):
+ return itertools.chain.from_iterable(l)
+
+ s = src()
+ self.assertTainted(s)
+ for s in src2():
+ self.assertTainted(s)
+ for s in flatten(src3()):
+ self.assertTainted(s)
+ for s in flatten(src4().values()):
+ self.assertTainted(s)
+
+ c = cln(s)
+ self.assertTainted(c)
+ self.assertMerits(c, [MeritFull])
+ self.assertTrue(snk(c))
+
+ with self.assertRaises(TaintError):
+ snk(s)
+
+ with self.assertRaises(TaintError):
+ snk('abc'.taint())
+
+ self.assertTrue(snk('abc'))
+
+
+class SimplePatcherTest(AbstractTaintTest):
+ class InnerClass(object):
+ @staticmethod
+ def static_source():
+ return "abc"
+
+ @staticmethod
+ def static_cleaner(s):
+ return s
+
+ @staticmethod
+ def static_sink(s):
+ return True
+
+ @classmethod
+ def class_source(cls):
+ return "abc"
+
+ @classmethod
+ def class_cleaner(cls, s):
+ return s
+
+
+ @classmethod
+ def class_sink(cls, s):
+ return True
+
+ def instance_source(self):
+ return "abc"
+
+ def instance_cleaner(self, s):
+ return s
+
+ def complex_sink(self, a, b, c, d=None, e=None, f=None):
+ return True
+
+ def instance_sink(self, s):
+ return True
+
+
+ def test_patcher_classes(self):
+ self.assertTainted(SimplePatcherTest.InnerClass.static_source())
+ with self.assertRaises(TaintError):
+ SimplePatcherTest.InnerClass.static_sink('a'.taint())
+ self.assertTrue(SimplePatcherTest.InnerClass.static_sink('a'))
+
+ self.assertTainted(SimplePatcherTest.InnerClass.class_source())
+ with self.assertRaises(TaintError):
+ SimplePatcherTest.InnerClass.class_sink('a'.taint())
+ self.assertTrue(SimplePatcherTest.InnerClass.class_sink('a'))
+
+ inc = SimplePatcherTest.InnerClass()
+ self.assertTainted(inc.instance_source())
+ with self.assertRaises(TaintError):
+ inc.instance_sink('a'.taint())
+ self.assertTrue(inc.instance_sink('a'))
+ self.assertMerits(inc.instance_cleaner('a'.taint()),
+ [MeritFull, MeritNone])
+
+ def test_patcher_toplevel(self):
+ self.assertTainted(toplevel_source())
+ with self.assertRaises(TaintError):
+ toplevel_sink('a'.taint())
+
+
+ def test_complex_patching(self):
+ inc = SimplePatcherTest.InnerClass()
+ self.assertTainted(inc.instance_source())
+ with self.assertRaises(TaintError):
+ inc.complex_sink('a'.taint(), 'b', 'c', d='d', e='e')
+ with self.assertRaises(TaintError):
+ inc.complex_sink('a'._cleanfor(MeritFull), 'b',
+ 'c'._cleanfor(MeritFull), d='d',
+ e='e'._cleanfor(MeritFull))
+ self.assertTrue(inc.complex_sink('a', 'b', 'c', d='d', e='e'))
+ with self.assertRaises(TaintError):
+ inc.complex_sink('a'._cleanfor(MeritFull)._cleanfor(MeritNone), 'b',
+ 'c'._cleanfor(MeritFull), d='d',
+ e='e'._cleanfor(MeritFull)._cleanfor(MeritNone))
+ self.assertTrue(inc.complex_sink(
+ 'a'._cleanfor(MeritFull)._cleanfor(MeritNone),
+ 'b',
+ 'c'._cleanfor(MeritFull)._cleanfor(MeritNone),
+ d='d',
+ e='e'._cleanfor(MeritFull)._cleanfor(MeritNone)))
+ self.assertTrue(inc.complex_sink('a', 'b', 'c', d='d', e='e',
+ f={'foo': 'bar'}))
+ with self.assertRaises(TaintError):
+ inc.complex_sink('a', 'b', 'c', d='d', e='e',
+ f={'foo': 'bar'.taint()})
+ self.assertTrue(inc.complex_sink('a', 'b', 'c', d='d', e='e',
+ f={'foo':
+ 'bar'._cleanfor(MeritFull)\
+ ._cleanfor(MeritNone)}))
+
+class ImportedObjectsPatchingTest(AbstractTaintTest):
+ def setUp(self):
+ taint.enable(CONFIG_2)
+
+ def testImportedPatching(self):
+ inc = MockModule.MockClass()
+ self.assertTainted(inc.instance_source())
+ with self.assertRaises(TaintError):
+ inc.instance_sink('a'.taint())
+ self.assertTrue(inc.instance_sink('a'))
+ self.assertMerits(inc.instance_cleaner('a'.taint()),
+ [MockModule.MeritX])
+
+
+class PropagationContextsTest(AbstractTaintTest):
+ def testContexts(self):
+ ut = 'ttt'
+ tt = 'ttt'
+ tf = 'ttt'._cleanfor(MeritFull)
+ tp = 'ttt'._cleanfor(MeritPart)
+ tn = 'ttt'._cleanfor(MeritNone)
+
+ with taint.unsafePropagationFull(MeritNone):
+ self.assertMerits(ut + tn, [MeritNone])
+ self.assertMerits(tn + tn, [MeritNone])
+
+ with taint.unsafePropagationNone(MeritFull):
+ self.assertMerits(ut + tf, [])
+ self.assertMerits(tf + tf, [])
+
+ with taint.unsafePropagationPartial(MeritNone):
+ self.assertMerits(tn + tn, [MeritNone])
+ self.assertMerits(ut + tn, [])
+
+
+class ConfigValidation(unittest.TestCase):
+ def setUp(self):
+ self.maxDiff = None
+
+ def test_validator_errors_small(self):
+ config = json.load(open(BROKEN_SMALL, "r"))
+ warnings, errors = taint.Validator(config).validate()
+ errors_expected = [
+ "Malformed sources in the taint config (expected a list, not"
+ " <type 'unicode'>)",
+ "Malformed cleaners in the taint config (expected a list, not"
+ " <type 'unicode'>)",
+ "Malformed sinks in the taint config (expected a list, not"
+ " <type 'unicode'>)",
+ ]
+ warnings_expected = []
+ self.assertItemsEqual(warnings, warnings_expected)
+ self.assertItemsEqual(errors, errors_expected)
+
+ def test_validator_errors_big(self):
+ config = json.load(open(BROKEN_BIG, "r"))
+ warnings, errors = taint.Validator(config).validate()
+ warnings = map(unicode, warnings)
+ errors = map(unicode, errors)
+ errors_expected = [
+ u"No merit specified for cleaner cleaner_f.",
+ u"No merit specified for sink function_f.",
+ u"Unexpected object in cleaners: [].",
+ u"Unexpected object in sinks: [u'unexpected', u'object'].",
+
+ # broken complex sink errors:
+ u"Malformed args (expected list) for complex sink complex_sink_3.",
+ u"Malformed kwargs (expected list) for complex sink"
+ " complex_sink_4.",
+ u"Malformed (keyword or positional) argument [u'qwer'] in complex"
+ " sink complex_sink_5.",
+ u"Malformed (keyword or positional) argument {} in complex sink"
+ " complex_sink_6.",
+ u"Malformed merits 1234 for argument aaa in complex sink"
+ " complex_sink_7.",
+ u"Malformed merits [5] for argument bbb in complex sink"
+ " complex_sink_8.",
+
+ # broken complex sinks:
+ u"Unexpected object in sinks: {u'complex_sink_3':"
+ " {u'args': u'zxcv'}}.",
+ u"Unexpected object in sinks: {u'complex_sink_4':"
+ " {u'kwargs': u'zxcv'}}.",
+ u"Unexpected object in sinks: {u'complex_sink_5':"
+ " {u'kwargs': [[u'qwer']]}}.",
+ u"Unexpected object in sinks: {u'complex_sink_6':"
+ " {u'kwargs': [{}]}}.",
+ u"Unexpected object in sinks: {u'complex_sink_7':"
+ " {u'kwargs': [{u'aaa': 1234}]}}.",
+ u"Unexpected object in sinks: {u'complex_sink_8':"
+ " {u'kwargs': [{u'bbb': [5]}]}}."
+ ]
+
+ warnings_expected = [
+ u"Unexpected fields in config: bar, baz, foo.",
+ u"No cleaners specified for merit MeritA.",
+ u"No cleaners specified for merit MeritC.",
+ u"No sinks specified for merit MeritD.",
+ u"Config may be confusing - simple sink function_g is preceded by"
+ " a complex sink, not simple sink or merit.",
+ u"Config may be confusing - complex sink complex_sink preceded"
+ " by a merit MeritE, not another sink.",
+ u"No sinks specified for merit MeritF.",
+ u"Unexpected options: bar, foo",
+
+ u"Unexpected key in complex_sink_2: foobar.",
+ ]
+
+ self.assertItemsEqual(warnings, warnings_expected)
+ self.assertItemsEqual(errors, errors_expected)
+
+
+class PropagatorTest(AbstractTaintTest):
+ class Person(object):
+ def __init__(self, name):
+ self.name = name
+
+ def rename(self, name):
+ self.name = name
+
+ def greet(self, greeting):
+ return "{}, {}!".format(greeting,
+ self.name)
+ def duplicate(self):
+ return PropagatorTest.Person(self.name)
+
+ def setUp(self):
+ taint.enable(CONFIG_3)
+
+ def test_propagator_decoration(self):
+ @taint.propagator
+ class Person2():
+ def __init__(self, name):
+ self.name = name
+
+ def rename(self, name):
+ self.name = name
+
+ def greet(self, greeting):
+ return "{}, {}!".format(greeting,
+ self.name)
+ def duplicate(self):
+ return Person2(self.name)
+
+ @taint.propagator
+ def const2(s):
+ return "abc"
+
+ self.assertClean(const2("abc"))
+ self.assertTainted(const2("abc".taint()))
+ self.assertMerits(const2("abc"._cleanfor(MeritNone)), [MeritNone])
+ pu = Person2("xyz")
+ p_none = Person2("xyz"._cleanfor(MeritNone))
+ p_part = Person2("xyz"._cleanfor(MeritPart))
+ p_full = Person2("xyz"._cleanfor(MeritFull))
+
+ self.assertMerits(pu.name, None)
+ self.assertMerits(p_none.name, [MeritNone])
+ self.assertMerits(p_part.name, [MeritPart])
+ self.assertMerits(p_full.name, [MeritFull])
+
+ self.assertMerits(pu.greet("hi"), None)
+ self.assertMerits(p_none.greet("hi"), [])
+ self.assertMerits(p_part.greet("hi"), [])
+ self.assertMerits(p_full.greet("hi"), [MeritFull])
+
+ self.assertMerits(pu.greet("hi".taint()), [])
+ self.assertMerits(p_none.greet("hi".taint()), [])
+ self.assertMerits(p_part.greet("hi".taint()), [])
+ self.assertMerits(p_full.greet("hi".taint()), [])
+
+ self.assertMerits(pu.duplicate().name, None)
+ self.assertMerits(p_none.duplicate().name, [])
+ self.assertMerits(p_part.duplicate().name, [])
+ self.assertMerits(p_full.duplicate().name, [MeritFull])
+
+ def test_propagator_config(self):
+ self.assertClean(toplevel_propagator("abc"))
+ self.assertTainted(toplevel_propagator("abc".taint()))
+ self.assertMerits(toplevel_propagator("abc"._cleanfor(MeritNone)),
+ [MeritNone])
+
+ pu = PropagatorTest.Person("xyz")
+ p_none = PropagatorTest.Person("xyz"._cleanfor(MeritNone))
+ p_part = PropagatorTest.Person("xyz"._cleanfor(MeritPart))
+ p_full = PropagatorTest.Person("xyz"._cleanfor(MeritFull))
+
+ self.assertMerits(pu.name, None)
+ self.assertMerits(p_none.name, [MeritNone])
+ self.assertMerits(p_part.name, [MeritPart])
+ self.assertMerits(p_full.name, [MeritFull])
+
+ self.assertMerits(pu.greet("hi"), None)
+ self.assertMerits(p_none.greet("hi"), [])
+ self.assertMerits(p_part.greet("hi"), [])
+ self.assertMerits(p_full.greet("hi"), [MeritFull])
+
+ self.assertMerits(pu.greet("hi".taint()), [])
+ self.assertMerits(p_none.greet("hi".taint()), [])
+ self.assertMerits(p_part.greet("hi".taint()), [])
+ self.assertMerits(p_full.greet("hi".taint()), [])
+
+ self.assertMerits(pu.duplicate().name, None)
+ self.assertMerits(p_none.duplicate().name, [])
+ self.assertMerits(p_part.duplicate().name, [])
+ self.assertMerits(p_full.duplicate().name, [MeritFull])
+
+ def test_taint_object_function(self):
+ class Person2():
+ def __init__(self, name):
+ self.name = name
+
+ def greet(self, greeting):
+ return "{}, {}!".format(greeting,
+ self.name)
+ def duplicate(self):
+ return Person2(self.name)
+
+ class Dict(dict): pass
+ class List(list): pass
+ class Tuple(tuple): pass
+ class Set(set): pass
+ class FrozenSet(frozenset): pass
+
+ pu = taint._taint_object(Person2("xyz"), None)
+ p_none = taint._taint_object(Person2("xyz"), [MeritNone])
+ p_part = taint._taint_object(Person2("xyz"), [MeritPart])
+ p_full = taint._taint_object(Person2("xyz"), [MeritFull])
+
+ self.assertMerits(pu.name, None)
+ self.assertMerits(p_none.name, [MeritNone])
+ self.assertMerits(p_part.name, [MeritPart])
+ self.assertMerits(p_full.name, [MeritFull])
+
+ self.assertMerits(pu.greet("hi"), None)
+ self.assertMerits(p_none.greet("hi"), [])
+ self.assertMerits(p_part.greet("hi"), [])
+ self.assertMerits(p_full.greet("hi"), [MeritFull])
+
+ self.assertMerits(pu.greet("hi".taint()), [])
+ self.assertMerits(p_none.greet("hi".taint()), [])
+ self.assertMerits(p_part.greet("hi".taint()), [])
+ self.assertMerits(p_full.greet("hi".taint()), [])
+
+ self.assertMerits(pu.duplicate().name, None)
+ self.assertMerits(p_none.duplicate().name, [])
+ self.assertMerits(p_part.duplicate().name, [])
+ self.assertMerits(p_full.duplicate().name, [MeritFull])
+
+ for Cls in [List, Tuple, Set, FrozenSet]:
+ x = Cls("a")
+ self.assertMeritsAll(taint._taint_object(x, [MeritFull]),
+ [MeritFull])
+ for item in taint._taint_object(x, None):
+ self.assertClean(item)
+
+ d = Dict()
+ d["a"] = "b"
+ self.assertMeritsAll(taint._taint_object(d, [MeritFull]).values(),
+ [MeritFull])
+
+class OptionsTest(AbstractTaintTest):
+ def setUp(self):
+ taint.enable(CONFIG_4)
+
+ def test_file_handles(self):
+ def fake_open(name):
+ class File():
+ def __init__(self, name):
+ self.name = name
+
+ def read(self):
+ return "qwerty"
+
+ return File(name)
+
+ # mock the open(), because patching it will mess up other tests
+ globals()["fake_open"] = taint._proxy_function(fake_open,
+ tainted=True)
+
+
+ def test_search(self):
+ ut = "abc"
+ tt = ut.taint()
+ t_full = ut._cleanfor(MeritFull)
+ t_part = ut._cleanfor(MeritPart)
+ t_none = ut._cleanfor(MeritNone)
+ t_all = ut._cleanfor(MeritFull)._cleanfor(MeritPart)\
+ ._cleanfor(MeritNone)
+
+ self.assertTainted(re.search(tt, tt).group(0))
+ self.assertTainted(re.search(tt, ut).group(0))
+ self.assertTainted(re.search(ut, tt).group(0))
+ # Note: the order of this tests matter, since we want to
+ # make sure we did not break caching
+ self.assertClean(re.search(ut, ut).group(0))
+
+ self.assertMerits(re.search(ut, tt).group(0), [])
+ self.assertMerits(re.search(ut, t_all).group(0), [MeritFull])
+ self.assertMerits(re.search(ut, t_full).group(0), [MeritFull])
+ self.assertMerits(re.search(ut, t_part).group(0), [])
+ self.assertMerits(re.search(ut, t_none).group(0), [])
+
+ self.assertMerits(re.search(tt, ut).group(0), [])
+ self.assertMerits(re.search(t_all, ut).group(0), [MeritFull])
+ self.assertMerits(re.search(t_full, ut).group(0), [MeritFull])
+ self.assertMerits(re.search(t_part, ut).group(0), [])
+ self.assertMerits(re.search(t_none, ut).group(0), [])
+
+ self.assertMerits(re.search(t_all, tt).group(0), [])
+ self.assertMerits(re.search(t_all, t_full).group(0), [MeritFull])
+ self.assertMerits(re.search(t_all, t_part).group(0), [])
+ self.assertMerits(re.search(t_all, t_none).group(0), [])
+
+ self.assertMerits(re.search(tt, t_all).group(0), [])
+ self.assertMerits(re.search(t_full, t_all).group(0), [MeritFull])
+ self.assertMerits(re.search(t_part, t_all).group(0), [])
+ self.assertMerits(re.search(t_none, t_all).group(0), [])
+
+ def test_match(self):
+ ut = "abc"
+ tt = ut.taint()
+ t_full = ut._cleanfor(MeritFull)
+ t_part = ut._cleanfor(MeritPart)
+ t_none = ut._cleanfor(MeritNone)
+ t_all = ut._cleanfor(MeritFull)._cleanfor(MeritPart)\
+ ._cleanfor(MeritNone)
+
+ self.assertTainted(re.match(tt, tt).group(0))
+ self.assertTainted(re.match(tt, ut).group(0))
+ self.assertTainted(re.match(ut, tt).group(0))
+ # the order of this tests matter, since we want to
+ # make sure we did not break caching
+ self.assertClean(re.match(ut, ut).group(0))
+
+ self.assertMerits(re.match(ut, tt).group(0), [])
+ self.assertMerits(re.match(ut, t_all).group(0), [MeritFull])
+ self.assertMerits(re.match(ut, t_full).group(0), [MeritFull])
+ self.assertMerits(re.match(ut, t_part).group(0), [])
+ self.assertMerits(re.match(ut, t_none).group(0), [])
+
+ self.assertMerits(re.match(tt, ut).group(0), [])
+ self.assertMerits(re.match(t_all, ut).group(0), [MeritFull])
+ self.assertMerits(re.match(t_full, ut).group(0), [MeritFull])
+ self.assertMerits(re.match(t_part, ut).group(0), [])
+ self.assertMerits(re.match(t_none, ut).group(0), [])
+
+ self.assertMerits(re.match(t_all, tt).group(0), [])
+ self.assertMerits(re.match(t_all, t_full).group(0), [MeritFull])
+ self.assertMerits(re.match(t_all, t_part).group(0), [])
+ self.assertMerits(re.match(t_all, t_none).group(0), [])
+
+ self.assertMerits(re.match(tt, t_all).group(0), [])
+ self.assertMerits(re.match(t_full, t_all).group(0), [MeritFull])
+ self.assertMerits(re.match(t_part, t_all).group(0), [])
+ self.assertMerits(re.match(t_none, t_all).group(0), [])
+
+ def test_sub(self):
+ ut = "abc"
+ tt = ut.taint()
+ t_full = ut._cleanfor(MeritFull)
+ t_part = ut._cleanfor(MeritPart)
+ t_none = ut._cleanfor(MeritNone)
+ t_all = ut._cleanfor(MeritFull)._cleanfor(MeritPart)\
+ ._cleanfor(MeritNone)
+
+ self.assertTainted(re.sub(tt, tt, tt))
+ self.assertTainted(re.sub(tt, tt, ut))
+ self.assertTainted(re.sub(tt, ut, tt))
+ self.assertTainted(re.sub(ut, tt, tt))
+ self.assertTainted(re.sub(ut, ut, tt))
+ self.assertTainted(re.sub(ut, tt, ut))
+ self.assertTainted(re.sub(tt, ut, ut))
+ # the order of this tests matter, since we want to
+ # make sure we did not break caching
+ self.assertClean(re.sub(ut, ut, ut))
+
+ self.assertMerits(re.sub(ut, ut, tt), [])
+ self.assertMerits(re.sub(ut, ut, t_all), [MeritFull])
+ self.assertMerits(re.sub(ut, ut, t_full), [MeritFull])
+ self.assertMerits(re.sub(ut, ut, t_part), [])
+ self.assertMerits(re.sub(ut, ut, t_none), [])
+
+ self.assertMerits(re.sub(ut, tt, ut), [])
+ self.assertMerits(re.sub(ut, t_all, ut), [MeritFull])
+ self.assertMerits(re.sub(ut, t_full, ut), [MeritFull])
+ self.assertMerits(re.sub(ut, t_part, ut), [])
+ self.assertMerits(re.sub(ut, t_none, ut), [])
+
+ self.assertMerits(re.sub(tt, ut, ut), [])
+ self.assertMerits(re.sub(t_all, ut, ut), [MeritFull])
+ self.assertMerits(re.sub(t_full, ut, ut), [MeritFull])
+ self.assertMerits(re.sub(t_part, ut, ut), [])
+ self.assertMerits(re.sub(t_none, ut, ut), [])
+
+ self.assertMerits(re.sub(t_all, t_all, tt), [])
+ self.assertMerits(re.sub(t_all, t_all, ut), [MeritFull])
+ self.assertMerits(re.sub(t_all, t_all, t_full), [MeritFull])
+ self.assertMerits(re.sub(t_all, t_all, t_part), [MeritPart])
+ self.assertMerits(re.sub(t_all, t_all, t_none), [])
+
+ self.assertMerits(re.sub(t_all, tt, t_all), [])
+ self.assertMerits(re.sub(t_all, ut, t_all), [MeritFull])
+ self.assertMerits(re.sub(t_all, t_full, t_all), [MeritFull])
+ self.assertMerits(re.sub(t_all, t_part, t_all), [MeritPart])
+ self.assertMerits(re.sub(t_all, t_none, t_all), [])
+
+ self.assertMerits(re.sub(tt, t_all, t_all), [])
+ self.assertMerits(re.sub(ut, t_all, t_all), [MeritFull])
+ self.assertMerits(re.sub(t_full, t_all, t_all), [MeritFull])
+ self.assertMerits(re.sub(t_part, t_all, t_all), [MeritPart])
+ self.assertMerits(re.sub(t_none, t_all, t_all), [])
+
+ def test_subn(self):
+ ut = "abc"
+ tt = ut.taint()
+ t_full = ut._cleanfor(MeritFull)
+ t_part = ut._cleanfor(MeritPart)
+ t_none = ut._cleanfor(MeritNone)
+ t_all = ut._cleanfor(MeritFull)._cleanfor(MeritPart)\
+ ._cleanfor(MeritNone)
+
+ self.assertTainted(re.subn(tt, tt, tt)[0])
+ self.assertTainted(re.subn(tt, tt, ut)[0])
+ self.assertTainted(re.subn(tt, ut, tt)[0])
+ self.assertTainted(re.subn(ut, tt, tt)[0])
+ self.assertTainted(re.subn(ut, ut, tt)[0])
+ self.assertTainted(re.subn(ut, tt, ut)[0])
+ self.assertTainted(re.subn(tt, ut, ut)[0])
+ # the order of this tests matter, since we want to
+ # make sure we did not break caching
+ self.assertClean(re.subn(ut, ut, ut)[0])
+
+ self.assertMerits(re.subn(ut, ut, tt)[0], [])
+ self.assertMerits(re.subn(ut, ut, t_all)[0], [MeritFull])
+ self.assertMerits(re.subn(ut, ut, t_full)[0], [MeritFull])
+ self.assertMerits(re.subn(ut, ut, t_part)[0], [])
+ self.assertMerits(re.subn(ut, ut, t_none)[0], [])
+
+ self.assertMerits(re.subn(ut, tt, ut)[0], [])
+ self.assertMerits(re.subn(ut, t_all, ut)[0], [MeritFull])
+ self.assertMerits(re.subn(ut, t_full, ut)[0], [MeritFull])
+ self.assertMerits(re.subn(ut, t_part, ut)[0], [])
+ self.assertMerits(re.subn(ut, t_none, ut)[0], [])
+
+ self.assertMerits(re.subn(tt, ut, ut)[0], [])
+ self.assertMerits(re.subn(t_all, ut, ut)[0], [MeritFull])
+ self.assertMerits(re.subn(t_full, ut, ut)[0], [MeritFull])
+ self.assertMerits(re.subn(t_part, ut, ut)[0], [])
+ self.assertMerits(re.subn(t_none, ut, ut)[0], [])
+
+ self.assertMerits(re.subn(t_all, t_all, tt)[0], [])
+ self.assertMerits(re.subn(t_all, t_all, ut)[0], [MeritFull])
+ self.assertMerits(re.subn(t_all, t_all, t_full)[0], [MeritFull])
+ self.assertMerits(re.subn(t_all, t_all, t_part)[0], [MeritPart])
+ self.assertMerits(re.subn(t_all, t_all, t_none)[0], [])
+
+ self.assertMerits(re.subn(t_all, tt, t_all)[0], [])
+ self.assertMerits(re.subn(t_all, ut, t_all)[0], [MeritFull])
+ self.assertMerits(re.subn(t_all, t_full, t_all)[0], [MeritFull])
+ self.assertMerits(re.subn(t_all, t_part, t_all)[0], [MeritPart])
+ self.assertMerits(re.subn(t_all, t_none, t_all)[0], [])
+
+ self.assertMerits(re.subn(tt, t_all, t_all)[0], [])
+ self.assertMerits(re.subn(ut, t_all, t_all)[0], [MeritFull])
+ self.assertMerits(re.subn(t_full, t_all, t_all)[0], [MeritFull])
+ self.assertMerits(re.subn(t_part, t_all, t_all)[0], [MeritPart])
+ self.assertMerits(re.subn(t_none, t_all, t_all)[0], [])
+
+ def test_split(self):
+ ut = "abc"
+ tt = ut.taint()
+ t_full = ut._cleanfor(MeritFull)
+ t_part = ut._cleanfor(MeritPart)
+ t_none = ut._cleanfor(MeritNone)
+ t_all = ut._cleanfor(MeritFull)._cleanfor(MeritPart)\
+ ._cleanfor(MeritNone)
+
+ self.assertTaintedAll(re.split(tt, tt[0]))
+ self.assertTaintedAll(re.split(tt, ut[0]))
+ self.assertTaintedAll(re.split(ut, tt[0]))
+ # the order of this tests matter, since we want to
+ # make sure we did not break caching
+ self.assertCleanAll(re.split(ut, ut[0]))
+
+ self.assertMeritsAll(re.split(ut, tt[0]), [])
+ self.assertMeritsAll(re.split(ut, t_all[0]), [MeritFull])
+ self.assertMeritsAll(re.split(ut, t_full[0]), [MeritFull])
+ self.assertMeritsAll(re.split(ut, t_part[0]), [])
+ self.assertMeritsAll(re.split(ut, t_none[0]), [])
+
+ self.assertMeritsAll(re.split(tt, ut[0]), [])
+ self.assertMeritsAll(re.split(t_all, ut[0]), [MeritFull])
+ self.assertMeritsAll(re.split(t_full, ut[0]), [MeritFull])
+ self.assertMeritsAll(re.split(t_part, ut[0]), [])
+ self.assertMeritsAll(re.split(t_none, ut[0]), [])
+
+ self.assertMeritsAll(re.split(t_all, tt[0]), [])
+ self.assertMeritsAll(re.split(t_all, t_full[0]), [MeritFull])
+ self.assertMeritsAll(re.split(t_all, t_part[0]), [MeritPart])
+ self.assertMeritsAll(re.split(t_all, t_none[0]), [])
+
+ self.assertMeritsAll(re.split(tt, t_all[0]), [])
+ self.assertMeritsAll(re.split(t_full, t_all[0]), [MeritFull])
+ self.assertMeritsAll(re.split(t_part, t_all[0]), [MeritPart])
+ self.assertMeritsAll(re.split(t_none, t_all[0]), [])
+
+ def test_findall(self):
+ ut = "abc"
+ tt = ut.taint()
+ t_full = ut._cleanfor(MeritFull)
+ t_part = ut._cleanfor(MeritPart)
+ t_none = ut._cleanfor(MeritNone)
+ t_all = ut._cleanfor(MeritFull)._cleanfor(MeritPart)\
+ ._cleanfor(MeritNone)
+
+ self.assertTaintedAll(re.findall(tt, tt[0]))
+ self.assertTaintedAll(re.findall(tt, ut[0]))
+ self.assertTaintedAll(re.findall(ut, tt[0]))
+ # the order of this tests matter, since we want to
+ # make sure we did not break caching
+ self.assertCleanAll(re.findall(ut, ut[0]))
+
+ self.assertMeritsAll(re.findall(ut, tt[0]), [])
+ self.assertMeritsAll(re.findall(ut, t_all[0]), [MeritFull])
+ self.assertMeritsAll(re.findall(ut, t_full[0]), [MeritFull])
+ self.assertMeritsAll(re.findall(ut, t_part[0]), [])
+ self.assertMeritsAll(re.findall(ut, t_none[0]), [])
+
+ self.assertMeritsAll(re.findall(tt, ut[0]), [])
+ self.assertMeritsAll(re.findall(t_all, ut[0]), [MeritFull])
+ self.assertMeritsAll(re.findall(t_full, ut[0]), [MeritFull])
+ self.assertMeritsAll(re.findall(t_part, ut[0]), [])
+ self.assertMeritsAll(re.findall(t_none, ut[0]), [])
+
+ self.assertMeritsAll(re.findall(t_all, tt[0]), [])
+ self.assertMeritsAll(re.findall(t_all, t_full[0]), [MeritFull])
+ self.assertMeritsAll(re.findall(t_all, t_part[0]), [MeritPart])
+ self.assertMeritsAll(re.findall(t_all, t_none[0]), [])
+
+ self.assertMeritsAll(re.findall(tt, t_all[0]), [])
+ self.assertMeritsAll(re.findall(t_full, t_all[0]), [MeritFull])
+ self.assertMeritsAll(re.findall(t_part, t_all[0]), [MeritPart])
+ self.assertMeritsAll(re.findall(t_none, t_all[0]), [])
+
+ def test_finditer(self):
+ ut = "abc"
+ tt = ut.taint()
+ t_full = ut._cleanfor(MeritFull)
+ t_part = ut._cleanfor(MeritPart)
+ t_none = ut._cleanfor(MeritNone)
+ t_all = ut._cleanfor(MeritFull)._cleanfor(MeritPart)\
+ ._cleanfor(MeritNone)
+
+ self.assertTaintedAll(re.finditer(tt, tt[0]))
+ self.assertTaintedAll(re.finditer(tt, ut[0]))
+ self.assertTaintedAll(re.finditer(ut, tt[0]))
+ # the order of this tests matter, since we want to
+ # make sure we did not break caching
+ self.assertCleanAll(re.finditer(ut, ut[0]))
+
+ self.assertMeritsAll(re.finditer(ut, tt[0]), [])
+ self.assertMeritsAll(re.finditer(ut, t_all[0]), [MeritFull])
+ self.assertMeritsAll(re.finditer(ut, t_full[0]), [MeritFull])
+ self.assertMeritsAll(re.finditer(ut, t_part[0]), [])
+ self.assertMeritsAll(re.finditer(ut, t_none[0]), [])
+
+ self.assertMeritsAll(re.finditer(tt, ut[0]), [])
+ self.assertMeritsAll(re.finditer(t_all, ut[0]), [MeritFull])
+ self.assertMeritsAll(re.finditer(t_full, ut[0]), [MeritFull])
+ self.assertMeritsAll(re.finditer(t_part, ut[0]), [])
+ self.assertMeritsAll(re.finditer(t_none, ut[0]), [])
+
+ self.assertMeritsAll(re.finditer(t_all, tt[0]), [])
+ self.assertMeritsAll(re.finditer(t_all, t_full[0]), [MeritFull])
+ self.assertMeritsAll(re.finditer(t_all, t_part[0]), [MeritPart])
+ self.assertMeritsAll(re.finditer(t_all, t_none[0]), [])
+
+ self.assertMeritsAll(re.finditer(tt, t_all[0]), [])
+ self.assertMeritsAll(re.finditer(t_full, t_all[0]), [MeritFull])
+ self.assertMeritsAll(re.finditer(t_part, t_all[0]), [MeritPart])
+ self.assertMeritsAll(re.finditer(t_none, t_all[0]), [])
+
+ def test_escape(self):
+ ut = "[a-z]+"
+ tt = ut.taint()
+ t_full = ut._cleanfor(MeritFull)
+ t_part = ut._cleanfor(MeritPart)
+ t_none = ut._cleanfor(MeritNone)
+ t_all = ut._cleanfor(MeritFull)._cleanfor(MeritPart)\
+ ._cleanfor(MeritNone)
+
+ self.assertMerits(re.escape(tt), [])
+ self.assertMerits(re.escape(t_full), [MeritFull])
+ self.assertMerits(re.escape(t_part), [MeritPart])
+ self.assertMerits(re.escape(t_none), [MeritNone])
+ self.assertMerits(re.escape(t_all), [MeritFull, MeritPart, MeritNone])
+ self.assertMerits(re.escape(ut), None)
+
+ def test_compile(self):
+ pattern = r"[a-z]+"
+ text = "asdf"
+ text_p = "asdf"._cleanfor(MeritPart)
+ ur = re.compile(pattern)
+ tr = re.compile(pattern.taint())
+ r_full = re.compile(pattern._cleanfor(MeritFull))
+ r_part = re.compile(pattern._cleanfor(MeritPart))
+ r_none = re.compile(pattern._cleanfor(MeritNone))
+ r_all = re.compile(pattern._cleanfor(MeritFull)._cleanfor(MeritPart)\
+ ._cleanfor(MeritNone))
+
+ # search
+ self.assertMerits(ur.search(text).group(0), None)
+ self.assertMerits(tr.search(text).group(0), [])
+ self.assertMerits(r_full.search(text).group(0), [MeritFull])
+ self.assertMerits(r_part.search(text).group(0), [])
+ self.assertMerits(r_none.search(text).group(0), [])
+ self.assertMerits(r_all.search(text).group(0), [MeritFull])
+
+ self.assertMerits(ur.search(text_p).group(0), [])
+ self.assertMerits(tr.search(text_p).group(0), [])
+ self.assertMerits(r_full.search(text_p).group(0), [])
+ self.assertMerits(r_part.search(text_p).group(0), [])
+ self.assertMerits(r_none.search(text_p).group(0), [])
+ self.assertMerits(r_all.search(text_p).group(0), [])
+
+ # match
+ self.assertMerits(ur.match(text).group(0), None)
+ self.assertMerits(tr.match(text).group(0), [])
+ self.assertMerits(r_full.match(text).group(0), [MeritFull])
+ self.assertMerits(r_part.match(text).group(0), [])
+ self.assertMerits(r_none.match(text).group(0), [])
+ self.assertMerits(r_all.match(text).group(0), [MeritFull])
+
+ self.assertMerits(ur.match(text_p).group(0), [])
+ self.assertMerits(tr.match(text_p).group(0), [])
+ self.assertMerits(r_full.match(text_p).group(0), [])
+ self.assertMerits(r_part.match(text_p).group(0), [])
+ self.assertMerits(r_none.match(text_p).group(0), [])
+ self.assertMerits(r_all.match(text_p).group(0), [])
+
+ # split
+ self.assertMeritsAll(ur.split(text), None)
+ self.assertMeritsAll(tr.split(text), [])
+ self.assertMeritsAll(r_full.split(text), [MeritFull])
+ self.assertMeritsAll(r_part.split(text), [])
+ self.assertMeritsAll(r_none.split(text), [])
+ self.assertMeritsAll(r_all.split(text), [MeritFull])
+
+ self.assertMeritsAll(ur.split(text_p), [])
+ self.assertMeritsAll(tr.split(text_p), [])
+ self.assertMeritsAll(r_full.split(text_p), [])
+ self.assertMeritsAll(r_part.split(text_p), [MeritPart])
+ self.assertMeritsAll(r_none.split(text_p), [])
+ self.assertMeritsAll(r_all.split(text_p), [MeritPart])
+
+ # sub
+ self.assertMerits(ur.sub(text, text), None)
+ self.assertMerits(tr.sub(text, text), [])
+ self.assertMerits(r_full.sub(text, text), [MeritFull])
+ self.assertMerits(r_part.sub(text, text), [])
+ self.assertMerits(r_none.sub(text, text), [])
+ self.assertMerits(r_all.sub(text, text), [MeritFull])
+
+ self.assertMerits(ur.sub(text_p, text), [])
+ self.assertMerits(tr.sub(text_p, text), [])
+ self.assertMerits(r_full.sub(text_p, text), [])
+ self.assertMerits(r_part.sub(text_p, text), [])
+ self.assertMerits(r_none.sub(text_p, text), [])
+ self.assertMerits(r_all.sub(text_p, text), [])
+
+ self.assertMerits(ur.sub(text, text_p), [])
+ self.assertMerits(tr.sub(text, text_p), [])
+ self.assertMerits(r_full.sub(text, text_p), [])
+ self.assertMerits(r_part.sub(text, text_p), [])
+ self.assertMerits(r_none.sub(text, text_p), [])
+ self.assertMerits(r_all.sub(text, text_p), [])
+
+ self.assertMerits(ur.sub(text_p, text_p), [])
+ self.assertMerits(tr.sub(text_p, text_p), [])
+ self.assertMerits(r_full.sub(text_p, text_p), [])
+ self.assertMerits(r_part.sub(text_p, text_p), [MeritPart])
+ self.assertMerits(r_none.sub(text_p, text_p), [])
+ self.assertMerits(r_all.sub(text_p, text_p), [MeritPart])
+
+ # subn
+ self.assertMerits(ur.subn(text, text)[0], None)
+ self.assertMerits(tr.subn(text, text)[0], [])
+ self.assertMerits(r_full.subn(text, text)[0], [MeritFull])
+ self.assertMerits(r_part.subn(text, text)[0], [])
+ self.assertMerits(r_none.subn(text, text)[0], [])
+ self.assertMerits(r_all.subn(text, text)[0], [MeritFull])
+
+ self.assertMerits(ur.subn(text_p, text)[0], [])
+ self.assertMerits(tr.subn(text_p, text)[0], [])
+ self.assertMerits(r_full.subn(text_p, text)[0], [])
+ self.assertMerits(r_part.subn(text_p, text)[0], [])
+ self.assertMerits(r_none.subn(text_p, text)[0], [])
+ self.assertMerits(r_all.subn(text_p, text)[0], [])
+
+ self.assertMerits(ur.subn(text, text_p)[0], [])
+ self.assertMerits(tr.subn(text, text_p)[0], [])
+ self.assertMerits(r_full.subn(text, text_p)[0], [])
+ self.assertMerits(r_part.subn(text, text_p)[0], [])
+ self.assertMerits(r_none.subn(text, text_p)[0], [])
+ self.assertMerits(r_all.subn(text, text_p)[0], [])
+
+ self.assertMerits(ur.subn(text_p, text_p)[0], [])
+ self.assertMerits(tr.subn(text_p, text_p)[0], [])
+ self.assertMerits(r_full.subn(text_p, text_p)[0], [])
+ self.assertMerits(r_part.subn(text_p, text_p)[0], [MeritPart])
+ self.assertMerits(r_none.subn(text_p, text_p)[0], [])
+ self.assertMerits(r_all.subn(text_p, text_p)[0], [MeritPart])
+
+ # findall
+ self.assertMeritsAll(ur.findall(text), None)
+ self.assertMeritsAll(tr.findall(text), [])
+ self.assertMeritsAll(r_full.findall(text), [MeritFull])
+ self.assertMeritsAll(r_part.findall(text), [])
+ self.assertMeritsAll(r_none.findall(text), [])
+ self.assertMeritsAll(r_all.findall(text), [MeritFull])
+
+ self.assertMeritsAll(ur.findall(text_p), [])
+ self.assertMeritsAll(tr.findall(text_p), [])
+ self.assertMeritsAll(r_full.findall(text_p), [])
+ self.assertMeritsAll(r_part.findall(text_p), [MeritPart])
+ self.assertMeritsAll(r_none.findall(text_p), [])
+ self.assertMeritsAll(r_all.findall(text_p), [MeritPart])
+
+ # finditer
+ self.assertMeritsAllGroups(ur.finditer(text), None)
+ self.assertMeritsAllGroups(tr.finditer(text), [])
+ self.assertMeritsAllGroups(r_full.finditer(text), [MeritFull])
+ self.assertMeritsAllGroups(r_part.finditer(text), [])
+ self.assertMeritsAllGroups(r_none.finditer(text), [])
+ self.assertMeritsAllGroups(r_all.finditer(text), [MeritFull])
+
+ self.assertMeritsAllGroups(ur.finditer(text_p), [])
+ self.assertMeritsAllGroups(tr.finditer(text_p), [])
+ self.assertMeritsAllGroups(r_full.finditer(text_p), [])
+ self.assertMeritsAllGroups(r_part.finditer(text_p), [MeritPart])
+ self.assertMeritsAllGroups(r_none.finditer(text_p), [])
+ self.assertMeritsAllGroups(r_all.finditer(text_p), [MeritPart])
+
+# functions for testing taint module - they should not live inside a class
+# because mechanism for patching class methods is a bit different than for
+# functions
+
+def toplevel_source():
+ return "abc"
+
+def toplevel_cleaner(s):
+ return s
+
+def toplevel_sink(s):
+ pass
+
+def toplevel_propagator(s):
+ return "abc"
+
+def test_main():
+ taint.enable(CONFIG_1)
+ test_support.run_unittest(DecoratorTest, SimplePatcherTest,
+ ImportedObjectsPatchingTest, ConfigValidation,
+ PropagationContextsTest, PropagatorTest,
+ OptionsTest)
diff --git a/Lib/test/test_taintunicode.py b/Lib/test/test_taintunicode.py
new file mode 100644
index 0000000..c523a53
--- /dev/null
+++ b/Lib/test/test_taintunicode.py
@@ -0,0 +1,1294 @@
+import unittest, string
+import sys
+from test import test_support, string_tests
+
+
+class MeritFull(Merit):
+ propagation = Merit.FullPropagation
+
+class MeritFull2(Merit):
+ propagation = Merit.FullPropagation
+
+class MeritPartial(Merit):
+ propagation = Merit.PartialPropagation
+
+class MeritPartial2(Merit):
+ propagation = Merit.PartialPropagation
+
+class MeritNone(Merit): # defaults to NonePropagation
+ pass
+
+class MeritNone2(Merit):
+ propagation = Merit.NonePropagation
+
+class AbstractTaintTest(unittest.TestCase):
+ def assertTainted(self, object):
+ self.assertTrue(object.istainted())
+ self.assertFalse(object.isclean())
+
+ def assertClean(self, object):
+ self.assertTrue(object.isclean())
+ self.assertFalse(object.istainted())
+
+ def assertMerits(self, object, merits):
+ if merits == None or object._merits() == None:
+ self.assertEqual(object._merits(), None)
+ self.assertEqual(merits, None)
+ self.assertClean(object)
+ else:
+ self.assertEqual(object._merits(), set(merits))
+
+class TaintTest(AbstractTaintTest):
+ def test_taint(self):
+ t = u'\u0230'.taint()
+ self.assertTainted(t)
+ t = u't'.taint()
+ self.assertTainted(t)
+
+ tt = t.taint()
+ self.assertTainted(tt)
+
+ ttt = u'\u0230a longer string that will be tainted'.taint()
+ self.assertTainted(ttt)
+
+ u = u'\u0230'
+ self.assertClean(u)
+
+ self.assertEqual(u'\u0230x', u'\u0230x'.taint())
+ self.assertEqual(u'\u0230a loooooooooooooooooooonger string', \
+ u'\u0230a loooooooooooooooooooonger string'.taint())
+
+ self.assertTainted(u''.taint())
+ self.assertClean(u'')
+ self.assertTainted(u'\u0230x'.taint())
+ self.assertClean(u'\u0230x')
+
+ def test_from_string(self):
+ u = unicode('ttttt')
+ t = unicode('ttttt'.taint())
+ t_full = unicode(u._cleanfor(MeritFull))
+ t_part = unicode(u._cleanfor(MeritPartial))
+ t_none = unicode(u._cleanfor(MeritNone))
+ t_all = unicode(u._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone))
+
+ self.assertClean(u)
+ self.assertTainted(t)
+ self.assertTainted(t_full)
+ self.assertTainted(t_part)
+ self.assertTainted(t_none)
+ self.assertTainted(t_all)
+
+ self.assertMerits(u, None)
+ self.assertMerits(t, [])
+ self.assertMerits(t_full, [MeritFull])
+ self.assertMerits(t_part, [MeritPartial])
+ self.assertMerits(t_none, [MeritNone])
+ self.assertMerits(t_all, [MeritFull, MeritPartial, MeritNone])
+
+class MeritsTest(AbstractTaintTest):
+ def test_propagate(self):
+ t = u'\u0230ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ s = u'\u0230sssss'
+ s_full = t._cleanfor(MeritFull2)._cleanfor(MeritFull)
+ s_part = t._cleanfor(MeritPartial2)._cleanfor(MeritPartial)
+ s_none = t._cleanfor(MeritNone2)._cleanfor(MeritNone)
+ s_all = t._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+
+ self.assertMerits(t._propagate(t_full), [MeritFull])
+ self.assertMerits(t._propagate(t_part), [MeritPartial])
+ self.assertMerits(t._propagate(t_none), [MeritNone])
+ self.assertMerits(t._propagate(t_all),
+ [MeritFull, MeritPartial, MeritNone])
+ self.assertMerits(t_part._propagate(s_full), [MeritFull, MeritFull2])
+ self.assertMerits(t_full._propagate(s_part), [MeritPartial,
+ MeritPartial2])
+ self.assertMerits(t_none._propagate(s_none), [MeritNone, MeritNone2])
+ self.assertMerits(t_all._propagate(s_all),
+ [MeritFull2, MeritPartial2, MeritNone2])
+
+ def test_listing_merits(self):
+ t = u'\u0230\u0230ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ t_full2 = t._cleanfor(MeritFull)._cleanfor(MeritFull)
+ t_part2 = t._cleanfor(MeritPartial)._cleanfor(MeritPartial)
+ t_none2 = t._cleanfor(MeritNone)._cleanfor(MeritNone)
+ t_all2 = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+ t_full3 = t._cleanfor(MeritFull2)._cleanfor(MeritFull)
+ t_part3 = t._cleanfor(MeritPartial2)._cleanfor(MeritPartial)
+ t_none3 = t._cleanfor(MeritNone2)._cleanfor(MeritNone)
+ t_all3 = t._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+
+ s = u'\u0230abcdef'
+ s_full = s._cleanfor(MeritFull)
+ s_part = s._cleanfor(MeritNone)
+ s_none = s._cleanfor(MeritNone)
+ s_all = s._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ u = u'\u0230uuuuu'
+ self.assertMerits(t, [])
+ self.assertMerits(s, None)
+ self.assertMerits(u, None)
+ self.assertMerits(t_full, [MeritFull])
+ self.assertMerits(t_part, [MeritPartial])
+ self.assertMerits(t_none, [MeritNone])
+ self.assertMerits(t_all, [MeritNone, MeritFull, MeritPartial])
+
+ self.assertMerits(t_full2, [MeritFull])
+ self.assertMerits(t_part2, [MeritPartial])
+ self.assertMerits(t_none2, [MeritNone])
+ self.assertMerits(t_all, [MeritNone, MeritFull, MeritPartial])
+
+ self.assertMerits(t_full3, [MeritFull, MeritFull2])
+ self.assertMerits(t_part3, [MeritPartial, MeritPartial2])
+ self.assertMerits(t_none3, [MeritNone, MeritNone2])
+
+ def test_full_propagation(self):
+ # this tests propagation semantics, not the _propagate method
+ t = u'\u0230ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ t_full2 = t._cleanfor(MeritFull)._cleanfor(MeritFull)
+ t_part2 = t._cleanfor(MeritPartial)._cleanfor(MeritPartial)
+ t_none2 = t._cleanfor(MeritNone)._cleanfor(MeritNone)
+ t_all2 = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+ t_full3 = t._cleanfor(MeritFull2)._cleanfor(MeritFull)
+ t_part3 = t._cleanfor(MeritPartial2)._cleanfor(MeritPartial)
+ t_none3 = t._cleanfor(MeritNone2)._cleanfor(MeritNone)
+ t_all3 = t._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+ t_all4 = t_all2._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+
+ s = u'\u0230abcdef'
+ s_full = s._cleanfor(MeritFull)
+ s_part = s._cleanfor(MeritNone)
+ s_none = s._cleanfor(MeritNone)
+ s_all = s._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ u = u'\u0230uuuuu'
+
+ self.assertMerits(u + s, None)
+
+ self.assertMerits(u + t_full, [MeritFull])
+ self.assertMerits(u + s_full, [MeritFull])
+ self.assertMerits(u + t_all, [MeritFull])
+ self.assertMerits(u + t_full2, [MeritFull])
+ self.assertMerits(u + t_all2, [MeritFull])
+ self.assertMerits(u + t_full3, [MeritFull, MeritFull2])
+ self.assertMerits(u + t_all3, [MeritFull2])
+ self.assertMerits(u + t_all4, [MeritFull2, MeritFull])
+
+ self.assertMerits(s + t_full, [MeritFull])
+ self.assertMerits(s + s_full, [MeritFull])
+ self.assertMerits(s + t_all, [MeritFull])
+ self.assertMerits(s + t_full2, [MeritFull])
+ self.assertMerits(s + t_all2, [MeritFull])
+ self.assertMerits(s + t_full3, [MeritFull, MeritFull2])
+ self.assertMerits(s + t_all3, [MeritFull2])
+ self.assertMerits(s + t_all4, [MeritFull2, MeritFull])
+
+ self.assertMerits(t_full + t_full2, [MeritFull])
+ self.assertMerits(t_full + t_full3, [MeritFull])
+ self.assertMerits(t_full + t_all, [MeritFull])
+ self.assertMerits(t_full + t_all2, [MeritFull])
+ self.assertMerits(t_full + t_all3, [])
+
+ self.assertMerits(t_full3 + t_full2, [MeritFull])
+ self.assertMerits(t_full3 + t_all3, [MeritFull2])
+ self.assertMerits(t_full3 + t_all4, [MeritFull2, MeritFull])
+ self.assertMerits(t_full3 + t_all, [MeritFull])
+
+ self.assertMerits(t_all + t_all2, [MeritFull, MeritPartial])
+ self.assertMerits(t_all + t_all3, [])
+ self.assertMerits(t_all2 + t_all3, [])
+ self.assertMerits(t_all4 + t_all3, [MeritFull2, MeritPartial2])
+ self.assertMerits(t_all4 + t_all, [MeritFull, MeritPartial])
+ self.assertMerits(t_all4 + t_all2, [MeritFull, MeritPartial])
+
+ def test_partial_propagation(self):
+ # this tests propagation semantics, not the _propagate method
+ t = u'\u0230ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ t_full2 = t._cleanfor(MeritFull)._cleanfor(MeritFull)
+ t_part2 = t._cleanfor(MeritPartial)._cleanfor(MeritPartial)
+ t_none2 = t._cleanfor(MeritNone)._cleanfor(MeritNone)
+ t_all2 = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+ t_full3 = t._cleanfor(MeritFull2)._cleanfor(MeritFull)
+ t_part3 = t._cleanfor(MeritPartial2)._cleanfor(MeritPartial)
+ t_none3 = t._cleanfor(MeritNone2)._cleanfor(MeritNone)
+ t_all3 = t._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+ t_all4 = t_all2._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+
+ s = u'\u0230abcdef'
+ s_full = s._cleanfor(MeritFull)
+ s_part = s._cleanfor(MeritNone)
+ s_none = s._cleanfor(MeritNone)
+ s_all = s._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ u = u'\u0230uuuuu'
+
+ self.assertMerits(u + s, None)
+
+ self.assertMerits(u + t_part, [])
+ self.assertMerits(u + s_part, [])
+ self.assertMerits(u + t_part2, [])
+ self.assertMerits(u + t_part3, [])
+ self.assertMerits(u + t_all, [MeritFull])
+ self.assertMerits(u + t_all2, [MeritFull])
+ self.assertMerits(u + t_all3, [MeritFull2])
+ self.assertMerits(u + t_all4, [MeritFull2, MeritFull])
+
+ self.assertMerits(s + t_part, [])
+ self.assertMerits(s + s_part, [])
+ self.assertMerits(s + t_part2, [])
+ self.assertMerits(s + t_part3, [])
+ self.assertMerits(s + t_all, [MeritFull])
+ self.assertMerits(s + t_all2, [MeritFull])
+ self.assertMerits(s + t_all3, [MeritFull2])
+ self.assertMerits(s + t_all4, [MeritFull2, MeritFull])
+
+ self.assertMerits(t_part + t_part2, [MeritPartial])
+ self.assertMerits(t_part + t_part3, [MeritPartial])
+ self.assertMerits(t_part + t_all, [MeritPartial])
+ self.assertMerits(t_part + t_all2, [MeritPartial])
+ self.assertMerits(t_part + t_all3, [])
+
+ self.assertMerits(t_part3 + t_part2, [MeritPartial])
+ self.assertMerits(t_part3 + t_all3, [MeritPartial2])
+ self.assertMerits(t_part3 + t_all4, [MeritPartial2, MeritPartial])
+ self.assertMerits(t_part3 + t_all, [MeritPartial])
+
+ self.assertMerits(t_all + t_all2, [MeritFull, MeritPartial])
+ self.assertMerits(t_all + t_all3, [])
+ self.assertMerits(t_all2 + t_all3, [])
+ self.assertMerits(t_all4 + t_all3, [MeritFull2, MeritPartial2])
+ self.assertMerits(t_all4 + t_all, [MeritFull, MeritPartial])
+ self.assertMerits(t_all4 + t_all2, [MeritFull, MeritPartial])
+
+ def test_none_propagation(self):
+ # this tests propagation semantics, not the _propagate method
+ t = u'\u0230ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ t_full2 = t._cleanfor(MeritFull)._cleanfor(MeritFull)
+ t_part2 = t._cleanfor(MeritPartial)._cleanfor(MeritPartial)
+ t_none2 = t._cleanfor(MeritNone)._cleanfor(MeritNone)
+ t_all2 = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+ t_full3 = t._cleanfor(MeritFull2)._cleanfor(MeritFull)
+ t_part3 = t._cleanfor(MeritPartial2)._cleanfor(MeritPartial)
+ t_none3 = t._cleanfor(MeritNone2)._cleanfor(MeritNone)
+ t_all3 = t._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+ t_all4 = t_all2._cleanfor(MeritFull2)._cleanfor(MeritPartial2)\
+ ._cleanfor(MeritNone2)
+
+ s = u'\u0230abcdef'
+ s_full = s._cleanfor(MeritFull)
+ s_part = s._cleanfor(MeritNone)
+ s_none = s._cleanfor(MeritNone)
+ s_all = s._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ u = u'\u0230uuuuu'
+
+ words = [t, t_full, t_part, t_none, t_all, t_full2, t_part2, t_none2,
+ t_all2, t_full3, t_part3, t_none3, t_all3, t_all4, s, s_full,
+ s_part, s_none, s_all, u]
+
+ for w in words:
+ for v in words:
+ m = (v + w)._merits()
+
+ if m == None:
+ self.assertClean(w)
+ self.assertClean(v)
+ continue
+ self.assertNotIn(MeritNone, m)
+ self.assertNotIn(MeritNone2, m)
+
+class UnaryUnicodeOperationTest(AbstractTaintTest):
+ """ Test string methods which use only one string argument - ie. where
+ taint is just copied from the argument to result. """
+
+ def test_repeat(self):
+ self.assertTainted(u'\u0230bcd'.taint() * 0)
+ self.assertTainted(u''.taint() * 100)
+ self.assertTainted(u'\u0230BCD asdf'.taint() * 15)
+ self.assertTainted(u'\u0230 am very long'.taint() * 10000)
+
+ self.assertTainted(u'\u0230bcd'._cleanfor(MeritFull) * 0)
+ self.assertTainted(u''._cleanfor(MeritFull) * 100)
+ self.assertTainted(u'\u0230BCD asdf'._cleanfor(MeritFull) * 15)
+ self.assertTainted(u'\u0230 am very long'._cleanfor(MeritFull) * 10000)
+
+ self.assertTainted(u'\u0230bcd'._cleanfor(MeritPartial) * 0)
+ self.assertTainted(u''._cleanfor(MeritPartial) * 100)
+ self.assertTainted(u'\u0230BCD asdf'._cleanfor(MeritPartial) * 15)
+ self.assertTainted(u'\u0230 am very long'._cleanfor(MeritPartial) * 10000)
+
+ self.assertTainted(u'\u0230bcd'._cleanfor(MeritNone) * 0)
+ self.assertTainted(u''._cleanfor(MeritNone) * 100)
+ self.assertTainted(u'\u0230BCD asdf'._cleanfor(MeritNone) * 15)
+ self.assertTainted(u'\u0230 am very long'._cleanfor(MeritNone) * 10000)
+
+ self.assertClean(u'\u0230bcd' * 0)
+ self.assertClean(u'' * 100)
+ self.assertClean(u'\u0230BCD' * 5)
+ self.assertClean(u'\u0230 very long string' * 10000)
+
+ def test_item(self):
+ u = u'\u0230aaa'
+ t = u'\u0230aaa'.taint()
+ c = u'\u0230aaa'._cleanfor(MeritFull)
+ self.assertClean(u[1])
+ self.assertClean(u[2])
+ self.assertClean(u[1])
+ self.assertClean(u[0])
+
+ self.assertTainted(t[0])
+ self.assertTainted(t[-1])
+ self.assertTainted(t[2])
+ self.assertTainted(t[-1])
+
+ self.assertTainted(c[0])
+ self.assertTainted(c[-2])
+ self.assertTainted(c[1])
+ self.assertTainted(c[2])
+
+ def test_slice(self):
+ u = u'\u0230aaaaaaaa'
+ t = u'\u0230tttttttt'.taint()
+ c = u'\u0230cccccccc'._cleanfor(MeritFull)
+
+ self.assertClean(u[1:])
+ self.assertClean(u[-1:])
+ self.assertClean(u[2:5])
+ self.assertClean(u[2:])
+ self.assertClean(u[:-2])
+ self.assertClean(u[1:5])
+ self.assertTainted(t[2:])
+ self.assertTainted(t[-1:])
+ self.assertTainted(t[3:10])
+ self.assertTainted(t[2:6])
+ self.assertTainted(t[:6])
+ self.assertTainted(t[:0])
+
+ self.assertTainted(c[1:])
+ self.assertTainted(c[-1:])
+ self.assertTainted(c[:0])
+ self.assertTainted(c[-1:])
+ self.assertTainted(c[4:3])
+
+ def test_subscript(self):
+ u = u'\u0230aaaaaaaa'
+ t = u'\u0230tttttttt'.taint()
+ c = u'\u0230cccccccc'._cleanfor(MeritFull)
+
+ self.assertClean(u[1::1])
+ self.assertClean(u[-1::1])
+ self.assertClean(u[2:5:1])
+ self.assertClean(u[1:9:2])
+ self.assertClean(u[8:1:-3])
+ self.assertClean(u[::-1])
+ self.assertClean(u[::-2])
+ self.assertClean(u[2::1])
+ self.assertClean(u[:-2:1])
+ self.assertClean(u[1:5:1])
+ self.assertClean(u[1:7:2])
+ self.assertClean(u[-1::-2])
+ self.assertClean(u[-1::2])
+ self.assertTainted(t[2::1])
+ self.assertTainted(t[-1::1])
+ self.assertTainted(t[3:10:1])
+ self.assertTainted(t[3:10:3])
+ self.assertTainted(t[9:1:-1])
+ self.assertTainted(t[2:6:1])
+ self.assertTainted(t[:6:1])
+ self.assertTainted(t[::3])
+ self.assertTainted(t[::-1])
+ self.assertTainted(t[-1::-1])
+ self.assertTainted(t[:0:1])
+
+ self.assertTainted(c[1::1])
+ self.assertTainted(c[-1::1])
+ self.assertTainted(c[:0:1])
+ self.assertTainted(c[1:9:2])
+ self.assertTainted(c[-1::1])
+ self.assertTainted(c[-1:-7:-2])
+ self.assertTainted(c[4:3:1])
+
+ def test_lower(self):
+ self.assertTainted(u'\u0230bcd'.taint().lower())
+ self.assertTainted(u'\u0230BCd 123'._cleanfor(MeritFull).lower())
+ self.assertTainted(u'\u0230BCD'.taint()._cleanfor(MeritNone).lower())
+ self.assertTainted(u'\u0230BCD XYZ'.taint().lower())
+ self.assertTainted(''.taint().lower())
+ self.assertTainted(u'\u0230 3 \n\n'.taint().lower())
+
+ self.assertClean(u'\u0230bcd'.lower())
+ self.assertClean(u'\u0230BCd 123'.lower())
+ self.assertClean(u'\u0230BCD'.lower())
+ self.assertClean(u'\u0230BCD XYZ'.lower())
+ self.assertClean(''.lower())
+ self.assertClean(u'\u0230 3 \n\n'.lower())
+
+ def test_upper(self):
+ self.assertTainted(u'\u0230bcd'.taint().upper())
+ self.assertTainted(u'\u0230BCd 123'._cleanfor(MeritFull).upper())
+ self.assertTainted(u'\u0230BCD'.taint()._cleanfor(MeritNone).upper())
+ self.assertTainted(u'\u0230BCD XYZ'.taint().upper())
+ self.assertTainted(''.taint().upper())
+ self.assertTainted(u'\u0230 3 \n\n'.taint().upper())
+
+ self.assertClean(u'\u0230bcd'.upper())
+ self.assertClean(u'\u0230BCd 123'.upper())
+ self.assertClean(u'\u0230BCD'.upper())
+ self.assertClean(u'\u0230BCD XYZ'.upper())
+ self.assertClean(''.upper())
+ self.assertClean(u'\u02301 3 \n\n'.upper())
+
+ def test_title(self):
+ self.assertTainted(u'\u0230bcd'.taint().title())
+ self.assertTainted(u'\u0230BCd 123'._cleanfor(MeritFull).title())
+ self.assertTainted(u'\u0230BCD'.taint()._cleanfor(MeritNone).title())
+ self.assertTainted(u'\u0230BCD XYZ'.taint().title())
+ self.assertTainted(''.taint().title())
+ self.assertTainted(u'\u0230 3 \n\n'.taint().title())
+
+ self.assertClean(u'\u0230bcd'.title())
+ self.assertClean(u'\u0230BCd 123'.title())
+ self.assertClean(u'\u0230BCD'.title())
+ self.assertClean(u'\u0230BCD XYZ'.title())
+ self.assertClean('u'.title())
+ self.assertClean(u'\u0230 3 \n\n'.title())
+
+ def test_capitalize(self):
+ self.assertTainted(u'\u0230abcd'.taint().title())
+ self.assertTainted(u'\u0230aBCd qwer asafd'._cleanfor(MeritFull).title())
+ self.assertTainted(u'\u0230ABCD'.taint()._cleanfor(MeritNone).title())
+ self.assertTainted(u'\u0230ABCD XYZ'.taint().title())
+ self.assertTainted(''.taint().title())
+ self.assertTainted(u'\u0230asdf zxcv \n hjkl\n'.taint().title())
+
+ self.assertClean(u'\u0230abcd'.title())
+ self.assertClean(u'\u0230aBCd 123'.title())
+ self.assertClean(u'\u0230ABCD'.title())
+ self.assertClean(u'\u0230ABCD XYZ HJKL'.title())
+ self.assertClean(''.title())
+ self.assertClean(u'\u02301 3 \n\n'.title())
+
+ def test_zfill(self):
+ self.assertTainted(u'12'.taint().zfill(10))
+ self.assertTainted(u'+1234'.taint().zfill(10))
+ self.assertTainted(u'-1234'.taint().zfill(2))
+ self.assertTainted(u''.taint().zfill(10))
+ self.assertTainted(u'400400'.taint().zfill(3))
+ self.assertTainted(u'123.432'.taint().zfill(10))
+
+ self.assertTainted(u'23400000'._cleanfor(MeritNone).zfill(100))
+ self.assertTainted(u'34434234'._cleanfor(MeritNone).zfill(3))
+ self.assertTainted(u'-123234234'._cleanfor(MeritPartial).zfill(100))
+ self.assertTainted(u'-999342'._cleanfor(MeritPartial).zfill(3))
+ self.assertTainted(u'345555.4663'._cleanfor(MeritFull).zfill(100))
+ self.assertTainted(u'3456765.466654'.\
+ _cleanfor(MeritFull).zfill(3))
+
+ self.assertClean(u'234'.zfill(2))
+ self.assertClean(u'-1453'.zfill(20))
+ self.assertClean(u'1345.3345'.zfill(2))
+ self.assertClean(u'6456.34354'.zfill(20))
+ self.assertClean(u'-9999.5345'.zfill(2))
+ self.assertClean(u'-1000.11234'.zfill(20))
+
+ self.assertTainted(u''.taint().zfill(1))
+ self.assertClean(u'')
+
+ def test_expandtabs(self):
+ self.assertTainted(u''.taint().expandtabs())
+ self.assertTainted(u'\t'.taint().expandtabs())
+ self.assertTainted(u'ab\u0230cd \t qwer'.taint().expandtabs())
+ self.assertTainted(u'\t\tAB\u0230CD'.taint().expandtabs())
+ self.assertTainted(u'ABCD\tXYZ'.taint().expandtabs())
+ self.assertTainted(u'asdf\t123@:\u0230#$L zxcv \t\t hjkl\n'.taint().expandtabs())
+
+ self.assertTainted(u''._cleanfor(MeritFull).expandtabs())
+ self.assertTainted(u'\t'._cleanfor(MeritFull).expandtabs())
+ self.assertTainted(u'abcd \t qwer'._cleanfor(MeritNone).expandtabs())
+ self.assertTainted(u'\t\tAB\u0230CD'._cleanfor(MeritNone).expandtabs())
+ self.assertTainted(u'ABCD\tXYZ'._cleanfor(MeritPartial).expandtabs())
+ self.assertTainted(u'asdf\t123@:\u0230#$L zxcv \t\t hjkl\n'.\
+ _cleanfor(MeritPartial).expandtabs())
+
+ self.assertClean(u''.expandtabs())
+ self.assertClean(u'\t'.expandtabs())
+ self.assertClean(u'abcd \t qw\u0230er'.expandtabs())
+ self.assertClean(u'\t\tABCD'.expandtabs())
+ self.assertClean(u'ABCD\t\u0230XYZ'.expandtabs())
+ self.assertClean(u'asdf\t123@:#$L zxcv \t\t hjkl\n'.expandtabs())
+
+ def test_swapcase(self):
+ self.assertTainted(u'\u0230abcd'.taint().swapcase())
+ self.assertTainted(u'\u0230aBCd 123'._cleanfor(MeritFull).swapcase())
+ self.assertTainted(u'\u0230ABCD'.taint()._cleanfor(MeritNone).swapcase())
+ self.assertTainted(u'\u0230ABcd xyZ'.taint().swapcase())
+ self.assertTainted(''.taint().swapcase())
+ self.assertTainted(u'\u02301 3 \n\n'.taint().swapcase())
+
+ self.assertClean(u'\u0230abcd'.swapcase())
+ self.assertClean(u'\u0230aBCd 123'.swapcase())
+ self.assertClean(u'\u0230aBCD'.swapcase())
+ self.assertClean(u'\u0230Abcd Xyz'.swapcase())
+ self.assertClean(''.swapcase())
+ self.assertClean(u'\u02301 3 \n\n'.swapcase())
+
+ def test_coding(self):
+ ut = u'ttttttt'
+ self.assertClean(str(ut))
+ self.assertMerits(str(ut.taint()), [])
+ self.assertMerits(str(ut._cleanfor(MeritFull)), [MeritFull])
+ self.assertMerits(str(ut._cleanfor(MeritFull)._cleanfor(MeritNone)),
+ [MeritFull, MeritNone])
+ self.assertClean(str(u''))
+ self.assertMerits(str(u''.taint()), [])
+ self.assertMerits(str(u''._cleanfor(MeritFull)), [MeritFull])
+ self.assertMerits(str(u''._cleanfor(MeritFull)._cleanfor(MeritNone)),
+ [MeritFull, MeritNone])
+ self.assertClean(unicode(ut))
+ self.assertMerits(unicode(ut.taint()), [])
+ self.assertMerits(unicode(ut._cleanfor(MeritFull)), [MeritFull])
+ self.assertMerits(unicode(ut._cleanfor(MeritFull)._cleanfor(MeritNone)),
+ [MeritFull, MeritNone])
+ self.assertClean(unicode(u''))
+ self.assertMerits(unicode(u''.taint()), [])
+ self.assertMerits(unicode(u''._cleanfor(MeritFull)), [MeritFull])
+ self.assertMerits(unicode(u''._cleanfor(MeritFull)._cleanfor(MeritNone)),
+ [MeritFull, MeritNone])
+
+ st = 'ttttttt'
+ self.assertClean(unicode(st))
+ self.assertMerits(unicode(st.taint()), [])
+ self.assertMerits(unicode(st._cleanfor(MeritFull)), [MeritFull])
+ self.assertMerits(unicode(st._cleanfor(MeritFull)._cleanfor(MeritNone)),
+ [MeritFull, MeritNone])
+ self.assertClean(unicode(''))
+ self.assertMerits(unicode(''.taint()), [])
+ self.assertMerits(unicode(''._cleanfor(MeritFull)), [MeritFull])
+ self.assertMerits(unicode(''._cleanfor(MeritFull)._cleanfor(MeritNone)),
+ [MeritFull, MeritNone])
+
+ ab = u'\x41\x42'
+ self.assertClean(ab.decode())
+ self.assertTainted(ab.taint().decode())
+ self.assertMerits(ab._cleanfor(MeritFull).decode(),
+ [MeritFull])
+ self.assertMerits(ab._cleanfor(MeritPartial).decode(),
+ [MeritPartial])
+ self.assertMerits(ab._cleanfor(MeritNone)._cleanfor(MeritFull).decode(),
+ [MeritFull, MeritNone])
+
+ self.assertClean(ab.encode())
+ self.assertTainted(ab.taint().encode())
+ self.assertMerits(ab._cleanfor(MeritFull).encode(),
+ [MeritFull])
+ self.assertMerits(ab._cleanfor(MeritPartial).encode(),
+ [MeritPartial])
+ self.assertMerits(ab._cleanfor(MeritNone)._cleanfor(MeritFull).encode(),
+ [MeritFull, MeritNone])
+
+ default_encoding = sys.getdefaultencoding()
+ for coding in ['utf-8', 'latin-1', 'ascii', 'utf-7', 'mbcs']:
+ # sys needs to be reloaded before changing encoding
+ reload(sys)
+ try:
+ sys.setdefaultencoding(coding)
+ except LookupError: # mbcs will work only on Windows
+ continue
+ self.assertClean(ab.decode())
+ self.assertTainted(ab.taint().decode())
+ self.assertMerits(ab._cleanfor(MeritFull).decode(),
+ [MeritFull])
+ self.assertMerits(ab._cleanfor(MeritPartial).decode(),
+ [MeritPartial])
+ self.assertMerits(ab._cleanfor(MeritNone)._cleanfor(MeritFull)
+ .decode(),
+ [MeritFull, MeritNone])
+
+ self.assertClean(ab.encode())
+ self.assertTainted(ab.taint().encode())
+ self.assertMerits(ab._cleanfor(MeritFull).encode(),
+ [MeritFull])
+ self.assertMerits(ab._cleanfor(MeritPartial).encode(),
+ [MeritPartial])
+ self.assertMerits(ab._cleanfor(MeritNone)._cleanfor(MeritFull)
+ .encode(),
+ [MeritFull, MeritNone])
+ reload(sys)
+ sys.setdefaultencoding(default_encoding)
+
+
+class VariadicUnicodeOperationTest(AbstractTaintTest):
+ """ Test unicode operations that take more than one argument and where
+ the propagation semantics is applied. """
+ def test_concatenation(self):
+ a = u'\u0230aaa'.taint()
+ b = 'bbb'.taint()
+ u = u'\u0230ccc'
+ self.assertTainted(a + b)
+ self.assertTainted(a + u)
+ self.assertTainted(u + a)
+ self.assertTainted(u + b)
+ self.assertTainted(b + u)
+ self.assertClean(u + u)
+
+ def test_rpartition(self):
+ t = u't t t tt tt'.taint()
+ u = u'u uu uuu uuuu u'
+ a = u'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone).\
+ _cleanfor(MeritPartial)
+ ss = u' '
+ tt = u' '.taint()
+
+ [self.assertTainted(x) for x in t.rpartition(' ')]
+ [self.assertTainted(x) for x in a.rpartition(' ')]
+ [self.assertTainted(x) for x in b.rpartition(' ')]
+ [self.assertTainted(x) for x in c.rpartition(' ')]
+ [self.assertClean(x) for x in u.rpartition(' ')]
+
+ [self.assertTainted(x) for x in t.rpartition(' '.taint())]
+ [self.assertTainted(x) for x in a.rpartition(' '.taint())]
+ [self.assertTainted(x) for x in b.rpartition(' '.taint())]
+ [self.assertTainted(x) for x in c.rpartition(' '.taint())]
+ [self.assertTainted(x) for x in u.rpartition(' '.taint())]
+
+ [self.assertTainted(x) for x in t.rpartition(ss)]
+ [self.assertTainted(x) for x in a.rpartition(ss)]
+ [self.assertTainted(x) for x in b.rpartition(ss)]
+ [self.assertTainted(x) for x in c.rpartition(ss)]
+ [self.assertClean(x) for x in u.rpartition(ss)]
+
+ [self.assertTainted(x) for x in t.rpartition(tt)]
+ [self.assertTainted(x) for x in a.rpartition(tt)]
+ [self.assertTainted(x) for x in b.rpartition(tt)]
+ [self.assertTainted(x) for x in c.rpartition(tt)]
+ [self.assertTainted(x) for x in u.rpartition(tt)]
+
+ [self.assertMerits(x, [MeritPartial]) for x in \
+ c.rpartition(' '._cleanfor(MeritPartial))]
+
+ def test_partition(self):
+ t = u't t t tt tt'.taint()
+ u = u'u uu uuu uuuu u'
+ a = u'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone).\
+ _cleanfor(MeritPartial)
+ ss = u' '
+ tt = u' '.taint()
+
+ [self.assertTainted(x) for x in t.partition(' ')]
+ [self.assertTainted(x) for x in a.partition(' ')]
+ [self.assertTainted(x) for x in b.partition(' ')]
+ [self.assertTainted(x) for x in c.partition(' ')]
+ [self.assertClean(x) for x in u.partition(' ')]
+
+ [self.assertTainted(x) for x in t.partition(' '.taint())]
+ [self.assertTainted(x) for x in a.partition(' '.taint())]
+ [self.assertTainted(x) for x in b.partition(' '.taint())]
+ [self.assertTainted(x) for x in c.partition(' '.taint())]
+ [self.assertTainted(x) for x in u.partition(' '.taint())]
+
+ [self.assertTainted(x) for x in t.partition(ss)]
+ [self.assertTainted(x) for x in a.partition(ss)]
+ [self.assertTainted(x) for x in b.partition(ss)]
+ [self.assertTainted(x) for x in c.partition(ss)]
+ [self.assertClean(x) for x in u.partition(ss)]
+
+ [self.assertTainted(x) for x in t.partition(tt)]
+ [self.assertTainted(x) for x in a.partition(tt)]
+ [self.assertTainted(x) for x in b.partition(tt)]
+ [self.assertTainted(x) for x in c.partition(tt)]
+ [self.assertTainted(x) for x in u.partition(tt)]
+
+ [self.assertMerits(x, [MeritPartial]) for x in \
+ c.partition(' '._cleanfor(MeritPartial))]
+
+ def test_strip(self):
+ t = u' t t t tt tt'.taint()
+ u = u'u uu uuu uuuu u'
+ a = u'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone)
+
+ x = u'xy'
+ y = 'xy'.taint()
+ z = u'xy'._cleanfor(MeritPartial)
+
+ self.assertTainted(t.strip())
+ self.assertTainted(a.strip())
+ self.assertTainted(b.strip())
+ self.assertTainted(c.strip())
+ self.assertClean(u.strip())
+
+ self.assertTainted(t.strip(x))
+ self.assertTainted(a.strip(x))
+ self.assertTainted(b.strip(x))
+ self.assertTainted(c.strip(x))
+ self.assertClean(u.strip(x))
+
+ self.assertTainted(t.strip(y))
+ self.assertTainted(a.strip(y))
+ self.assertTainted(b.strip(y))
+ self.assertTainted(c.strip(y))
+ self.assertTainted(u.strip(y))
+
+ self.assertTainted(t.strip(z))
+ self.assertTainted(a.strip(z))
+ self.assertTainted(b.strip(z))
+ self.assertTainted(c.strip(z))
+ self.assertTainted(u.strip(z))
+
+ def test_rstrip(self):
+ t = u' t t t tt tt'.taint()
+ u = u'u uu uuu uuuu u'
+ a = u'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone)
+
+ x = u'xy'
+ y = 'xy'.taint()
+ z = u'xy'._cleanfor(MeritPartial)
+
+ self.assertTainted(t.strip())
+ self.assertTainted(a.strip())
+ self.assertTainted(b.strip())
+ self.assertTainted(c.strip())
+ self.assertClean(u.strip())
+
+ self.assertTainted(t.strip(x))
+ self.assertTainted(a.strip(x))
+ self.assertTainted(b.strip(x))
+ self.assertTainted(c.strip(x))
+ self.assertClean(u.strip(x))
+
+ self.assertTainted(t.strip(y))
+ self.assertTainted(a.strip(y))
+ self.assertTainted(b.strip(y))
+ self.assertTainted(c.strip(y))
+ self.assertTainted(u.strip(y))
+
+ self.assertTainted(t.strip(z))
+ self.assertTainted(a.strip(z))
+ self.assertTainted(b.strip(z))
+ self.assertTainted(c.strip(z))
+ self.assertTainted(u.strip(z))
+
+ def test_lstrip(self):
+ t = u' t t t tt tt'.taint()
+ u = u'u uu uuu uuuu u'
+ a = u'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone)
+
+ x = u'xy'
+ y = 'xy'.taint()
+ z = u'xy'._cleanfor(MeritPartial)
+
+ self.assertTainted(t.strip())
+ self.assertTainted(a.strip())
+ self.assertTainted(b.strip())
+ self.assertTainted(c.strip())
+ self.assertClean(u.strip())
+
+ self.assertTainted(t.strip(x))
+ self.assertTainted(a.strip(x))
+ self.assertTainted(b.strip(x))
+ self.assertTainted(c.strip(x))
+ self.assertClean(u.strip(x))
+
+ self.assertTainted(t.strip(y))
+ self.assertTainted(a.strip(y))
+ self.assertTainted(b.strip(y))
+ self.assertTainted(c.strip(y))
+ self.assertTainted(u.strip(y))
+
+ self.assertTainted(t.strip(z))
+ self.assertTainted(a.strip(z))
+ self.assertTainted(b.strip(z))
+ self.assertTainted(c.strip(z))
+ self.assertTainted(u.strip(z))
+
+ def test_ljust(self):
+ pass
+
+ def test_rjust(self):
+ pass
+
+ def test_center(self):
+ pass
+
+ def test_replace(self):
+ s = u'abc def def def'
+ a = u'def'
+ b = u'xyz'
+ st = s.taint()
+ at = s.taint()
+ bt = s.taint()
+
+ self.assertClean(s.replace(a, b))
+ self.assertTainted(st.replace(a, b))
+ self.assertTainted(s.replace(at, b))
+ self.assertTainted(st.replace(at, b))
+ self.assertTainted(s.replace(a, bt))
+ self.assertTainted(st.replace(a, bt))
+ self.assertTainted(s.replace(at, bt))
+ self.assertTainted(st.replace(at, bt))
+
+ self.assertTainted(u'u'.replace(u'x', u''.taint()))
+ self.assertClean(u'u')
+ self.assertTainted(u'u'.replace(u'x'.taint(), u''))
+ self.assertClean(u'u')
+ self.assertTainted(u'u'.taint().replace(u'x', u''))
+ self.assertClean(u'u')
+
+ def test_split(self):
+ t = u't t t tt tt'.taint()
+ u = u'u uu uuu uuuu u'
+ a = u'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone)\
+ ._cleanfor(MeritPartial)
+ ss = u' '
+
+ [self.assertTainted(x) for x in t.split()]
+ [self.assertTainted(x) for x in a.split()]
+ [self.assertTainted(x) for x in b.split()]
+ [self.assertTainted(x) for x in c.split()]
+ [self.assertClean(x) for x in u.split()]
+
+ [self.assertTainted(x) for x in t.split(u' ')]
+ [self.assertTainted(x) for x in a.split(u' ')]
+ [self.assertTainted(x) for x in b.split(u' ')]
+ [self.assertTainted(x) for x in c.split(u' ')]
+ [self.assertClean(x) for x in u.split(u' ')]
+
+ [self.assertTainted(x) for x in t.split(u' '.taint())]
+ [self.assertTainted(x) for x in a.split(u' '.taint())]
+ [self.assertTainted(x) for x in b.split(u' '.taint())]
+ [self.assertTainted(x) for x in c.split(u' '.taint())]
+ [self.assertTainted(x) for x in u.split(u' '.taint())]
+
+ [self.assertTainted(x) for x in t.split(' '.taint())]
+ [self.assertTainted(x) for x in a.split(' '.taint())]
+ [self.assertTainted(x) for x in b.split(' '.taint())]
+ [self.assertTainted(x) for x in c.split(' '.taint())]
+ [self.assertTainted(x) for x in u.split(' '.taint())]
+
+ [self.assertTainted(x) for x in t.split(ss)]
+ [self.assertTainted(x) for x in a.split(ss)]
+ [self.assertTainted(x) for x in b.split(ss)]
+ [self.assertTainted(x) for x in c.split(ss)]
+ [self.assertClean(x) for x in u.split(ss)]
+
+ [self.assertMerits(x, [MeritPartial]) for x in \
+ c.split(u' '._cleanfor(MeritPartial))]
+
+ def test_rsplit(self):
+ t = u't t t tt tt'.taint()
+ u = u'u uu uuu uuuu u'
+ a = u'a aa aa a a'._cleanfor(MeritFull)
+ b = 'b bbb bb b'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone).\
+ _cleanfor(MeritPartial)
+ ss = u' '
+
+ [self.assertTainted(x) for x in t.rsplit()]
+ [self.assertTainted(x) for x in a.rsplit()]
+ [self.assertTainted(x) for x in b.rsplit()]
+ [self.assertTainted(x) for x in c.rsplit()]
+ [self.assertClean(x) for x in u.rsplit()]
+
+ [self.assertTainted(x) for x in t.rsplit(u' ')]
+ [self.assertTainted(x) for x in a.rsplit(u' ')]
+ [self.assertTainted(x) for x in b.rsplit(u' ')]
+ [self.assertTainted(x) for x in c.rsplit(u' ')]
+ [self.assertClean(x) for x in u.rsplit(u' ')]
+
+ [self.assertTainted(x) for x in t.rsplit(u' '.taint())]
+ [self.assertTainted(x) for x in a.rsplit(u' '.taint())]
+ [self.assertTainted(x) for x in b.rsplit(u' '.taint())]
+ [self.assertTainted(x) for x in c.rsplit(u' '.taint())]
+ [self.assertTainted(x) for x in u.rsplit(u' '.taint())]
+
+ [self.assertTainted(x) for x in t.split(' '.taint())]
+ [self.assertTainted(x) for x in a.split(' '.taint())]
+ [self.assertTainted(x) for x in b.split(' '.taint())]
+ [self.assertTainted(x) for x in c.split(' '.taint())]
+ [self.assertTainted(x) for x in u.split(' '.taint())]
+
+ [self.assertTainted(x) for x in t.rsplit(ss)]
+ [self.assertTainted(x) for x in a.rsplit(ss)]
+ [self.assertTainted(x) for x in b.rsplit(ss)]
+ [self.assertTainted(x) for x in c.rsplit(ss)]
+ [self.assertClean(x) for x in u.rsplit(ss)]
+
+ [self.assertMerits(x, [MeritPartial]) for x in \
+ c.rsplit(u' '._cleanfor(MeritPartial))]
+
+ def test_splitlines(self):
+ t = u't \n t\n t tt \n\n\n tt'.taint()
+ u = u'\nu uu n\n\n \n uuuu u'
+ a = u'\n\na \n aa aa a \n\n a'._cleanfor(MeritFull)
+ b = 'b bbb\n bb \n\nb'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone).\
+ _cleanfor(MeritPartial)
+
+ [self.assertTainted(x) for x in t.splitlines()]
+ [self.assertTainted(x) for x in a.splitlines()]
+ [self.assertTainted(x) for x in b.splitlines()]
+ [self.assertTainted(x) for x in c.splitlines()]
+ [self.assertClean(x) for x in u.splitlines()]
+
+ [self.assertTainted(x) for x in t.splitlines()]
+ [self.assertTainted(x) for x in a.splitlines()]
+ [self.assertTainted(x) for x in b.splitlines()]
+ [self.assertTainted(x) for x in c.splitlines()]
+ [self.assertClean(x) for x in u.splitlines()]
+
+ [self.assertTainted(x) for x in t.splitlines()]
+ [self.assertTainted(x) for x in a.splitlines()]
+ [self.assertTainted(x) for x in b.splitlines()]
+ [self.assertTainted(x) for x in c.splitlines()]
+ [self.assertClean(x) for x in u.splitlines()]
+
+ [self.assertTainted(x) for x in t.splitlines()]
+ [self.assertTainted(x) for x in a.splitlines()]
+ [self.assertTainted(x) for x in b.splitlines()]
+ [self.assertTainted(x) for x in c.splitlines()]
+ [self.assertClean(x) for x in u.splitlines()]
+
+ def test_format_operator(self):
+ # test formatting using the % operator
+ t = u'ttttt'.taint()
+ ts = 'ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ fmt = u'%s %s %s %%'
+ fmt_taint = fmt.taint()
+ fmt_full = fmt_taint._cleanfor(MeritFull)
+ fmt_part = fmt_taint._cleanfor(MeritPartial)
+ fmt_none = fmt_taint._cleanfor(MeritNone)
+ fmt_all = fmt_full._cleanfor(MeritPartial)._cleanfor(MeritNone)
+
+ self.assertClean(fmt % (u'a', u'b', u'c'))
+ self.assertTainted(fmt_taint % (u'a', u'b', u'c'))
+ self.assertTainted(fmt_taint % (t, t, t))
+ self.assertTainted(fmt % (u'a', u'b', t))
+ self.assertTainted(fmt % (t, u'b', t))
+ self.assertTainted(fmt % (u'a', t, u'b'))
+
+ self.assertMerits(fmt % (t, u'a', t_full), [])
+ self.assertMerits(fmt % (u'b', u'a', t_full), [MeritFull])
+ self.assertMerits(fmt % (t_all , t_full, u'a'), [MeritFull])
+ self.assertMerits(fmt_taint % (t, u'a', t_full), [])
+ self.assertMerits(fmt_taint % (u'b', u'a', t_full), [])
+ self.assertMerits(fmt_taint % (t_all , t_full, u'a'), [])
+
+ self.assertMerits(fmt % (str(t), str(u'a'), str(t_full)), [])
+ self.assertMerits(fmt % (str(u'b'), str(u'a'), str(t_full)), [MeritFull])
+ self.assertMerits(fmt % (str(t_all ), str(t_full), str(u'a')), [MeritFull])
+ self.assertMerits(fmt_taint % (str(t), str(u'a'), str(t_full)), [])
+ self.assertMerits(fmt_taint % (str(u'b'), str(u'a'), str(t_full)), [])
+ self.assertMerits(fmt_taint % (str(t_all ), str(t_full), str(u'a')), [])
+
+ self.assertMerits(fmt_full % (t_all , t_full, u'a'), [MeritFull])
+ self.assertMerits(fmt_all % (t_all , t_full, u'a'), [MeritFull])
+ self.assertMerits(fmt_part % (t_part , t_all, t_all), [MeritPartial])
+ self.assertMerits(fmt_all % (t_all, t_part, t_all), [MeritPartial])
+ self.assertMerits(fmt_none % (t_none , t_all, u'c'), [])
+ self.assertMerits(fmt_all % (t_all, t_none, t_all), [])
+
+ self.assertMerits(fmt % ('t'.taint(), u'a', t_full), [])
+ self.assertMerits(fmt % (u'b', 'a', t_full), [MeritFull])
+ self.assertMerits(fmt % (t_all , t_full, 'a'), [MeritFull])
+ self.assertMerits(fmt_taint % (t, 'a', t_full), [])
+ self.assertMerits(fmt_taint % (u'b', u'a', 't'._cleanfor(MeritFull)), [])
+
+ def test_format_method(self):
+ # test formatting using the format method
+ t = u'ttttt'.taint()
+ t_full = t._cleanfor(MeritFull)
+ t_part = t._cleanfor(MeritPartial)
+ t_none = t._cleanfor(MeritNone)
+ t_all = t._cleanfor(MeritFull)._cleanfor(MeritPartial)\
+ ._cleanfor(MeritNone)
+
+ fmt = u'{} {} {} {{}}'
+ fmt_taint = fmt.taint()
+ fmt_full = fmt_taint._cleanfor(MeritFull)
+ fmt_part = fmt_taint._cleanfor(MeritPartial)
+ fmt_none = fmt_taint._cleanfor(MeritNone)
+ fmt_all = fmt_full._cleanfor(MeritPartial)._cleanfor(MeritNone)
+
+ self.assertClean(fmt.format(u'a', u'b', u'c'))
+
+ # TODO(marcinf) specification says that result of below operation
+ # should be tainted. However, since the last argument (u'd') is not
+ # interpolated into format string, it is clean. Change the docs
+ # accordingly to mention that taint is propagated only across the
+ # relevant arguments.
+ self.assertClean(fmt.format(u'a', u'b', u'c', u'd'.taint()))
+
+ self.assertTainted(fmt_taint.format(u'a', u'b', u'c'))
+ self.assertTainted(fmt_taint.format(t, t, t))
+ self.assertTainted(fmt.format(u'a', u'b', t))
+ self.assertTainted(fmt.format(t, u'b', t))
+ self.assertTainted(fmt.format(u'a', t, u'b'))
+
+ self.assertMerits(fmt.format(t, u'a', t_full), [])
+ self.assertMerits(fmt.format(u'b', u'a', t_full), [MeritFull])
+ self.assertMerits(fmt.format(t_all , t_full, u'a'), [MeritFull])
+ self.assertMerits(fmt_taint.format(t, u'a', t_full), [])
+ self.assertMerits(fmt_taint.format(u'b', u'a', t_full), [])
+ self.assertMerits(fmt_taint.format(t_all , t_full, u'a'), [])
+
+ self.assertMerits(fmt_full.format(t_all , t_full, u'a'), [MeritFull])
+ self.assertMerits(fmt_all.format(t_all , t_full, u'a'), [MeritFull])
+ self.assertMerits(fmt_part.format(t_part , t_all, t_all),
+ [MeritPartial])
+ self.assertMerits(fmt_all.format(t_all, t_part, t_all),
+ [MeritPartial])
+ self.assertMerits(fmt_none.format(t_none , t_all, u'c'), [])
+ self.assertMerits(fmt_all.format(t_all, t_none, t_all), [])
+
+ fmt = u'{2} {0} {1} {{}}'
+ fmt_taint = fmt.taint()
+ fmt_full = fmt_taint._cleanfor(MeritFull)
+ fmt_part = fmt_taint._cleanfor(MeritPartial)
+ fmt_none = fmt_taint._cleanfor(MeritNone)
+ fmt_all = fmt_full._cleanfor(MeritPartial)._cleanfor(MeritNone)
+
+ self.assertClean(fmt.format(u'a', u'b', u'c'))
+ self.assertClean(fmt.format(u'a', u'b', u'c', u'd'.taint()))
+ self.assertTainted(fmt_taint.format(u'a', u'b', u'c'))
+ self.assertTainted(fmt_taint.format(t, t, t))
+ self.assertTainted(fmt.format(u'a', u'b', t))
+ self.assertTainted(fmt.format(t, u'b', t))
+ self.assertTainted(fmt.format(u'a', t, u'b'))
+
+ self.assertMerits(fmt.format(t, u'a', t_full), [])
+ self.assertMerits(fmt.format(u'b', u'a', t_full), [MeritFull])
+ self.assertMerits(fmt.format(t_all , t_full, u'a'), [MeritFull])
+ self.assertMerits(fmt_taint.format(t, u'a', t_full), [])
+ self.assertMerits(fmt_taint.format(u'b', u'a', t_full), [])
+ self.assertMerits(fmt_taint.format(t_all , t_full, u'a'), [])
+
+ self.assertMerits(fmt_full.format(t_all , t_full, u'a'), [MeritFull])
+ self.assertMerits(fmt_all.format(t_all , t_full, u'a'), [MeritFull])
+ self.assertMerits(fmt_part.format(t_part , t_all, t_all),
+ [MeritPartial])
+ self.assertMerits(fmt_all.format(t_all, t_part, t_all),
+ [MeritPartial])
+ self.assertMerits(fmt_none.format(t_none , t_all, u'c'), [])
+ self.assertMerits(fmt_all.format(t_all, t_none, t_all), [])
+
+ fmt = u'{x} {y[0]} {z.t} {{}}'
+ fmt_taint = fmt.taint()
+ fmt_full = fmt_taint._cleanfor(MeritFull)
+ fmt_part = fmt_taint._cleanfor(MeritPartial)
+ fmt_none = fmt_taint._cleanfor(MeritNone)
+ fmt_all = fmt_full._cleanfor(MeritPartial)._cleanfor(MeritNone)
+
+ def pack(x):
+ """ Create a dummy object d satisfying d.t == x. This is for
+ testing formatting string with objects' attributes. """
+ return type('zt', (), {'t': x})
+
+ self.assertClean(fmt.format(x=u'a', y=[u'b'], z=pack(u'c')))
+ self.assertClean(fmt.format(x=u'a', y=[u'b'], z=pack(u'c'),
+ t=u'd'.taint()))
+ self.assertTainted(fmt_taint.format(x=u'a', y=[u'b'], z=pack(u'c')))
+ self.assertTainted(fmt_taint.format(x=t, y=[t], z=pack(t)))
+ self.assertTainted(fmt.format(x=u'a', y=[u'b'], z=pack(t)))
+ self.assertTainted(fmt.format(x=t, y=[u'b'], z=pack(t)))
+ self.assertTainted(fmt.format(x=u'a', y=[t], z=pack(u'b')))
+
+ self.assertMerits(fmt.format(x=t, y=[u'a'], z=pack(t_full)),
+ [])
+ self.assertMerits(fmt.format(x=u'b', y=[u'a'], z=pack(t_full)),
+ [MeritFull])
+ self.assertMerits(fmt.format(x=t_all , y=[t_full], z=pack(u'a')),
+ [MeritFull])
+ self.assertMerits(fmt_taint.format(x=t, y=[u'a'], z=pack(t_full)),
+ [])
+ self.assertMerits(fmt_taint.format(x=u'b', y=[u'a'], z=pack(t_full)),
+ [])
+ self.assertMerits(fmt_taint.format(x=t_all , y=[t_full], z=pack(u'a')),
+ [])
+
+ self.assertMerits(fmt_full.format(x=t_all , y=[t_full], z=pack(u'a')),
+ [MeritFull])
+ self.assertMerits(fmt_all.format(x=t_all , y=[t_full], z=pack(u'a')),
+ [MeritFull])
+ self.assertMerits(fmt_part.format(x=t_part , y=[t_all], z=pack(t_all)),
+ [MeritPartial])
+ self.assertMerits(fmt_all.format(x=t_all, y=[t_part], z=pack(t_all)),
+ [MeritPartial])
+ self.assertMerits(fmt_none.format(x=t_none , y=[t_all], z=pack(u'c')),
+ [])
+ self.assertMerits(fmt_all.format(x=t_all, y=[t_none], z=pack(t_all)),
+ [])
+
+ nested = u'{0:{a}}'
+ self.assertMerits(nested.taint().format(u't', a=u's'), [])
+ self.assertMerits(nested._cleanfor(MeritFull).format(u't', a=u's'),
+ [MeritFull])
+ self.assertMerits(nested._cleanfor(MeritPartial).format(u't', a=u's'),
+ [])
+ self.assertMerits(nested._cleanfor(MeritPartial).format(
+ t_part, a=u's'._cleanfor(MeritPartial)),
+ [MeritPartial])
+ self.assertMerits(nested._cleanfor(MeritNone).format(
+ t_none, a=u's'._cleanfor(MeritNone)),
+ [])
+
+ def test_join(self):
+ t = u'\u0230ttttt'.taint()
+ u = u'\u0230uuuuu'
+ a = 'aaaaa'._cleanfor(MeritFull)
+ b = u'\u0230bbbbb'._cleanfor(MeritNone)
+ c = u._cleanfor(MeritFull)._cleanfor(MeritNone)
+
+ self.assertTainted(t.join([]))
+ self.assertTainted(a.join([]))
+ self.assertTainted(b.join([]))
+ self.assertTainted(c.join([]))
+ self.assertClean(u.join([]))
+
+ self.assertTainted(t.join(['a']))
+ self.assertTainted(a.join(['a']))
+ self.assertTainted(b.join(['a']))
+ self.assertTainted(c.join(['a']))
+ self.assertClean(u.join(['a']))
+
+ self.assertTainted(t.join([u'a']))
+ self.assertTainted(a.join([u'a']))
+ self.assertTainted(b.join([u'a']))
+ self.assertTainted(c.join([u'a']))
+ self.assertClean(u.join([u'a']))
+
+ self.assertTainted(t.join(['a', u'']))
+ self.assertTainted(a.join([u'', 'a']))
+ self.assertTainted(b.join(['a', u'']))
+ self.assertTainted(c.join(['', 'a', u'']))
+ self.assertClean(u.join(['a', u'']))
+
+ self.assertTainted(t.join(['']))
+ self.assertTainted(a.join(['', '']))
+ self.assertTainted(c.join(['', '', '']))
+ self.assertClean(u.join(['', u'', '', '', '']))
+ self.assertTainted(u.join(['', u''.taint(), '', '', '', '']))
+ self.assertTainted(u.join([''._cleanfor(MeritFull), '', '', '']))
+ self.assertTainted(u.join(['', u'', t]))
+
+ self.assertTainted(t.join([u'a', 'xx']))
+ self.assertTainted(t.join([u'aaaaaaaaaaaa']))
+ self.assertTainted(a.join([u'b', 'axxxk']))
+ self.assertTainted(b.join([u'a', 'aa', 'f', 'g', 'h', 'r']))
+ self.assertTainted(c.join([u'c', 'afff', 'dddd']))
+ self.assertClean(u.join([u'aaaa']))
+ self.assertClean(u.join(['aa', u'bb', 'cc', 'd']))
+ self.assertTainted(u.join([u'aa'.taint(), 'bb', u'cc', 'd']))
+ self.assertTainted(u.join(['aa', u'bb'._cleanfor(MeritFull),\
+ 'cc'._cleanfor(MeritNone), 'd']))
+
+ def test_translate(self):
+ s1 = u'abcdef'
+ s1_t = u'abcdef'.taint()
+ s1_full = u'abcdef'._cleanfor(MeritFull)
+ s1_part = u'abcdef'._cleanfor(MeritPartial)
+ s1_none = u'abcdef'._cleanfor(MeritNone)
+ s2 = u'ghijkl'
+ t1 = {97 : None,
+ 98 : u'x'}
+ tp = {97 : u'y'._cleanfor(MeritPartial),
+ 98 : u'x'._cleanfor(MeritPartial)}
+ tf = {97 : u'y'._cleanfor(MeritFull),
+ 98 : u'x'._cleanfor(MeritFull)}
+ tn = {97 : u'y'._cleanfor(MeritFull),
+ 98 : u'x'._cleanfor(MeritNone)}
+
+ self.assertClean(s1.translate(t1))
+ self.assertMerits(s1.translate(tf), [MeritFull])
+ self.assertMerits(s1.translate(tp), [])
+ self.assertMerits(s1.translate(tn), [])
+
+ self.assertTainted(s1_t.translate(t1))
+ self.assertMerits(s1_t.translate(tf), [])
+ self.assertMerits(s1_t.translate(tp), [])
+ self.assertMerits(s1_t.translate(tn), [])
+
+ self.assertTainted(s1_full.translate(t1))
+ self.assertMerits(s1_full.translate(tf), [MeritFull])
+ self.assertMerits(s1_full.translate(tp), [])
+ self.assertMerits(s1_full.translate(tn), [])
+
+ self.assertTainted(s1_part.translate(t1))
+ self.assertMerits(s1_part.translate(tf), [])
+ self.assertMerits(s1_part.translate(tp), [MeritPartial])
+ self.assertMerits(s1_part.translate(tn), [])
+
+ self.assertTainted(s1_none.translate(t1))
+ self.assertMerits(s1_none.translate(tf), [])
+ self.assertMerits(s1_none.translate(tp), [])
+ self.assertMerits(s1_none.translate(tn), [])
+
+ self.assertClean(s2.translate(t1))
+ self.assertClean(s2.translate(tf))
+ self.assertClean(s2.translate(tp))
+ self.assertClean(s2.translate(tn))
+
+def test_main():
+ test_support.run_unittest(TaintTest, MeritsTest, UnaryUnicodeOperationTest,
+ VariadicUnicodeOperationTest)
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 9d55550..8e32455 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -370,6 +370,7 @@ OBJECT_OBJS= \
Objects/longobject.o \
Objects/dictobject.o \
Objects/memoryobject.o \
+ Objects/meritobject.o \
Objects/methodobject.o \
Objects/moduleobject.o \
Objects/object.o \
@@ -379,6 +380,7 @@ OBJECT_OBJS= \
Objects/sliceobject.o \
Objects/stringobject.o \
Objects/structseq.o \
+ Objects/taintobject.o \
Objects/tupleobject.o \
Objects/typeobject.o \
Objects/weakrefobject.o \
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index 0f86cfb..ca1c849 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -1950,6 +1950,11 @@ SimpleExtendsException(PyExc_StandardError, MemoryError, "Out of memory.");
*/
SimpleExtendsException(PyExc_StandardError, BufferError, "Buffer error.");
+/*
+ * TaintError extends StandardError
+ */
+SimpleExtendsException(PyExc_StandardError, TaintError,
+ "Taint policy violation.");
/* Warning category docstrings */
@@ -2102,6 +2107,7 @@ _PyExc_Init(void)
PRE_INIT(ReferenceError)
PRE_INIT(MemoryError)
PRE_INIT(BufferError)
+ PRE_INIT(TaintError)
PRE_INIT(Warning)
PRE_INIT(UserWarning)
PRE_INIT(DeprecationWarning)
@@ -2171,6 +2177,7 @@ _PyExc_Init(void)
POST_INIT(ReferenceError)
POST_INIT(MemoryError)
POST_INIT(BufferError)
+ POST_INIT(TaintError)
POST_INIT(Warning)
POST_INIT(UserWarning)
POST_INIT(DeprecationWarning)
diff --git a/Objects/meritobject.c b/Objects/meritobject.c
new file mode 100644
index 0000000..22d70f4
--- /dev/null
+++ b/Objects/meritobject.c
@@ -0,0 +1,180 @@
+#define PY_SSIZE_T_CLEAN
+
+#include <Python.h>
+
+typedef struct {
+ PyObject_HEAD
+} PyMeritObject;
+
+static void
+Merit_dealloc(PyObject* self)
+{
+ Py_TYPE(self)->tp_free(self);
+}
+
+
+PyTypeObject PyMerit_MeritType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "taint.Merit", /* tp_name */
+ sizeof(PyMeritObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)Merit_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "Merit object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ &PyBaseObject_Type, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+};
+
+typedef struct {
+ PyObject_HEAD
+ char ob_strat;
+} PyPropagationObject;
+
+static void
+Propagation_dealloc(PyObject* self)
+{
+ Py_TYPE(self)->tp_free(self);
+}
+
+static char *propagation_names[3] = {
+ "Full",
+ "None",
+ "Partial"
+};
+
+#define PROPAGATION_NAMES_SIZE \
+ (sizeof(propagation_names)/sizeof(*propagation_names))
+
+PyObject *
+Propagation_repr(PyObject* self) {
+ int s = (int)((PyPropagationObject*)self)->ob_strat;
+ if (s >= 0 && s < PROPAGATION_NAMES_SIZE)
+ return PyString_FromFormat("<taint.Merit.%sPropagation>",
+ propagation_names[s]);
+
+ // TODO(marcinf) what should happen here - exception or exit?
+ return NULL;
+}
+
+PyObject *
+Propagation_str(PyObject* self) {
+ int s = (int)((PyPropagationObject*)self)->ob_strat;
+ if (s >= 0 && s < PROPAGATION_NAMES_SIZE)
+ return PyString_FromFormat("<%sPropagation>",
+ propagation_names[s]);
+
+ // TODO(marcinf) what should happen here - exception or exit?
+ return NULL;
+}
+
+
+PyTypeObject PyMerit_PropagationType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "taint.Propagation", /* tp_name */
+ sizeof(PyPropagationObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)Propagation_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ Propagation_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ Propagation_str, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ "Propagation object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ &PyBaseObject_Type, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+};
+
+PyObject *_PyMerit_FullPropagation;
+PyObject *_PyMerit_PartialPropagation;
+PyObject *_PyMerit_NonePropagation;
+
+void
+_PyTaint_Init(void)
+{
+ PyMerit_MeritType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyMerit_MeritType) < 0) {
+ return;
+ }
+
+ PyMerit_PropagationType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyMerit_PropagationType) < 0) {
+ return;
+ }
+
+ _PyMerit_FullPropagation = (PyObject*)PyObject_New(PyPropagationObject,
+ &PyMerit_PropagationType);
+ ((PyPropagationObject*)_PyMerit_FullPropagation)->ob_strat = 0;
+ _PyMerit_NonePropagation = (PyObject*)PyObject_New(PyPropagationObject,
+ &PyMerit_PropagationType);
+ ((PyPropagationObject*)_PyMerit_NonePropagation)->ob_strat = 1;
+ _PyMerit_PartialPropagation = (PyObject*)PyObject_New(PyPropagationObject,
+ &PyMerit_PropagationType);
+ ((PyPropagationObject*)_PyMerit_PartialPropagation)->ob_strat = 2;
+
+ PyDict_SetItemString(PyMerit_MeritType.tp_dict, "FullPropagation",
+ _PyMerit_FullPropagation);
+ PyDict_SetItemString(PyMerit_MeritType.tp_dict, "PartialPropagation",
+ _PyMerit_PartialPropagation);
+ PyDict_SetItemString(PyMerit_MeritType.tp_dict, "NonePropagation",
+ _PyMerit_NonePropagation);
+ PyDict_SetItemString(PyMerit_MeritType.tp_dict, "propagation",
+ _PyMerit_NonePropagation);
+}
diff --git a/Objects/stringlib/string_format.h b/Objects/stringlib/string_format.h
index 965e1ad..483b7de 100644
--- a/Objects/stringlib/string_format.h
+++ b/Objects/stringlib/string_format.h
@@ -47,7 +47,8 @@ typedef struct {
/* forward declaration for recursion */
static PyObject *
build_string(SubString *input, PyObject *args, PyObject *kwargs,
- int recursion_depth, AutoNumber *auto_number);
+ int recursion_depth, AutoNumber *auto_number,
+ PyTaintObject **taint_obj);
@@ -197,6 +198,7 @@ get_integer(const SubString *str)
{
Py_ssize_t accumulator = 0;
Py_ssize_t digitval;
+ Py_ssize_t oldaccumulator;
STRINGLIB_CHAR *p;
/* empty string is an error */
@@ -208,17 +210,19 @@ get_integer(const SubString *str)
if (digitval < 0)
return -1;
/*
- Detect possible overflow before it happens:
-
- accumulator * 10 + digitval > PY_SSIZE_T_MAX if and only if
- accumulator > (PY_SSIZE_T_MAX - digitval) / 10.
+ This trick was copied from old Unicode format code. It's cute,
+ but would really suck on an old machine with a slow divide
+ implementation. Fortunately, in the normal case we do not
+ expect too many digits.
*/
- if (accumulator > (PY_SSIZE_T_MAX - digitval) / 10) {
+ oldaccumulator = accumulator;
+ accumulator *= 10;
+ if ((accumulator+10)/10 != oldaccumulator+1) {
PyErr_Format(PyExc_ValueError,
"Too many decimal digits in format string");
return -1;
}
- accumulator = accumulator * 10 + digitval;
+ accumulator += digitval;
}
return accumulator;
}
@@ -557,7 +561,8 @@ error:
appends to the output.
*/
static int
-render_field(PyObject *fieldobj, SubString *format_spec, OutputString *output)
+render_field(PyObject *fieldobj, SubString *format_spec, OutputString *output,
+ PyTaintObject **taintobj)
{
int ok = 0;
PyObject *result = NULL;
@@ -588,6 +593,9 @@ render_field(PyObject *fieldobj, SubString *format_spec, OutputString *output)
else if (PyFloat_CheckExact(fieldobj))
formatter = _PyFloat_FormatAdvanced;
#endif
+ if (PyTaint_IsTaintable(fieldobj)) {
+ PyTaint_PropagateTo(taintobj, _PyTaint_GetFromObject(fieldobj));
+ }
if (formatter) {
/* we know exactly which formatter will be called when __format__ is
@@ -882,7 +890,8 @@ static int
output_markup(SubString *field_name, SubString *format_spec,
int format_spec_needs_expanding, STRINGLIB_CHAR conversion,
OutputString *output, PyObject *args, PyObject *kwargs,
- int recursion_depth, AutoNumber *auto_number)
+ int recursion_depth, AutoNumber *auto_number,
+ PyTaintObject **taint_obj)
{
PyObject *tmp = NULL;
PyObject *fieldobj = NULL;
@@ -909,10 +918,18 @@ output_markup(SubString *field_name, SubString *format_spec,
/* if needed, recurively compute the format_spec */
if (format_spec_needs_expanding) {
tmp = build_string(format_spec, args, kwargs, recursion_depth-1,
- auto_number);
+ auto_number, taint_obj);
if (tmp == NULL)
goto done;
+ Py_XINCREF(*taint_obj);
+ // build_string will steal reference to *taint_obj, possibly modify it,
+ // and assign to tmp. however, we want to reuse *taint_obj in
+ // render_field, so incref. (Also *taint_obj can not be increfed before
+ // calling build_string, because it may be swapped with a new object)
+
+ assert(_PyTaint_GetFromObject(tmp) == *taint_obj);
+
/* note that in the case we're expanding the format string,
tmp must be kept around until after the call to
render_field. */
@@ -923,7 +940,7 @@ output_markup(SubString *field_name, SubString *format_spec,
else
actual_format_spec = format_spec;
- if (render_field(fieldobj, actual_format_spec, output) == 0)
+ if (render_field(fieldobj, actual_format_spec, output, taint_obj) == 0)
goto done;
result = 1;
@@ -943,7 +960,8 @@ done:
*/
static int
do_markup(SubString *input, PyObject *args, PyObject *kwargs,
- OutputString *output, int recursion_depth, AutoNumber *auto_number)
+ OutputString *output, int recursion_depth, AutoNumber *auto_number,
+ PyTaintObject **taint_obj)
{
MarkupIterator iter;
int format_spec_needs_expanding;
@@ -964,7 +982,8 @@ do_markup(SubString *input, PyObject *args, PyObject *kwargs,
if (field_present)
if (!output_markup(&field_name, &format_spec,
format_spec_needs_expanding, conversion, output,
- args, kwargs, recursion_depth, auto_number))
+ args, kwargs, recursion_depth, auto_number,
+ taint_obj))
return 0;
}
return result;
@@ -977,7 +996,8 @@ do_markup(SubString *input, PyObject *args, PyObject *kwargs,
*/
static PyObject *
build_string(SubString *input, PyObject *args, PyObject *kwargs,
- int recursion_depth, AutoNumber *auto_number)
+ int recursion_depth, AutoNumber *auto_number,
+ PyTaintObject **taint_obj)
{
OutputString output;
PyObject *result = NULL;
@@ -1000,7 +1020,7 @@ build_string(SubString *input, PyObject *args, PyObject *kwargs,
goto done;
if (!do_markup(input, args, kwargs, &output, recursion_depth,
- auto_number)) {
+ auto_number, taint_obj)) {
goto done;
}
@@ -1012,8 +1032,10 @@ build_string(SubString *input, PyObject *args, PyObject *kwargs,
/* transfer ownership to result */
result = output.obj;
output.obj = NULL;
+ result = PyTaint_AssignToObject(result, *taint_obj);
done:
+ Py_XDECREF(*taint_obj);
Py_XDECREF(output.obj);
return result;
}
@@ -1027,6 +1049,7 @@ static PyObject *
do_string_format(PyObject *self, PyObject *args, PyObject *kwargs)
{
SubString input;
+ PyTaintObject *taint = NULL;
/* PEP 3101 says only 2 levels, so that
"{0:{1}}".format('abc', 's') # works
@@ -1038,7 +1061,15 @@ do_string_format(PyObject *self, PyObject *args, PyObject *kwargs)
AutoNumber_Init(&auto_number);
SubString_init(&input, STRINGLIB_STR(self), STRINGLIB_LEN(self));
- return build_string(&input, args, kwargs, recursion_depth, &auto_number);
+#if STRINGLIB_IS_UNICODE
+ taint = PyUnicode_GET_MERITS(self);
+#else
+ taint = PyString_GET_MERITS(self);
+#endif
+ Py_XINCREF(taint);
+
+ return build_string(&input, args, kwargs, recursion_depth, &auto_number,
+ &taint);
}
diff --git a/Objects/stringobject.c b/Objects/stringobject.c
index 1209197..470f7a3 100644
--- a/Objects/stringobject.c
+++ b/Objects/stringobject.c
@@ -20,6 +20,10 @@ static PyStringObject *nullstring;
Another way to look at this is that to say that the actual reference
count of a string is: s->ob_refcnt + (s->ob_sstate?2:0)
+
+ Note that only untainted strings can be interned. (Passing a tainted string
+ to `intern` python builtin will cause TypeError, whereas C API Python
+ functions like PyString_InternInPlace will ignore tainted strings).
*/
static PyObject *interned;
@@ -32,6 +36,48 @@ static PyObject *interned;
#define PyStringObject_SIZE (offsetof(PyStringObject, ob_sval) + 1)
/*
+ Build a string in similar way to PyString_FromStringAndSize without
+ checking if the arguments are valid, also it does not intern short strings.
+*/
+static PyStringObject *
+unsafe_build_string(const char *str, Py_ssize_t size)
+{
+ register PyStringObject *op;
+ op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size);
+ if (op == NULL)
+ return (PyStringObject *)PyErr_NoMemory();
+ PyObject_INIT_VAR(op, &PyString_Type, size);
+ op->ob_shash = -1;
+ op->ob_sstate = SSTATE_NOT_INTERNED;
+
+ if (str != NULL)
+ Py_MEMCPY(op->ob_sval, str, size);
+ op->ob_sval[size] = '\0';
+ op->ob_merits = NULL;
+
+ return op;
+}
+
+/*
+ Utility for building strings, checks if string size is valid.
+*/
+static int
+check_string_size(Py_ssize_t size)
+{
+ if (size < 0) {
+ PyErr_SetString(PyExc_SystemError,
+ "Negative size passed to PyString_FromStringAndSize");
+ return -1;
+ }
+
+ if (size > PY_SSIZE_T_MAX - PyStringObject_SIZE) {
+ PyErr_SetString(PyExc_OverflowError, "string is too large");
+ return -1;
+ }
+ return 1;
+}
+
+/*
For PyString_FromString(), the parameter `str' points to a null-terminated
string containing exactly `size' bytes.
@@ -46,6 +92,12 @@ static PyObject *interned;
PyString object must be treated as immutable and you must not fill in nor
alter the data yourself, since the strings may be shared.
+ For PyString_FromStringAndSizeNoIntern(), the parameters `str' and `size'
+ work the same as in PyString_FromStringAndSize(), however the returned
+ string object will not be interned. This is useful for string methods when
+ merits of result is not yet known, yet it is certain that the result will
+ be tainted (so it cannot be interned).
+
The PyObject member `op->ob_size', which denotes the number of "extra
items" in a variable-size object, will contain the number of bytes
allocated for string data, not counting the null terminating character.
@@ -57,11 +109,7 @@ PyObject *
PyString_FromStringAndSize(const char *str, Py_ssize_t size)
{
register PyStringObject *op;
- if (size < 0) {
- PyErr_SetString(PyExc_SystemError,
- "Negative size passed to PyString_FromStringAndSize");
- return NULL;
- }
+
if (size == 0 && (op = nullstring) != NULL) {
#ifdef COUNT_ALLOCS
null_strings++;
@@ -79,21 +127,12 @@ PyString_FromStringAndSize(const char *str, Py_ssize_t size)
return (PyObject *)op;
}
- if (size > PY_SSIZE_T_MAX - PyStringObject_SIZE) {
- PyErr_SetString(PyExc_OverflowError, "string is too large");
+ if (check_string_size(size) == -1)
return NULL;
- }
- /* Inline PyObject_NewVar */
- op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size);
+ op = unsafe_build_string(str, size);
if (op == NULL)
- return PyErr_NoMemory();
- PyObject_INIT_VAR(op, &PyString_Type, size);
- op->ob_shash = -1;
- op->ob_sstate = SSTATE_NOT_INTERNED;
- if (str != NULL)
- Py_MEMCPY(op->ob_sval, str, size);
- op->ob_sval[size] = '\0';
+ return NULL;
/* share short strings */
if (size == 0) {
PyObject *t = (PyObject *)op;
@@ -108,6 +147,7 @@ PyString_FromStringAndSize(const char *str, Py_ssize_t size)
characters[*str & UCHAR_MAX] = op;
Py_INCREF(op);
}
+ assert(!PyString_CHECK_TAINTED(op));
return (PyObject *) op;
}
@@ -138,15 +178,11 @@ PyString_FromString(const char *str)
Py_INCREF(op);
return (PyObject *)op;
}
-
- /* Inline PyObject_NewVar */
- op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size);
+ if (check_string_size(size) == -1)
+ return NULL;
+ op = unsafe_build_string(str, size);
if (op == NULL)
return PyErr_NoMemory();
- PyObject_INIT_VAR(op, &PyString_Type, size);
- op->ob_shash = -1;
- op->ob_sstate = SSTATE_NOT_INTERNED;
- Py_MEMCPY(op->ob_sval, str, size+1);
/* share short strings */
if (size == 0) {
PyObject *t = (PyObject *)op;
@@ -161,10 +197,142 @@ PyString_FromString(const char *str)
characters[*str & UCHAR_MAX] = op;
Py_INCREF(op);
}
+
return (PyObject *) op;
}
PyObject *
+PyString_FromStringAndSizeNoIntern(const char *str, Py_ssize_t size)
+{
+ register PyStringObject *op;
+
+ if (check_string_size(size) == -1)
+ return NULL;
+
+ op = unsafe_build_string(str, size);
+ if (op == NULL)
+ return NULL;
+
+ op->ob_merits = NULL;
+
+ return (PyObject *) op;
+}
+
+/*
+ PyString_FromStringAndSizeSameMerits works the same as
+ PyString_FromStringAndSize when NULL merits are passed. If the merits are
+ non-NULL, PyString_FromStringAndSizeSameMerits will create a string object
+ that references those merits. This function does not steal reference; upon
+ sucessful creation of new object refcount of passed merits will be
+ increased.
+*/
+PyObject *
+PyString_FromStringAndSizeSameMerits(const char *str, Py_ssize_t size,
+ PyTaintObject *merits)
+{
+ register PyObject *op;
+
+ if (merits == NULL) {
+ op = PyString_FromStringAndSize(str, size);
+ } else {
+ op = PyString_FromStringAndSizeNoIntern(str, size);
+ }
+
+ if (op == NULL)
+ return NULL;
+
+ if (merits != NULL)
+ PyString_ASSIGN_MERITS(op, merits);
+
+ return op;
+}
+
+/*
+ Based on taint propagation semantics, propagate taint between a and b and
+ store result in the result. Returns 1 on success, -1 on failure. The
+ target's ob_merits will be replaced by a new object, so this function is
+ safe to use even if they're shared with another string.
+*/
+int _PyString_BinaryTaintPropagateInPlace(register PyStringObject *result,
+ register PyStringObject *a,
+ register PyStringObject *b)
+{
+ return PyTaint_PropagationResult(&(result->ob_merits),
+ PyString_GET_MERITS(a),
+ PyString_GET_MERITS(b));
+}
+
+/*
+ Based on taint propagation rules for each merit, propagate merits between
+ source and target (modifying target's merits). Returns 1 on success, -1 on
+ failure. The target's ob_merits will be replaced by a new object, so this
+ function is safe to use even if they're shared with another string.
+*/
+int
+_PyString_PropagateTaintInPlace(register PyStringObject *target,
+ register PyStringObject *source)
+{
+ PyTaintObject *result = NULL;
+ if (PyTaint_PropagationResult(&result, PyString_GET_MERITS(target),
+ PyString_GET_MERITS(source)) == -1)
+ return -1;
+
+ Py_XDECREF(target->ob_merits);
+ PyString_ASSIGN_MERITS(target, result);
+ return 1;
+}
+
+/* PyString_AssignTaint will return string with same contents as str and taint
+ value taint. The return value may be either the same object as str or a new
+ stringobject.
+
+ When sucessful, steals reference to str. Returns NULL on failure. */
+
+PyObject*
+PyString_AssignTaint(PyStringObject *str, PyTaintObject *taint) {
+ PyObject *result;
+
+ if (!PyString_Check(str)) {
+ PyErr_Format(PyExc_TypeError,
+ "Trying to assign taint to object of type %.200s\
+ (expected string).", Py_TYPE(str)->tp_name);
+ return NULL;
+ }
+
+ if (PyString_GET_MERITS(str) == taint)
+ return (PyObject*)str;
+
+ if (str->ob_refcnt == 1 && !PyString_CHECK_INTERNED(str)) {
+ result = (PyObject*)str;
+ Py_XDECREF(PyString_GET_MERITS(result));
+ } else {
+ result = PyString_FromStringAndSizeNoIntern(PyString_AS_STRING(str),
+ PyString_GET_SIZE(str));
+ if (result == NULL)
+ return result;
+ Py_DECREF(str);
+ }
+ PyString_ASSIGN_MERITS(result, taint);
+
+ return result;
+}
+
+int
+_PyString_CopyTaint(register PyStringObject *target,
+ register PyStringObject *source)
+{
+ if (PyString_CHECK_INTERNED(target)) {
+ PyErr_SetString(PyExc_ValueError,
+ "Attempted tainting of interned string.");
+ return -1;
+ }
+
+ Py_XDECREF(target->ob_merits);
+ PyString_ASSIGN_MERITS(target, source->ob_merits);
+ return 1;
+}
+
+PyObject *
PyString_FromFormatV(const char *format, va_list vargs)
{
va_list count;
@@ -453,7 +621,7 @@ PyObject *PyString_AsDecodedObject(PyObject *str,
if (v == NULL)
goto onError;
- return v;
+ return PyTaint_AssignToObject(v, PyString_GET_MERITS(str));
onError:
return NULL;
@@ -533,7 +701,7 @@ PyObject *PyString_AsEncodedObject(PyObject *str,
if (v == NULL)
goto onError;
- return v;
+ return PyTaint_AssignToObject(v, PyString_GET_MERITS(str));
onError:
return NULL;
@@ -594,6 +762,7 @@ string_dealloc(PyObject *op)
default:
Py_FatalError("Inconsistent interned string state.");
}
+ Py_XDECREF(((PyStringObject*)op)->ob_merits);
Py_TYPE(op)->tp_free(op);
}
@@ -1066,6 +1235,13 @@ string_concat(register PyStringObject *a, register PyObject *bb)
Py_MEMCPY(op->ob_sval, a->ob_sval, Py_SIZE(a));
Py_MEMCPY(op->ob_sval + Py_SIZE(a), b->ob_sval, Py_SIZE(b));
op->ob_sval[size] = '\0';
+ op->ob_merits = NULL;
+
+ if (PyTaint_PropagationResult(&PyString_GET_MERITS(op),
+ PyString_GET_MERITS(a),
+ PyString_GET_MERITS(b)) == -1)
+ return NULL;
+
return (PyObject *) op;
#undef b
}
@@ -1106,6 +1282,13 @@ string_repeat(register PyStringObject *a, register Py_ssize_t n)
op->ob_shash = -1;
op->ob_sstate = SSTATE_NOT_INTERNED;
op->ob_sval[size] = '\0';
+ op->ob_merits = NULL;
+
+ if (_PyString_CopyTaint(op, a) == -1) {
+ Py_DECREF(op);
+ return NULL;
+ }
+
if (Py_SIZE(a) == 1 && n > 0) {
memset(op->ob_sval, a->ob_sval[0] , n);
return (PyObject *) op;
@@ -1143,7 +1326,9 @@ string_slice(register PyStringObject *a, register Py_ssize_t i,
}
if (j < i)
j = i;
- return PyString_FromStringAndSize(a->ob_sval + i, j-i);
+ return PyString_FromStringAndSizeSameMerits(a->ob_sval + i,
+ j - i,
+ a->ob_merits);
}
static int
@@ -1175,14 +1360,26 @@ string_item(PyStringObject *a, register Py_ssize_t i)
return NULL;
}
pchar = a->ob_sval[i];
- v = (PyObject *)characters[pchar & UCHAR_MAX];
- if (v == NULL)
- v = PyString_FromStringAndSize(&pchar, 1);
- else {
+ if (a->ob_merits == NULL) {
+ // string is untainted - pick an interned character
+ v = (PyObject *)characters[pchar & UCHAR_MAX];
+ if (v == NULL)
+ v = PyString_FromStringAndSize(&pchar, 1);
+ else {
#ifdef COUNT_ALLOCS
- one_strings++;
+ one_strings++;
#endif
- Py_INCREF(v);
+ Py_INCREF(v);
+ }
+ } else {
+ // tainted string - create a new one character string
+ v = PyString_FromStringAndSizeNoIntern(&pchar, 1);
+ if (v == NULL)
+ return NULL;
+ if (_PyString_CopyTaint((PyStringObject*)v, a) == -1) {
+ Py_DECREF(v);
+ return NULL;
+ }
}
return v;
}
@@ -1317,7 +1514,8 @@ string_subscript(PyStringObject* self, PyObject* item)
}
if (slicelength <= 0) {
- return PyString_FromStringAndSize("", 0);
+ return PyString_FromStringAndSizeSameMerits("", 0,
+ self->ob_merits);
}
else if (start == 0 && step == 1 &&
slicelength == PyString_GET_SIZE(self) &&
@@ -1326,9 +1524,9 @@ string_subscript(PyStringObject* self, PyObject* item)
return (PyObject *)self;
}
else if (step == 1) {
- return PyString_FromStringAndSize(
- PyString_AS_STRING(self) + start,
- slicelength);
+ return PyString_FromStringAndSizeSameMerits(
+ PyString_AS_STRING(self) + start, slicelength,
+ PyString_GET_MERITS(self));
}
else {
source_buf = PyString_AsString((PyObject*)self);
@@ -1341,8 +1539,8 @@ string_subscript(PyStringObject* self, PyObject* item)
result_buf[i] = source_buf[cur];
}
- result = PyString_FromStringAndSize(result_buf,
- slicelength);
+ result = PyString_FromStringAndSizeSameMerits(
+ result_buf, slicelength, PyString_GET_MERITS(self));
PyMem_Free(result_buf);
return result;
}
@@ -1429,7 +1627,141 @@ static PyBufferProcs string_as_buffer = {
0, /* XXX */
};
+PyDoc_STRVAR(istainted__doc__,
+"S.istainted() -> bool\n\
+\n\
+Return True if S is tainted, False otherwise.");
+static PyObject *
+string_istainted(PyStringObject *self)
+{
+ long taint_val = (long)(self->ob_merits != NULL);
+ return PyBool_FromLong(taint_val);
+}
+
+PyDoc_STRVAR(isclean__doc__,
+"S.isclean([merit]) -> bool\n\
+\n\
+If no merit is specified return False if S is tainted, True otherwise. If a\n\
+merit is specified return False when S is tainted and doesn't have given\n\
+merit, True otherwise.");
+
+static PyObject *
+string_isclean(PyStringObject *self, PyObject *args)
+{
+ PyObject *merit = Py_None;
+ if (!PyArg_ParseTuple(args, "|O:isclean", &merit))
+ return NULL;
+
+ if (self->ob_merits == NULL) {
+ Py_RETURN_TRUE;
+ }
+
+ if (merit == Py_None) {
+ Py_RETURN_FALSE;
+ }
+
+ if (!PyObject_HasAttrString(merit, "propagation")) {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid merit object passed.");
+ return NULL;
+ }
+
+ switch (PySequence_Contains((PyObject*)PyString_GET_MERITS(self),
+ merit)) {
+ case -1:
+ return NULL;
+ case 1:
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+PyDoc_STRVAR(taint__doc__,
+"S.taint() -> str\n\
+\n\
+Return a tainted copy of S without any merits.");
+
+static PyObject *
+string_taint(PyStringObject *self)
+{
+ PyTaintObject *taint = PyTaint_EmptyMerits();
+ if (taint == NULL)
+ return NULL;
+
+ return PyString_FromStringAndSizeSameMerits(PyString_AS_STRING(self),
+ PyTuple_GET_SIZE(self),
+ taint);
+}
+
+PyDoc_STRVAR(cleanfor__doc__,
+"S._cleanfor(M) -> string\n\
+\n\
+Return a tainted copy of S which has merit M. All other merits of S are also\n\
+copied to return value.");
+
+static PyObject *
+string_cleanfor(PyStringObject *self, PyObject *merit)
+{
+ PyStringObject *newobj;
+ PyTaintObject *taint, *new_taint;
+
+ if (_PyTaint_ValidMerit(merit) == -1)
+ return NULL;
+
+ if (PyString_CHECK_TAINTED(self)) {
+ taint = PyString_GET_MERITS(self);
+ Py_INCREF(taint);
+ } else {
+ taint = PyTaint_EmptyMerits();
+ if (taint == NULL)
+ return NULL;
+ }
+
+ new_taint = _PyTaint_AddMerit(taint, merit);
+ Py_DECREF(taint);
+ if (new_taint == NULL)
+ return NULL;
+ newobj = (PyStringObject*)PyString_FromStringAndSizeSameMerits(
+ PyString_AS_STRING(self),
+ PyString_GET_SIZE(self),
+ new_taint);
+
+
+ if (newobj == NULL)
+ return NULL;
+ Py_DECREF(new_taint);
+ return (PyObject *)newobj;
+}
+
+PyDoc_STRVAR(listmerits__doc__,
+"S._merits() -> list of merits\n\
+\n\
+For tainted string S, return its set of merits. If S is untainted return\n\
+None.");
+
+static PyObject*
+string_listmerits(PyStringObject *self)
+{
+ if (self->ob_merits == NULL) {
+ Py_RETURN_NONE;
+ }
+
+ return PySet_New((PyObject*)PyString_GET_MERITS(self));
+}
+
+PyDoc_STRVAR(propagate__doc__,
+"S._propagate(T) -> string\n\
+\n\
+Return a copy of S, which has the same merits as T.");
+
+static PyObject *
+string_propagate(PyStringObject *self, PyStringObject *source)
+{
+ return PyString_FromStringAndSizeSameMerits(PyString_AS_STRING(self),
+ PyString_GET_SIZE(self),
+ PyString_GET_MERITS(source));
+}
#define LEFTSTRIP 0
#define RIGHTSTRIP 1
@@ -1456,25 +1788,48 @@ string_split(PyStringObject *self, PyObject *args)
Py_ssize_t maxsplit = -1;
const char *s = PyString_AS_STRING(self), *sub;
PyObject *subobj = Py_None;
+ PyObject *result;
+ PyTaintObject *taintobj = NULL;
if (!PyArg_ParseTuple(args, "|On:split", &subobj, &maxsplit))
return NULL;
if (maxsplit < 0)
maxsplit = PY_SSIZE_T_MAX;
- if (subobj == Py_None)
- return stringlib_split_whitespace((PyObject*) self, s, len, maxsplit);
- if (PyString_Check(subobj)) {
- sub = PyString_AS_STRING(subobj);
- n = PyString_GET_SIZE(subobj);
- }
+ if (subobj == Py_None) {
+ result = stringlib_split_whitespace((PyObject*) self, s, len, maxsplit);
+ if (result == NULL)
+ return NULL;
+ taintobj = (PyTaintObject*)PyString_GET_MERITS(self);
+ Py_XINCREF(taintobj);
+ } else {
+ if (PyString_Check(subobj)) {
+ sub = PyString_AS_STRING(subobj);
+ n = PyString_GET_SIZE(subobj);
+ }
#ifdef Py_USING_UNICODE
- else if (PyUnicode_Check(subobj))
- return PyUnicode_Split((PyObject *)self, subobj, maxsplit);
+ else if (PyUnicode_Check(subobj))
+ return PyUnicode_Split((PyObject *)self, subobj, maxsplit);
#endif
- else if (PyObject_AsCharBuffer(subobj, &sub, &n))
- return NULL;
+ else if (PyObject_AsCharBuffer(subobj, &sub, &n))
+ return NULL;
+ result = stringlib_split((PyObject*) self, s, len, sub, n, maxsplit);
+ if (result == NULL)
+ return NULL;
+ if (PyTaint_PropagationResult(&taintobj, PyString_GET_MERITS(self),
+ PyString_GET_MERITS(subobj)) == -1)
+ goto onError;
+ }
- return stringlib_split((PyObject*) self, s, len, sub, n, maxsplit);
+ if (_PyTaint_TaintStringListItems(result, taintobj) == -1)
+ goto onError;
+
+ Py_XDECREF(taintobj);
+ return result;
+
+onError:
+ Py_XDECREF(taintobj);
+ Py_DECREF(result);
+ return NULL;
}
PyDoc_STRVAR(partition__doc__,
@@ -1489,6 +1844,7 @@ string_partition(PyStringObject *self, PyObject *sep_obj)
{
const char *sep;
Py_ssize_t sep_len;
+ PyTaintObject* taintobj = NULL;
if (PyString_Check(sep_obj)) {
sep = PyString_AS_STRING(sep_obj);
@@ -1501,11 +1857,35 @@ string_partition(PyStringObject *self, PyObject *sep_obj)
else if (PyObject_AsCharBuffer(sep_obj, &sep, &sep_len))
return NULL;
- return stringlib_partition(
+ PyObject* result = stringlib_partition(
(PyObject*) self,
PyString_AS_STRING(self), PyString_GET_SIZE(self),
sep_obj, sep, sep_len
);
+
+ if (result == NULL)
+ return NULL;
+
+ if (!PyString_CHECK_TAINTED(self) && !PyString_CHECK_TAINTED(sep_obj))
+ return result;
+
+ if (-1 == PyTaint_PropagationResult(&taintobj,
+ PyString_GET_MERITS(self),
+ PyString_GET_MERITS(sep_obj)))
+ goto onError;
+
+ result = _PyTaint_TaintStringTupleItems(result, taintobj);
+
+ if (result == NULL)
+ goto onError;
+
+ Py_XDECREF(taintobj);
+ return result;
+
+onError:
+ Py_XDECREF(taintobj);
+ Py_DECREF(result);
+ return NULL;
}
PyDoc_STRVAR(rpartition__doc__,
@@ -1520,6 +1900,7 @@ string_rpartition(PyStringObject *self, PyObject *sep_obj)
{
const char *sep;
Py_ssize_t sep_len;
+ PyTaintObject* taintobj = NULL;
if (PyString_Check(sep_obj)) {
sep = PyString_AS_STRING(sep_obj);
@@ -1532,11 +1913,35 @@ string_rpartition(PyStringObject *self, PyObject *sep_obj)
else if (PyObject_AsCharBuffer(sep_obj, &sep, &sep_len))
return NULL;
- return stringlib_rpartition(
+ PyObject* result = stringlib_rpartition(
(PyObject*) self,
PyString_AS_STRING(self), PyString_GET_SIZE(self),
sep_obj, sep, sep_len
);
+
+ if (result == NULL)
+ return NULL;
+
+ if (!PyString_CHECK_TAINTED(self) && !PyString_CHECK_TAINTED(sep_obj))
+ return result;
+
+ if (-1 == PyTaint_PropagationResult(&taintobj,
+ PyString_GET_MERITS(self),
+ PyString_GET_MERITS(sep_obj)))
+ goto onError;
+
+ result = _PyTaint_TaintStringTupleItems(result, taintobj);
+
+ if (result == NULL)
+ goto onError;
+
+ Py_XDECREF(taintobj);
+ return result;
+
+onError:
+ Py_XDECREF(taintobj);
+ Py_DECREF(result);
+ return NULL;
}
PyDoc_STRVAR(rsplit__doc__,
@@ -1555,25 +1960,50 @@ string_rsplit(PyStringObject *self, PyObject *args)
Py_ssize_t maxsplit = -1;
const char *s = PyString_AS_STRING(self), *sub;
PyObject *subobj = Py_None;
+ PyObject *result;
+ PyTaintObject *taintobj = NULL;
if (!PyArg_ParseTuple(args, "|On:rsplit", &subobj, &maxsplit))
return NULL;
if (maxsplit < 0)
maxsplit = PY_SSIZE_T_MAX;
- if (subobj == Py_None)
- return stringlib_rsplit_whitespace((PyObject*) self, s, len, maxsplit);
- if (PyString_Check(subobj)) {
- sub = PyString_AS_STRING(subobj);
- n = PyString_GET_SIZE(subobj);
- }
+ if (subobj == Py_None) {
+ result = stringlib_rsplit_whitespace((PyObject*) self, s, len, maxsplit);
+ if (result == NULL)
+ return NULL;
+ taintobj = (PyTaintObject*)PyString_GET_MERITS(self);
+ Py_XINCREF(taintobj);
+ } else {
+ if (PyString_Check(subobj)) {
+ sub = PyString_AS_STRING(subobj);
+ n = PyString_GET_SIZE(subobj);
+ }
#ifdef Py_USING_UNICODE
- else if (PyUnicode_Check(subobj))
- return PyUnicode_RSplit((PyObject *)self, subobj, maxsplit);
+ else if (PyUnicode_Check(subobj))
+ return PyUnicode_RSplit((PyObject *)self, subobj, maxsplit);
#endif
- else if (PyObject_AsCharBuffer(subobj, &sub, &n))
- return NULL;
+ else if (PyObject_AsCharBuffer(subobj, &sub, &n))
+ return NULL;
+ result = stringlib_rsplit((PyObject*) self, s, len, sub, n, maxsplit);
+ if (result == NULL)
+ return NULL;
+
+ if (-1 == PyTaint_PropagationResult(&taintobj,
+ PyString_GET_MERITS(self),
+ PyString_GET_MERITS(subobj)))
+ goto onError;
+ }
- return stringlib_rsplit((PyObject*) self, s, len, sub, n, maxsplit);
+ if (_PyTaint_TaintStringListItems(result, taintobj) == -1)
+ goto onError;
+
+ Py_XDECREF(taintobj);
+ return result;
+
+onError:
+ Py_XDECREF(taintobj);
+ Py_DECREF(result);
+ return NULL;
}
@@ -1594,6 +2024,7 @@ string_join(PyStringObject *self, PyObject *orig)
size_t sz = 0;
Py_ssize_t i;
PyObject *seq, *item;
+ PyTaintObject *taint = NULL;
seq = PySequence_Fast(orig, "");
if (seq == NULL) {
@@ -1601,16 +2032,35 @@ string_join(PyStringObject *self, PyObject *orig)
}
seqlen = PySequence_Size(seq);
+ taint = PyString_GET_MERITS(self);
+ Py_XINCREF(taint);
+
if (seqlen == 0) {
- Py_DECREF(seq);
- return PyString_FromString("");
+ res = PyString_FromStringAndSizeSameMerits("", 0, taint);
+ goto done;
}
+
if (seqlen == 1) {
item = PySequence_Fast_GET_ITEM(seq, 0);
if (PyString_CheckExact(item) || PyUnicode_CheckExact(item)) {
- Py_INCREF(item);
- Py_DECREF(seq);
- return item;
+ if (PyTaint_PropagateTo(&taint, _PyTaint_GetFromObject(item)) == -1)
+ goto onError;
+ if (PyTaint_IS_CLEAN(taint)) {
+ Py_INCREF(item);
+ Py_DECREF(seq);
+ return item;
+ }
+ if (PyString_CheckExact(item))
+ res = PyString_FromStringAndSizeSameMerits(
+ PyString_AS_STRING(item),
+ PyString_GET_SIZE(item),
+ taint);
+ else // PyUnicode_CheckExact(item) is true
+ res = PyUnicode_FromUnicodeSameMerits(
+ PyUnicode_AS_UNICODE(item),
+ PyUnicode_GET_SIZE(item),
+ taint);
+ goto done;
}
}
@@ -1641,8 +2091,7 @@ string_join(PyStringObject *self, PyObject *orig)
"sequence item %zd: expected string,"
" %.80s found",
i, Py_TYPE(item)->tp_name);
- Py_DECREF(seq);
- return NULL;
+ goto onError;
}
sz += PyString_GET_SIZE(item);
if (i != 0)
@@ -1650,17 +2099,18 @@ string_join(PyStringObject *self, PyObject *orig)
if (sz < old_sz || sz > PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_OverflowError,
"join() result is too long for a Python string");
- Py_DECREF(seq);
- return NULL;
+ goto onError;
+ }
+ if (PyTaint_PropagateTo(&taint, PyString_GET_MERITS(item)) == -1) {
+ Py_DECREF(item);
+ goto onError;
}
}
- /* Allocate result space. */
- res = PyString_FromStringAndSize((char*)NULL, sz);
- if (res == NULL) {
- Py_DECREF(seq);
- return NULL;
- }
+ res = PyString_FromStringAndSizeSameMerits((char*)NULL, sz, taint);
+
+ if (res == NULL)
+ goto onError;
/* Catenate everything. */
p = PyString_AS_STRING(res);
@@ -1676,8 +2126,16 @@ string_join(PyStringObject *self, PyObject *orig)
}
}
+done:
Py_DECREF(seq);
+ Py_XDECREF(taint);
return res;
+
+onError:
+ Py_XDECREF(res);
+ Py_XDECREF(taint);
+ Py_DECREF(seq);
+ return NULL;
}
PyObject *
@@ -1826,6 +2284,7 @@ do_xstrip(PyStringObject *self, int striptype, PyObject *sepobj)
char *sep = PyString_AS_STRING(sepobj);
Py_ssize_t seplen = PyString_GET_SIZE(sepobj);
Py_ssize_t i, j;
+ PyTaintObject *taint = NULL;
i = 0;
if (striptype != RIGHTSTRIP) {
@@ -1842,12 +2301,16 @@ do_xstrip(PyStringObject *self, int striptype, PyObject *sepobj)
j++;
}
- if (i == 0 && j == len && PyString_CheckExact(self)) {
+ if (PyTaint_PropagationResult(&taint, PyString_GET_MERITS(self),
+ PyString_GET_MERITS(sepobj)) == -1)
+ return NULL;
+
+ if (i == 0 && j == len && PyString_CheckExact(self) && \
+ taint == PyString_GET_MERITS(self)) {
Py_INCREF(self);
return (PyObject*)self;
}
- else
- return PyString_FromStringAndSize(s+i, j-i);
+ return PyString_FromStringAndSizeSameMerits(s+i, j-i, taint);
}
@@ -1877,7 +2340,8 @@ do_strip(PyStringObject *self, int striptype)
return (PyObject*)self;
}
else
- return PyString_FromStringAndSize(s+i, j-i);
+ return PyString_FromStringAndSizeSameMerits(s+i, j-i,
+ PyString_GET_MERITS(self));
}
@@ -1987,7 +2451,9 @@ string_lower(PyStringObject *self)
Py_ssize_t i, n = PyString_GET_SIZE(self);
PyObject *newobj;
- newobj = PyString_FromStringAndSize(NULL, n);
+ newobj = PyString_FromStringAndSizeSameMerits(NULL, n,
+ PyString_GET_MERITS(self));
+
if (!newobj)
return NULL;
@@ -2020,7 +2486,9 @@ string_upper(PyStringObject *self)
Py_ssize_t i, n = PyString_GET_SIZE(self);
PyObject *newobj;
- newobj = PyString_FromStringAndSize(NULL, n);
+ newobj = PyString_FromStringAndSizeSameMerits(NULL, n,
+ PyString_GET_MERITS(self));
+
if (!newobj)
return NULL;
@@ -2051,7 +2519,9 @@ string_title(PyStringObject *self)
int previous_is_cased = 0;
PyObject *newobj;
- newobj = PyString_FromStringAndSize(NULL, n);
+ newobj = PyString_FromStringAndSizeSameMerits(NULL, n,
+ PyString_GET_MERITS(self));
+
if (newobj == NULL)
return NULL;
s_new = PyString_AsString(newobj);
@@ -2085,7 +2555,9 @@ string_capitalize(PyStringObject *self)
Py_ssize_t i, n = PyString_GET_SIZE(self);
PyObject *newobj;
- newobj = PyString_FromStringAndSize(NULL, n);
+ newobj = PyString_FromStringAndSizeSameMerits(NULL, n,
+ PyString_GET_MERITS(self));
+
if (newobj == NULL)
return NULL;
s_new = PyString_AsString(newobj);
@@ -2164,7 +2636,8 @@ string_swapcase(PyStringObject *self)
Py_ssize_t i, n = PyString_GET_SIZE(self);
PyObject *newobj;
- newobj = PyString_FromStringAndSize(NULL, n);
+ newobj = PyString_FromStringAndSizeSameMerits(NULL, n,
+ PyString_GET_MERITS(self));
if (newobj == NULL)
return NULL;
s_new = PyString_AsString(newobj);
@@ -2206,14 +2679,19 @@ string_translate(PyStringObject *self, PyObject *args)
PyObject *result;
int trans_table[256];
PyObject *tableobj, *delobj = NULL;
+ PyTaintObject *taint = NULL;
if (!PyArg_UnpackTuple(args, "translate", 1, 2,
&tableobj, &delobj))
return NULL;
+ taint = PyString_GET_MERITS(self);
+ Py_XINCREF(taint);
+
if (PyString_Check(tableobj)) {
table = PyString_AS_STRING(tableobj);
tablen = PyString_GET_SIZE(tableobj);
+ PyTaint_PropagateTo(&taint, PyString_GET_MERITS(tableobj));
}
else if (tableobj == Py_None) {
table = NULL;
@@ -2245,6 +2723,7 @@ string_translate(PyStringObject *self, PyObject *args)
if (PyString_Check(delobj)) {
del_table = PyString_AS_STRING(delobj);
dellen = PyString_GET_SIZE(delobj);
+ PyTaint_PropagateTo(&taint, PyString_GET_MERITS(delobj));
}
#ifdef Py_USING_UNICODE
else if (PyUnicode_Check(delobj)) {
@@ -2264,7 +2743,7 @@ string_translate(PyStringObject *self, PyObject *args)
inlen = PyString_GET_SIZE(input_obj);
result = PyString_FromStringAndSize((char *)NULL, inlen);
if (result == NULL)
- return NULL;
+ goto error;
output_start = output = PyString_AsString(result);
input = PyString_AS_STRING(input_obj);
@@ -2276,10 +2755,8 @@ string_translate(PyStringObject *self, PyObject *args)
changed = 1;
}
if (changed || !PyString_CheckExact(input_obj))
- return result;
- Py_DECREF(result);
- Py_INCREF(input_obj);
- return input_obj;
+ goto done;
+ goto nochanges; //return tainted input_obj
}
if (table == NULL) {
@@ -2301,14 +2778,26 @@ string_translate(PyStringObject *self, PyObject *args)
changed = 1;
}
if (!changed && PyString_CheckExact(input_obj)) {
- Py_DECREF(result);
- Py_INCREF(input_obj);
- return input_obj;
+ goto nochanges; //return tainted input_obj
}
/* Fix the size of the resulting string */
if (inlen > 0 && _PyString_Resize(&result, output - output_start))
- return NULL;
+ goto error;
+ goto done;
+
+ nochanges: //return tainted input_obj
+ Py_DECREF(result);
+ Py_INCREF(input_obj);
+ result = input_obj;
+
+ done:
+ result = PyString_AssignTaint((PyStringObject*)result, taint);
+ Py_XDECREF(taint);
return result;
+
+ error:
+ Py_XDECREF(taint);
+ return NULL;
}
@@ -2820,40 +3309,65 @@ static PyObject *
string_replace(PyStringObject *self, PyObject *args)
{
Py_ssize_t count = -1;
- PyObject *from, *to;
+ PyObject *from, *to, *result;
const char *from_s, *to_s;
Py_ssize_t from_len, to_len;
+ PyTaintObject *taint;
if (!PyArg_ParseTuple(args, "OO|n:replace", &from, &to, &count))
return NULL;
+ taint = PyString_GET_MERITS(self);
+
if (PyString_Check(from)) {
from_s = PyString_AS_STRING(from);
from_len = PyString_GET_SIZE(from);
+ if (PyTaint_PropagateTo(&taint, PyString_GET_MERITS(from)) == -1) {
+ goto onError;
+ }
}
#ifdef Py_USING_UNICODE
- if (PyUnicode_Check(from))
+ if (PyUnicode_Check(from)) {
+ Py_XDECREF(taint);
return PyUnicode_Replace((PyObject *)self,
from, to, count);
+ }
#endif
- else if (PyObject_AsCharBuffer(from, &from_s, &from_len))
- return NULL;
+ else if (PyObject_AsCharBuffer(from, &from_s, &from_len)) {
+ goto onError;
+ }
if (PyString_Check(to)) {
to_s = PyString_AS_STRING(to);
to_len = PyString_GET_SIZE(to);
+ if (PyTaint_PropagateTo(&taint, PyString_GET_MERITS(to)) == -1) {
+ goto onError;
+ }
}
#ifdef Py_USING_UNICODE
- else if (PyUnicode_Check(to))
+ else if (PyUnicode_Check(to)) {
+ Py_XDECREF(taint);
return PyUnicode_Replace((PyObject *)self,
from, to, count);
+ }
#endif
- else if (PyObject_AsCharBuffer(to, &to_s, &to_len))
- return NULL;
+ else if (PyObject_AsCharBuffer(to, &to_s, &to_len)) {
+ goto onError;
+ }
- return (PyObject *)replace((PyStringObject *) self,
+ result = (PyObject *)replace((PyStringObject *) self,
from_s, from_len,
to_s, to_len, count);
+ if (result == NULL)
+ goto onError;
+
+ result = PyString_AssignTaint((PyStringObject*)result, taint);
+ Py_XDECREF(taint);
+ return result;
+
+ onError:
+ Py_XDECREF(taint);
+ return NULL;
}
/** End DALKE **/
@@ -3117,7 +3631,9 @@ string_expandtabs(PyStringObject *self, PyObject *args)
goto overflow1;
/* Second pass: create output string and fill it */
- u = PyString_FromStringAndSize(NULL, i + j);
+ u = PyString_FromStringAndSizeSameMerits(NULL, i + j,
+ PyString_GET_MERITS(self));
+
if (!u)
return NULL;
@@ -3170,8 +3686,7 @@ pad(PyStringObject *self, Py_ssize_t left, Py_ssize_t right, char fill)
return (PyObject *)self;
}
- u = PyString_FromStringAndSize(NULL,
- left + PyString_GET_SIZE(self) + right);
+ u = PyString_FromStringAndSize(NULL, left + PyString_GET_SIZE(self) + right);
if (u) {
if (left)
memset(PyString_AS_STRING(u), fill, left);
@@ -3186,27 +3701,102 @@ pad(PyStringObject *self, Py_ssize_t left, Py_ssize_t right, char fill)
return u;
}
-PyDoc_STRVAR(ljust__doc__,
-"S.ljust(width[, fillchar]) -> string\n"
-"\n"
-"Return S left-justified in a string of length width. Padding is\n"
-"done using the specified fill character (default is a space).");
+#define PAD_CENTER 0
+#define PAD_LJUST 1
+#define PAD_RJUST 2
-static PyObject *
-string_ljust(PyStringObject *self, PyObject *args)
+Py_LOCAL_INLINE(PyObject *)
+do_padding(PyStringObject *self, PyObject *args, int padtype)
{
+ Py_ssize_t left, right, marg;
Py_ssize_t width;
+ PyObject *result = NULL;
+ PyTaintObject *taintobj = NULL;
+ PyObject* padding = NULL;
char fillchar = ' ';
- if (!PyArg_ParseTuple(args, "n|c:ljust", &width, &fillchar))
+ if (!PyArg_ParseTuple(args, "n|O:ljust", &width, &padding))
return NULL;
+ if (padding != NULL) {
+ // string_ljust, string_rjust, string_center (ie. all methods using
+ // do_padding) originally accepted a char instead of object as their
+ // last argument (ie. the format string was "n|c"). The code below
+ // mimicks original error they raised when when a non-character
+ // argument was passed
+
+ if (!PyString_Check(padding))
+ return PyErr_Format(PyExc_TypeError,
+ "must be char, not %s",
+ Py_TYPE(padding)->tp_name);
+ if (PyString_GET_SIZE(padding) != 1)
+ return PyErr_Format(PyExc_TypeError, "must be char, not str");
+ fillchar = PyString_AS_STRING(padding)[0];
+ if (PyTaint_PropagationResult(&taintobj,
+ PyString_GET_MERITS(self),
+ PyString_GET_MERITS(padding)) == -1)
+ return NULL;
+ } else {
+ taintobj = PyString_GET_MERITS(self);
+ }
+
+
if (PyString_GET_SIZE(self) >= width && PyString_CheckExact(self)) {
- Py_INCREF(self);
- return (PyObject*) self;
+ if (PyString_GET_MERITS(self) == taintobj) {
+ Py_INCREF(self);
+ result = (PyObject*)self;
+ goto done;
+ }
+ result = PyString_FromStringAndSizeSameMerits(PyString_AS_STRING(self),
+ PyString_GET_SIZE(self),
+ taintobj);
+ // no error check because either way cleanup is the same; also
+ // in case of failure, result is NULL
+ goto done;
+ } else {
+ switch (padtype) {
+ case PAD_CENTER:
+ marg = width - PyString_GET_SIZE(self);
+ left = marg / 2 + (marg & width & 1);
+ right = marg - left;
+ break;
+ case PAD_LJUST:
+ left = 0;
+ right = width - PyString_GET_SIZE(self);
+ break;
+ case PAD_RJUST:
+ left = width - PyString_GET_SIZE(self);
+ right = 0;
+ break;
+ default:
+ Py_FatalError("Unknown padding type passed to do_padding");
+ return NULL;
+ }
+ result = pad(self, left, right, fillchar);
+ if (result == NULL)
+ goto done;
+
+ result = (PyObject*)PyString_AssignTaint((PyStringObject*)result,
+ taintobj);
}
- return pad(self, 0, width - PyString_GET_SIZE(self), fillchar);
+ done:
+ if (padding != NULL)
+ Py_XDECREF(taintobj);
+ // when padding == NULL, taintobj is borrowed from self, so no decref
+ return result;
+}
+
+PyDoc_STRVAR(ljust__doc__,
+"S.ljust(width[, fillchar]) -> string\n"
+"\n"
+"Return S left-justified in a string of length width. Padding is\n"
+"done using the specified fill character (default is a space).");
+
+static PyObject *
+string_ljust(PyStringObject *self, PyObject *args)
+{
+ return (PyObject*)do_padding(self, args, PAD_LJUST);
}
@@ -3219,18 +3809,7 @@ PyDoc_STRVAR(rjust__doc__,
static PyObject *
string_rjust(PyStringObject *self, PyObject *args)
{
- Py_ssize_t width;
- char fillchar = ' ';
-
- if (!PyArg_ParseTuple(args, "n|c:rjust", &width, &fillchar))
- return NULL;
-
- if (PyString_GET_SIZE(self) >= width && PyString_CheckExact(self)) {
- Py_INCREF(self);
- return (PyObject*) self;
- }
-
- return pad(self, width - PyString_GET_SIZE(self), 0, fillchar);
+ return do_padding(self, args, PAD_RJUST);
}
@@ -3243,22 +3822,7 @@ PyDoc_STRVAR(center__doc__,
static PyObject *
string_center(PyStringObject *self, PyObject *args)
{
- Py_ssize_t marg, left;
- Py_ssize_t width;
- char fillchar = ' ';
-
- if (!PyArg_ParseTuple(args, "n|c:center", &width, &fillchar))
- return NULL;
-
- if (PyString_GET_SIZE(self) >= width && PyString_CheckExact(self)) {
- Py_INCREF(self);
- return (PyObject*) self;
- }
-
- marg = width - PyString_GET_SIZE(self);
- left = marg / 2 + (marg & width & 1);
-
- return pad(self, left, marg - left, fillchar);
+ return (PyObject*)do_padding(self, args, PAD_CENTER);
}
PyDoc_STRVAR(zfill__doc__,
@@ -3271,7 +3835,7 @@ static PyObject *
string_zfill(PyStringObject *self, PyObject *args)
{
Py_ssize_t fill;
- PyObject *s;
+ PyObject *s, *r;
char *p;
Py_ssize_t width;
@@ -3283,11 +3847,13 @@ string_zfill(PyStringObject *self, PyObject *args)
Py_INCREF(self);
return (PyObject*) self;
}
- else
- return PyString_FromStringAndSize(
- PyString_AS_STRING(self),
- PyString_GET_SIZE(self)
- );
+ s = PyString_FromStringAndSizeSameMerits(PyString_AS_STRING(self),
+ PyString_GET_SIZE(self),
+ PyString_GET_MERITS(self));
+
+ if (s == NULL)
+ return NULL;
+ return s;
}
fill = width - PyString_GET_SIZE(self);
@@ -3304,7 +3870,15 @@ string_zfill(PyStringObject *self, PyObject *args)
p[fill] = '0';
}
- return (PyObject*) s;
+ if (PyString_CHECK_TAINTED(self)) {
+ r = PyString_FromStringAndSizeSameMerits(PyString_AS_STRING(s),
+ PyString_GET_SIZE(s),
+ PyString_GET_MERITS(self));
+ Py_DECREF(s);
+ return r;
+ } else {
+ return s;
+ }
}
PyDoc_STRVAR(isspace__doc__,
@@ -3559,14 +4133,26 @@ static PyObject*
string_splitlines(PyStringObject *self, PyObject *args)
{
int keepends = 0;
+ PyObject *result;
if (!PyArg_ParseTuple(args, "|i:splitlines", &keepends))
return NULL;
- return stringlib_splitlines(
+ result = stringlib_splitlines(
(PyObject*) self, PyString_AS_STRING(self), PyString_GET_SIZE(self),
keepends
);
+
+ if (result == NULL)
+ return NULL;
+
+ if (_PyTaint_TaintStringListItems(result,
+ PyString_GET_MERITS(self)) == -1) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
}
PyDoc_STRVAR(sizeof__doc__,
@@ -3674,6 +4260,12 @@ string_methods[] = {
{"center", (PyCFunction)string_center, METH_VARARGS, center__doc__},
{"zfill", (PyCFunction)string_zfill, METH_VARARGS, zfill__doc__},
{"format", (PyCFunction) do_string_format, METH_VARARGS | METH_KEYWORDS, format__doc__},
+ {"taint", (PyCFunction)string_taint, METH_NOARGS, taint__doc__},
+ {"isclean", (PyCFunction)string_isclean, METH_VARARGS, isclean__doc__},
+ {"istainted", (PyCFunction)string_istainted, METH_NOARGS, istainted__doc__},
+ {"_cleanfor", (PyCFunction)string_cleanfor, METH_O, cleanfor__doc__},
+ {"_merits", (PyCFunction)string_listmerits, METH_NOARGS, listmerits__doc__},
+ {"_propagate", (PyCFunction)string_propagate, METH_O, propagate__doc__},
{"__format__", (PyCFunction) string__format__, METH_VARARGS, p_format__doc__},
{"_formatter_field_name_split", (PyCFunction) formatter_field_name_split, METH_NOARGS},
{"_formatter_parser", (PyCFunction) formatter_parser, METH_NOARGS},
@@ -4233,8 +4825,9 @@ PyString_Format(PyObject *format, PyObject *args)
Py_ssize_t reslen, rescnt, fmtcnt;
int args_owned = 0;
PyObject *result, *orig_args;
+ PyTaintObject *taint;
#ifdef Py_USING_UNICODE
- PyObject *v, *w;
+ PyObject *v = NULL, *w;
#endif
PyObject *dict = NULL;
if (format == NULL || !PyString_Check(format) || args == NULL) {
@@ -4246,6 +4839,9 @@ PyString_Format(PyObject *format, PyObject *args)
fmtcnt = PyString_GET_SIZE(format);
reslen = rescnt = fmtcnt + 100;
result = PyString_FromStringAndSize((char *)NULL, reslen);
+ taint = PyString_GET_MERITS(format);
+ Py_XINCREF(taint);
+
if (result == NULL)
return NULL;
res = PyString_AsString(result);
@@ -4473,6 +5069,10 @@ PyString_Format(PyObject *format, PyObject *args)
Py_DECREF(temp);
goto error;
}
+ if (PyTaint_PropagateTo(&taint, PyString_GET_MERITS(temp)) == -1) {
+ Py_DECREF(temp);
+ goto error;
+ }
pbuf = PyString_AS_STRING(temp);
len = PyString_GET_SIZE(temp);
if (prec >= 0 && len > prec)
@@ -4669,7 +5269,8 @@ PyString_Format(PyObject *format, PyObject *args)
}
if (_PyString_Resize(&result, reslen - rescnt))
return NULL;
- return result;
+
+ return PyString_AssignTaint((PyStringObject*)result, taint);
#ifdef Py_USING_UNICODE
unicode:
@@ -4709,9 +5310,18 @@ PyString_Format(PyObject *format, PyObject *args)
Py_DECREF(format);
if (v == NULL)
goto error;
+ /* Assign taint to what we have so far, so that concat won't mess up
+ taint propagation. */
+ result = (PyObject*)PyString_AssignTaint((PyStringObject*)result,
+ taint);
+
+ if (result == NULL)
+ goto error;
/* Paste what we have (result) to what the Unicode formatting
function returned (v) and return the result (or error) */
w = PyUnicode_Concat(result, v);
+
+ Py_XDECREF(taint);
Py_DECREF(result);
Py_DECREF(v);
Py_DECREF(args);
@@ -4719,7 +5329,9 @@ PyString_Format(PyObject *format, PyObject *args)
#endif /* Py_USING_UNICODE */
error:
- Py_DECREF(result);
+ Py_XDECREF(v);
+ Py_XDECREF(result);
+ Py_XDECREF(taint);
if (args_owned) {
Py_DECREF(args);
}
@@ -4737,6 +5349,9 @@ PyString_InternInPlace(PyObject **p)
it in the interned dict might do. */
if (!PyString_CheckExact(s))
return;
+ if (PyString_GET_MERITS(s) != NULL) {
+ Py_FatalError("PyString_InternInPlace: untainted strings only please!");
+ }
if (PyString_CHECK_INTERNED(s))
return;
if (interned == NULL) {
diff --git a/Objects/taintobject.c b/Objects/taintobject.c
new file mode 100644
index 0000000..6d4acc6
--- /dev/null
+++ b/Objects/taintobject.c
@@ -0,0 +1,336 @@
+#define PY_SSIZE_T_CLEAN
+
+#include "Python.h"
+#include <ctype.h>
+#include <stddef.h>
+
+PyTaintObject *
+PyTaint_EmptyMerits()
+{
+ return (PyTaintObject*)PyTuple_New(0);
+}
+
+PyTaintObject *
+_PyTaint_AddMerit(PyTaintObject *taint, PyObject *merit)
+{
+ PyObject *result;
+ Py_ssize_t n, i;
+ PyObject *item;
+ n = PyTuple_GET_SIZE(taint);
+ result = PyTuple_New(n + 1);
+ if (result == NULL)
+ return NULL;
+
+ for (i = 0; i < n; i++) {
+ item = PyTuple_GET_ITEM(taint, i);
+ Py_INCREF(item);
+ PyTuple_SET_ITEM(result, i, item);
+ }
+ Py_INCREF(merit);
+ PyTuple_SET_ITEM(result, n, merit);
+
+ return (PyTaintObject*)result;
+}
+
+int
+_PyTaint_TaintStringListItems(PyObject *target, PyTaintObject *source) {
+ if (PyTaint_IS_CLEAN(source))
+ return 1;
+ Py_ssize_t i, n;
+ PyObject *item, *old_item;
+ n = PyList_GET_SIZE(target);
+
+ for (i = 0; i < n; i++) {
+ item = PyList_GET_ITEM(target, i);
+ if (PyString_CHECK_INTERNED(item) ||
+ item->ob_refcnt > 1) {
+ old_item = item;
+ item = PyString_FromStringAndSizeSameMerits(
+ PyString_AS_STRING(item),
+ PyString_GET_SIZE(item),
+ source);
+ if (item == NULL)
+ return -1;
+ Py_DECREF(old_item);
+ PyList_SET_ITEM(target, i, item);
+ } else {
+ PyString_ASSIGN_MERITS(item, source);
+ }
+ }
+ return 1;
+}
+
+PyObject*
+_PyTaint_TaintStringTupleItems(PyObject *target, PyTaintObject *source) {
+ Py_ssize_t i, n;
+ PyObject *item, *old_item, *result;
+ n = PyTuple_GET_SIZE(target);
+
+ if (PyTaint_IS_CLEAN(source))
+ return target; // We assume that target is not tainted.
+
+ if (target->ob_refcnt == 1) {
+ result = target; // Taint in place
+ } else {
+ result = PyTuple_New(n);
+ if (result == NULL) {
+ goto done;
+ }
+ }
+
+ for (i = 0; i < n; i++) {
+ item = PyTuple_GET_ITEM(target, i);
+ if (PyString_CHECK_INTERNED(item) ||
+ item->ob_refcnt > 1) {
+ old_item = item;
+ item = PyString_FromStringAndSizeSameMerits(
+ PyString_AS_STRING(item),
+ PyString_GET_SIZE(item),
+ source);
+ if (item == NULL) {
+ result = NULL;
+ goto done;
+ }
+ if (target == result)
+ Py_DECREF(old_item);
+ PyTuple_SET_ITEM(result, i, item);
+ } else {
+ PyString_ASSIGN_MERITS(item, source);
+ }
+ }
+
+ done:
+ // steal reference
+ if (target != result)
+ Py_DECREF(target);
+
+ return result;
+}
+
+PyObject*
+_PyTaint_TaintUnicodeTupleItems(PyObject *target, PyTaintObject *source) {
+ Py_ssize_t i, n;
+ PyObject *item, *old_item, *result;
+ n = PyTuple_GET_SIZE(target);
+
+ if (PyTaint_IS_CLEAN(source))
+ return target; // We assume that target is not tainted.
+
+ if (target->ob_refcnt == 1) {
+ result = target; // Taint in place
+ } else {
+ result = PyTuple_New(n);
+ if (result == NULL) {
+ goto done;
+ }
+ }
+
+ for (i = 0; i < n; i++) {
+ item = PyTuple_GET_ITEM(target, i);
+ if (item->ob_refcnt > 1 ||
+ PyUnicode_IsShared((PyUnicodeObject*)item)) {
+ old_item = item;
+ item = PyUnicode_FromUnicodeSameMerits(
+ PyUnicode_AS_UNICODE(item),
+ PyUnicode_GET_SIZE(item),
+ source);
+ if (item == NULL) {
+ result = NULL;
+ goto done;
+ }
+ if (target == result)
+ Py_DECREF(old_item);
+ PyTuple_SET_ITEM(result, i, item);
+ } else {
+ PyUnicode_ASSIGN_MERITS(item, source);
+ }
+ }
+
+ done:
+ // steal reference
+ if (target != result)
+ Py_DECREF(target);
+
+ return result;
+}
+
+
+PyObject*
+PyTaint_AssignToObject(PyObject *obj, PyTaintObject* taint) {
+ if (PyString_Check(obj))
+ return PyString_AssignTaint((PyStringObject*)obj, taint);
+ if (PyUnicode_Check(obj))
+ return PyUnicode_AssignTaint((PyUnicodeObject*)obj, taint);
+
+ PyErr_Format(PyExc_TypeError,
+ "Attempting tainting of non-taintable object of type %.200s",
+ Py_TYPE(obj)->tp_name);
+ return NULL;
+}
+
+int
+PyTaint_IsTaintable(PyObject *obj) {
+ return PyString_Check(obj) || PyUnicode_Check(obj);
+}
+
+int
+_PyTaint_TaintUnicodeListItems(PyObject *target, PyTaintObject *source) {
+ if (PyTaint_IS_CLEAN(source))
+ return 1;
+ Py_ssize_t i, n;
+ PyObject *item, *old_item;
+ n = PyList_GET_SIZE(target);
+
+ for (i = 0; i < n; i++) {
+ item = PyList_GET_ITEM(target, i);
+ if (PyUnicode_IsShared((PyUnicodeObject*)item) ||
+ item->ob_refcnt > 1) {
+ old_item = item;
+ item = PyUnicode_FromUnicodeSameMerits(
+ PyUnicode_AS_UNICODE(item),
+ PyUnicode_GET_SIZE(item),
+ source);
+ if (item == NULL)
+ return -1;
+ Py_DECREF(old_item);
+ PyList_SET_ITEM(target, i, item);
+ } else {
+ PyUnicode_ASSIGN_MERITS(item, source);
+ }
+ }
+ return 1;
+}
+
+int
+PyTaint_PropagateTo(PyTaintObject **target,
+ PyTaintObject *source) {
+ PyTaintObject *result = NULL;
+ if (PyTaint_PropagationResult(&result, *target, source) == -1)
+ return -1;
+ Py_XDECREF(*target);
+ *target = result;
+ return 1;
+}
+
+PyTaintObject*
+_PyTaint_GetFromObject(PyObject *obj) {
+ if (PyString_Check(obj)) {
+ Py_XINCREF(PyString_GET_MERITS(obj));
+ return PyString_GET_MERITS(obj);
+ }
+ if (PyUnicode_Check(obj)) {
+ Py_XINCREF(PyUnicode_GET_MERITS(obj));
+ return PyUnicode_GET_MERITS(obj);
+ }
+ Py_FatalError("Attempting to obtain taint from non-taintable object.");
+ // the line below is to surpress compiler warning
+ return NULL;
+}
+
+int
+PyTaint_PropagationResult(PyTaintObject **target,
+ PyTaintObject *a,
+ PyTaintObject *b)
+{
+ PyObject *new_merits = NULL;
+ register PyObject *src, *m;
+ // n is upper bound of new_merits size, j is its actual size at given
+ // moment
+ register Py_ssize_t i, n, j;
+ int contains;
+
+ // Both untainted
+ if (PyTaint_IS_CLEAN(a) && PyTaint_IS_CLEAN(b)) {
+ goto done;
+ }
+
+ j = 0;
+
+ // Both tainted - intersect two merits list
+ if (!PyTaint_IS_CLEAN(a) && !PyTaint_IS_CLEAN(b)) {
+ n = PyTuple_GET_SIZE(a) > PyTuple_GET_SIZE(b) ?
+ PyTuple_GET_SIZE(a) : PyTuple_GET_SIZE(b);
+ new_merits = PyTuple_New(n);
+ if (new_merits == NULL)
+ return -1;
+
+ for (i = 0; i < PyTuple_GET_SIZE(a); i++) {
+ m = PyTuple_GET_ITEM(a, i);
+ contains = PySequence_Contains((PyObject*)b, m);
+ if (contains == -1)
+ goto onError;
+ if (contains == 1) {
+ if (PyMerit_FULL_PROPAGATION(m) || \
+ PyMerit_PARTIAL_PROPAGATION(m)) {
+ Py_INCREF(m);
+ PyTuple_SET_ITEM(new_merits, j, m);
+ j += 1;
+ } else if(!PyMerit_NONE_PROPAGATION(m)) {
+ PyErr_SetString(PyExc_ValueError,
+ "Invalid taint propagation strategy.");
+ goto onError;
+ }
+ }
+ }
+ // _PyTupleResize will set new_merits to NULL on failure, so instead of
+ // goto onError just return -1
+ if (_PyTuple_Resize(&new_merits, j) != 0)
+ return -1;
+
+ goto done;
+ }
+ // One untainted, other tainted
+ if (!PyTaint_IS_CLEAN(a)) {
+ src = (PyObject*)a;
+ } else { // ie. !PyTaint_IS_CLEAN(b) is true
+ src = (PyObject*)b;
+ }
+ n = PyTuple_GET_SIZE(src);
+ new_merits = PyTuple_New(n);
+ if (new_merits == NULL)
+ return -1;
+
+ for (i = 0; i < PyTuple_GET_SIZE(src); i++) {
+ m = PyTuple_GET_ITEM(src, i);
+ if (PyMerit_FULL_PROPAGATION(m)) {
+ Py_INCREF(m);
+ PyTuple_SET_ITEM(new_merits, j, m);
+ j += 1;
+ } else if (!PyMerit_NONE_PROPAGATION(m) && \
+ !PyMerit_PARTIAL_PROPAGATION(m)) {
+ PyErr_SetString(PyExc_ValueError,
+ "Invalid taint propagation strategy.");
+ }
+ }
+ // _PyTupleResize will set new_merits to NULL on failure, so instead of
+ // goto onError just return
+ if (_PyTuple_Resize(&new_merits, j) != 0)
+ return -1;
+
+ done:
+ Py_XDECREF(*target);
+ *target = (PyTaintObject*)new_merits;
+ return 1;
+
+ onError:
+ Py_DECREF(new_merits);
+ return -1;
+}
+
+int
+_PyTaint_ValidMerit(PyObject *merit) {
+ if (!PyObject_HasAttrString(merit, "propagation")) {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid merit object passed.");
+ return -1;
+ }
+
+ if (!(PyMerit_FULL_PROPAGATION(merit) || \
+ PyMerit_NONE_PROPAGATION(merit) || \
+ PyMerit_PARTIAL_PROPAGATION(merit))) {
+ PyErr_SetString(PyExc_TypeError,
+ "Merit object has invalid propagation strategy.");
+ return -1;
+ }
+ return 1;
+}
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 0ead06f..611a3da 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -270,10 +270,7 @@ int unicode_resize(register PyUnicodeObject *unicode,
objects) in-place is not allowed. Use PyUnicode_Resize()
instead ! */
- if (unicode == unicode_empty ||
- (unicode->length == 1 &&
- unicode->str[0] < 256U &&
- unicode_latin1[unicode->str[0]] == unicode)) {
+ if (PyUnicode_IS_SHARED(unicode)) {
PyErr_SetString(PyExc_SystemError,
"can't resize shared unicode objects");
return -1;
@@ -374,6 +371,7 @@ PyUnicodeObject *_PyUnicode_New(Py_ssize_t length)
unicode->length = length;
unicode->hash = -1;
unicode->defenc = NULL;
+ unicode->merits = NULL;
return unicode;
onError:
@@ -387,6 +385,7 @@ PyUnicodeObject *_PyUnicode_New(Py_ssize_t length)
static
void unicode_dealloc(register PyUnicodeObject *unicode)
{
+ Py_XDECREF(PyUnicode_GET_MERITS(unicode));
if (PyUnicode_CheckExact(unicode) &&
numfree < PyUnicode_MAXFREELIST) {
/* Keep-Alive optimization */
@@ -451,6 +450,65 @@ int PyUnicode_Resize(PyObject **unicode, Py_ssize_t length)
return _PyUnicode_Resize((PyUnicodeObject **)unicode, length);
}
+PyObject *PyUnicode_FromUnicodeNoSharing(const Py_UNICODE *u,
+ Py_ssize_t size)
+{
+ PyUnicodeObject *unicode;
+ if (size == 0) {
+ size_t new_size;
+ unicode = PyObject_New(PyUnicodeObject, &PyUnicode_Type);
+ if (unicode == NULL)
+ return NULL;
+ new_size = sizeof(Py_UNICODE) * ((size_t)size + 1);
+ unicode->str = (Py_UNICODE*) PyObject_MALLOC(new_size);
+
+ if (!unicode->str) {
+ PyErr_NoMemory();
+ _Py_DEC_REFTOTAL;
+ _Py_ForgetReference((PyObject *)unicode);
+ PyObject_Del(unicode);
+ return NULL;
+ }
+ unicode->str[0] = 0;
+ unicode->str[size] = 0;
+ unicode->length = size;
+ unicode->hash = -1;
+ unicode->defenc = NULL;
+ unicode->merits = NULL;
+ return (PyObject*)unicode;
+ }
+ unicode = _PyUnicode_New(size);
+ if (unicode == NULL)
+ return NULL;
+
+ if (u != NULL)
+ Py_UNICODE_COPY(unicode->str, u, size);
+
+ return (PyObject*)unicode;
+}
+
+PyObject *PyUnicode_FromUnicodeSameMerits(const Py_UNICODE *u,
+ Py_ssize_t size,
+ PyTaintObject *merits)
+{
+ PyObject *unicode;
+ if (merits == NULL) {
+ unicode = PyUnicode_FromUnicode(u, size);
+ } else {
+ unicode = PyUnicode_FromUnicodeNoSharing(u, size);
+ }
+
+ if (unicode == NULL)
+ return NULL;
+
+ if (merits != NULL) {
+ ((PyUnicodeObject*)unicode)->merits = merits;
+ Py_INCREF(merits);
+ }
+
+ return (PyObject*)unicode;
+}
+
PyObject *PyUnicode_FromUnicode(const Py_UNICODE *u,
Py_ssize_t size)
{
@@ -466,7 +524,7 @@ PyObject *PyUnicode_FromUnicode(const Py_UNICODE *u,
/* Single character Unicode objects in the Latin-1 range are
shared when using this constructor */
- if (size == 1 && *u < 256) {
+ if (size == 1 && PyUnicode_IS_LATIN_CHAR(u)) {
unicode = unicode_latin1[*u];
if (!unicode) {
unicode = _PyUnicode_New(1);
@@ -636,6 +694,59 @@ PyObject *PyUnicode_FromWideChar(register const wchar_t *w,
#endif /* CONVERT_WCHAR_TO_SURROGATES */
#undef CONVERT_WCHAR_TO_SURROGATES
+PyObject*
+PyUnicode_AssignTaint(PyUnicodeObject *u, PyTaintObject *taint) {
+ PyObject *result;
+
+ if (!PyUnicode_Check(u)) {
+ PyErr_Format(PyExc_TypeError,
+ "Trying to assign taint to object of type %.200s\
+ (expected unicode).", Py_TYPE(u)->tp_name);
+ return NULL;
+ }
+
+ if (PyUnicode_IS_SHARED(u) || u->ob_refcnt > 1) {
+ result = PyUnicode_FromUnicodeSameMerits(PyUnicode_AS_UNICODE(u),
+ PyUnicode_GET_SIZE(u),
+ taint);
+ Py_DECREF(u);
+ } else {
+ result = (PyObject*)u;
+ Py_XDECREF(PyUnicode_GET_MERITS(u));
+ PyUnicode_ASSIGN_MERITS(u, taint);
+ }
+
+ return result;
+}
+
+
+void
+_PyUnicode_CopyTaint(PyUnicodeObject *target,
+ PyUnicodeObject *source)
+{
+ // check if it is a shared string?
+ if (PyUnicode_IS_SHARED(target)) {
+ Py_FatalError("Attempted tainting of a shared unicode object");
+ }
+
+ Py_CLEAR(target->merits);
+ if (source->merits != NULL) {
+ target->merits = source->merits;
+ Py_INCREF(target->merits);
+ }
+}
+
+int
+PyUnicode_IsShared(PyUnicodeObject *u) {
+ if (!PyUnicode_Check(u)) {
+ PyErr_SetString(PyExc_TypeError,
+ "Checking a non-unicode object for unicode sharing");
+ return -1;
+ }
+ return u == unicode_empty ||
+ (u->length == 1 && PyUnicode_IS_LATIN_CHAR(u->str) && \
+ unicode_latin1[u->str[0]] == u);
+}
static void
makefmt(char *fmt, int longflag, int size_tflag, int zeropad, int width, int precision, char c)
@@ -1103,8 +1214,9 @@ PyObject *PyUnicode_FromObject(register PyObject *obj)
if (PyUnicode_Check(obj)) {
/* For a Unicode subtype that's not a Unicode object,
return a true Unicode object with the same data. */
- return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(obj),
- PyUnicode_GET_SIZE(obj));
+ return PyUnicode_FromUnicodeSameMerits(PyUnicode_AS_UNICODE(obj),
+ PyUnicode_GET_SIZE(obj),
+ PyUnicode_GET_MERITS(obj));
}
return PyUnicode_FromEncodedObject(obj, NULL, "strict");
}
@@ -1171,10 +1283,17 @@ PyObject *PyUnicode_FromEncodedObject(register PyObject *obj,
}
/* Convert to Unicode */
- if (len == 0)
- _Py_RETURN_UNICODE_EMPTY();
+ if (len == 0) {
+ Py_INCREF(unicode_empty);
+ v = (PyObject *)unicode_empty;
+ }
+ else
+ v = PyUnicode_Decode(s, len, encoding, errors);
+
+ if (v != NULL && PyString_Check(obj))
+ v = PyUnicode_AssignTaint((PyUnicodeObject*)v,
+ PyString_GET_MERITS(obj));
- v = PyUnicode_Decode(s, len, encoding, errors);
return v;
onError:
@@ -1243,7 +1362,8 @@ PyObject *PyUnicode_AsDecodedObject(PyObject *unicode,
v = PyCodec_Decode(unicode, encoding, errors);
if (v == NULL)
goto onError;
- return v;
+
+ return PyTaint_AssignToObject(v, PyUnicode_GET_MERITS(unicode));
onError:
return NULL;
@@ -1282,7 +1402,8 @@ PyObject *PyUnicode_AsEncodedObject(PyObject *unicode,
v = PyCodec_Encode(unicode, encoding, errors);
if (v == NULL)
goto onError;
- return v;
+
+ return PyTaint_AssignToObject(v, PyUnicode_GET_MERITS(unicode));
onError:
return NULL;
@@ -1304,16 +1425,24 @@ PyObject *PyUnicode_AsEncodedString(PyObject *unicode,
/* Shortcuts for common default encodings */
if (errors == NULL) {
- if (strcmp(encoding, "utf-8") == 0)
- return PyUnicode_AsUTF8String(unicode);
- else if (strcmp(encoding, "latin-1") == 0)
- return PyUnicode_AsLatin1String(unicode);
+ if (strcmp(encoding, "utf-8") == 0) {
+ v = PyUnicode_AsUTF8String(unicode);
+ goto done;
+ }
+ else if (strcmp(encoding, "latin-1") == 0) {
+ v = PyUnicode_AsLatin1String(unicode);
+ goto done;
+ }
#if defined(MS_WINDOWS) && defined(HAVE_USABLE_WCHAR_T)
- else if (strcmp(encoding, "mbcs") == 0)
- return PyUnicode_AsMBCSString(unicode);
+ else if (strcmp(encoding, "mbcs") == 0) {
+ v = PyUnicode_AsMBCSString(unicode);
+ goto done;
+ }
#endif
- else if (strcmp(encoding, "ascii") == 0)
- return PyUnicode_AsASCIIString(unicode);
+ else if (strcmp(encoding, "ascii") == 0) {
+ v = PyUnicode_AsASCIIString(unicode);
+ goto done;
+ }
}
/* Encode via the codec registry */
@@ -1327,7 +1456,14 @@ PyObject *PyUnicode_AsEncodedString(PyObject *unicode,
Py_DECREF(v);
goto onError;
}
- return v;
+ return PyString_AssignTaint((PyStringObject*)v,
+ PyUnicode_GET_MERITS(unicode));
+
+ done:
+ if (v == NULL)
+ goto onError;
+ return PyString_AssignTaint((PyStringObject*)v,
+ PyUnicode_GET_MERITS(unicode));
onError:
return NULL;
@@ -4961,7 +5097,8 @@ int charmaptranslate_output(const Py_UNICODE *startinp, const Py_UNICODE *curinp
PyObject *PyUnicode_TranslateCharmap(const Py_UNICODE *p,
Py_ssize_t size,
PyObject *mapping,
- const char *errors)
+ const char *errors,
+ PyTaintObject *taint)
{
/* output object */
PyObject *res = NULL;
@@ -5002,8 +5139,12 @@ PyObject *PyUnicode_TranslateCharmap(const Py_UNICODE *p,
goto onError;
}
Py_XDECREF(x);
- if (x!=Py_None) /* it worked => adjust input pointer */
+ if (x!=Py_None) { /* it worked => adjust input pointer */
++p;
+ if (x != NULL && PyUnicode_Check(x)) {
+ PyTaint_PropagateTo(&taint, PyUnicode_GET_MERITS(x));
+ }
+ }
else { /* untranslatable character */
PyObject *repunicode = NULL; /* initialize to prevent gcc warning */
Py_ssize_t repsize;
@@ -5091,12 +5232,15 @@ PyObject *PyUnicode_TranslateCharmap(const Py_UNICODE *p,
}
Py_XDECREF(exc);
Py_XDECREF(errorHandler);
+ res = PyUnicode_AssignTaint((PyUnicodeObject*)res, taint);
+ Py_XDECREF(taint);
return res;
onError:
Py_XDECREF(res);
Py_XDECREF(exc);
Py_XDECREF(errorHandler);
+ Py_XDECREF(taint);
return NULL;
}
@@ -5109,10 +5253,12 @@ PyObject *PyUnicode_Translate(PyObject *str,
str = PyUnicode_FromObject(str);
if (str == NULL)
goto onError;
+ Py_XINCREF(PyUnicode_GET_MERITS(str)); // TranslateCharmap will steal them
result = PyUnicode_TranslateCharmap(PyUnicode_AS_UNICODE(str),
PyUnicode_GET_SIZE(str),
mapping,
- errors);
+ errors,
+ PyUnicode_GET_MERITS(str));
Py_DECREF(str);
return result;
@@ -5403,7 +5549,10 @@ PyObject *fixup(PyUnicodeObject *self,
PyUnicodeObject *u;
- u = (PyUnicodeObject*) PyUnicode_FromUnicode(NULL, self->length);
+ u = (PyUnicodeObject*)PyUnicode_FromUnicodeSameMerits(NULL,
+ self->length,
+ self->merits);
+
if (u == NULL)
return NULL;
@@ -5560,6 +5709,7 @@ PyUnicode_Join(PyObject *separator, PyObject *seq)
Py_ssize_t seqlen; /* len(fseq) -- number of items in sequence */
PyObject *item;
Py_ssize_t i;
+ PyTaintObject *taint = NULL;
fseq = PySequence_Fast(seq, "");
if (fseq == NULL) {
@@ -5574,17 +5724,27 @@ PyUnicode_Join(PyObject *separator, PyObject *seq)
* is invariant.
*/
seqlen = PySequence_Fast_GET_SIZE(fseq);
+ taint = _PyTaint_GetFromObject(separator);
/* If empty sequence, return u"". */
if (seqlen == 0) {
- res = _PyUnicode_New(0); /* empty sequence; return u"" */
+ res = (PyUnicodeObject*)PyUnicode_FromUnicodeSameMerits(NULL, 0, taint);
goto Done;
}
/* If singleton sequence with an exact Unicode, return that. */
if (seqlen == 1) {
item = PySequence_Fast_GET_ITEM(fseq, 0);
if (PyUnicode_CheckExact(item)) {
- Py_INCREF(item);
- res = (PyUnicodeObject *)item;
+ if (PyTaint_PropagateTo(&taint, PyUnicode_GET_MERITS(item)) == -1)
+ goto onError;
+ if (PyTaint_IS_CLEAN(taint)) {
+ Py_INCREF(item);
+ res = (PyUnicodeObject *)item;
+ } else {
+ res = (PyUnicodeObject *)PyUnicode_FromUnicodeSameMerits(
+ PyUnicode_AS_UNICODE(item),
+ PyUnicode_GET_SIZE(item),
+ taint);
+ }
goto Done;
}
}
@@ -5666,6 +5826,13 @@ PyUnicode_Join(PyObject *separator, PyObject *seq)
Py_UNICODE_COPY(res_p, sep, seplen);
res_p += seplen;
}
+
+ /* Propagate taint from item */
+ if (PyTaint_PropagateTo(&taint, PyUnicode_GET_MERITS(item)) == -1) {
+ Py_DECREF(item);
+ goto onError;
+ }
+
Py_DECREF(item);
res_used = new_res_used;
}
@@ -5676,9 +5843,12 @@ PyUnicode_Join(PyObject *separator, PyObject *seq)
if (_PyUnicode_Resize(&res, res_used) < 0)
goto onError;
+ res = (PyUnicodeObject*)PyUnicode_AssignTaint(res, taint);
+
Done:
Py_XDECREF(internal_separator);
Py_DECREF(fseq);
+ Py_XDECREF(taint);
return (PyObject *)res;
Overflow:
@@ -5690,6 +5860,7 @@ PyUnicode_Join(PyObject *separator, PyObject *seq)
onError:
Py_XDECREF(internal_separator);
Py_DECREF(fseq);
+ Py_XDECREF(taint);
Py_XDECREF(res);
return NULL;
}
@@ -5729,20 +5900,134 @@ PyUnicodeObject *pad(PyUnicodeObject *self,
return u;
}
+#define PAD_CENTER 0
+#define PAD_LJUST 1
+#define PAD_RJUST 2
+
+static
+PyUnicodeObject *do_padding(PyUnicodeObject *self,
+ PyObject *args,
+ int padtype) {
+ Py_ssize_t left, right, marg;
+ Py_ssize_t width;
+ PyObject *padding = Py_None;
+ Py_UNICODE fillchar = ' ';
+ PyObject *result = NULL;
+ PyTaintObject *taintobj = NULL;
+
+ if (!PyArg_ParseTuple(args, "n|O:center", &width, &padding))
+ return NULL;
+
+ if (padding == Py_None) {
+ fillchar = ' ';
+ taintobj = PyUnicode_GET_MERITS(self);
+ } else {
+ padding = PyUnicode_FromObject(padding);
+ if (PyUnicode_GET_SIZE(padding) != 1) {
+ PyErr_SetString(PyExc_TypeError,
+ "The fill character must be exactly one character long");
+ Py_DECREF(padding);
+ return 0;
+ }
+ fillchar = ((PyUnicodeObject*)padding)->str[0];
+ if (PyTaint_PropagationResult(&taintobj,
+ PyUnicode_GET_MERITS(self),
+ PyUnicode_GET_MERITS(padding)) == -1)
+ return NULL;
+ }
+
+ if (self->length >= width && PyUnicode_CheckExact(self)) {
+ if (PyString_GET_MERITS(self) == taintobj) {
+ Py_INCREF(self);
+ result = (PyObject*)self;
+ goto done;
+ }
+ result = PyUnicode_FromUnicodeSameMerits(PyUnicode_AS_UNICODE(self),
+ PyUnicode_GET_SIZE(self),
+ PyUnicode_GET_MERITS(self));
+
+ // no error check because either way cleanup is the same; also
+ // in case of failure, result is NULL
+ goto done;
+ } else {
+ switch (padtype) {
+ case PAD_CENTER:
+ marg = width - PyUnicode_GET_SIZE(self);
+ left = marg / 2 + (marg & width & 1);
+ right = marg - left;
+ break;
+ case PAD_LJUST:
+ left = 0;
+ right = width - PyUnicode_GET_SIZE(self);
+ break;
+ case PAD_RJUST:
+ left = width - PyUnicode_GET_SIZE(self);
+ right = 0;
+ break;
+ default:
+ Py_FatalError("Unknown padding type passed to do_padding");
+ return NULL;
+ }
+ result = (PyObject*)pad(self, left, right, fillchar);
+ if (result == NULL)
+ goto done;
+
+ if (!PyTaint_IS_CLEAN(taintobj)) {
+ // try to reuse result
+ if (PyUnicode_IS_SHARED(result)) {
+ ((PyUnicodeObject*)result)->merits = taintobj;
+ Py_XINCREF(taintobj);
+ } else {
+ PyObject *tmp;
+ tmp = PyUnicode_FromUnicodeSameMerits(
+ PyUnicode_AS_UNICODE(result),
+ PyUnicode_GET_SIZE(result),
+ taintobj);
+
+ // again, error check is unnecessary
+ Py_DECREF(result);
+ result = tmp;
+ }
+ }
+ }
+
+ done:
+ if (padding != Py_None)
+ Py_XDECREF(taintobj);
+ // when padding == NULL, taintobj is borrowed from self, so no decref
+ return (PyUnicodeObject*)result;
+
+}
+
PyObject *PyUnicode_Splitlines(PyObject *string, int keepends)
{
PyObject *list;
+ PyTaintObject *taint = NULL;
string = PyUnicode_FromObject(string);
if (string == NULL)
return NULL;
+ taint = PyUnicode_GET_MERITS(string);
+ Py_XINCREF(taint);
+
list = stringlib_splitlines(
(PyObject*) string, PyUnicode_AS_UNICODE(string),
PyUnicode_GET_SIZE(string), keepends);
Py_DECREF(string);
+ if (list == NULL)
+ goto onError;
+ if (_PyTaint_TaintUnicodeListItems(list, taint) == -1)
+ goto onError;
+
+ Py_XDECREF(taint);
return list;
+
+ onError:
+ Py_XDECREF(list);
+ Py_XDECREF(taint);
+ return NULL;
}
static
@@ -5750,19 +6035,43 @@ PyObject *split(PyUnicodeObject *self,
PyUnicodeObject *substring,
Py_ssize_t maxcount)
{
+ PyObject *result = NULL;
+ PyTaintObject *taint = NULL;
+
if (maxcount < 0)
maxcount = PY_SSIZE_T_MAX;
- if (substring == NULL)
- return stringlib_split_whitespace(
+ if (substring == NULL) {
+ taint = PyUnicode_GET_MERITS(self);
+ Py_XINCREF(taint);
+ result = stringlib_split_whitespace(
(PyObject*) self, self->str, self->length, maxcount
);
+ } else {
+ if (PyTaint_PropagationResult(&taint,
+ PyUnicode_GET_MERITS(self),
+ PyUnicode_GET_MERITS(substring)) == -1)
+ goto onError;
- return stringlib_split(
- (PyObject*) self, self->str, self->length,
- substring->str, substring->length,
- maxcount
- );
+ result = stringlib_split(
+ (PyObject*) self, self->str, self->length,
+ substring->str, substring->length,
+ maxcount
+ );
+ }
+ if (result == NULL)
+ goto onError;
+
+ if (_PyTaint_TaintUnicodeListItems(result, taint) == -1)
+ goto onError;
+
+ Py_XDECREF(taint);
+ return result;
+
+ onError:
+ Py_XDECREF(taint);
+ Py_XDECREF(result);
+ return NULL;
}
static
@@ -5770,19 +6079,42 @@ PyObject *rsplit(PyUnicodeObject *self,
PyUnicodeObject *substring,
Py_ssize_t maxcount)
{
+ PyObject *result = NULL;
+ PyTaintObject *taint = NULL;
+
if (maxcount < 0)
maxcount = PY_SSIZE_T_MAX;
- if (substring == NULL)
- return stringlib_rsplit_whitespace(
+ if (substring == NULL) {
+ taint = PyUnicode_GET_MERITS(self);
+ Py_XINCREF(taint);
+ result = stringlib_rsplit_whitespace(
(PyObject*) self, self->str, self->length, maxcount
);
+ } else {
+ if (PyTaint_PropagationResult(&taint,
+ PyUnicode_GET_MERITS(self),
+ PyUnicode_GET_MERITS(substring)) == -1)
+ goto onError;
- return stringlib_rsplit(
- (PyObject*) self, self->str, self->length,
- substring->str, substring->length,
- maxcount
- );
+ result = stringlib_rsplit(
+ (PyObject*) self, self->str, self->length,
+ substring->str, substring->length,
+ maxcount
+ );
+ }
+ if (result == NULL)
+ goto onError;
+ if (_PyTaint_TaintUnicodeListItems(result, taint) == -1)
+ goto onError;
+
+ Py_XDECREF(taint);
+ return result;
+
+ onError:
+ Py_XDECREF(taint);
+ Py_XDECREF(result);
+ return NULL;
}
static
@@ -5792,6 +6124,17 @@ PyObject *replace(PyUnicodeObject *self,
Py_ssize_t maxcount)
{
PyUnicodeObject *u;
+ PyTaintObject *taint = NULL;
+
+ if (PyTaint_PropagationResult(&taint, PyUnicode_GET_MERITS(self),
+ PyUnicode_GET_MERITS(str1)) == -1)
+ return NULL;
+ if (PyTaint_PropagateTo(&taint, PyUnicode_GET_MERITS(str2)) == -1) {
+ Py_XDECREF(taint);
+ return NULL;
+ }
+
+
if (maxcount < 0)
maxcount = PY_SSIZE_T_MAX;
@@ -5808,7 +6151,8 @@ PyObject *replace(PyUnicodeObject *self,
Py_UNICODE u1, u2;
if (!findchar(self->str, self->length, str1->str[0]))
goto nothing;
- u = (PyUnicodeObject*) PyUnicode_FromUnicode(NULL, self->length);
+ u = (PyUnicodeObject*) PyUnicode_FromUnicodeSameMerits(NULL,
+ self->length, taint);
if (!u)
return NULL;
Py_UNICODE_COPY(u->str, self->str, self->length);
@@ -5826,7 +6170,8 @@ PyObject *replace(PyUnicodeObject *self,
);
if (i < 0)
goto nothing;
- u = (PyUnicodeObject*) PyUnicode_FromUnicode(NULL, self->length);
+ u = (PyUnicodeObject*) PyUnicode_FromUnicodeSameMerits(NULL,
+ self->length, taint);
if (!u)
return NULL;
Py_UNICODE_COPY(u->str, self->str, self->length);
@@ -5874,7 +6219,8 @@ PyObject *replace(PyUnicodeObject *self,
return NULL;
}
}
- u = _PyUnicode_New(new_size);
+ u = (PyUnicodeObject*)PyUnicode_FromUnicodeSameMerits(NULL,
+ new_size, taint);
if (!u)
return NULL;
i = 0;
@@ -5918,11 +6264,11 @@ PyObject *replace(PyUnicodeObject *self,
nothing:
/* nothing to replace; return original string (when possible) */
- if (PyUnicode_CheckExact(self)) {
+ if (PyUnicode_CheckExact(self) && PyUnicode_GET_MERITS(self) == taint) {
Py_INCREF(self);
return (PyObject *) self;
}
- return PyUnicode_FromUnicode(self->str, self->length);
+ return PyUnicode_FromUnicodeSameMerits(self->str, self->length, taint);
}
/* --- Unicode Object Methods --------------------------------------------- */
@@ -6025,22 +6371,7 @@ done using the specified fill character (default is a space)");
static PyObject *
unicode_center(PyUnicodeObject *self, PyObject *args)
{
- Py_ssize_t marg, left;
- Py_ssize_t width;
- Py_UNICODE fillchar = ' ';
-
- if (!PyArg_ParseTuple(args, "n|O&:center", &width, convert_uc, &fillchar))
- return NULL;
-
- if (self->length >= width && PyUnicode_CheckExact(self)) {
- Py_INCREF(self);
- return (PyObject*) self;
- }
-
- marg = width - self->length;
- left = marg / 2 + (marg & width & 1);
-
- return (PyObject*) pad(self, left, marg - left, fillchar);
+ return (PyObject*)do_padding(self, args, PAD_CENTER);
}
#if 0
@@ -6273,18 +6604,28 @@ PyObject *PyUnicode_Concat(PyObject *left,
if (v == NULL)
goto onError;
- /* Shortcuts */
- if (v == unicode_empty) {
- Py_DECREF(v);
- return (PyObject *)u;
- }
- if (u == unicode_empty) {
- Py_DECREF(u);
- return (PyObject *)v;
+ if (PyUnicode_CHECK_TAINTED(u) || PyUnicode_CHECK_TAINTED(v)) {
+ w = (PyUnicodeObject*)PyUnicode_FromUnicodeNoSharing(NULL,
+ u->length + v->length);
+ if (PyTaint_PropagationResult(&PyUnicode_GET_MERITS(w),
+ PyUnicode_GET_MERITS(u),
+ PyUnicode_GET_MERITS(v)) == -1)
+ goto onError;
+ } else {
+ /* Shortcuts */
+ if (v == unicode_empty) {
+ Py_DECREF(v);
+ return (PyObject *)u;
+ }
+ if (u == unicode_empty) {
+ Py_DECREF(u);
+ return (PyObject *)v;
+ }
+
+ w = _PyUnicode_New(u->length + v->length);
}
/* Concat the two Unicode strings */
- w = _PyUnicode_New(u->length + v->length);
if (w == NULL)
goto onError;
Py_UNICODE_COPY(w->str, u->str, u->length);
@@ -6456,7 +6797,8 @@ unicode_expandtabs(PyUnicodeObject *self, PyObject *args)
goto overflow1;
/* Second pass: create output string and fill it */
- u = _PyUnicode_New(i + j);
+ u = (PyUnicodeObject*)PyUnicode_FromUnicodeSameMerits(NULL, i + j,
+ PyUnicode_GET_MERITS(self));
if (!u)
return NULL;
@@ -6534,7 +6876,8 @@ unicode_getitem(PyUnicodeObject *self, Py_ssize_t index)
return NULL;
}
- return (PyObject*) PyUnicode_FromUnicode(&self->str[index], 1);
+ return (PyObject*) PyUnicode_FromUnicodeSameMerits(
+ &self->str[index], 1, PyUnicode_GET_MERITS(self));
}
static long
@@ -6927,18 +7270,7 @@ done using the specified fill character (default is a space).");
static PyObject *
unicode_ljust(PyUnicodeObject *self, PyObject *args)
{
- Py_ssize_t width;
- Py_UNICODE fillchar = ' ';
-
- if (!PyArg_ParseTuple(args, "n|O&:ljust", &width, convert_uc, &fillchar))
- return NULL;
-
- if (self->length >= width && PyUnicode_CheckExact(self)) {
- Py_INCREF(self);
- return (PyObject*) self;
- }
-
- return (PyObject*) pad(self, 0, width - self->length, fillchar);
+ return (PyObject*)do_padding(self, args, PAD_LJUST);
}
PyDoc_STRVAR(lower__doc__,
@@ -6970,6 +7302,10 @@ _PyUnicode_XStrip(PyUnicodeObject *self, int striptype, PyObject *sepobj)
Py_UNICODE *sep = PyUnicode_AS_UNICODE(sepobj);
Py_ssize_t seplen = PyUnicode_GET_SIZE(sepobj);
Py_ssize_t i, j;
+ PyTaintObject *taint = NULL;
+ if (PyTaint_PropagationResult(&taint, PyUnicode_GET_MERITS(self),
+ PyUnicode_GET_MERITS(sepobj)) == -1)
+ return NULL;
BLOOM_MASK sepmask = make_bloom_mask(sep, seplen);
@@ -6988,12 +7324,13 @@ _PyUnicode_XStrip(PyUnicodeObject *self, int striptype, PyObject *sepobj)
j++;
}
- if (i == 0 && j == len && PyUnicode_CheckExact(self)) {
+ if (i == 0 && j == len && PyUnicode_CheckExact(self) &&
+ PyUnicode_GET_MERITS(self) == taint) {
Py_INCREF(self);
return (PyObject*)self;
}
else
- return PyUnicode_FromUnicode(s+i, j-i);
+ return PyUnicode_FromUnicodeSameMerits(s+i, j-i, taint);
}
@@ -7023,7 +7360,8 @@ do_strip(PyUnicodeObject *self, int striptype)
return (PyObject*)self;
}
else
- return PyUnicode_FromUnicode(s+i, j-i);
+ return PyUnicode_FromUnicodeSameMerits(s+i, j-i,
+ PyUnicode_GET_MERITS(self));
}
@@ -7143,7 +7481,9 @@ unicode_repeat(PyUnicodeObject *str, Py_ssize_t len)
"repeated string is too long");
return NULL;
}
- u = _PyUnicode_New(nchars);
+
+ u = (PyUnicodeObject*)PyUnicode_FromUnicodeSameMerits(
+ NULL, nchars, PyUnicode_GET_MERITS(str));
if (!u)
return NULL;
@@ -7315,18 +7655,7 @@ done using the specified fill character (default is a space).");
static PyObject *
unicode_rjust(PyUnicodeObject *self, PyObject *args)
{
- Py_ssize_t width;
- Py_UNICODE fillchar = ' ';
-
- if (!PyArg_ParseTuple(args, "n|O&:rjust", &width, convert_uc, &fillchar))
- return NULL;
-
- if (self->length >= width && PyUnicode_CheckExact(self)) {
- Py_INCREF(self);
- return (PyObject*) self;
- }
-
- return (PyObject*) pad(self, width - self->length, 0, fillchar);
+ return (PyObject*)do_padding(self, args, PAD_RJUST);
}
static PyObject*
@@ -7347,8 +7676,8 @@ unicode_slice(PyUnicodeObject *self, Py_ssize_t start, Py_ssize_t end)
if (start > end)
start = end;
/* copy slice */
- return (PyObject*) PyUnicode_FromUnicode(self->str + start,
- end - start);
+ return (PyObject*) PyUnicode_FromUnicodeSameMerits(
+ self->str + start, end - start, PyUnicode_GET_MERITS(self));
}
PyObject *PyUnicode_Split(PyObject *s,
@@ -7407,6 +7736,7 @@ PyUnicode_Partition(PyObject *str_in, PyObject *sep_in)
PyObject* str_obj;
PyObject* sep_obj;
PyObject* out;
+ PyTaintObject *taint = NULL;
str_obj = PyUnicode_FromObject(str_in);
if (!str_obj)
@@ -7422,6 +7752,22 @@ PyUnicode_Partition(PyObject *str_in, PyObject *sep_in)
sep_obj, PyUnicode_AS_UNICODE(sep_obj), PyUnicode_GET_SIZE(sep_obj)
);
+ if (out == NULL)
+ goto done;
+
+ if (PyUnicode_CHECK_TAINTED(str_obj) || PyUnicode_CHECK_TAINTED(sep_obj)) {
+ if (PyTaint_PropagationResult(&taint, PyUnicode_GET_MERITS(str_obj),
+ PyUnicode_GET_MERITS(sep_obj)) == -1) {
+ Py_DECREF(out);
+ out = NULL;
+ goto done;
+ }
+ out = _PyTaint_TaintUnicodeTupleItems(out, taint);
+ // Can't be NULL, because str_obj or sep_obj (or both) is tainted
+ Py_DECREF(taint);
+ }
+
+ done:
Py_DECREF(sep_obj);
Py_DECREF(str_obj);
@@ -7435,6 +7781,7 @@ PyUnicode_RPartition(PyObject *str_in, PyObject *sep_in)
PyObject* str_obj;
PyObject* sep_obj;
PyObject* out;
+ PyTaintObject *taint = NULL;
str_obj = PyUnicode_FromObject(str_in);
if (!str_obj)
@@ -7450,6 +7797,22 @@ PyUnicode_RPartition(PyObject *str_in, PyObject *sep_in)
sep_obj, PyUnicode_AS_UNICODE(sep_obj), PyUnicode_GET_SIZE(sep_obj)
);
+ if (out == NULL)
+ goto done;
+
+ if (PyUnicode_CHECK_TAINTED(str_obj) || PyUnicode_CHECK_TAINTED(sep_obj)) {
+ if (PyTaint_PropagationResult(&taint, PyUnicode_GET_MERITS(str_obj),
+ PyUnicode_GET_MERITS(sep_obj)) == -1) {
+ Py_DECREF(out);
+ out = NULL;
+ goto done;
+ }
+ out = _PyTaint_TaintUnicodeTupleItems(out, taint);
+ // Can't be NULL, because str_obj or sep_obj (or both) is tainted
+ Py_DECREF(taint);
+ }
+
+ done:
Py_DECREF(sep_obj);
Py_DECREF(str_obj);
@@ -7580,10 +7943,12 @@ are deleted.");
static PyObject*
unicode_translate(PyUnicodeObject *self, PyObject *table)
{
+ Py_XINCREF(PyUnicode_GET_MERITS(self)); // TranslateCharmap will steal them
return PyUnicode_TranslateCharmap(self->str,
self->length,
table,
- "ignore");
+ "ignore",
+ PyUnicode_GET_MERITS(self));
}
PyDoc_STRVAR(upper__doc__,
@@ -7619,9 +7984,10 @@ unicode_zfill(PyUnicodeObject *self, PyObject *args)
return (PyObject*) self;
}
else
- return PyUnicode_FromUnicode(
+ return PyUnicode_FromUnicodeSameMerits(
PyUnicode_AS_UNICODE(self),
- PyUnicode_GET_SIZE(self)
+ PyUnicode_GET_SIZE(self),
+ PyUnicode_GET_MERITS(self)
);
}
@@ -7638,6 +8004,19 @@ unicode_zfill(PyUnicodeObject *self, PyObject *args)
u->str[fill] = '0';
}
+ if (u != self && PyUnicode_CHECK_TAINTED(self)) {
+ if (PyUnicode_IS_SHARED(u)) {
+ PyObject *tmp;
+ tmp = PyUnicode_FromUnicodeSameMerits(PyUnicode_AS_UNICODE(u),
+ PyUnicode_GET_SIZE(u),
+ PyUnicode_GET_MERITS(self));
+ Py_DECREF(u);
+ u = (PyUnicodeObject*)tmp;
+ } else {
+ PyUnicode_ASSIGN_MERITS(u, PyUnicode_GET_MERITS(self));
+ }
+ }
+
return (PyObject*) u;
}
@@ -7802,6 +8181,142 @@ PyDoc_STRVAR(sizeof__doc__,
");
static PyObject *
+unicode_taint(PyUnicodeObject *v)
+{
+ PyObject *u;
+ PyTaintObject *t;
+ t = PyTaint_EmptyMerits();
+
+ if (t == NULL)
+ return NULL;
+
+ u = PyUnicode_FromUnicodeSameMerits(PyUnicode_AS_UNICODE(v),
+ PyUnicode_GET_SIZE(v),
+ t);
+ Py_DECREF(t);
+ return u;
+}
+
+PyDoc_STRVAR(taint__doc__,
+ "S.taint() -> unicode\n\
+\n\
+Return a tainted copy of S without any merits.");
+
+static PyObject *
+unicode_istainted(PyUnicodeObject *v)
+{
+ long taint_val = (long)(PyUnicode_CHECK_TAINTED(v));
+ return PyBool_FromLong(taint_val);
+}
+
+PyDoc_STRVAR(istainted__doc__,
+ "S.istainted() -> bool\n\
+\n\
+Return True if S is tainted, False otherwise.");
+
+static PyObject *
+unicode_isclean(PyUnicodeObject *v, PyObject *args)
+{
+ PyObject *merit = Py_None;
+ if (!PyArg_ParseTuple(args, "|O:isclean", &merit))
+ return NULL;
+
+ if (v->merits == NULL) {
+ Py_RETURN_TRUE;
+ }
+
+ if (merit == Py_None) {
+ Py_RETURN_FALSE;
+ }
+
+ if (!PyObject_HasAttrString(merit, "propagation")) {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid merit object passed.");
+ return NULL;
+ }
+
+ switch (PySequence_Contains((PyObject*)PyUnicode_GET_MERITS(v),
+ merit)) {
+ case -1:
+ return NULL;
+ case 1:
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+PyDoc_STRVAR(isclean__doc__,
+ "S.isclean([merit]) -> bool\n\
+\n\
+If no merit is specified return False if S is tainted, True otherwise. If a\n\
+merit is specified return False when S is tainted and doesn't have given\n\
+merit, True otherwise.");
+
+static PyObject *
+unicode_cleanfor(PyUnicodeObject *v, PyObject *merit)
+{
+ PyUnicodeObject *u;
+ PyTaintObject *taint, *new_taint;
+
+ if (_PyTaint_ValidMerit(merit) == -1)
+ return NULL;
+
+ if (PyUnicode_CHECK_TAINTED(v)) {
+ taint = PyUnicode_GET_MERITS(v);
+ Py_INCREF(taint);
+ } else {
+ taint = PyTaint_EmptyMerits();
+ if (taint == NULL)
+ return NULL;
+ }
+
+ new_taint = _PyTaint_AddMerit(taint, merit);
+ Py_DECREF(taint);
+ u = (PyUnicodeObject*)PyUnicode_FromUnicodeSameMerits(
+ PyUnicode_AS_UNICODE(v),
+ PyUnicode_GET_SIZE(v),
+ new_taint);
+ if (u == NULL)
+ return NULL;
+ Py_DECREF(new_taint);
+ return (PyObject*)u;
+}
+
+PyDoc_STRVAR(cleanfor__doc__,
+ "S._cleanfor(M) -> unicode\n\
+\n\
+Return a tainted copy of S which has merit M. All other merits of S are also\n\
+copied to return value.");
+
+static PyObject *
+unicode_listmerits(PyUnicodeObject *v)
+{
+ if (!PyUnicode_CHECK_TAINTED(v)) {
+ Py_RETURN_NONE;
+ }
+
+ return PySet_New((PyObject*)PyUnicode_GET_MERITS(v));
+}
+
+PyDoc_STRVAR(listmerits__doc__,
+ "S._merits() -> set of merits\n\
+\n\
+If S is tainted, return its set of merits. If S is untainted, return None.");
+
+static PyObject *
+unicode_propagate(PyUnicodeObject *v, PyUnicodeObject *source)
+{
+ return PyUnicode_FromUnicodeSameMerits(PyUnicode_AS_UNICODE(v),
+ PyUnicode_GET_SIZE(v),
+ PyUnicode_GET_MERITS(source));
+}
+
+PyDoc_STRVAR(propagate__doc__,
+ "S._propagate(T) -> unicode\n\
+\n\
+Return a copy of S, which has the same merits as T.");
+
+static PyObject *
unicode_getnewargs(PyUnicodeObject *v)
{
return Py_BuildValue("(u#)", v->str, v->length);
@@ -7827,6 +8342,12 @@ static PyMethodDef unicode_methods[] = {
{"lstrip", (PyCFunction) unicode_lstrip, METH_VARARGS, lstrip__doc__},
{"decode", (PyCFunction) unicode_decode, METH_VARARGS | METH_KEYWORDS, decode__doc__},
/* {"maketrans", (PyCFunction) unicode_maketrans, METH_VARARGS, maketrans__doc__}, */
+ {"taint", (PyCFunction)unicode_taint, METH_NOARGS, taint__doc__},
+ {"isclean", (PyCFunction)unicode_isclean, METH_VARARGS, isclean__doc__},
+ {"istainted", (PyCFunction)unicode_istainted, METH_NOARGS, istainted__doc__},
+ {"_cleanfor", (PyCFunction)unicode_cleanfor, METH_O, cleanfor__doc__},
+ {"_merits", (PyCFunction)unicode_listmerits, METH_NOARGS, listmerits__doc__},
+ {"_propagate", (PyCFunction)unicode_propagate, METH_O, propagate__doc__},
{"rfind", (PyCFunction) unicode_rfind, METH_VARARGS, rfind__doc__},
{"rindex", (PyCFunction) unicode_rindex, METH_VARARGS, rindex__doc__},
{"rjust", (PyCFunction) unicode_rjust, METH_VARARGS, rjust__doc__},
@@ -7918,13 +8439,15 @@ unicode_subscript(PyUnicodeObject* self, PyObject* item)
}
if (slicelength <= 0) {
- return PyUnicode_FromUnicode(NULL, 0);
+ return PyUnicode_FromUnicodeSameMerits(NULL, 0,
+ PyUnicode_GET_MERITS(self));
} else if (start == 0 && step == 1 && slicelength == self->length &&
PyUnicode_CheckExact(self)) {
Py_INCREF(self);
return (PyObject *)self;
} else if (step == 1) {
- return PyUnicode_FromUnicode(self->str + start, slicelength);
+ return PyUnicode_FromUnicodeSameMerits(self->str + start,
+ slicelength, PyUnicode_GET_MERITS(self));
} else {
source_buf = PyUnicode_AS_UNICODE((PyObject*)self);
result_buf = (Py_UNICODE *)PyObject_MALLOC(slicelength*
@@ -7937,7 +8460,8 @@ unicode_subscript(PyUnicodeObject* self, PyObject* item)
result_buf[i] = source_buf[cur];
}
- result = PyUnicode_FromUnicode(result_buf, slicelength);
+ result = PyUnicode_FromUnicodeSameMerits(result_buf,
+ slicelength, PyUnicode_GET_MERITS(self));
PyObject_FREE(result_buf);
return result;
}
@@ -8262,6 +8786,7 @@ PyObject *PyUnicode_Format(PyObject *format,
PyUnicodeObject *result = NULL;
PyObject *dict = NULL;
PyObject *uformat;
+ PyTaintObject *taint = NULL;
if (format == NULL || args == NULL) {
PyErr_BadInternalCall();
@@ -8272,6 +8797,8 @@ PyObject *PyUnicode_Format(PyObject *format,
return NULL;
fmt = PyUnicode_AS_UNICODE(uformat);
fmtcnt = PyUnicode_GET_SIZE(uformat);
+ taint = PyUnicode_GET_MERITS(uformat);
+ Py_XINCREF(taint);
reslen = rescnt = fmtcnt + 100;
result = _PyUnicode_New(reslen);
@@ -8513,6 +9040,11 @@ PyObject *PyUnicode_Format(PyObject *format,
goto onError;
}
}
+ if (PyTaint_PropagateTo(&taint,
+ PyUnicode_GET_MERITS(temp)) == -1) {
+ Py_XDECREF(temp);
+ goto onError;
+ }
pbuf = PyUnicode_AS_UNICODE(temp);
len = PyUnicode_GET_SIZE(temp);
if (prec >= 0 && len > prec)
@@ -8702,9 +9234,11 @@ PyObject *PyUnicode_Format(PyObject *format,
Py_DECREF(args);
}
Py_DECREF(uformat);
- return (PyObject *)result;
+
+ return PyUnicode_AssignTaint(result, taint);
onError:
+ Py_XDECREF(taint);
Py_XDECREF(result);
Py_DECREF(uformat);
if (args_owned) {
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index cdc9080..b6d60e1 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -1269,6 +1269,11 @@ builtin_intern(PyObject *self, PyObject *args)
"can't intern subclass of string");
return NULL;
}
+ if (((PyStringObject *)s)->ob_merits != NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "tainted strings can't be interned");
+ return NULL;
+ }
Py_INCREF(s);
PyString_InternInPlace(&s);
return s;
@@ -2715,6 +2720,7 @@ _PyBuiltin_Init(void)
SETBUILTIN("int", &PyInt_Type);
SETBUILTIN("list", &PyList_Type);
SETBUILTIN("long", &PyLong_Type);
+ SETBUILTIN("Merit", &PyMerit_MeritType);
SETBUILTIN("object", &PyBaseObject_Type);
SETBUILTIN("reversed", &PyReversed_Type);
SETBUILTIN("set", &PySet_Type);
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index a2e1e74..af3a164 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -211,6 +211,8 @@ Py_InitializeEx(int install_sigs)
_PyUnicode_Init();
#endif
+ _PyTaint_Init();
+
bimod = _PyBuiltin_Init();
if (bimod == NULL)
Py_FatalError("Py_Initialize: can't initialize __builtin__");
--
1.7.9.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment