Commits

Christian Heimes committed b20f4b2

Python 3.1 support

Comments (0)

Files changed (5)

 SETUPFLAGS=
 COMPILEFLAGS=
 INSTALLFLAGS=
-PYTHONS=python2.6 python2.7 python3.2 python3.3
+PYTHONS=python2.6 python2.7 python3.1 python3.2 python3.3
 
 .PHONY: inplace all rebuild test_inplace test fulltests clean distclean
 .PHONY: sdist install

Modules31/_elementtree.c

 
     PyObject* target = NULL;
     char* encoding = NULL;
-    static char* kwlist[] = { "target", "encoding", NULL };
-    if (!PyArg_ParseTupleAndKeywords(args, kw, "|Oz:XMLParser", kwlist,
-                                     &target, &encoding))
+    int ignore_dtd_flag = 0;
+    PyObject *ignore_dtd = NULL, *indirections = NULL, *expansions = NULL;
+    unsigned long max_indirections;
+    unsigned long max_expansions;
+
+    static char *kwlist[] = {"target", "encoding",
+                              "max_entity_indirections",
+                              "max_entity_expansions", "ignore_dtd", 0};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kw, "|OzOOO:XMLParser", kwlist,
+                                     &target, &encoding,
+                                     &indirections, &expansions,
+                                     &ignore_dtd)) {
         return NULL;
+    }
+
+    if (indirections == NULL || indirections == Py_None) {
+#ifdef XML_BOMB_PROTECTION
+        max_indirections = XML_DEFAULT_MAX_ENTITY_INDIRECTIONS;
+#else
+        max_indirections = 0;
+#endif
+    }
+    else {
+        max_indirections = PyLong_AsUnsignedLong(indirections);
+        if ((max_indirections == (unsigned long)-1) && PyErr_Occurred()) {
+            return NULL;
+        }
+        if (max_indirections > UINT_MAX) {
+            PyErr_Format(PyExc_ValueError,
+                         "max_entity_indirections must not be greater than %i",
+                         UINT_MAX);
+            return NULL;
+        }
+    }
+
+    if (expansions == NULL || expansions == Py_None) {
+#ifdef XML_BOMB_PROTECTION
+        max_expansions = XML_DEFAULT_MAX_ENTITY_EXPANSIONS;
+#else
+        max_expansions = 0;
+#endif
+    }
+    else {
+        max_expansions = PyLong_AsUnsignedLong(expansions);
+        if ((max_expansions == (unsigned long)-1) && PyErr_Occurred()) {
+            return NULL;
+        }
+        if (max_expansions > UINT_MAX) {
+            PyErr_Format(PyExc_ValueError,
+                         "max_entity_expansions must not be greater than %i",
+                         UINT_MAX);
+            return NULL;
+        }
+    }
+
+    if (ignore_dtd == NULL) {
+        ignore_dtd_flag = 0;
+    }
+    else if ((ignore_dtd_flag = PyObject_IsTrue(ignore_dtd)) == -1) {
+             return NULL;
+    }
+
 
 #if defined(USE_PYEXPAT_CAPI)
     if (!expat_capi) {
         PyObject_Del(self);
         return NULL;
     }
-     
+
     self->names = PyDict_New();
     if (!self->names) {
         PyObject_Del(self->entity);
         return NULL;
     }
 
+#ifdef XML_BOMB_PROTECTION
+    EXPAT(SetMaxEntityIndirections)(self->parser, (unsigned int)max_indirections);
+    assert(EXPAT(GetMaxEntityIndirections)(self->parser) ==
+               (unsigned int)max_indirections);
+    EXPAT(SetMaxEntityExpansions)(self->parser,
+                                 (unsigned int)max_expansions);
+    assert(EXPAT(GetMaxEntityExpansions)(self->parser) ==
+           (unsigned int)max_expansions);
+    EXPAT(SetResetDTDFlag)(self->parser,
+                           ignore_dtd_flag ? XML_TRUE : XML_FALSE);
+#endif
+
     /* setup target handlers */
     if (!target) {
         target = treebuilder_new();
         sprintf(buffer, "Expat %d.%d.%d", XML_MAJOR_VERSION,
                 XML_MINOR_VERSION, XML_MICRO_VERSION);
         return PyBytes_FromString(buffer);
+#ifdef XML_BOMB_PROTECTION
+    }
+    else if (strcmp(name, "ignore_dtd") == 0) {
+         return PyBool_FromLong(EXPAT(GetResetDTDFlag)
+                                (self->parser));
+    }
+    else if (strcmp(name, "max_entity_indirections") == 0) {
+         return PyLong_FromUnsignedLong(EXPAT(GetMaxEntityIndirections)
+                                        (self->parser));
+    }
+    else if (strcmp(name, "max_entity_expansions") == 0) {
+         return PyLong_FromUnsignedLong(EXPAT(GetMaxEntityExpansions)
+                                        (self->parser));
+#endif
     } else {
         return PyObject_GenericGetAttr((PyObject*) self, nameobj);
     }

Modules31/pyexpat.c

         if (strcmp(name, "buffer_used") == 0)
             return PyLong_FromLong((long) self->buffer_used);
     }
+#ifdef XML_BOMB_PROTECTION
+    if (name[0] == 'm') {
+        if (strcmp(name, "max_entity_indirections") == 0)
+            return PyLong_FromLong((long)
+                                    XML_GetMaxEntityIndirections(self->itself));
+        if (strcmp(name, "max_entity_expansions") == 0)
+            return PyLong_FromLong((long)
+                                    XML_GetMaxEntityExpansions(self->itself));
+    }
+#endif
     if (strcmp(name, "namespace_prefixes") == 0)
         return get_pybool(self->ns_prefixes);
     if (strcmp(name, "ordered_attributes") == 0)
             return self->intern;
         }
     }
-
+#ifdef XML_BOMB_PROTECTION
+    if (strcmp(name, "reset_dtd") == 0) {
+        if (XML_GetResetDTDFlag(self->itself) == XML_TRUE) {
+            Py_RETURN_TRUE;
+        }
+        else {
+            Py_RETURN_FALSE;
+        }
+    }
+#endif
     return PyObject_GenericGetAttr((PyObject*)self, nameobj);
 }
 
     APPEND(rc, "buffer_text");
     APPEND(rc, "buffer_used");
     APPEND(rc, "namespace_prefixes");
+    APPEND(rc, "max_entity_expansions");
+    APPEND(rc, "max_entity_indirections");
+    APPEND(rc, "reset_dtd");
     APPEND(rc, "ordered_attributes");
     APPEND(rc, "specified_attributes");
     APPEND(rc, "intern");
       self->buffer_size = new_buffer_size;
       return 0;
     }
+#ifdef XML_BOMB_PROTECTION
+    if (strcmp(name, "max_entity_expansions") == 0) {
+        unsigned long value;
+
+        value = PyLong_AsUnsignedLong(v);
+        if ((value == (unsigned long)-1) && PyErr_Occurred()) {
+            return -1;
+        }
+        if (value > UINT_MAX) {
+            PyErr_Format(PyExc_ValueError,
+                         "max_entity_expansions must not be greater than %i",
+                         UINT_MAX);
+            return -1;
+        }
+        XML_SetMaxEntityExpansions(self->itself, (unsigned int)value);
+        return 0;
+    }
+
+    if (strcmp(name, "max_entity_indirections") == 0) {
+        unsigned long value;
+
+        value = PyLong_AsUnsignedLong(v);
+        if ((value == (unsigned long)-1) && PyErr_Occurred()) {
+            return -1;
+        }
+        if (value > UINT_MAX) {
+            PyErr_Format(PyExc_ValueError,
+                         "max_entity_indirections must not be greater than %i",
+                         UINT_MAX);
+            return -1;
+        }
+        XML_SetMaxEntityIndirections(self->itself, (unsigned int)value);
+        return 0;
+    }
+
+    if (strcmp(name, "reset_dtd") == 0) {
+        int value;
+
+        if ((value = PyObject_IsTrue(v)) == -1) {
+            return -1;
+        }
+        XML_SetResetDTDFlag(self->itself, value ? XML_TRUE : XML_FALSE);
+        return 0;
+    }
+#endif
 
     if (strcmp(name, "CharacterDataHandler") == 0) {
         /* If we're changing the character data handler, flush all
     MYCONST(XML_PARAM_ENTITY_PARSING_NEVER);
     MYCONST(XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE);
     MYCONST(XML_PARAM_ENTITY_PARSING_ALWAYS);
+#ifdef XML_BOMB_PROTECTION
+    MYCONST(XML_DEFAULT_MAX_ENTITY_INDIRECTIONS);
+    MYCONST(XML_DEFAULT_MAX_ENTITY_EXPANSIONS);
+    PyModule_AddObject(m, "XML_BOMB_PROTECTION", Py_True);
+    Py_INCREF(Py_True);
+#else
+    PyModule_AddIntConstant(m, "XML_DEFAULT_MAX_ENTITY_INDIRECTIONS", 0);
+    PyModule_AddIntConstant(m, "XML_DEFAULT_MAX_ENTITY_EXPANSIONS", 0);
+    PyModule_AddObject(m, "XML_BOMB_PROTECTION", Py_False);
+    Py_INCREF(Py_False);
+#endif
 #undef MYCONST
 
 #define MYCONST(c) PyModule_AddIntConstant(model_module, #c, c)
     capi.SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler;
     capi.SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler;
     capi.SetUserData = XML_SetUserData;
+#ifdef XML_BOMB_PROTECTION
+    capi.GetMaxEntityIndirections = XML_GetMaxEntityIndirections;
+    capi.SetMaxEntityIndirections = XML_SetMaxEntityIndirections;
+    capi.GetEntityExpansions = XML_GetEntityExpansions;
+    capi.GetMaxEntityExpansions = XML_GetMaxEntityExpansions;
+    capi.SetMaxEntityExpansions = XML_SetMaxEntityExpansions;
+    capi.GetResetDTDFlag = XML_GetResetDTDFlag;
+    capi.SetResetDTDFlag = XML_SetResetDTDFlag;
+#endif
 
     /* export using capsule */
     capi_object = PyCapsule_New(&capi, PyExpat_CAPSULE_NAME, NULL);

Modules31/pyexpat.h

 
 /* note: you must import expat.h before importing this module! */
 
+#ifdef XML_BOMB_PROTECTION
+#define PyExpat_CAPI_MAGIC  "pyexpat.expat_CAPI 1.1 bomb protection"
+#else
 #define PyExpat_CAPI_MAGIC  "pyexpat.expat_CAPI 1.0"
+#endif
 #define PyExpat_CAPSULE_NAME "pyexpat.expat_CAPI"
 
 struct PyExpat_CAPI 
         XML_Parser parser, XML_UnknownEncodingHandler handler,
         void *encodingHandlerData);
     void (*SetUserData)(XML_Parser parser, void *userData);
+
+#ifdef XML_BOMB_PROTECTION
+    /* CAPI 1.1 bomb protection additions */
+    unsigned int (*GetMaxEntityIndirections)(XML_Parser parser);
+    void (*SetMaxEntityIndirections)(XML_Parser parser, unsigned int value);
+
+    unsigned int (*GetEntityExpansions)(XML_Parser parser);
+    unsigned int (*GetMaxEntityExpansions)(XML_Parser parser);
+    void (*SetMaxEntityExpansions)(XML_Parser parser, unsigned int value);
+
+    XML_Bool (*GetResetDTDFlag)(XML_Parser parser);
+    void (*SetResetDTDFlag)(XML_Parser parser, XML_Bool value);
+#endif
+
     /* always add new stuff to the end! */
 };
 
 HERE = os.path.dirname(os.path.abspath(__file__))
 PY3 = sys.version_info[0] > 2
 PY26 = sys.version_info[:2] == (2, 6)
+PY31 = sys.version_info[:2] == (3, 1)
 
 
 # Python 2.6
 <bomb>&a;</bomb>
 """
 
-if PY26:
+if PY26 or PY31:
     class _AssertRaisesContext(object):
         """A context manager used to implement TestCase.assertRaises* methods."""
 
     xml_quadratic = os.path.join(HERE, "xmltestdata", "quadratic.xml")
     xml_bomb = os.path.join(HERE, "xmltestdata", "xmlbomb.xml")
 
-    if PY26:
+    if PY26 or PY31:
         def assertRaises(self, excClass, callableObj=None, *args, **kwargs):
             context = _AssertRaisesContext(excClass, self)
             if callableObj is None:
                 return context
             with context:
                 callableObj(*args, **kwargs)
+
         def assertIn(self, member, container, msg=None):
             """Just like self.assertTrue(a in b), but with a nicer default message."""
             if member not in container:
         # and raises an exception because it doesn't expand external entities
         with self.assertRaises(ParseError) as e:
             ET.parse(self.xml_external)
-        self.assertEqual(str(e.exception),
-            "undefined entity &ee;: line 4, column 6")
+        if PY31:
+            # Python 3.1 bug
+            self.assertTrue(str(e.exception).startswith("undefined entity"),
+                            str(e.exception))
+        else:
+            self.assertEqual(str(e.exception),
+                "undefined entity &ee;: line 4, column 6")
 
         with self.assertRaises(ParseError) as e:
             ET.parse(self.xml_bomb)