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