Skip to content

Add resource-exhaustion guards for untrusted archives and URLs - #2296

Open
Sash (SashaMIT) wants to merge 2 commits into
microsoft:mainfrom
SashaMIT:fix/zip-bomb-and-url-fetch-guards
Open

Add resource-exhaustion guards for untrusted archives and URLs#2296
Sash (SashaMIT) wants to merge 2 commits into
microsoft:mainfrom
SashaMIT:fix/zip-bomb-and-url-fetch-guards

Conversation

@SashaMIT

Copy link
Copy Markdown

Problem

When converting untrusted input, two code paths allow unbounded resource consumption:

  1. ZipConverter extracts every archive member into memory with no limits on member count, total uncompressed size, or compression ratio. A small crafted archive (a zip bomb, including nested archives processed recursively) can expand into gigabytes of memory and CPU work.
  2. convert_uri() fetches remote content with no explicit timeout, and convert_response() buffers the entire response body with no size cap. A slow or endless endpoint can hang a conversion indefinitely or stream unbounded data into memory.

Changes

ZipConverter guards (_zip_converter.py), each overridable per conversion via keyword arguments:

  • zip_max_members (default 10000): maximum number of archive members
  • zip_max_total_uncompressed_size (default 500 MB): enforced against declared sizes up front, and against actual bytes read during chunked extraction, since header sizes in crafted archives cannot be trusted
  • zip_max_compression_ratio (default 100): per-member ratio check, applied only to members larger than 1 MB so small legitimately compressible files are not affected

Archives exceeding a limit fail with a FileConversionException describing the violated limit.

URL fetch guards (_markitdown.py):

  • Explicit timeout passed to requests (default 30 seconds, overridable via the timeout keyword argument); requests.Timeout surfaces on slow endpoints
  • max_response_size (default 100 MB, overridable via keyword argument): fast-fail on oversized Content-Length headers, and streaming reads abort past the limit with a FileConversionException

Defaults are conservative but chosen so normal documents are unaffected; the existing test_files.zip vector and the existing module tests pass unchanged.

Tests

New tests/test_resource_guards.py (11 tests, no live network; HTTP is mocked):

  • Zip: member count, compression ratio, and total size limits each reject crafted archives; small compressible members and normal archives still convert
  • URL: default and custom timeout plumbed through to session.get; timeout errors surface; oversized Content-Length rejected before streaming; streaming past the limit aborts; a response exactly at the limit still converts

The guard tests were verified to fail without the source changes (revert-test), and test_module_vectors.py plus test_module_misc.py pass.

Refs #1514 and #1167.

Made with Cursor

ZipConverter extracted every archive member into memory with no limits
on member count, total uncompressed size, or compression ratio, so a
small crafted archive (zip bomb) could exhaust memory and CPU.

Add three guards with conservative defaults, each overridable per
conversion via keyword arguments:

- zip_max_members (default 10000)
- zip_max_total_uncompressed_size (default 500 MB), enforced against
  declared sizes up front and against actual bytes read during
  chunked extraction
- zip_max_compression_ratio (default 100), applied only to members
  larger than 1 MB to avoid false positives on small compressible files

Archives exceeding a limit fail with a FileConversionException.
convert_uri() fetched remote content with no explicit timeout and
convert_response() buffered the entire response body with no size cap,
so a slow or endless endpoint could hang a conversion indefinitely or
exhaust memory.

- Pass an explicit timeout to requests (default 30 seconds,
  overridable via the "timeout" keyword argument)
- Cap the buffered response size (default 100 MB, overridable via the
  "max_response_size" keyword argument), failing fast on oversized
  Content-Length headers and aborting streaming reads past the limit
  with a FileConversionException
@SashaMIT

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant