Skip to content

Commit fb344ab

Browse files
committed
TIKA-4692 -- bug fixes
1 parent 99dcffc commit fb344ab

2 files changed

Lines changed: 138 additions & 8 deletions

File tree

tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-microsoft-module/src/main/java/org/apache/tika/parser/microsoft/ooxml/SXWPFWordExtractorDecorator.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ private void detectSecurityFeatures(PackagePart documentPart, XHTMLContentHandle
194194
PackageRelationshipCollection settingsRels =
195195
documentPart.getRelationshipsByType(SETTINGS_RELATION);
196196
if (settingsRels != null && settingsRels.size() > 0) {
197-
PackagePart settingsPart = documentPart.getRelatedPart(settingsRels.getRelationship(0));
197+
PackagePart settingsPart = safeGetRelatedPart(documentPart, settingsRels.getRelationship(0));
198198
if (settingsPart != null) {
199199
try (InputStream is = settingsPart.getInputStream()) {
200200
WordSettingsHandler handler = new WordSettingsHandler(xhtml);
@@ -214,7 +214,7 @@ private void detectSecurityFeatures(PackagePart documentPart, XHTMLContentHandle
214214
PackageRelationshipCollection webSettingsRels =
215215
documentPart.getRelationshipsByType(WEB_SETTINGS_RELATION);
216216
if (webSettingsRels != null && webSettingsRels.size() > 0) {
217-
PackagePart webSettingsPart = documentPart.getRelatedPart(webSettingsRels.getRelationship(0));
217+
PackagePart webSettingsPart = safeGetRelatedPart(documentPart, webSettingsRels.getRelationship(0));
218218
if (webSettingsPart != null) {
219219
try (InputStream is = webSettingsPart.getInputStream()) {
220220
WebSettingsHandler handler = new WebSettingsHandler(xhtml);
@@ -272,7 +272,10 @@ private void handleDocumentPart(PackagePart documentPart, XHTMLContentHandler xh
272272
if (headersPRC != null) {
273273
for (int i = 0; i < headersPRC.size(); i++) {
274274
PackagePart header =
275-
documentPart.getRelatedPart(headersPRC.getRelationship(i));
275+
safeGetRelatedPart(documentPart, headersPRC.getRelationship(i));
276+
if (header == null) {
277+
continue;
278+
}
276279
handlePart(header, styles, listManager, xhtml,
277280
OOXMLInlineBodyPartMap.EMPTY);
278281
}
@@ -305,7 +308,10 @@ private void handleDocumentPart(PackagePart documentPart, XHTMLContentHandler xh
305308
if (prc != null) {
306309
for (int i = 0; i < prc.size(); i++) {
307310
PackagePart packagePart =
308-
documentPart.getRelatedPart(prc.getRelationship(i));
311+
safeGetRelatedPart(documentPart, prc.getRelationship(i));
312+
if (packagePart == null) {
313+
continue;
314+
}
309315
handlePart(packagePart, styles, listManager, xhtml,
310316
OOXMLInlineBodyPartMap.EMPTY);
311317
}
@@ -457,7 +463,7 @@ private XWPFStylesShim loadStyles(PackagePart packagePart)
457463
if (stylesRelationShip == null) {
458464
return null;
459465
}
460-
PackagePart stylesPart = packagePart.getRelatedPart(stylesRelationShip);
466+
PackagePart stylesPart = safeGetRelatedPart(packagePart, stylesRelationShip);
461467
if (stylesPart == null) {
462468
return null;
463469
}
@@ -477,7 +483,7 @@ private XWPFNumbering loadNumbering(PackagePart packagePart) {
477483
if (numberingRelationShip == null) {
478484
return null;
479485
}
480-
PackagePart numberingPart = packagePart.getRelatedPart(numberingRelationShip);
486+
PackagePart numberingPart = safeGetRelatedPart(packagePart, numberingRelationShip);
481487
if (numberingPart == null) {
482488
return null;
483489
}
@@ -518,8 +524,10 @@ private void addRelatedParts(PackagePart documentPart, List<PackagePart> related
518524
if (prc != null) {
519525
for (int i = 0; i < prc.size(); i++) {
520526
PackagePart packagePart =
521-
documentPart.getRelatedPart(prc.getRelationship(i));
522-
relatedParts.add(packagePart);
527+
safeGetRelatedPart(documentPart, prc.getRelationship(i));
528+
if (packagePart != null) {
529+
relatedParts.add(packagePart);
530+
}
523531
}
524532
}
525533
} catch (InvalidFormatException e) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.tika.parser.microsoft.ooxml;
18+
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertNotNull;
21+
22+
import java.io.ByteArrayInputStream;
23+
import java.io.ByteArrayOutputStream;
24+
import java.io.InputStream;
25+
import java.util.List;
26+
27+
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
28+
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
29+
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
30+
import org.apache.commons.io.IOUtils;
31+
import org.junit.jupiter.api.Test;
32+
33+
import org.apache.tika.TikaTest;
34+
import org.apache.tika.metadata.Metadata;
35+
import org.apache.tika.metadata.TikaCoreProperties;
36+
import org.apache.tika.parser.ParseContext;
37+
import org.apache.tika.parser.microsoft.OfficeParserConfig;
38+
39+
/**
40+
* A docx may declare a relationship to an optional part (numbering.xml,
41+
* settings.xml, ...) whose target is missing from the package -- a truncated or
42+
* otherwise malformed file. POI's {@code PackagePart.getRelatedPart} then throws
43+
* an unchecked {@code IllegalArgumentException}. The streaming (SAX) docx
44+
* extractor must skip the missing part and keep going rather than aborting the
45+
* whole parse (which also drops the file's embedded documents).
46+
*
47+
* <p>Regression guard for the 3.3.2 tika-eval finding: 740 docx crashed on a
48+
* missing numbering.xml (484) or settings.xml (254) because those
49+
* getRelatedPart calls bypassed {@code safeGetRelatedPart}.
50+
*/
51+
public class SXWPFMissingRelatedPartTest extends TikaTest {
52+
53+
@Test
54+
public void testMissingNumberingPart() throws Exception {
55+
assertParsesWithoutPart("word/numbering.xml");
56+
}
57+
58+
@Test
59+
public void testMissingSettingsPart() throws Exception {
60+
assertParsesWithoutPart("word/settings.xml");
61+
}
62+
63+
@Test
64+
public void testMissingStylesPart() throws Exception {
65+
assertParsesWithoutPart("word/styles.xml");
66+
}
67+
68+
@Test
69+
public void testMissingWebSettingsPart() throws Exception {
70+
assertParsesWithoutPart("word/webSettings.xml");
71+
}
72+
73+
/**
74+
* Strips {@code partToDrop} from a known-good docx (leaving the dangling
75+
* relationship in document.xml.rels) and asserts the SAX docx extractor
76+
* still parses it -- without the fix, suppressException=false rethrows the
77+
* IllegalArgumentException and this fails.
78+
*/
79+
private void assertParsesWithoutPart(String partToDrop) throws Exception {
80+
byte[] docx = docxWithoutPart("testWORD_numbered_list.docx", partToDrop);
81+
List<Metadata> metadataList;
82+
try (InputStream is = new ByteArrayInputStream(docx)) {
83+
metadataList = getRecursiveMetadata(is, new Metadata(), saxDocxContext(), false);
84+
}
85+
assertEquals(1, metadataList.size(), "dropped " + partToDrop);
86+
Metadata m = metadataList.get(0);
87+
assertEquals("application/vnd.openxmlformats-officedocument.wordprocessingml.document",
88+
m.get(Metadata.CONTENT_TYPE), "dropped " + partToDrop);
89+
String content = m.get(TikaCoreProperties.TIKA_CONTENT);
90+
assertNotNull(content, "dropped " + partToDrop);
91+
//body text is still recovered, not lost to a catastrophic abort
92+
assertContains("This is another list", content);
93+
assertContains("Within cell 1", content);
94+
}
95+
96+
private ParseContext saxDocxContext() {
97+
ParseContext pc = new ParseContext();
98+
OfficeParserConfig config = new OfficeParserConfig();
99+
config.setUseSAXDocxExtractor(true);
100+
pc.set(OfficeParserConfig.class, config);
101+
return pc;
102+
}
103+
104+
/** Copies the resource docx, omitting a single zip entry. */
105+
private byte[] docxWithoutPart(String resource, String entryName) throws Exception {
106+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
107+
try (ZipArchiveInputStream zin =
108+
new ZipArchiveInputStream(getResourceAsStream("/test-documents/" + resource));
109+
ZipArchiveOutputStream zout = new ZipArchiveOutputStream(bos)) {
110+
ZipArchiveEntry entry;
111+
while ((entry = zin.getNextEntry()) != null) {
112+
if (entry.getName().equals(entryName)) {
113+
continue;
114+
}
115+
zout.putArchiveEntry(new ZipArchiveEntry(entry.getName()));
116+
IOUtils.copy(zin, zout);
117+
zout.closeArchiveEntry();
118+
}
119+
}
120+
return bos.toByteArray();
121+
}
122+
}

0 commit comments

Comments
 (0)