MarkItDown
MarkItDown security: constrain file access, fetching and conversion
Apply the project’s documented I/O boundary using narrow conversion APIs, isolated workers and explicit plugin/cloud settings for document ingestion.
What you will learn
- A narrow conversion API is not an operating-system sandbox.
- Plugins and external services require distinct configuration decisions.
- Converted document instructions remain untrusted content.
Before you start
- Basic Python and command-line usage
- A non-sensitive document whose contents you can verify
Use the chapter checklist to explain and verify this part of a document ingestion workflow.
Key takeaways
- A narrow conversion API is not an operating-system sandbox.
- Plugins and external services require distinct configuration decisions.
- Converted document instructions remain untrusted content.
Choose the entry point that matches the application
The README warns that conversion performs I/O using the current process privileges. The general convert method accepts multiple input kinds. If your application accepts local files only, use convert_local after resolving an allowed path; if it controls the bytes itself, pass a binary stream to convert_stream.
The narrower method clarifies which acquisition behavior your application intends, but it is not an operating-system sandbox. A process running under an overprivileged account still has that account’s access. Limit the worker’s file mounts and account permissions, and validate a user-supplied path before passing it to any local conversion API.
Make optional behavior explicit
The project keeps third-party plugins disabled by default. Enabling them adds code that can register converters and run in the process. Review the selected plugin and keep its version in the deployment record; installing an optional format dependency is a different decision from authorizing an arbitrary plugin.
Cloud conversion and model-assisted descriptions can send document content to an external service. A local-only product should expose and enforce that choice in its configuration. When a workflow accepts remote inputs, implement the intended URL, redirect and network policy in the acquisition layer rather than assuming the converter provides it.
Operate on untrusted files with bounded resources
Document parsing can consume substantial CPU and memory, and archives can contain many nested items. Apply job limits outside the library and preserve the original failure class in logs. Log identifiers, versions and error summaries instead of storing entire sensitive document contents by default.
Generated Markdown is derived data, not a trusted instruction for a downstream agent. Preserve its source identity and keep it in a document context. A document that tells an assistant to reveal secrets or run a command has not gained authority because it passed through a converter.
Implementation steps
- 1
Define allowed file acquisition and use the corresponding API.
- 2
Limit the worker account, mounts and resource budget.
- 3
Record plugin and external-service configuration.
- 4
Keep source identity and redact sensitive content in logs.
Copy-ready example
from pathlib import Path
from markitdown import MarkItDown
root = Path("/input").resolve()
candidate = (root / "example.docx").resolve()
if not candidate.is_relative_to(root):
raise ValueError("Input must stay inside the assigned directory")
# Use an immutable input mount to avoid path changes after this check.
result = MarkItDown(enable_plugins=False).convert_local(candidate)Frequently asked questions
Does convert_stream make all parsing safe?
No. It gives the caller control of input bytes. Parsers, plugins, resource use and process privileges still need appropriate boundaries.
Can Markdown contain prompt injection?
Yes. Treat its contents as source documents, not as privileged instructions to a model or tool.
Sources
- Pinned sourceSource checked 2026-09-07
- Core dispatcherSource checked 2026-09-07
- Plain-text converterSource checked 2026-09-07
- Public exportsSource checked 2026-09-07