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
- Remove DTD/entity declarations from the document (re-save with Word/LibreOffice).
- Replace entity references with literal text before attaching.
- If legitimate entity use is required, preprocess the XML (resolve entities safely, e.g. with a hardened parser) before extraction.
- 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
- Never disable this check — it blocks XXE/billion-laughs attacks.
- Re-save documents with Word/LibreOffice to normalize XML and drop custom entities.
- Sanitize third-party-generated office files before ingesting.
- Log rejected files for security review; they may be probing.
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
- Office XML exceeds the parser input limit.
- XLSX workbook contained no sheets.
- External URL protocol "${parsed.protocol}" is not allowed.
- invalid_payload
- Attachment hostname ${hostname} resolved to a private or res
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/017df4631041cfb2.
Report an issue: GitHub.