IT Security 4 weeks ago

Docling HTML Rendering Vulnerability – Critical File Disclosure Risk & Fix

Docling versions 2.82.0 through 2.90.x contain a high‑severity HTML rendering flaw that lets attackers access arbitrary local files. Upgrade to 2.118.1 or later and apply mitigation strategies such as disabling render_page for untrusted HTML. Docling versions 2.82.0 through 2.90.x contain a high‑severity HTML rendering flaw that lets attackers access arbitrary local files. Upgrade to 2.118.1 or later and apply mitigation strategies such…

IT Security

Docling HTML Rendering Vulnerability: What You Need to Know

Executive Summary

A critical vulnerability has been identified in Docling versions 2.82.0 through 2.90.x that allows attackers to access arbitrary local files through HTML rendering. The flaw exists in the HTML backend’s failure to enforce the enable_local_fetch security option when rendering pages in headless browser mode. This vulnerability can lead to unauthorized disclosure of sensitive files, including environment variables, configuration files, and user credentials. Organizations using affected versions should prioritize upgrading to version 2.118.1 or later immediately.

Severity Level: High
CVSS Estimate: 7.5 (High)
Affected Versions: 2.82.0 – 2.90.x
Fixed Version: 2.118.1 and later


Understanding the Docling Vulnerability

What the Vulnerability Is and How It Works

Docling is a document intelligence library that processes various file formats, including HTML. When configured to render HTML pages using a headless browser (via HTMLBackendOptions(render_page=True)), the application should restrict access to local files through the enable_local_fetch configuration option.

However, in affected versions, this security control fails to function properly. Attackers can craft malicious HTML documents containing file:// URLs that reference local system files. When Docling renders these pages, the headless browser processes the file references without validation, allowing the attacker to read arbitrary files from the system.

The vulnerability occurs because:

  • The enable_local_fetch setting is not properly enforced during HTML rendering operations
  • File URL validation is bypassed in render mode
  • No restrictions prevent the headless browser from accessing the local filesystem

Why the enable_local_fetch Option Fails in Render Mode

The enable_local_fetch parameter is designed to control whether Docling can fetch resources from the local filesystem. In document conversion mode, this setting works as intended. However, when HTML rendering is enabled (render_page=True), the security check is not applied to the headless browser’s resource loading process.

This occurs because:

  1. Separate Processing Paths: The HTML rendering pipeline uses a different code path than standard document processing
  2. Browser-Level Access: The headless browser operates at a lower level and doesn’t inherit the application-level security restrictions
  3. Incomplete Implementation: The security control was not extended to cover all rendering scenarios

Which Versions Are Affected

The vulnerability impacts:

  • Docling 2.82.0 through 2.90.x (inclusive)
  • All configurations using HTMLBackendOptions(render_page=True)
  • Applications processing untrusted or user-supplied HTML

Versions 2.118.1 and later include the security patch and are safe to use.


What Is Affected

Applications Processing Untrusted HTML with render_page=True

Any application that:

  • Accepts HTML input from users or external sources
  • Uses Docling’s HTML backend with rendering enabled
  • Processes this HTML without strict validation

These applications are directly vulnerable to file disclosure attacks.

Shared Hosting Environments and Multi-Tenant Deployments

Organizations running multiple applications or customers on shared infrastructure face elevated risk:

  • One compromised application could expose files from other applications
  • Shared file permissions may allow cross-user file access
  • Environment variables and secrets may be accessible across tenant boundaries

Systems Handling Sensitive Files or Environment Variables

Systems storing sensitive data in locations accessible to the Docling process are at particular risk:

  • .env files containing API keys and database credentials
  • Configuration files with authentication tokens
  • SSH keys and private certificates
  • Application source code
  • Database connection strings

Attack Scenarios and Real-World Impact

How Attackers Exploit HTML Injection to Access Local Files

Scenario 1: Direct File Reference

An attacker submits the following HTML to a document processing application:

<html>
<body>
  <img src="file:///etc/passwd" />
  <img src="file:///.env" />
  <img src="file:///home/user/.ssh/id_rsa" />
</body>
</html>

When Docling renders this HTML with render_page=True, the headless browser attempts to load these files. The rendered output image contains the file contents, which the attacker can retrieve.

Scenario 2: Environment Variable Exfiltration

An attacker crafts HTML that references environment variables through JavaScript:

<html>
<script>
  // Attempt to access environment through various methods
  document.body.innerHTML = process.env.DATABASE_URL;
</script>
</body>
</html>

While JavaScript execution may be limited, the attempt demonstrates the attack surface.

Data Exfiltration Through Rendered Page Images

The rendered output from Docling includes:

  • Text content from accessed files
  • Configuration data
  • Credentials and tokens
  • Source code snippets

Attackers can:

  1. Submit malicious HTML to the vulnerable application
  2. Retrieve the rendered page image
  3. Extract sensitive information from the image
  4. Use extracted credentials for further attacks

Cross-User File Access in Shared Environments

In multi-tenant or shared hosting scenarios:

  • User A uploads HTML that references User B’s files
  • If file permissions allow, User B’s data is exposed
  • The vulnerability becomes a privilege escalation vector
  • Attackers can map the filesystem and discover other users’ sensitive files

Immediate Actions: Upgrade and Patch

Upgrading to Docling 2.118.1 and Later

Step 1: Identify Your Current Version

pip show docling

Look for the version number in the output.

Step 2: Update Docling

pip install --upgrade docling>=2.118.1

Or if using a requirements file:

pip install -r requirements.txt --upgrade

Step 3: Verify the Upgrade

pip show docling
# Confirm version is 2.118.1 or higher

Verifying Your Current Version

Check your deployment across all environments:

import docling
print(docling.__version__)

For containerized deployments, verify the Dockerfile or image:

FROM python:3.10
RUN pip install docling>=2.118.1

Testing After Patching

After upgrading, conduct the following tests:

Test 1: Verify File Access Restrictions

from docling.backend.html import HTMLBackendOptions
from docling.document_converter import DocumentConverter
# This should now properly restrict file access
converter = DocumentConverter(
    format_options={
        "html": HTMLBackendOptions(
            render_page=True,
            enable_local_fetch=False  # Should be enforced
        )
    }
)

Test 2: Test with Malicious HTML

malicious_html = """
<html>
<body>
  <img src="file:///etc/passwd" />
</body>
</html>

In patched versions, this should fail gracefully without exposing file contents.

Test 3: Regression Testing

Ensure legitimate HTML rendering still works:

safe_html = """
<html>
<body>
  <h1>Test Document</h1>
  <p>This is a legitimate document.</p>
</body>
</html>

Mitigation Strategies for Your Environment

Disabling render_page=True for Untrusted HTML

The most effective mitigation is to disable HTML rendering for user-supplied content:

from docling.backend.html import HTMLBackendOptions
from docling.document_converter import DocumentConverter
# Safe configuration for untrusted HTML
converter = DocumentConverter(
    format_options={
        "html": HTMLBackendOptions(
            render_page=False  # Disable rendering for untrusted sources
        )
    }
)

When to use this approach:

  • Processing user-uploaded HTML files
  • Handling HTML from external APIs
  • Any scenario where HTML source cannot be fully trusted

Using Streams Instead of File Paths for Input Handling

Instead of passing file paths, use streams to prevent filesystem access:

# Vulnerable approach (file path)
from pathlib import Path
result = converter.convert(Path("user_upload.html"))
# Safer approach (stream)
from io import BytesIO
with open("user_upload.html", "rb") as f:
    html_content = f.read()
    stream = BytesIO(html_content)
    result = converter.convert(stream)

This approach:

  • Prevents Docling from accessing the filesystem directly
  • Allows you to validate content before processing
  • Enables content filtering and sanitization

Implementing Strict File Access Controls

Configure operating system-level restrictions:

Linux/Unix File Permissions:

# Restrict access to sensitive files
chmod 600 .env
chmod 600 config.yml
chmod 700 /path/to/sensitive/directory
# Ensure application runs with minimal privileges
chown app:app /app/directory
chmod 750 /app/directory

Docker Security:

FROM python:3.10
# Run as non-root user
RUN useradd -m -u 1000 appuser
USER appuser
# Copy application files
COPY --chown=appuser:appuser . /app
WORKDIR /app
# Install dependencies
RUN pip install --user docling>=2.118.1

Restricting Sensitive File Permissions

Implement a defense-in-depth approach:

# Restrict environment files
chmod 600 .env
chmod 600 .env.local
chmod 600 .env.*.local
# Restrict configuration files
chmod 600 config/database.yml
chmod 600 config/secrets.yml
# Restrict SSH keys and certificates
chmod 600 ~/.ssh/id_rsa
chmod 600 /etc/ssl/private/*.key
# Restrict application directories
chmod 750 /var/www/app
chmod 750 /opt/application

Detection and Monitoring

Reviewing Application Logs for Suspicious File Access Patterns

Monitor for indicators of exploitation:

Log Patterns to Watch:

- Requests containing "file://" URLs
- HTML uploads with unusual file references
- Rendering operations on suspicious content
- Failed file access attempts
- Unusual file read operations

Example Log Analysis:

# Search for file:// references in application logs
grep -r "file://" /var/log/application/
# Look for rendering errors with file paths
grep -i "render.*error\|file.*access" /var/log/application/
# Monitor for suspicious HTML uploads
grep -i "html.*upload\|upload.*html" /var/log/application/

Implementing File Access Auditing

Enable system-level file access monitoring:

Linux auditd Configuration:

# Monitor access to sensitive files
auditctl -w /etc/passwd -p wa -k passwd_changes
auditctl -w /root/.ssh -p wa -k ssh_key_access
auditctl -w /home -p wa -k home_access
# Monitor application directory
auditctl -w /var/www/app -p wa -k app_access
# View audit logs
ausearch -k app_access

Application-Level Logging:

import logging
import os
# Configure detailed logging
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Log file access attempts
def safe_convert(file_path):
    logger.info(f"Processing file: {file_path}")
    logger.info(f"File size: {os.path.getsize(file_path)}")
    try:
        result = converter.convert(file_path)
        logger.info("Conversion successful")
        return result
    except Exception as e:
        logger.error(f"Conversion failed: {e}")
        raise

Conducting Static Code Analysis for Vulnerable Configurations

Scan your codebase for vulnerable patterns:

Search for Vulnerable Code:

# Find render_page=True usage
grep -r "render_page\s*=\s*True" .
# Find HTMLBackendOptions usage
grep -r "HTMLBackendOptions" .
# Find Path-based conversions
grep -r "converter.convert(Path" .

Automated Analysis with Python:

import ast
import os
def find_vulnerable_patterns(directory):
    vulnerable_files = []
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith('.py'):
                filepath = os.path.join(root, file)
                with open(filepath, 'r') as f:
                    try:
                        tree = ast.parse(f.read())
                        for node in ast.walk(tree):
                            if isinstance(node, ast.Call):
                                # Check for render_page=True
                                for keyword in node.keywords:
                                    if keyword.arg == 'render_page':
                                        if isinstance(keyword.value, ast.Constant):
                                            if keyword.value.value is True:
                                                vulnerable_files.append(filepath)
                    except:
                        pass
    return vulnerable_files
vulnerable = find_vulnerable_patterns('.')
for file in vulnerable:
    print(f"Review: {file}")
Security monitoring dashboard showing file access patterns and detection of suspicious HTML rendering attempts.
Security monitoring dashboard showing file access patterns and detection of suspicious HTML rendering attempts.

Long-Term Security Practices

Validating and Sanitizing HTML Inputs

Implement input validation before processing:

from html.parser import HTMLParser
import re
class HTMLValidator:
    """Validate HTML before processing with Docling"""
    DANGEROUS_TAGS = ['script', 'iframe', 'object', 'embed']
    DANGEROUS_ATTRIBUTES = ['onload', 'onerror', 'onclick', 'onmouseover']
    @staticmethod
    def is_safe(html_content):
        """Check if HTML contains dangerous elements"""
        # Check for file:// URLs
        if re.search(r'file://', html_content, re.IGNORECASE):
            return False
        # Check for dangerous tags
        for tag in HTMLValidator.DANGEROUS_TAGS:
            if re.search(f'<{tag}', html_content, re.IGNORECASE):
                return False
        # Check for dangerous attributes
        for attr in HTMLValidator.DANGEROUS_ATTRIBUTES:
            if re.search(f'{attr}\s*=', html_content, re.IGNORECASE):
                return False
        return True
# Usage
user_html = request.files['document'].read().decode('utf-8')
if not HTMLValidator.is_safe(user_html):
    raise ValueError("HTML contains potentially dangerous content")
result = converter.convert(user_html)

Maintaining an Inventory of Docling Deployments

Track all systems using Docling:

Inventory Template:

System Version Environment Last Updated Status
API Server 1 2.118.1 Production 2024-01-15 Patched
API Server 2 2.118.1 Production 2024-01-15 Patched
Dev Environment 2.118.1 Development 2024-01-14 Patched
Legacy System 2.85.0 Production 2023-06-01 Vulnerable

Automated Inventory Script:

import subprocess
import json
from datetime import datetime
def get_docling_version():
    """Get installed Docling version"""
    result = subprocess.run(
        ['pip', 'show', 'docling', '--format', 'json'],
        capture_output=True,
        text=True
    )
    return json.loads(result.stdout)
inventory = {
    'timestamp': datetime.now().isoformat(),
    'docling': get_docling_version(),
    'python_version': subprocess.run(
        ['python', '--version'],
        capture_output=True,
        text=True
    ).stdout.strip()
}
print(json.dumps(inventory, indent=2))

Establishing a Patch Management Schedule

Create a formal process for security updates:

Patch Management Workflow:

  1. Detection (Immediate)
  • Monitor security advisories
  • Subscribe to Docling security notifications
  • Review CVE databases
  1. Assessment (Within 24 hours)
  • Identify affected systems
  • Evaluate business impact
  • Plan deployment
  1. Testing (Within 48 hours)
  • Test patches in development environment
  • Verify functionality
  • Conduct regression testing
  1. Deployment (Within 1 week for critical vulnerabilities)
  • Deploy to staging
  • Final validation
  • Deploy to production
  • Monitor for issues
  1. Verification (Ongoing)
  • Confirm all systems updated
  • Update inventory
  • Document completion

Monitoring Security Advisories for Future Updates

Stay informed about security issues:

Recommended Sources:

  • GitHub Security Advisories: Watch the Docling repository for security alerts
  • Python Package Index: Monitor PyPI for security notices
  • Security Mailing Lists: Subscribe to relevant security communities
  • Vendor Notifications: Enable notifications from Docling maintainers

Automated Monitoring:

import requests
from datetime import datetime, timedelta
def check_docling_security():
    """Check for Docling security advisories"""
    # Check GitHub API for security advisories
    url = "https://api.github.com/repos/docling/docling/security/advisories"
    try:
        response = requests.get(url)
        advisories = response.json()
        for advisory in advisories:
            if advisory['state'] == 'published':
                print(f"Advisory: {advisory['summary']}")
                print(f"Severity: {advisory['severity']}")
                print(f"Published: {advisory['published_at']}")
                print()
    except Exception as e:
        print(f"Error checking advisories: {e}")
# Run periodically (e.g., daily)
check_docling_security()

Frequently Asked Questions

What is the primary risk of the Docling vulnerability?

The primary risk is unauthorized access to local files on systems running affected versions of Docling. Attackers can craft malicious HTML documents that reference local files using file:// URLs. When Docling renders these documents with render_page=True, the headless browser reads the files without proper validation, exposing sensitive information such as:

  • Environment variables containing API keys and database credentials
  • Configuration files with authentication tokens
  • SSH private keys and certificates
  • Application source code
  • User data and personal information

In shared hosting or multi-tenant environments, this vulnerability can also enable cross-user file access, allowing one user to read another user’s sensitive files.

How do I check if my application is affected?

Follow these steps to determine if your application is vulnerable:

Step 1: Check Your Docling Version

pip show docling

If the version is between 2.82.0 and 2.90.x (inclusive), your installation is vulnerable.

Step 2: Review Your Code

Search your codebase for:

grep -r "render_page\s*=\s*True" .
grep -r "HTMLBackendOptions" .

If you find render_page=True in your code, you’re using the vulnerable feature.

Step 3: Assess Your Input Sources

Determine if your application processes HTML from untrusted sources:

  • User uploads
  • External APIs
  • Web forms
  • Third-party integrations

If you process untrusted HTML with rendering enabled, your application is vulnerable.

What should I do if I cannot upgrade immediately?

If you cannot upgrade to version 2.118.1 immediately, implement these mitigation measures:

Immediate Actions:

  1. Disable HTML Rendering
   HTMLBackendOptions(render_page=False)
  1. Use Streams Instead of File Paths
   from io import BytesIO
   stream = BytesIO(html_content)
   result = converter.convert(stream)
  1. Implement Input Validation
   if "file://" in html_content:
       raise ValueError("HTML contains file:// references")
  1. Restrict File Permissions
   chmod 600 .env
   chmod 600 config/*.yml
  1. Enable Monitoring
  • Monitor application logs for suspicious activity
  • Implement file access auditing
  • Alert on unusual rendering operations

Timeline for Upgrade:

  • Critical systems: Upgrade within 24-48 hours
  • Production systems: Upgrade within 1 week
  • Non-production systems: Upgrade within 2 weeks

Does this vulnerability affect all Docling versions?

No. The vulnerability affects specific versions:

Vulnerable Versions:

  • 2.82.0 through 2.90.x (inclusive)

Safe Versions:

  • 2.118.1 and later (patched)
  • Versions prior to 2.82.0 (not affected)

Note: Versions prior to 2.82.0 may have other vulnerabilities. It’s recommended to use the latest stable version.

Can I safely use Docling with trusted HTML sources?

Yes, Docling can be safely used with HTML from trusted sources, even in affected versions, if you follow these guidelines:

Safe Usage Conditions:

  1. Disable Rendering for Untrusted Content
   # Only enable rendering for trusted HTML
   if is_trusted_source(html_source):
       options = HTMLBackendOptions(render_page=True)
   else:
       options = HTMLBackendOptions(render_page=False)
  1. Validate HTML Content
   if not contains_dangerous_patterns(html_content):
       result = converter.convert(html_content)
  1. Use Streams for Input
   # Safer than file paths
   stream = BytesIO(trusted_html_content)
   result = converter.convert(stream)
  1. Implement Access Controls
  • Restrict file system permissions
  • Run Docling with minimal privileges
  • Isolate the process in containers

Best Practice: Even with trusted sources, upgrade to version 2.118.1 or later to eliminate the vulnerability entirely.

How do I implement file access monitoring?

Implement monitoring at multiple levels:

Operating System Level (Linux):

# Install auditd if not present
sudo apt-get install auditd
# Monitor sensitive files
sudo auditctl -w /etc/passwd -p wa -k passwd_changes
sudo auditctl -w /root/.ssh -p wa -k ssh_access
sudo auditctl -w /var/www/app -p wa -k app_access
# View logs
sudo ausearch -k app_access

Application Level (Python):

import logging
import functools
import os
# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('file_access.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)
def log_file_access(func):
    """Decorator to log file access"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        logger.info(f"Function called: {func.__name__}")
        logger.info(f"Arguments: {args}, {kwargs}")
        try:
            result = func(*args, **kwargs)
            logger.info(f"Function succeeded: {func.__name__}")
            return result
        except Exception as e:
            logger.error(f"Function failed: {func.__name__} - {e}")
            raise
    return wrapper
@log_file_access
def convert_document(file_path):
    """Convert document with logging"""
    logger.info(f"Processing file: {file_path}")
    logger.info(f"File size: {os.path.getsize(file_path)} bytes")
    return converter.convert(file_path)

Container Level (Docker):

FROM python:3.10
# Enable security logging
RUN apt-get update && apt-get install -y auditd
# Copy application
COPY . /app
WORKDIR /app
# Install dependencies
RUN pip install docling>=2.118.1
# Run with security options
CMD ["python", "app.py"]

What is the difference between using streams versus file paths?

File Paths Approach:

from pathlib import Path
# Vulnerable approach
file_path = Path("user_upload.html")
result = converter.convert(file_path)

Risks:

  • Docling accesses the filesystem directly
  • No opportunity to validate content before processing
  • Vulnerable to directory traversal attacks
  • Harder to control what files are accessed

Streams Approach:

from io import BytesIO
# Safer approach
with open("user_upload.html", "rb") as f:
    content = f.read()
    stream = BytesIO(content)
    result = converter.convert(stream)

Benefits:

  • You control file access
  • Opportunity to validate content before processing
  • Can implement content filtering
  • Easier to audit and monitor
  • Works with in-memory content
  • Prevents directory traversal

Comparison Table:

Aspect File Path Stream
Filesystem Access Direct Controlled
Content Validation After processing Before processing
Security Lower Higher
Performance Slightly faster Slightly slower
Flexibility Limited High
Auditability Harder Easier

Recommendation: Use streams for all untrusted content, especially user uploads and external sources.


References


Conclusion

The Docling HTML rendering vulnerability represents a significant security risk for organizations processing untrusted HTML content. The immediate priority is to upgrade to version 2.118.1 or later. For systems that cannot be upgraded immediately, disabling HTML rendering and implementing strict input validation provide effective interim protection.

By following the mitigation strategies outlined in this advisory, maintaining an inventory of Docling deployments, and establishing a formal patch management process, organizations can significantly reduce their exposure to this and future vulnerabilities. Regular monitoring and adherence to security best practices will help ensure the continued security of your systems.

For questions or additional information, contact your security team or consult the official Docling documentation.


Document Version: 1.0
Last Updated: January 2024
Classification: Security Advisory

← Back to IT Security