gh-90949: expose Expat API to tune exponential expansion protections (#139368)
Expose the XML Expat 2.7.2 APIs to tune protections against
"billion laughs" [1] attacks.
The exposed APIs are available on Expat parsers, that is,
parsers created by `xml.parsers.expat.ParserCreate()`, as:
- `parser.SetBillionLaughsAttackProtectionActivationThreshold(threshold)`, and
- `parser.SetBillionLaughsAttackProtectionMaximumAmplification(max_factor)`.
This completes the work in f04bea44c3,
and improves the existing related documentation.
[1]: https://en.wikipedia.org/wiki/Billion_laughs_attack
This commit is contained in:
@@ -238,16 +238,71 @@ XMLParser Objects
|
||||
.. versionadded:: 3.13
|
||||
|
||||
|
||||
:class:`!xmlparser` objects have the following methods to mitigate some
|
||||
common XML vulnerabilities.
|
||||
:class:`!xmlparser` objects have the following methods to tune protections
|
||||
against some common XML vulnerabilities.
|
||||
|
||||
.. method:: xmlparser.SetBillionLaughsAttackProtectionActivationThreshold(threshold, /)
|
||||
|
||||
Sets the number of output bytes needed to activate protection against
|
||||
`billion laughs`_ attacks.
|
||||
|
||||
The number of output bytes includes amplification from entity expansion
|
||||
and reading DTD files.
|
||||
|
||||
Parser objects usually have a protection activation threshold of 8 MiB,
|
||||
but the actual default value depends on the underlying Expat library.
|
||||
|
||||
An :exc:`ExpatError` is raised if this method is called on a
|
||||
|xml-non-root-parser| parser.
|
||||
The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset`
|
||||
should not be used as they may have no special meaning.
|
||||
|
||||
.. note::
|
||||
|
||||
Activation thresholds below 4 MiB are known to break support for DITA 1.3
|
||||
payload and are hence not recommended.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. method:: xmlparser.SetBillionLaughsAttackProtectionMaximumAmplification(max_factor, /)
|
||||
|
||||
Sets the maximum tolerated amplification factor for protection against
|
||||
`billion laughs`_ attacks.
|
||||
|
||||
The amplification factor is calculated as ``(direct + indirect) / direct``
|
||||
while parsing, where ``direct`` is the number of bytes read from
|
||||
the primary document in parsing and ``indirect`` is the number of
|
||||
bytes added by expanding entities and reading of external DTD files.
|
||||
|
||||
The *max_factor* value must be a non-NaN :class:`float` value greater than
|
||||
or equal to 1.0. Peak amplifications of factor 15,000 for the entire payload
|
||||
and of factor 30,000 in the middle of parsing have been observed with small
|
||||
benign files in practice. In particular, the activation threshold should be
|
||||
carefully chosen to avoid false positives.
|
||||
|
||||
Parser objects usually have a maximum amplification factor of 100,
|
||||
but the actual default value depends on the underlying Expat library.
|
||||
|
||||
An :exc:`ExpatError` is raised if this method is called on a
|
||||
|xml-non-root-parser| parser or if *max_factor* is outside the valid range.
|
||||
The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset`
|
||||
should not be used as they may have no special meaning.
|
||||
|
||||
.. note::
|
||||
|
||||
The maximum amplification factor is only considered if the threshold
|
||||
that can be adjusted by :meth:`.SetBillionLaughsAttackProtectionActivationThreshold`
|
||||
is exceeded.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. method:: xmlparser.SetAllocTrackerActivationThreshold(threshold, /)
|
||||
|
||||
Sets the number of allocated bytes of dynamic memory needed to activate
|
||||
protection against disproportionate use of RAM.
|
||||
|
||||
By default, parser objects have an allocation activation threshold of 64 MiB,
|
||||
or equivalently 67,108,864 bytes.
|
||||
Parser objects usually have an allocation activation threshold of 64 MiB,
|
||||
but the actual default value depends on the underlying Expat library.
|
||||
|
||||
An :exc:`ExpatError` is raised if this method is called on a
|
||||
|xml-non-root-parser| parser.
|
||||
@@ -271,7 +326,8 @@ common XML vulnerabilities.
|
||||
near the start of parsing even with benign files in practice. In particular,
|
||||
the activation threshold should be carefully chosen to avoid false positives.
|
||||
|
||||
By default, parser objects have a maximum amplification factor of 100.0.
|
||||
Parser objects usually have a maximum amplification factor of 100,
|
||||
but the actual default value depends on the underlying Expat library.
|
||||
|
||||
An :exc:`ExpatError` is raised if this method is called on a
|
||||
|xml-non-root-parser| parser or if *max_factor* is outside the valid range.
|
||||
@@ -1010,4 +1066,6 @@ The ``errors`` module has the following attributes:
|
||||
not. See https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDecl
|
||||
and https://www.iana.org/assignments/character-sets/character-sets.xhtml.
|
||||
|
||||
|
||||
.. _billion laughs: https://en.wikipedia.org/wiki/Billion_laughs_attack
|
||||
.. |xml-non-root-parser| replace:: :ref:`non-root <xmlparser-non-root>`
|
||||
|
||||
@@ -558,10 +558,18 @@ xml.parsers.expat
|
||||
|
||||
* Add :meth:`~xml.parsers.expat.xmlparser.SetAllocTrackerActivationThreshold`
|
||||
and :meth:`~xml.parsers.expat.xmlparser.SetAllocTrackerMaximumAmplification`
|
||||
to :ref:`xmlparser <xmlparser-objects>` objects to prevent use of
|
||||
disproportional amounts of dynamic memory from within an Expat parser.
|
||||
to :ref:`xmlparser <xmlparser-objects>` objects to tune protections against
|
||||
disproportional amounts of dynamic memory usage from within an Expat parser.
|
||||
(Contributed by Bénédikt Tran in :gh:`90949`.)
|
||||
|
||||
* Add :meth:`~xml.parsers.expat.xmlparser.SetBillionLaughsAttackProtectionActivationThreshold`
|
||||
and :meth:`~xml.parsers.expat.xmlparser.SetBillionLaughsAttackProtectionMaximumAmplification`
|
||||
to :ref:`xmlparser <xmlparser-objects>` objects to tune protections against
|
||||
`billion laughs`_ attacks.
|
||||
(Contributed by Bénédikt Tran in :gh:`90949`.)
|
||||
|
||||
.. _billion laughs: https://en.wikipedia.org/wiki/Billion_laughs_attack
|
||||
|
||||
|
||||
zlib
|
||||
----
|
||||
|
||||
@@ -57,6 +57,11 @@ struct PyExpat_CAPI
|
||||
XML_Parser parser, unsigned long long activationThresholdBytes);
|
||||
XML_Bool (*SetAllocTrackerMaximumAmplification)(
|
||||
XML_Parser parser, float maxAmplificationFactor);
|
||||
/* might be NULL for expat < 2.4.0 */
|
||||
XML_Bool (*SetBillionLaughsAttackProtectionActivationThreshold)(
|
||||
XML_Parser parser, unsigned long long activationThresholdBytes);
|
||||
XML_Bool (*SetBillionLaughsAttackProtectionMaximumAmplification)(
|
||||
XML_Parser parser, float maxAmplificationFactor);
|
||||
/* always add new stuff to the end! */
|
||||
};
|
||||
|
||||
|
||||
@@ -958,6 +958,64 @@ class AttackProtectionTestBase(abc.ABC):
|
||||
self.assert_root_parser_failure(setter, 123.45)
|
||||
|
||||
|
||||
@unittest.skipIf(expat.version_info < (2, 4, 0), "requires Expat >= 2.4.0")
|
||||
class ExpansionProtectionTest(AttackProtectionTestBase, unittest.TestCase):
|
||||
|
||||
def assert_rejected(self, func, /, *args, **kwargs):
|
||||
"""Check that func(*args, **kwargs) hits the allocation limit."""
|
||||
msg = (
|
||||
r"limit on input amplification factor \(from DTD and entities\) "
|
||||
r"breached: line \d+, column \d+"
|
||||
)
|
||||
self.assertRaisesRegex(expat.ExpatError, msg, func, *args, **kwargs)
|
||||
|
||||
def set_activation_threshold(self, parser, threshold):
|
||||
return parser.SetBillionLaughsAttackProtectionActivationThreshold(threshold)
|
||||
|
||||
def set_maximum_amplification(self, parser, max_factor):
|
||||
return parser.SetBillionLaughsAttackProtectionMaximumAmplification(max_factor)
|
||||
|
||||
def test_set_activation_threshold__threshold_reached(self):
|
||||
parser = expat.ParserCreate()
|
||||
# Choose a threshold expected to be always reached.
|
||||
self.set_activation_threshold(parser, 3)
|
||||
# Check that the threshold is reached by choosing a small factor
|
||||
# and a payload whose peak amplification factor exceeds it.
|
||||
self.assertIsNone(self.set_maximum_amplification(parser, 1.0))
|
||||
payload = self.exponential_expansion_payload(ncols=10, nrows=4)
|
||||
self.assert_rejected(parser.Parse, payload, True)
|
||||
|
||||
def test_set_activation_threshold__threshold_not_reached(self):
|
||||
parser = expat.ParserCreate()
|
||||
# Choose a threshold expected to be never reached.
|
||||
self.set_activation_threshold(parser, pow(10, 5))
|
||||
# Check that the threshold is reached by choosing a small factor
|
||||
# and a payload whose peak amplification factor exceeds it.
|
||||
self.assertIsNone(self.set_maximum_amplification(parser, 1.0))
|
||||
payload = self.exponential_expansion_payload(ncols=10, nrows=4)
|
||||
self.assertIsNotNone(parser.Parse(payload, True))
|
||||
|
||||
def test_set_maximum_amplification__amplification_exceeded(self):
|
||||
parser = expat.ParserCreate()
|
||||
# Unconditionally enable maximum activation factor.
|
||||
self.set_activation_threshold(parser, 0)
|
||||
# Choose a max amplification factor expected to always be exceeded.
|
||||
self.assertIsNone(self.set_maximum_amplification(parser, 1.0))
|
||||
# Craft a payload for which the peak amplification factor is > 1.0.
|
||||
payload = self.exponential_expansion_payload(ncols=1, nrows=2)
|
||||
self.assert_rejected(parser.Parse, payload, True)
|
||||
|
||||
def test_set_maximum_amplification__amplification_not_exceeded(self):
|
||||
parser = expat.ParserCreate()
|
||||
# Unconditionally enable maximum activation factor.
|
||||
self.set_activation_threshold(parser, 0)
|
||||
# Choose a max amplification factor expected to never be exceeded.
|
||||
self.assertIsNone(self.set_maximum_amplification(parser, 1e4))
|
||||
# Craft a payload for which the peak amplification factor is < 1e4.
|
||||
payload = self.exponential_expansion_payload(ncols=1, nrows=2)
|
||||
self.assertIsNotNone(parser.Parse(payload, True))
|
||||
|
||||
|
||||
@unittest.skipIf(expat.version_info < (2, 7, 2), "requires Expat >= 2.7.2")
|
||||
class MemoryProtectionTest(AttackProtectionTestBase, unittest.TestCase):
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Add :meth:`~xml.parsers.expat.xmlparser.SetAllocTrackerActivationThreshold`
|
||||
and :meth:`~xml.parsers.expat.xmlparser.SetAllocTrackerMaximumAmplification`
|
||||
to :ref:`xmlparser <xmlparser-objects>` objects to prevent use of
|
||||
disproportional amounts of dynamic memory from within an Expat parser.
|
||||
to :ref:`xmlparser <xmlparser-objects>` objects to tune protections against
|
||||
disproportional amounts of dynamic memory usage from within an Expat parser.
|
||||
Patch by Bénédikt Tran.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
Add
|
||||
:meth:`~xml.parsers.expat.xmlparser.SetBillionLaughsAttackProtectionActivationThreshold`
|
||||
and
|
||||
:meth:`~xml.parsers.expat.xmlparser.SetBillionLaughsAttackProtectionMaximumAmplification`
|
||||
to :ref:`xmlparser <xmlparser-objects>` objects to tune protections against
|
||||
`billion laughs <https://en.wikipedia.org/wiki/Billion_laughs_attack>`_ attacks.
|
||||
Patch by Bénédikt Tran.
|
||||
150
Modules/clinic/pyexpat.c.h
generated
150
Modules/clinic/pyexpat.c.h
generated
@@ -409,6 +409,140 @@ exit:
|
||||
|
||||
#endif /* (XML_COMBINED_VERSION >= 19505) */
|
||||
|
||||
#if (XML_COMBINED_VERSION >= 20400)
|
||||
|
||||
PyDoc_STRVAR(pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold__doc__,
|
||||
"SetBillionLaughsAttackProtectionActivationThreshold($self, threshold, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Sets the number of output bytes needed to activate protection against billion laughs attacks.\n"
|
||||
"\n"
|
||||
"The number of output bytes includes amplification from entity expansion\n"
|
||||
"and reading DTD files.\n"
|
||||
"\n"
|
||||
"Parser objects usually have a protection activation threshold of 8 MiB,\n"
|
||||
"but the actual default value depends on the underlying Expat library.\n"
|
||||
"\n"
|
||||
"Activation thresholds below 4 MiB are known to break support for DITA 1.3\n"
|
||||
"payload and are hence not recommended.");
|
||||
|
||||
#define PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONACTIVATIONTHRESHOLD_METHODDEF \
|
||||
{"SetBillionLaughsAttackProtectionActivationThreshold", _PyCFunction_CAST(pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold__doc__},
|
||||
|
||||
static PyObject *
|
||||
pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold_impl(xmlparseobject *self,
|
||||
PyTypeObject *cls,
|
||||
unsigned long long threshold);
|
||||
|
||||
static PyObject *
|
||||
pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
|
||||
# define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty)
|
||||
#else
|
||||
# define KWTUPLE NULL
|
||||
#endif
|
||||
|
||||
static const char * const _keywords[] = {"", NULL};
|
||||
static _PyArg_Parser _parser = {
|
||||
.keywords = _keywords,
|
||||
.fname = "SetBillionLaughsAttackProtectionActivationThreshold",
|
||||
.kwtuple = KWTUPLE,
|
||||
};
|
||||
#undef KWTUPLE
|
||||
PyObject *argsbuf[1];
|
||||
unsigned long long threshold;
|
||||
|
||||
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
|
||||
/*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
|
||||
if (!args) {
|
||||
goto exit;
|
||||
}
|
||||
if (!_PyLong_UnsignedLongLong_Converter(args[0], &threshold)) {
|
||||
goto exit;
|
||||
}
|
||||
return_value = pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold_impl((xmlparseobject *)self, cls, threshold);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
#endif /* (XML_COMBINED_VERSION >= 20400) */
|
||||
|
||||
#if (XML_COMBINED_VERSION >= 20400)
|
||||
|
||||
PyDoc_STRVAR(pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification__doc__,
|
||||
"SetBillionLaughsAttackProtectionMaximumAmplification($self, max_factor,\n"
|
||||
" /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Sets the maximum tolerated amplification factor for protection against billion laughs attacks.\n"
|
||||
"\n"
|
||||
"The amplification factor is calculated as \"(direct + indirect) / direct\"\n"
|
||||
"while parsing, where \"direct\" is the number of bytes read from the primary\n"
|
||||
"document in parsing and \"indirect\" is the number of bytes added by expanding\n"
|
||||
"entities and reading external DTD files, combined.\n"
|
||||
"\n"
|
||||
"The \'max_factor\' value must be a non-NaN floating point value greater than\n"
|
||||
"or equal to 1.0. Amplification factors greater than 30,000 can be observed\n"
|
||||
"in the middle of parsing even with benign files in practice. In particular,\n"
|
||||
"the activation threshold should be carefully chosen to avoid false positives.\n"
|
||||
"\n"
|
||||
"Parser objects usually have a maximum amplification factor of 100,\n"
|
||||
"but the actual default value depends on the underlying Expat library.");
|
||||
|
||||
#define PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONMAXIMUMAMPLIFICATION_METHODDEF \
|
||||
{"SetBillionLaughsAttackProtectionMaximumAmplification", _PyCFunction_CAST(pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification__doc__},
|
||||
|
||||
static PyObject *
|
||||
pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification_impl(xmlparseobject *self,
|
||||
PyTypeObject *cls,
|
||||
float max_factor);
|
||||
|
||||
static PyObject *
|
||||
pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
|
||||
# define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty)
|
||||
#else
|
||||
# define KWTUPLE NULL
|
||||
#endif
|
||||
|
||||
static const char * const _keywords[] = {"", NULL};
|
||||
static _PyArg_Parser _parser = {
|
||||
.keywords = _keywords,
|
||||
.fname = "SetBillionLaughsAttackProtectionMaximumAmplification",
|
||||
.kwtuple = KWTUPLE,
|
||||
};
|
||||
#undef KWTUPLE
|
||||
PyObject *argsbuf[1];
|
||||
float max_factor;
|
||||
|
||||
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
|
||||
/*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
|
||||
if (!args) {
|
||||
goto exit;
|
||||
}
|
||||
if (PyFloat_CheckExact(args[0])) {
|
||||
max_factor = (float) (PyFloat_AS_DOUBLE(args[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
max_factor = (float) PyFloat_AsDouble(args[0]);
|
||||
if (max_factor == -1.0 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
return_value = pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification_impl((xmlparseobject *)self, cls, max_factor);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
#endif /* (XML_COMBINED_VERSION >= 20400) */
|
||||
|
||||
#if (XML_COMBINED_VERSION >= 20702)
|
||||
|
||||
PyDoc_STRVAR(pyexpat_xmlparser_SetAllocTrackerActivationThreshold__doc__,
|
||||
@@ -417,7 +551,8 @@ PyDoc_STRVAR(pyexpat_xmlparser_SetAllocTrackerActivationThreshold__doc__,
|
||||
"\n"
|
||||
"Sets the number of allocated bytes of dynamic memory needed to activate protection against disproportionate use of RAM.\n"
|
||||
"\n"
|
||||
"By default, parser objects have an allocation activation threshold of 64 MiB.");
|
||||
"Parser objects usually have an allocation activation threshold of 64 MiB,\n"
|
||||
"but the actual default value depends on the underlying Expat library.");
|
||||
|
||||
#define PYEXPAT_XMLPARSER_SETALLOCTRACKERACTIVATIONTHRESHOLD_METHODDEF \
|
||||
{"SetAllocTrackerActivationThreshold", _PyCFunction_CAST(pyexpat_xmlparser_SetAllocTrackerActivationThreshold), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pyexpat_xmlparser_SetAllocTrackerActivationThreshold__doc__},
|
||||
@@ -481,7 +616,8 @@ PyDoc_STRVAR(pyexpat_xmlparser_SetAllocTrackerMaximumAmplification__doc__,
|
||||
"near the start of parsing even with benign files in practice. In particular,\n"
|
||||
"the activation threshold should be carefully chosen to avoid false positives.\n"
|
||||
"\n"
|
||||
"By default, parser objects have a maximum amplification factor of 100.0.");
|
||||
"Parser objects usually have a maximum amplification factor of 100,\n"
|
||||
"but the actual default value depends on the underlying Expat library.");
|
||||
|
||||
#define PYEXPAT_XMLPARSER_SETALLOCTRACKERMAXIMUMAMPLIFICATION_METHODDEF \
|
||||
{"SetAllocTrackerMaximumAmplification", _PyCFunction_CAST(pyexpat_xmlparser_SetAllocTrackerMaximumAmplification), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pyexpat_xmlparser_SetAllocTrackerMaximumAmplification__doc__},
|
||||
@@ -679,6 +815,14 @@ exit:
|
||||
#define PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF
|
||||
#endif /* !defined(PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF) */
|
||||
|
||||
#ifndef PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONACTIVATIONTHRESHOLD_METHODDEF
|
||||
#define PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONACTIVATIONTHRESHOLD_METHODDEF
|
||||
#endif /* !defined(PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONACTIVATIONTHRESHOLD_METHODDEF) */
|
||||
|
||||
#ifndef PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONMAXIMUMAMPLIFICATION_METHODDEF
|
||||
#define PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONMAXIMUMAMPLIFICATION_METHODDEF
|
||||
#endif /* !defined(PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONMAXIMUMAMPLIFICATION_METHODDEF) */
|
||||
|
||||
#ifndef PYEXPAT_XMLPARSER_SETALLOCTRACKERACTIVATIONTHRESHOLD_METHODDEF
|
||||
#define PYEXPAT_XMLPARSER_SETALLOCTRACKERACTIVATIONTHRESHOLD_METHODDEF
|
||||
#endif /* !defined(PYEXPAT_XMLPARSER_SETALLOCTRACKERACTIVATIONTHRESHOLD_METHODDEF) */
|
||||
@@ -686,4 +830,4 @@ exit:
|
||||
#ifndef PYEXPAT_XMLPARSER_SETALLOCTRACKERMAXIMUMAMPLIFICATION_METHODDEF
|
||||
#define PYEXPAT_XMLPARSER_SETALLOCTRACKERMAXIMUMAMPLIFICATION_METHODDEF
|
||||
#endif /* !defined(PYEXPAT_XMLPARSER_SETALLOCTRACKERMAXIMUMAMPLIFICATION_METHODDEF) */
|
||||
/*[clinic end generated code: output=e73935658c04c83e input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=81101a16a409daf6 input=a9049054013a1b77]*/
|
||||
|
||||
@@ -1174,7 +1174,7 @@ pyexpat_xmlparser_UseForeignDTD_impl(xmlparseobject *self, PyTypeObject *cls,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if XML_COMBINED_VERSION >= 20702
|
||||
#if XML_COMBINED_VERSION >= 20400
|
||||
static PyObject *
|
||||
set_activation_threshold(xmlparseobject *self,
|
||||
PyTypeObject *cls,
|
||||
@@ -1218,6 +1218,80 @@ set_maximum_amplification(xmlparseobject *self,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if XML_COMBINED_VERSION >= 20400
|
||||
/*[clinic input]
|
||||
@permit_long_summary
|
||||
@permit_long_docstring_body
|
||||
pyexpat.xmlparser.SetBillionLaughsAttackProtectionActivationThreshold
|
||||
|
||||
cls: defining_class
|
||||
threshold: unsigned_long_long
|
||||
/
|
||||
|
||||
Sets the number of output bytes needed to activate protection against billion laughs attacks.
|
||||
|
||||
The number of output bytes includes amplification from entity expansion
|
||||
and reading DTD files.
|
||||
|
||||
Parser objects usually have a protection activation threshold of 8 MiB,
|
||||
but the actual default value depends on the underlying Expat library.
|
||||
|
||||
Activation thresholds below 4 MiB are known to break support for DITA 1.3
|
||||
payload and are hence not recommended.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold_impl(xmlparseobject *self,
|
||||
PyTypeObject *cls,
|
||||
unsigned long long threshold)
|
||||
/*[clinic end generated code: output=0c082342f1c78114 input=fa2f91f26b62a42a]*/
|
||||
{
|
||||
return set_activation_threshold(
|
||||
self, cls, threshold,
|
||||
XML_SetBillionLaughsAttackProtectionActivationThreshold
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if XML_COMBINED_VERSION >= 20400
|
||||
/*[clinic input]
|
||||
@permit_long_summary
|
||||
@permit_long_docstring_body
|
||||
pyexpat.xmlparser.SetBillionLaughsAttackProtectionMaximumAmplification
|
||||
|
||||
cls: defining_class
|
||||
max_factor: float
|
||||
/
|
||||
|
||||
Sets the maximum tolerated amplification factor for protection against billion laughs attacks.
|
||||
|
||||
The amplification factor is calculated as "(direct + indirect) / direct"
|
||||
while parsing, where "direct" is the number of bytes read from the primary
|
||||
document in parsing and "indirect" is the number of bytes added by expanding
|
||||
entities and reading external DTD files, combined.
|
||||
|
||||
The 'max_factor' value must be a non-NaN floating point value greater than
|
||||
or equal to 1.0. Amplification factors greater than 30,000 can be observed
|
||||
in the middle of parsing even with benign files in practice. In particular,
|
||||
the activation threshold should be carefully chosen to avoid false positives.
|
||||
|
||||
Parser objects usually have a maximum amplification factor of 100,
|
||||
but the actual default value depends on the underlying Expat library.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification_impl(xmlparseobject *self,
|
||||
PyTypeObject *cls,
|
||||
float max_factor)
|
||||
/*[clinic end generated code: output=c590439eadf463fa input=cc1e97c1fd2bd950]*/
|
||||
{
|
||||
return set_maximum_amplification(
|
||||
self, cls, max_factor,
|
||||
XML_SetBillionLaughsAttackProtectionMaximumAmplification
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if XML_COMBINED_VERSION >= 20702
|
||||
/*[clinic input]
|
||||
@permit_long_summary
|
||||
@@ -1230,14 +1304,15 @@ pyexpat.xmlparser.SetAllocTrackerActivationThreshold
|
||||
|
||||
Sets the number of allocated bytes of dynamic memory needed to activate protection against disproportionate use of RAM.
|
||||
|
||||
By default, parser objects have an allocation activation threshold of 64 MiB.
|
||||
Parser objects usually have an allocation activation threshold of 64 MiB,
|
||||
but the actual default value depends on the underlying Expat library.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
pyexpat_xmlparser_SetAllocTrackerActivationThreshold_impl(xmlparseobject *self,
|
||||
PyTypeObject *cls,
|
||||
unsigned long long threshold)
|
||||
/*[clinic end generated code: output=bed7e93207ba08c5 input=54182cd71ad69978]*/
|
||||
/*[clinic end generated code: output=bed7e93207ba08c5 input=b7a7a3e3d054286a]*/
|
||||
{
|
||||
return set_activation_threshold(
|
||||
self, cls, threshold,
|
||||
@@ -1268,14 +1343,15 @@ or equal to 1.0. Amplification factors greater than 100.0 can be observed
|
||||
near the start of parsing even with benign files in practice. In particular,
|
||||
the activation threshold should be carefully chosen to avoid false positives.
|
||||
|
||||
By default, parser objects have a maximum amplification factor of 100.0.
|
||||
Parser objects usually have a maximum amplification factor of 100,
|
||||
but the actual default value depends on the underlying Expat library.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl(xmlparseobject *self,
|
||||
PyTypeObject *cls,
|
||||
float max_factor)
|
||||
/*[clinic end generated code: output=6e44bd48c9b112a0 input=3544abf9dd7ae055]*/
|
||||
/*[clinic end generated code: output=6e44bd48c9b112a0 input=c6af7ccb76ae5c6b]*/
|
||||
{
|
||||
return set_maximum_amplification(
|
||||
self, cls, max_factor,
|
||||
@@ -1293,6 +1369,8 @@ static struct PyMethodDef xmlparse_methods[] = {
|
||||
PYEXPAT_XMLPARSER_EXTERNALENTITYPARSERCREATE_METHODDEF
|
||||
PYEXPAT_XMLPARSER_SETPARAMENTITYPARSING_METHODDEF
|
||||
PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF
|
||||
PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONACTIVATIONTHRESHOLD_METHODDEF
|
||||
PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONMAXIMUMAMPLIFICATION_METHODDEF
|
||||
PYEXPAT_XMLPARSER_SETALLOCTRACKERACTIVATIONTHRESHOLD_METHODDEF
|
||||
PYEXPAT_XMLPARSER_SETALLOCTRACKERMAXIMUMAMPLIFICATION_METHODDEF
|
||||
PYEXPAT_XMLPARSER_SETREPARSEDEFERRALENABLED_METHODDEF
|
||||
@@ -2307,6 +2385,13 @@ pyexpat_exec(PyObject *mod)
|
||||
capi->SetAllocTrackerActivationThreshold = NULL;
|
||||
capi->SetAllocTrackerMaximumAmplification = NULL;
|
||||
#endif
|
||||
#if XML_COMBINED_VERSION >= 20400
|
||||
capi->SetBillionLaughsAttackProtectionActivationThreshold = XML_SetBillionLaughsAttackProtectionActivationThreshold;
|
||||
capi->SetBillionLaughsAttackProtectionMaximumAmplification = XML_SetBillionLaughsAttackProtectionMaximumAmplification;
|
||||
#else
|
||||
capi->SetAllocTrackerActivationThreshold = NULL;
|
||||
capi->SetAllocTrackerMaximumAmplification = NULL;
|
||||
#endif
|
||||
|
||||
/* export using capsule */
|
||||
PyObject *capi_object = PyCapsule_New(capi, PyExpat_CAPSULE_NAME,
|
||||
|
||||
Reference in New Issue
Block a user