XmlExternalReferenceTest.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tika.parser;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import org.apache.tika.exception.TikaException;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.HttpHeaders;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;
import org.apache.tika.mime.MediaType;
import org.apache.tika.mime.MediaTypeRegistry;
import org.apache.tika.utils.XMLReaderUtils;
/**
* Refused external references land on the metadata of the document whose XML made them,
* and the container is flagged when an embedded document made any.
*/
public class XmlExternalReferenceTest {
private static final String DTD = "file:///couldnt_possibly_exist/a.dtd";
private static final String ENTITY = "http://127.0.0.1:9/e.txt";
private static final String XML = "<!DOCTYPE r SYSTEM \"" + DTD + "\" [<!ENTITY e SYSTEM \"" +
ENTITY + "\">]><r>&e;</r>";
// parses XML through the two chokepoints, and can parse a nested "embedded" document
private static class XmlParser implements Parser {
private final boolean dom;
private Parser embedded;
private byte[] embeddedBytes;
private Metadata innerMetadata;
XmlParser(boolean dom) {
this.dom = dom;
}
@Override
public Set<MediaType> getSupportedTypes(ParseContext context) {
return Collections.singleton(MediaType.APPLICATION_XML);
}
@Override
public void parse(TikaInputStream stream, ContentHandler handler, Metadata metadata,
ParseContext context) throws IOException, SAXException, TikaException {
if (dom) {
XMLReaderUtils.buildDOM(stream, context);
} else {
XMLReaderUtils.parseSAX(stream, new DefaultHandler(), context);
}
if (embedded != null) {
innerMetadata = new Metadata();
innerMetadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, "inner.xml");
innerMetadata.set(HttpHeaders.CONTENT_TYPE, "application/xml");
try (TikaInputStream tis = TikaInputStream.get(embeddedBytes)) {
embedded.parse(tis, new DefaultHandler(), innerMetadata, context);
}
}
}
}
private static Metadata parse(XmlParser xmlParser, String xml) throws Exception {
CompositeParser composite = new CompositeParser(MediaTypeRegistry.getDefaultRegistry(),
List.of(xmlParser));
Metadata metadata = new Metadata();
metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, "outer.xml");
metadata.set(HttpHeaders.CONTENT_TYPE, "application/xml");
try (TikaInputStream tis = TikaInputStream.get(xml.getBytes(StandardCharsets.UTF_8))) {
composite.parse(tis, new DefaultHandler(), metadata, new ParseContext());
}
return metadata;
}
@Test
public void testSaxRecordsOnDocument() throws Exception {
Metadata m = parse(new XmlParser(false), XML);
assertArrayEquals(new String[]{DTD, ENTITY},
m.getValues(TikaCoreProperties.XML_EXTERNAL_REFERENCE));
assertEquals(2, m.getInt(TikaCoreProperties.XML_EXTERNAL_REFERENCE_COUNT));
assertNull(m.get(TikaCoreProperties.XML_EXTERNAL_REFERENCE_EMBEDDED));
}
@Test
public void testDomRecordsOnDocument() throws Exception {
Metadata m = parse(new XmlParser(true), XML);
assertTrue(m.getInt(TikaCoreProperties.XML_EXTERNAL_REFERENCE_COUNT) >= 1);
assertEquals(DTD, m.getValues(TikaCoreProperties.XML_EXTERNAL_REFERENCE)[0]);
}
@Test
public void testCleanDocumentRecordsNothing() throws Exception {
Metadata m = parse(new XmlParser(false), "<r>plain</r>");
assertNull(m.get(TikaCoreProperties.XML_EXTERNAL_REFERENCE));
assertNull(m.get(TikaCoreProperties.XML_EXTERNAL_REFERENCE_COUNT));
}
@Test
public void testEmbeddedRecordsOnInnerAndFlagsOuter() throws Exception {
XmlParser outer = new XmlParser(false);
outer.embedded = new CompositeParser(MediaTypeRegistry.getDefaultRegistry(),
List.of(new XmlParser(false)));
outer.embeddedBytes = XML.getBytes(StandardCharsets.UTF_8);
Metadata m = parse(outer, "<r>plain outer</r>");
assertNull(m.get(TikaCoreProperties.XML_EXTERNAL_REFERENCE));
assertEquals("true", m.get(TikaCoreProperties.XML_EXTERNAL_REFERENCE_EMBEDDED));
assertArrayEquals(new String[]{DTD, ENTITY},
outer.innerMetadata.getValues(TikaCoreProperties.XML_EXTERNAL_REFERENCE));
assertNull(outer.innerMetadata.get(TikaCoreProperties.XML_EXTERNAL_REFERENCE_EMBEDDED));
}
}