different-ai/openwork · error · Error

Office XML DTD and entity declarations are not supported.

Error message

Office XML DTD and entity declarations are not supported.

What it means

assertSafeOfficeXml rejects Office XML containing <!doctype or <!entity declarations. Entity expansion (billion laughs) and external DTDs are classic XXE attacks, so the extractor only accepts plain XML without DTD/entity constructs.

Source

Thrown at apps/server/src/opencode-plugins/openwork-office-attachments.ts:353

  if (kind === "docx") {
    return name === "word/document.xml"
      || /^word\/header\d+\.xml$/.test(name)
      || /^word\/footer\d+\.xml$/.test(name)
      || name === "word/footnotes.xml"
      || name === "word/endnotes.xml"
      || name === "word/comments.xml";
  }
  return /^ppt\/slides\/slide\d+\.xml$/.test(name) || /^ppt\/notesSlides\/notesSlide\d+\.xml$/.test(name);
}

function compareEntryName(left: ZipEntry, right: ZipEntry): number {
  return left.name.localeCompare(right.name, undefined, { numeric: true, sensitivity: "base" });
}

function assertSafeOfficeXml(xml: string): void {
  if (Buffer.byteLength(xml, "utf8") > MAX_ENTRY_UNCOMPRESSED_BYTES) throw new Error("Office XML exceeds the parser input limit.");
  const lower = xml.toLowerCase();
  if (lower.includes("<!doctype") || lower.includes("<!entity")) throw new Error("Office XML DTD and entity declarations are not supported.");
}

function xmlLocalName(name: string): string {
  const colon = name.lastIndexOf(":");
  return (colon === -1 ? name : name.slice(colon + 1)).toLowerCase();
}

function parsedXmlText(xml: string, tagSeparator: string): string {
  assertSafeOfficeXml(xml);
  let text = "";
  let omittedDepth = 0;
  const omittedSeparator = tagSeparator || " ";
  const parser = new Parser({
    onopentag(name) {
      if (omittedDepth > 0) {
        omittedDepth += 1;
      } else if (xmlLocalName(name) === "script" || xmlLocalName(name) === "style") {
        text += omittedSeparator;

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Remove DTD/entity declarations from the document (re-save with Word/LibreOffice).
  2. Replace entity references with literal text before attaching.
  3. If legitimate entity use is required, preprocess the XML (resolve entities safely, e.g. with a hardened parser) before extraction.
  4. Do not disable this check — it is a security guard; sanitize the input instead.

Example fix

// before
<?xml version="1.0"?><!DOCTYPE document [<!ENTITY x "y">]><w:document>...</w:document>
// after
<?xml version="1.0"?><w:document>...</w:document>
Defensive patterns

Strategy: validation

Validate before calling

function xmlHasDtdOrEntity(xml: string): boolean {
  const lower = xml.toLowerCase();
  return lower.includes("<!doctype") || lower.includes("<!entity");
}

Type guard

function isPlainOfficeXml(xml: string): xml is string {
  const lower = xml.toLowerCase();
  return !lower.includes("<!doctype") && !lower.includes("<!entity");
}

Try / catch

try {
  const text = extractOfficeText(kind, bytes);
} catch (err) {
  if (err instanceof Error && err.message.includes("DTD and entity")) {
    throw new ApiError(400, "unsafe_xml", "Attachment contains DTD/entity declarations and was rejected.");
  }
  throw err;
}

Prevention

When it happens

Trigger: extractOfficeText on a docx/pptx whose XML part includes a DOCTYPE declaration or entity definitions — crafted malicious attachments, or files produced by tools that embed custom entities.

Common situations: Security testing with XXE payloads; rare office generators emitting custom entity definitions; hand-edited XML parts.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/017df4631041cfb2. Report an issue: GitHub.