|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2014 the original author or authors. |
|
|
|
* Copyright 2002-2015 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -16,10 +16,16 @@ |
|
|
|
|
|
|
|
|
|
|
|
package org.springframework.http.converter.xml; |
|
|
|
package org.springframework.http.converter.xml; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import static org.custommonkey.xmlunit.XMLAssert.*; |
|
|
|
|
|
|
|
import static org.junit.Assert.assertEquals; |
|
|
|
|
|
|
|
import static org.junit.Assert.*; |
|
|
|
|
|
|
|
import static org.junit.Assert.assertTrue; |
|
|
|
|
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
import java.io.IOException; |
|
|
|
import java.io.InputStreamReader; |
|
|
|
import java.io.InputStreamReader; |
|
|
|
import java.io.StringReader; |
|
|
|
import java.io.StringReader; |
|
|
|
import java.nio.charset.Charset; |
|
|
|
import java.nio.charset.Charset; |
|
|
|
|
|
|
|
|
|
|
|
import javax.xml.parsers.DocumentBuilderFactory; |
|
|
|
import javax.xml.parsers.DocumentBuilderFactory; |
|
|
|
import javax.xml.stream.XMLStreamReader; |
|
|
|
import javax.xml.stream.XMLStreamReader; |
|
|
|
import javax.xml.transform.Source; |
|
|
|
import javax.xml.transform.Source; |
|
|
|
@ -29,7 +35,9 @@ import javax.xml.transform.stax.StAXSource; |
|
|
|
import javax.xml.transform.stream.StreamSource; |
|
|
|
import javax.xml.transform.stream.StreamSource; |
|
|
|
|
|
|
|
|
|
|
|
import org.junit.Before; |
|
|
|
import org.junit.Before; |
|
|
|
|
|
|
|
import org.junit.Rule; |
|
|
|
import org.junit.Test; |
|
|
|
import org.junit.Test; |
|
|
|
|
|
|
|
import org.junit.rules.ExpectedException; |
|
|
|
import org.w3c.dom.Document; |
|
|
|
import org.w3c.dom.Document; |
|
|
|
import org.w3c.dom.Element; |
|
|
|
import org.w3c.dom.Element; |
|
|
|
import org.xml.sax.InputSource; |
|
|
|
import org.xml.sax.InputSource; |
|
|
|
@ -42,13 +50,10 @@ import org.springframework.core.io.Resource; |
|
|
|
import org.springframework.http.MediaType; |
|
|
|
import org.springframework.http.MediaType; |
|
|
|
import org.springframework.http.MockHttpInputMessage; |
|
|
|
import org.springframework.http.MockHttpInputMessage; |
|
|
|
import org.springframework.http.MockHttpOutputMessage; |
|
|
|
import org.springframework.http.MockHttpOutputMessage; |
|
|
|
|
|
|
|
import org.springframework.http.converter.HttpMessageNotReadableException; |
|
|
|
import org.springframework.util.FileCopyUtils; |
|
|
|
import org.springframework.util.FileCopyUtils; |
|
|
|
|
|
|
|
|
|
|
|
import static org.custommonkey.xmlunit.XMLAssert.*; |
|
|
|
|
|
|
|
// Do NOT statically import org.junit.Assert.*, since XMLAssert extends junit.framework.Assert
|
|
|
|
// Do NOT statically import org.junit.Assert.*, since XMLAssert extends junit.framework.Assert
|
|
|
|
import static org.junit.Assert.assertEquals; |
|
|
|
|
|
|
|
import static org.junit.Assert.assertNotEquals; |
|
|
|
|
|
|
|
import static org.junit.Assert.assertTrue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @author Arjen Poutsma |
|
|
|
* @author Arjen Poutsma |
|
|
|
@ -61,6 +66,10 @@ public class SourceHttpMessageConverterTests { |
|
|
|
|
|
|
|
|
|
|
|
private String bodyExternal; |
|
|
|
private String bodyExternal; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Rule |
|
|
|
|
|
|
|
public ExpectedException thrown = ExpectedException.none(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Before |
|
|
|
@Before |
|
|
|
public void setUp() throws IOException { |
|
|
|
public void setUp() throws IOException { |
|
|
|
converter = new SourceHttpMessageConverter<Source>(); |
|
|
|
converter = new SourceHttpMessageConverter<Source>(); |
|
|
|
@ -97,12 +106,40 @@ public class SourceHttpMessageConverterTests { |
|
|
|
public void readDOMSourceExternal() throws Exception { |
|
|
|
public void readDOMSourceExternal() throws Exception { |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(bodyExternal.getBytes("UTF-8")); |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(bodyExternal.getBytes("UTF-8")); |
|
|
|
inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); |
|
|
|
inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); |
|
|
|
|
|
|
|
converter.setSupportDtd(true); |
|
|
|
DOMSource result = (DOMSource) converter.read(DOMSource.class, inputMessage); |
|
|
|
DOMSource result = (DOMSource) converter.read(DOMSource.class, inputMessage); |
|
|
|
Document document = (Document) result.getNode(); |
|
|
|
Document document = (Document) result.getNode(); |
|
|
|
assertEquals("Invalid result", "root", document.getDocumentElement().getLocalName()); |
|
|
|
assertEquals("Invalid result", "root", document.getDocumentElement().getLocalName()); |
|
|
|
assertNotEquals("Invalid result", "Foo Bar", document.getDocumentElement().getTextContent()); |
|
|
|
assertNotEquals("Invalid result", "Foo Bar", document.getDocumentElement().getTextContent()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
public void readDomSourceWithXmlBomb() throws Exception { |
|
|
|
|
|
|
|
// https://en.wikipedia.org/wiki/Billion_laughs
|
|
|
|
|
|
|
|
// https://msdn.microsoft.com/en-us/magazine/ee335713.aspx
|
|
|
|
|
|
|
|
String content = "<?xml version=\"1.0\"?>\n" + |
|
|
|
|
|
|
|
"<!DOCTYPE lolz [\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol \"lol\">\n" + |
|
|
|
|
|
|
|
" <!ELEMENT lolz (#PCDATA)>\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">\n" + |
|
|
|
|
|
|
|
"]>\n" + |
|
|
|
|
|
|
|
"<root>&lol9;</root>"; |
|
|
|
|
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(content.getBytes("UTF-8")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.thrown.expect(HttpMessageNotReadableException.class); |
|
|
|
|
|
|
|
this.thrown.expectMessage("DOCTYPE is disallowed"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.converter.read(DOMSource.class, inputMessage); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void readSAXSource() throws Exception { |
|
|
|
public void readSAXSource() throws Exception { |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(BODY.getBytes("UTF-8")); |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(BODY.getBytes("UTF-8")); |
|
|
|
@ -117,6 +154,7 @@ public class SourceHttpMessageConverterTests { |
|
|
|
public void readSAXSourceExternal() throws Exception { |
|
|
|
public void readSAXSourceExternal() throws Exception { |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(bodyExternal.getBytes("UTF-8")); |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(bodyExternal.getBytes("UTF-8")); |
|
|
|
inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); |
|
|
|
inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); |
|
|
|
|
|
|
|
converter.setSupportDtd(true); |
|
|
|
SAXSource result = (SAXSource) converter.read(SAXSource.class, inputMessage); |
|
|
|
SAXSource result = (SAXSource) converter.read(SAXSource.class, inputMessage); |
|
|
|
InputSource inputSource = result.getInputSource(); |
|
|
|
InputSource inputSource = result.getInputSource(); |
|
|
|
XMLReader reader = result.getXMLReader(); |
|
|
|
XMLReader reader = result.getXMLReader(); |
|
|
|
@ -130,6 +168,37 @@ public class SourceHttpMessageConverterTests { |
|
|
|
reader.parse(inputSource); |
|
|
|
reader.parse(inputSource); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
public void readSAXSourceWithXmlBomb() throws Exception { |
|
|
|
|
|
|
|
// https://en.wikipedia.org/wiki/Billion_laughs
|
|
|
|
|
|
|
|
// https://msdn.microsoft.com/en-us/magazine/ee335713.aspx
|
|
|
|
|
|
|
|
String content = "<?xml version=\"1.0\"?>\n" + |
|
|
|
|
|
|
|
"<!DOCTYPE lolz [\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol \"lol\">\n" + |
|
|
|
|
|
|
|
" <!ELEMENT lolz (#PCDATA)>\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">\n" + |
|
|
|
|
|
|
|
"]>\n" + |
|
|
|
|
|
|
|
"<root>&lol9;</root>"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(content.getBytes("UTF-8")); |
|
|
|
|
|
|
|
SAXSource result = (SAXSource) this.converter.read(SAXSource.class, inputMessage); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.thrown.expect(SAXException.class); |
|
|
|
|
|
|
|
this.thrown.expectMessage("DOCTYPE is disallowed"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
InputSource inputSource = result.getInputSource(); |
|
|
|
|
|
|
|
XMLReader reader = result.getXMLReader(); |
|
|
|
|
|
|
|
reader.parse(inputSource); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void readStAXSource() throws Exception { |
|
|
|
public void readStAXSource() throws Exception { |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(BODY.getBytes("UTF-8")); |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(BODY.getBytes("UTF-8")); |
|
|
|
@ -149,6 +218,7 @@ public class SourceHttpMessageConverterTests { |
|
|
|
public void readStAXSourceExternal() throws Exception { |
|
|
|
public void readStAXSourceExternal() throws Exception { |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(bodyExternal.getBytes("UTF-8")); |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(bodyExternal.getBytes("UTF-8")); |
|
|
|
inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); |
|
|
|
inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); |
|
|
|
|
|
|
|
converter.setSupportDtd(true); |
|
|
|
StAXSource result = (StAXSource) converter.read(StAXSource.class, inputMessage); |
|
|
|
StAXSource result = (StAXSource) converter.read(StAXSource.class, inputMessage); |
|
|
|
XMLStreamReader streamReader = result.getXMLStreamReader(); |
|
|
|
XMLStreamReader streamReader = result.getXMLStreamReader(); |
|
|
|
assertTrue(streamReader.hasNext()); |
|
|
|
assertTrue(streamReader.hasNext()); |
|
|
|
@ -161,6 +231,39 @@ public class SourceHttpMessageConverterTests { |
|
|
|
streamReader.close(); |
|
|
|
streamReader.close(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
public void readStAXSourceWithXmlBomb() throws Exception { |
|
|
|
|
|
|
|
// https://en.wikipedia.org/wiki/Billion_laughs
|
|
|
|
|
|
|
|
// https://msdn.microsoft.com/en-us/magazine/ee335713.aspx
|
|
|
|
|
|
|
|
String content = "<?xml version=\"1.0\"?>\n" + |
|
|
|
|
|
|
|
"<!DOCTYPE lolz [\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol \"lol\">\n" + |
|
|
|
|
|
|
|
" <!ELEMENT lolz (#PCDATA)>\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">\n" + |
|
|
|
|
|
|
|
" <!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">\n" + |
|
|
|
|
|
|
|
"]>\n" + |
|
|
|
|
|
|
|
"<root>&lol9;</root>"; |
|
|
|
|
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(content.getBytes("UTF-8")); |
|
|
|
|
|
|
|
StAXSource result = (StAXSource) this.converter.read(StAXSource.class, inputMessage); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
XMLStreamReader streamReader = result.getXMLStreamReader(); |
|
|
|
|
|
|
|
assertTrue(streamReader.hasNext()); |
|
|
|
|
|
|
|
streamReader.next(); |
|
|
|
|
|
|
|
streamReader.next(); |
|
|
|
|
|
|
|
String s = streamReader.getLocalName(); |
|
|
|
|
|
|
|
assertEquals("root", s); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.thrown.expectMessage("\"lol9\""); |
|
|
|
|
|
|
|
s = streamReader.getElementText(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
public void readStreamSource() throws Exception { |
|
|
|
public void readStreamSource() throws Exception { |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(BODY.getBytes("UTF-8")); |
|
|
|
MockHttpInputMessage inputMessage = new MockHttpInputMessage(BODY.getBytes("UTF-8")); |
|
|
|
|