XML Input Limits
A syntactically valid document can still use too much memory or contain fields your service does not accept. Put limits around the business contract.
How it works
Check a byte limit before building a tree, then validate the expected root and record count. Keep the parser’s normal depth protection enabled; huge_tree deliberately relaxes protections and is inappropriate as a default for unknown documents. These tiny limits are teaching values, not production recommendations. Real services also need request-body limits, deadlines, authentication where relevant and a plan for concurrent requests.
Run the code
Use Python 3 with lxml 6.1.1 in a separate virtual environment. The complete listing embeds its own XML and any schema or stylesheet; run python limits.py. It does not need Apache or MySQL. The shared restrictive parse function rejects declarations for untrusted input. The trusted DTD/entity and streaming lessons explicitly explain their separate fixed-input boundaries.
Download the complete limits.py source. Keep each lab in its own folder. Sources are served as text, not as public executable server scripts. Review the starting setup.
Example
from lxml import etree as E
class DenyExternal(E.Resolver):
def resolve(self, url, public_id, context):
raise ValueError("External resources are disabled")
def parse(text):
if "<!DOCTYPE" in text.upper() or "<!ENTITY" in text.upper():
raise ValueError("Declarations are disabled for untrusted XML")
parser = E.XMLParser(resolve_entities=False, load_dtd=False,
no_network=True, huge_tree=False)
parser.resolvers.add(DenyExternal())
return E.fromstring(text.encode("utf-8"), parser)
source = "<exchange region=\"west\">\n <packet id=\"p1\" available=\"true\"><name>Marigold</name><quantity>12</quantity><category>flower</category></packet>\n <packet id=\"p2\" available=\"false\"><name>Fenugreek</name><quantity>0</quantity><category>herb</category></packet>\n <packet id=\"p3\" available=\"true\"><name>Basil</name><quantity>7</quantity><category>herb</category></packet>\n</exchange>"
root = parse(source)
data = source.encode("utf-8")
if len(data) > 4096:
raise ValueError("Too large")
if root.tag != "exchange" or len(root.findall("packet")) > 10:
raise ValueError("Unsupported exchange")
print("Accepted", len(root.findall("packet")), "packets")
Expected result
Accepted 3 packets
The example accepts the small fixture and checks its root and count. In a real ingress function move the byte-length check before parse; this listing already has a small fixed source to focus on the contract checks. Test just below and above your chosen limit.
Check the exact contract in the XML specification, XPath 1.0, the XSD 1.0 primer, lxml validation documentation and Saxon’s processor documentation.