Security

On this page

This page covers security-relevant behavior found directly in the implementation. It is not a security audit — it's a description of what the code does and does not do, so you can assess fit for your threat model.

SSL/TLS verification (WebLoader, MultiURLLoader)

WebLoaderConfig.verify_ssl defaults to True — TLS certificates are verified by default for all web fetches. Setting verify_ssl=False disables this check entirely (passed straight through to requests.get(..., verify=False)), which exposes fetches to man-in-the-middle attacks. Only disable this for trusted internal/dev environments with self-signed certificates, never for fetching arbitrary external content.

Python · 4 lines
from zerda.document_loaders import WebLoader, WebLoaderConfig

# Insecure — avoid in production against untrusted hosts:
config = WebLoaderConfig(verify_ssl=False)

No URL allowlisting or SSRF protection

Neither WebLoader nor AutoLoader (which routes any http(s):///ftp://-prefixed string to WebLoader) perform any validation of the target host — no denylist for internal/private IP ranges (127.0.0.1, 169.254.169.254 cloud metadata endpoints, 10.0.0.0/8, etc.), no scheme restriction beyond the three prefixes checked. If URLs passed to AutoLoader.load()/WebLoader originate from untrusted user input, this is a potential Server-Side Request Forgery (SSRF) vector — a malicious input could cause your server to fetch internal-network resources or cloud metadata endpoints. Implement your own allowlist/validation layer before passing untrusted URLs to these loaders if this is a concern in your deployment.

Password handling (PDFLoader, DocxLoader)

PDF and .doc passwords are passed as plain Python strings through PDFLoaderConfig.password and are used directly in memory (e.g. pdf.authenticate(password), reader.decrypt(password)) — never logged, and not persisted anywhere by this package. Standard secret-handling hygiene (not hardcoding passwords in source, not logging config objects that might contain them) is the caller's responsibility, as it would be for any credential.

LibreOffice subprocess execution (DocxLoader, legacy .doc)

DocxLoader._load_legacy_doc() shells out to a libreoffice/soffice binary found via shutil.which() on the system PATH, with the target file path passed as a command-line argument:

Python · 2 lines
cmd = ["libreoffice", "--headless", "--convert-to", "docx", "--outdir", tmpdir, str(self.file_path)]
subprocess.run(cmd, capture_output=True, timeout=30, check=True)

This uses subprocess.run with a list of arguments (not shell=True), which avoids shell-injection risk from the file path itself. However, be aware that:

  • LibreOffice conversion of a malicious/malformed .doc file executes third-party, complex binary-format parsing code (LibreOffice's own document parser) — the same class of risk inherent to opening any untrusted Office document with any office suite. This package does not sandbox or otherwise isolate that subprocess beyond a 30-second timeout.
  • The converted .docx is written to a tempfile.TemporaryDirectory(), which is cleaned up automatically on exit (including on exceptions, since it's a context manager) — no persistent artifact is left behind under normal operation.

No input sanitization for XML parsing (GraphML/GEXF)

GraphLoader._parse_graphml() and _parse_gexf() use xml.etree.ElementTree.parse(), which is Python's standard-library XML parser. Standard library ElementTree is documented upstream (in Python's own security notes) as vulnerable to certain XML entity expansion attacks ("billion laughs" and similar) unless additional protections are applied — no such protections (e.g. defusedxml) are used in this codebase. If you load GraphML/GEXF files from untrusted sources, treat this the same as parsing any untrusted XML — consider using defusedxml's drop-in ElementTree replacement if this is a concern, since the current implementation does not guard against it.

No sandboxing of format-parsing libraries generally

Every format loader delegates parsing to a third-party library (PyMuPDF, pdfplumber, pypdf, python-docx, openpyxl, BeautifulSoup, pytesseract, etc.). This package adds no sandboxing, resource limits, or malformed-input hardening beyond what those libraries themselves provide. Loading files from untrusted sources carries whatever risk those underlying libraries carry for malformed/malicious input — keep them updated, and consider running ingestion of untrusted files in an isolated environment (container, restricted user, etc.) if your threat model requires it.

Encoding fallback to latin-1 can mask data issues, not security issues

The chardet → latin-1 fallback chain in TextLoader/MarkdownLoader/CSVLoader is a data-quality concern (see Troubleshooting), not a security one — latin-1 decoding cannot fail or execute anything; it simply produces text that may be garbled if the true encoding was something else.

Summary checklist for untrusted input

If your pipeline processes files or URLs from untrusted sources (user uploads, public web content, third-party integrations), consider:

  • Validate/allowlist URLs before passing to WebLoader/MultiURLLoader/AutoLoader to mitigate SSRF risk.
  • Keep verify_ssl=True (the default) for all web fetches against external hosts.
  • Consider defusedxml in place of xml.etree.ElementTree if ingesting GraphML/GEXF from untrusted sources — this requires a code change, since GraphLoader does not currently expose a way to swap the XML parser.
  • Run PDF/Office/image parsing (and especially LibreOffice .doc conversion) in an isolated environment when the source files are untrusted.
  • Keep all optional third-party parsing dependencies (PyMuPDF, pypdf, python-docx, openpyxl, BeautifulSoup, Pillow, Tesseract) up to date, since this package inherits their parsing-security posture directly.

Related pages