Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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)