DOCX + ReactUntrusted preview HTML

DOCX preview renders unsafe HTML: secure uploaded Word documents

Treat HTML produced from an uploaded DOCX as untrusted content. A DOCX-to-HTML converter does not establish that links and markup are safe to inject into your application page. Sanitize the HTML with a narrow allowlist, restrict link protocols, and consider an isolated preview frame for documents from other users.

This guide covers check 04: Input validation and error handling in the Zenveus Production Readiness Standard.

For builders

What this means, in plain words

Converting a DOCX file to HTML does not make that HTML safe for a browser. A document can contain links or markup your preview should never execute. Sanitize the converted HTML, authorize file access, and test malicious fixtures in an isolated preview.

A scoped repair request

Using an AI builder? Paste this

Use this prompt in Lovable, Cursor, Replit, or Claude Code with the relevant server files available.

Audit my DOCX conversion and preview path for unsafe HTML rendering. Authorize file access before conversion, limit uploads and conversion resources, sanitize output with an explicit HTML and URL allowlist, and test disallowed protocols and markup in isolated fixtures. Work on a branch with synthetic data and mocked external services. Show the smallest diff, identify required adapters and deployment settings, and add allowed and denied tests that prove side effects cannot happen before checks pass. Do not disable security checks to make a test pass.

The same failure may appear as

  • DOCX preview injects HTML into React
  • Word document contains a dangerous link
  • Admin reviews an uploaded document
  • Large DOCX conversion uses excessive resources

Find the failure layer

Run these checks before rewriting anything

Each check removes a class of causes. Keep the first failing result, its timestamp, and the production log beside it.

01

Preview path

Note who can upload and who later views the preview, including admins.

If this failsUpload or retrieval lacks ownership checks; enforce file access before conversion or returning preview HTML.
02

HTML sanitizer

File extension and MIME checks do not sanitize document contents.

If this failsConverted HTML reaches an unsafe sink unsanitized; add an explicit sanitizer policy and avoid mutating HTML after sanitization.
03

Hostile fixtures

Confirm the preview removes or neutralizes it. Test large documents for conversion time and memory limits.

If this failsDisallowed markup survives or executes; inspect the sanitizer policy, library version, and post-sanitization render path.

Ranked diagnosis

Common root causes, in the order we would test them

01

Converter output is trusted

The app returns Mammoth HTML and inserts it without a visible sanitizer.

02

File validation stops at format

A DOCX extension does not validate links or HTML generated from document content.

03

Reviewer sees other users' documents

An authorized admin can still encounter untrusted content uploaded by a different user.

04

Conversion is unbounded

A pathological document may consume excessive CPU or memory without isolation.

Step-by-step repair

How to fix this in your app

Edit the DOCX conversion endpoint and DocumentViewer.tsx at the point that uses dangerouslySetInnerHTML. Apply authorization and upload limits before conversion.

Production safety ruleNever disable access controls, expose service keys, or add wildcard CORS as a routine shortcut.

01

Authorize the stored document before converting it

Resolve the document using both ID and the caller permission. Limit compressed upload size, expanded archive size, conversion time, and memory in an isolated worker; small ZIP files can expand substantially.

02

Sanitize the converter output before rendering

Use a maintained HTML sanitizer with explicit tags and attributes needed for your preview. Start with text, headings, lists, and tables. Exclude links and embedded media until you implement and test their URL policy.

03

Render only the sanitized result

Pass sanitized HTML into dangerouslySetInnerHTML. Do not modify it afterward or run another transformation that can reintroduce unsafe markup. Keep sanitizer dependencies updated.

04

Add isolation for third-party documents

For external uploads, consider a separately isolated preview with restrictive CSP and a sandbox that does not combine script execution with same-origin privileges. Keep authenticated app actions outside the preview.

Implementation example

React browser preview with an explicit allowlist

import DOMPurify from 'dompurify';
export function DocumentPreview({ convertedHtml }) {
  const clean = DOMPurify.sanitize(convertedHtml, {
    ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'h1', 'h2', 'h3',
      'ul', 'ol', 'li', 'table', 'thead', 'tbody', 'tr', 'th', 'td'],
    ALLOWED_ATTR: []
  });
  return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}

This example runs in a browser Client Component and intentionally strips links, images, styles, and attributes. Install and maintain DOMPurify; server-side sanitization needs a supported DOM implementation. Authorize the document on the server as well.

Prove the repair

How to check that the fix worked

Run these checks with synthetic data in your test environment, then repeat the relevant acceptance checks after deployment.

  • Normal DOCX fixture: headings, emphasis, lists, and tables remain readable.
  • Fixture with unsafe links, event attributes, or embedded elements: removed and no script executes.
  • Another user requests the document: denied before conversion.
  • Oversized or highly expanded archive: rejected or worker terminated within limits.

If the check still fails

If unsafe content returns, find rendering paths that bypass sanitation or mutate the sanitized string. If formatting is lost, add only the specific required tags and attributes with regression fixtures.

When the built-in AI fix makes it worse

Recover one reproducible failure.

Pause generated changes, restore a known working branch, and capture one failing request with its logs. Change one layer and rerun the allowed and denied checks before proceeding.

Engineering handoff

What Zenveus checks when the quick fix is not enough

We trace one production request through the complete path, isolate the failing boundary, and leave behind evidence your team can repeat.

File authorization

Upload ownership and access checks before conversion and preview delivery.

Conversion limits

File size, conversion resources, worker isolation, and failure handling.

Sanitizer policy

Allowed HTML, attributes, URL protocols, library version, and later mutations.

Preview fixtures

Disallowed markup and protocols tested against the actual browser rendering sink.

Typical repair pattern

Formatted documents render without unsafe HTML

Authorized users see permitted formatting; a disallowed test link is neutralized and oversized documents fail safely.

Evidence left behind
  • Root-cause note
  • Verified production check
  • Rollback and prevention steps

Before the next release

Prevent this failure from returning

Review every new file-to-HTML conversion before rendering it.
Keep sanitization and resource-limit tests for uploaded documents.

Clear answers

Questions teams ask before they touch production

01Does allowing only `.docx` files make the preview safe?

No. Extension checks select a file format; they do not validate the HTML produced from its contents.

02Is `dangerouslySetInnerHTML` always unsafe?

It is appropriate only when the inserted HTML has been made safe for that context and trusted according to a clear policy.

03Can I just escape the HTML?

Escaping is safe but removes formatted preview. A strict sanitizer can retain permitted formatting while removing unsafe links and markup.

04How do I know the fix worked in my app?

Run the verification checks on this page against your test environment, then repeat the relevant checks after deployment. Example code needs your app's authentication, data model, and configuration; reading the guide alone does not verify your deployment.

Official documentation and library references

Free next step

Check the boundary before you hand it over

The free tool helps you inspect this symptom. Its result does not establish whether the whole app is production ready.

The Verdict

Know whether the symptom is contained or structural.

We can see the symptom from here. What we cannot tell you from outside is whether it is contained or structural. A scanner collects evidence. A named senior engineer makes the decision. For $299, a named senior engineer reads your code and signs a written Verdict against the nine checks in the Zenveus Production Readiness Standard. The 48-hour clock begins when the required access and context are available. If the report does not give your developer a list they can act on, you do not pay.

The 48-hour clock starts when the required access and context are available.

Scroll to Top