Skip to content
Open
16 changes: 16 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@
History
-------

3.2.0
Comment thread
oschwald marked this conversation as resolved.
+++++

* Added limits to pure Python record and metadata decoding to prevent denial
of service from crafted databases: 65,536 values, 512 nesting levels, and
2 MiB of string and bytes payload. Exceeding a limit raises
``InvalidDatabaseError``. CPython may reach its recursion limit earlier,
which raises the same error.
* Rejected unsigned integers longer than 16 bytes and signed integers longer
than 4 bytes before reading their payload.
* Updated the vendored libmaxminddb to 1.14.0, which adds the same resource
limits to the C extension.
* Truncated reads that previously raised ``IndexError`` or ``struct.error``
now raise ``InvalidDatabaseError``.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bullet is broader than the code. Truncated payload reads — uint, string, bytes, and int32 with size < 4 — still return silently wrong values rather than raising, because slicing doesn't raise (details in my comment on _decode_uint).

What actually changed is narrower: a truncated ctrl byte, a truncated size code, and a short pointer now raise InvalidDatabaseError. Either scope the bullet to that, or add the four length checks and keep the bullet as written.

🤖 Comment by Claude (Claude Code) on behalf of Will.

* Improved pure Python lookup performance.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth one more bullet: Reader now raises InvalidDatabaseError at open time for a database whose search tree extends past the end of the file. Previously such a file opened successfully and failed at the first lookup with a different message.

Confirmed against GeoIP2-City-Test-Invalid-Node-Count.mmdb: on main it opens and get() raises "Invalid node in search tree"; on this branch the constructor raises. That's a user-visible move of a failure from lookup to open, and anyone catching around get() but not around open_database() will notice.

🤖 Comment by Claude (Claude Code) on behalf of Will.


3.1.1 (2026-03-05)
++++++++++++++++++

Expand Down
5 changes: 5 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ The module will return an ``InvalidDatabaseError`` if the database is corrupt
or otherwise invalid. A ``ValueError`` will be thrown if you look up an
invalid IP address or an IPv6 address in an IPv4 database.

The reader also raises ``InvalidDatabaseError`` when one record, or the
database metadata, exceeds its resource limits: 65,536 decoded values, 512
levels of nesting, or 2 MiB of string and bytes data. Real databases stay far

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"512 levels of nesting" is stated flatly here but isn't reachable in the default configuration — a stock CPython caller gets InvalidDatabaseError at roughly 490 levels, via RecursionError (measured; details in my comment on _MAX_DEPTH).

HISTORY.rst hedges this correctly ("CPython may reach its recursion limit earlier, which raises the same error"). This is the user-facing document, so it should carry the same hedge — e.g. "at most 512 levels of nesting; CPython's own recursion limit may stop a decode sooner."

🤖 Comment by Claude (Claude Code) on behalf of Will.

below these limits.

Thread Safety
-------------

Expand Down
Loading
Loading