| name | secure-microsoft-word-validation |
| description | Generate secure microsoft word file validation code. Enforces secure generation of code validating a microsoft word file. Invoke when writing any microsoft word file validation related code. See "security-considerations" metadata for security limitations. |
| allowed-tools | Read Grep Glob |
| metadata | {"category":"security","security-considerations":["Remote template references and external linked-image/content relationships are not blocked by this skill so that legitimate Word templates and linked images remain usable.","Apply network-level controls or a dedicated content-inspection layer to cover those vectors if needed."]} |
Secure Microsoft Word File Validation Code Generation Rules
Apply all rules below when generating or reviewing any code related to validation of a Microsoft Word file.
1. Microsoft Word file validation (CRITICAL)
- ALWAYS ensure that the file is a real Microsoft Word file.
- ALWAYS ensure that the file use the standard named
Office Open XML.
- ALWAYS ensure that the file use the file type named
DOCX.
- ALWAYS ensure that the file has a single extension and is
docx.
- ALWAYS ensure that the file size does not exceed 5 megabytes before opening or parsing it.
- ALWAYS ensure that the total uncompressed size of all ZIP entries does not exceed 50 megabytes before opening or parsing it.
- ALWAYS ensure that the file has no Visual Basic for Application (VBA) macros.
- ALWAYS ensure that the file has no Object Linking and Embedding (OLE) package.
- ALWAYS ensure that the file has no Dynamic Data Exchange (DDE) fields.
Intentional scope limits: remote template references and external linked-image/content relationships are not blocked by this skill so that legitimate Word templates and linked images remain usable. Apply network-level controls or a dedicated content-inspection layer to cover those vectors if needed.
import org.apache.poi.xwpf.usermodel.*;
import java.io.*;
import java.util.List;
public class UnsafeReadWordFile {
public static void main(String[] args) {
String filePath = "document.docx";
try (FileInputStream fis = new FileInputStream(filePath);
XWPFDocument document = new XWPFDocument(fis)) {
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
System.out.println(paragraph.getText());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
import org.apache.poi.openxml4j.opc.*;
import org.apache.poi.xwpf.usermodel.*;
import java.io.*;
public class SafeWordFileReader {
public static void main(String[] args) {
try {
File file = new File("document.docx");
String name = file.getName();
int dotCount = name.length() - name.replace(".", "").length();
if (dotCount != 1) {
throw new SecurityException("File must have exactly one extension. Found: " + name);
}
if (!name.toLowerCase().endsWith(".docx")) {
throw new SecurityException("File extension must be '.docx'. Found: " + name);
}
long maxSizeBytes = 5L * 1024 * 1024;
if (file.length() > maxSizeBytes) {
throw new SecurityException("File size exceeds the maximum allowed size of 5 MB. Found: " + file.length() + " bytes.");
}
try (FileInputStream fis = new FileInputStream(file)) {
byte[] header = new byte[4];
int bytesRead = fis.read(header);
if (bytesRead < 4 || header[0] != 0x50 || header[1] != 0x4B || header[2] != 0x03 || header[3] != 0x04) {
throw new SecurityException("File is not a valid Office Open XML (OOXML/ZIP) file. Magic bytes do not match PK\\x03\\x04.");
}
}
long maxUncompressedBytes = 50L * 1024 * 1024;
long totalUncompressedSize = 0;
byte[] drainBuffer = new byte[8192];
try (java.util.zip.ZipInputStream zis = new java.util.zip.ZipInputStream(new java.io.BufferedInputStream(new FileInputStream(file)))) {
java.util.zip.ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
int bytesRead;
while ((bytesRead = zis.read(drainBuffer)) != -1) {
totalUncompressedSize += bytesRead;
if (totalUncompressedSize > maxUncompressedBytes) {
throw new SecurityException("File total uncompressed size exceeds the maximum allowed size of 50 MB. Possible ZIP bomb detected.");
}
}
zis.closeEntry();
}
}
String oleObjectUri = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject";
String activeXUri = "http://schemas.microsoft.com/office/2006/relationships/activeX";
try (OPCPackage pkg = OPCPackage.open(file)) {
if (pkg.getPartsByName(java.util.regex.Pattern.compile("/word/document\\.xml")).isEmpty()) {
throw new SecurityException("File does not contain 'word/document.xml'. It is not a valid DOCX file.");
}
if (!pkg.getPartsByName(java.util.regex.Pattern.compile("(?i)/word/vbaProject\\.bin")).isEmpty()) {
throw new SecurityException("File contains a VBA macro project (vbaProject.bin). Macro-enabled documents (DOCM) are not allowed.");
}
for (PackagePart part : pkg.getParts()) {
for (PackageRelationship rel : part.getRelationships()) {
String relType = rel.getRelationshipType();
if (relType != null) {
String lower = relType.toLowerCase();
if (lower.startsWith(oleObjectUri.toLowerCase()) || lower.startsWith(activeXUri.toLowerCase())) {
throw new SecurityException("File contains an embedded OLE object in part: " + part.getPartName() + ". OLE packages are not allowed.");
}
}
}
}
java.util.List<PackagePart> docParts = pkg.getPartsByName(java.util.regex.Pattern.compile("/word/document\\.xml"));
if (!docParts.isEmpty()) {
try (java.io.InputStream docIs = docParts.get(0).getInputStream()) {
String docXml = new String(docIs.readAllBytes(), java.nio.charset.StandardCharsets.UTF_8).toUpperCase();
if (java.util.regex.Pattern.compile("\\bDDEAUTO\\b|\\bDDE\\b").matcher(docXml).find()) {
throw new SecurityException("File contains DDE (Dynamic Data Exchange) fields. DDE fields are not allowed.");
}
}
}
}
try (FileInputStream fis = new FileInputStream(file);
XWPFDocument document = new XWPFDocument(fis)) {
System.out.println("=== Paragraphs ===");
for (XWPFParagraph para : document.getParagraphs()) {
if (!para.getText().isBlank()) {
System.out.println(para.getText());
}
}
System.out.println("\n=== Tables ===");
for (XWPFTable table : document.getTables()) {
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
System.out.print(cell.getText() + "\t");
}
System.out.println();
}
}
}
} catch (SecurityException e) {
System.err.println("Security validation failed: " + e.getMessage());
} catch (Exception e) {
System.err.println("Error reading file: " + e.getMessage());
}
}
}
2. Output Checklist
Before finalizing generated code, verify:
References