html-to-markdown processes untrusted HTML that users save from the web. The threat model
treats every input document as potentially hostile. This document describes the defenses and how to
report issues.
Please open a private security advisory on the repository, or email the maintainers listed in
pyproject.toml. Do not file public issues for undisclosed vulnerabilities. We aim to acknowledge
reports within a few days.
| Threat | Defense |
|---|---|
| Hostile HTML | Parsed statically with a tolerant parser; no JavaScript is ever executed. <script> (except JSON-LD), <style>, <template>, <noscript>, <iframe>, and comments are stripped before processing. |
| Path traversal | Output paths and local image paths are resolved and confined: safe_join rejects any path that escapes the target directory; local images that resolve outside the HTML file's directory are refused. |
| SSRF (remote images) | Remote fetching is off by default. When enabled, URLs must be http(s), the host is DNS-resolved, and any loopback/link-local/private/reserved/multicast/unspecified address is rejected (blocks 169.254.169.254, 127.0.0.1, RFC1918, localhost, [::1], …). |
| Oversized inputs / images | Remote downloads are size-limited (remote_max_bytes, default 25 MiB) and timeout-limited; content type must be image/*. |
| Malicious images | Bytes are written verbatim only after extension is determined by magic-byte sniffing; SVGs are sanitized (see below). |
| Active SVG | sanitize_svg removes <script>, on* event handlers, <foreignObject>, <use>, and external/javascript: hrefs; only safe fragment and inline-image refs survive. |
| Decompression bombs | The tool does not decompress archives. Remote responses are read with a hard byte cap; it does not follow content-encoding into unbounded buffers. |
| Symlinks | Input must be a regular file (symlink targets are resolved and re-checked). Outputs are written to resolved, contained paths. |
| Unwanted writes / clobbering | Non-destructive by default: existing files are never overwritten without --overwrite; --dry-run writes nothing. |
| Credential leakage | Remote fetches send no cookies or credentials and use a neutral User-Agent. |
| Vulnerable dependencies | Dependencies are pinned to ranges in pyproject.toml; CI can run pip-audit. Keep beautifulsoup4/lxml current. |
The default mode performs zero network activity and executes no page JavaScript. The only
possible outbound traffic is opt-in remote image download (--download-remote-images), which is
SSRF-screened as above.
This tool does not, and must not, bypass paywalls, authentication, or DRM, nor fetch live pages or simulate a browser to evade access controls. It only processes content the user already has.