This is an automated email from the git hooks/post-receive script. apo-guest pushed a commit to branch master in repository libspring-java.
commit a8fbd23b3048058d221c01004ff187262d6c5a7b Author: Markus Koschany <a...@gambaru.de> Date: Fri Jan 24 18:00:09 2014 +0100 Fix CVE-2013-6429 CVE-2013-6430. --- debian/changelog | 18 + debian/patches/CVE-2013-6429.patch | 712 +++++++++++++++++++++++++++++++++++++ debian/patches/CVE-2013-6430.patch | 151 ++++++++ debian/patches/series | 2 + 4 files changed, 883 insertions(+) diff --git a/debian/changelog b/debian/changelog index b2876ce..4883ed5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,21 @@ +libspring-java (3.0.6.RELEASE-11) unstable; urgency=high + + * Team upload. + * Fix CVE-2013-6429 and CVE-2013-6430. (Closes: #735420) + - New patches: CVE-2013-6429.patch and CVE-2013-6430.patch. + - Spring MVC's SourceHttpMessageConverter also processed user provided XML + and neither disabled XML external entities nor provided an option to + disable them. SourceHttpMessageConverter has been modified to provide an + option to control the processing of XML external entities and that + processing is now disabled by default. + - The JavaScriptUtils.javaScriptEscape() method did not escape all + characters that are sensitive within either a JS single quoted string, JS + double quoted string, or HTML script data context. In most cases this + will result in an unexploitable parse error but in some cases it could + result in an XSS vulnerability. + + -- Markus Koschany <a...@gambaru.de> Fri, 24 Jan 2014 19:22:14 +0100 + libspring-java (3.0.6.RELEASE-10) unstable; urgency=high * Team upload. diff --git a/debian/patches/CVE-2013-6429.patch b/debian/patches/CVE-2013-6429.patch new file mode 100644 index 0000000..3334c0e --- /dev/null +++ b/debian/patches/CVE-2013-6429.patch @@ -0,0 +1,712 @@ +From: Markus Koschany <a...@gambaru.de> +Date: Fri, 24 Jan 2014 16:46:07 +0100 +Subject: CVE-2013-6429 + +Bug: http://bugs.debian.org/735420 +--- + .../java/org/springframework/util/StreamUtils.java | 183 ++++++++++++++++++++ + .../org/springframework/util/xml/StaxUtils.java | 15 +- + .../converter/xml/SourceHttpMessageConverter.java | 190 +++++++++++++++++---- + .../xml/SourceHttpMessageConverterTests.java | 145 +++++++++++++--- + .../http/converter/xml/external.txt | 1 + + 5 files changed, 478 insertions(+), 56 deletions(-) + create mode 100644 projects/org.springframework.core/src/main/java/org/springframework/util/StreamUtils.java + create mode 100644 projects/org.springframework.web/src/test/resources/org/springframework/http/converter/xml/external.txt + +diff --git a/projects/org.springframework.core/src/main/java/org/springframework/util/StreamUtils.java b/projects/org.springframework.core/src/main/java/org/springframework/util/StreamUtils.java +new file mode 100644 +index 0000000..cc3107d +--- /dev/null ++++ b/projects/org.springframework.core/src/main/java/org/springframework/util/StreamUtils.java +@@ -0,0 +1,183 @@ ++/* ++ * Copyright 2002-2013 the original author or authors. ++ * ++ * Licensed 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.springframework.util; ++ ++import java.io.ByteArrayOutputStream; ++import java.io.FilterInputStream; ++import java.io.FilterOutputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.InputStreamReader; ++import java.io.OutputStream; ++import java.io.OutputStreamWriter; ++import java.io.Writer; ++import java.nio.charset.Charset; ++ ++ ++/** ++ * Simple utility methods for dealing with streams. The copy methods of this class are ++ * similar to those defined in {@link FileCopyUtils} except that all affected streams are ++ * left open when done. All copy methods use a block size of 4096 bytes. ++ * ++ * <p>Mainly for use within the framework, but also useful for application code. ++ * ++ * @author Juergen Hoeller ++ * @author Phillip Webb ++ * @since 3.2.2 ++ * @see FileCopyUtils ++ */ ++public abstract class StreamUtils { ++ ++ public static final int BUFFER_SIZE = 4096; ++ ++ ++ /** ++ * Copy the contents of the given InputStream into a new byte array. ++ * Leaves the stream open when done. ++ * @param in the stream to copy from ++ * @return the new byte array that has been copied to ++ * @throws IOException in case of I/O errors ++ */ ++ public static byte[] copyToByteArray(InputStream in) throws IOException { ++ ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE); ++ copy(in, out); ++ return out.toByteArray(); ++ } ++ ++ /** ++ * Copy the contents of the given InputStream into a String. ++ * Leaves the stream open when done. ++ * @param in the InputStream to copy from ++ * @return the String that has been copied to ++ * @throws IOException in case of I/O errors ++ */ ++ public static String copyToString(InputStream in, Charset charset) throws IOException { ++ Assert.notNull(in, "No InputStream specified"); ++ StringBuilder out = new StringBuilder(); ++ InputStreamReader reader = new InputStreamReader(in, charset); ++ char[] buffer = new char[BUFFER_SIZE]; ++ int bytesRead = -1; ++ while ((bytesRead = reader.read(buffer)) != -1) { ++ out.append(buffer, 0, bytesRead); ++ } ++ return out.toString(); ++ } ++ ++ /** ++ * Copy the contents of the given byte array to the given OutputStream. ++ * Leaves the stream open when done. ++ * @param in the byte array to copy from ++ * @param out the OutputStream to copy to ++ * @throws IOException in case of I/O errors ++ */ ++ public static void copy(byte[] in, OutputStream out) throws IOException { ++ Assert.notNull(in, "No input byte array specified"); ++ Assert.notNull(out, "No OutputStream specified"); ++ out.write(in); ++ } ++ ++ /** ++ * Copy the contents of the given String to the given output OutputStream. ++ * Leaves the stream open when done. ++ * @param in the String to copy from ++ * @param charset the Charset ++ * @param out the OutputStream to copy to ++ * @throws IOException in case of I/O errors ++ */ ++ public static void copy(String in, Charset charset, OutputStream out) throws IOException { ++ Assert.notNull(in, "No input String specified"); ++ Assert.notNull(charset, "No charset specified"); ++ Assert.notNull(out, "No OutputStream specified"); ++ Writer writer = new OutputStreamWriter(out, charset); ++ writer.write(in); ++ writer.flush(); ++ } ++ ++ /** ++ * Copy the contents of the given InputStream to the given OutputStream. ++ * Leaves both streams open when done. ++ * @param in the InputStream to copy from ++ * @param out the OutputStream to copy to ++ * @return the number of bytes copied ++ * @throws IOException in case of I/O errors ++ */ ++ public static int copy(InputStream in, OutputStream out) throws IOException { ++ Assert.notNull(in, "No InputStream specified"); ++ Assert.notNull(out, "No OutputStream specified"); ++ int byteCount = 0; ++ byte[] buffer = new byte[BUFFER_SIZE]; ++ int bytesRead = -1; ++ while ((bytesRead = in.read(buffer)) != -1) { ++ out.write(buffer, 0, bytesRead); ++ byteCount += bytesRead; ++ } ++ out.flush(); ++ return byteCount; ++ } ++ ++ /** ++ * Returns a variant of the given {@link InputStream} where calling ++ * {@link InputStream#close() close()} has no effect. ++ * @param in the InputStream to decorate ++ * @return a version of the InputStream that ignores calls to close ++ */ ++ public static InputStream nonClosing(InputStream in) { ++ Assert.notNull(in, "No InputStream specified"); ++ return new NonClosingInputStream(in); ++ } ++ ++ /** ++ * Returns a variant of the given {@link OutputStream} where calling ++ * {@link OutputStream#close() close()} has no effect. ++ * @param out the OutputStream to decorate ++ * @return a version of the OutputStream that ignores calls to close ++ */ ++ public static OutputStream nonClosing(OutputStream out) { ++ Assert.notNull(out, "No OutputStream specified"); ++ return new NonClosingOutputStream(out); ++ } ++ ++ ++ private static class NonClosingInputStream extends FilterInputStream { ++ ++ public NonClosingInputStream(InputStream in) { ++ super(in); ++ } ++ ++ @Override ++ public void close() throws IOException { ++ } ++ } ++ ++ ++ private static class NonClosingOutputStream extends FilterOutputStream { ++ ++ public NonClosingOutputStream(OutputStream out) { ++ super(out); ++ } ++ ++ @Override ++ public void write(byte[] b, int off, int let) throws IOException { ++ // It is critical that we override this method for performance ++ out.write(b, off, let); ++ } ++ ++ @Override ++ public void close() throws IOException { ++ } ++ } ++} +diff --git a/projects/org.springframework.core/src/main/java/org/springframework/util/xml/StaxUtils.java b/projects/org.springframework.core/src/main/java/org/springframework/util/xml/StaxUtils.java +index 4bd4a7a..dee73ca 100644 +--- a/projects/org.springframework.core/src/main/java/org/springframework/util/xml/StaxUtils.java ++++ b/projects/org.springframework.core/src/main/java/org/springframework/util/xml/StaxUtils.java +@@ -113,7 +113,16 @@ public abstract class StaxUtils { + * 1.4 {@link StAXSource}; {@code false} otherwise. + */ + public static boolean isStaxSource(Source source) { +- return (source instanceof StaxSource || (jaxp14Available && Jaxp14StaxHandler.isStaxSource(source))); ++ return ((source instanceof StaxSource) || (jaxp14Available && Jaxp14StaxHandler.isStaxSource(source))); ++ } ++ ++ /** ++ * Indicate whether the given class is a StAX Source class. ++ * @return {@code true} if {@code source} is a custom StAX source or JAXP ++ * 1.4 {@link StAXSource} class; {@code false} otherwise. ++ */ ++ public static boolean isStaxSourceClass(Class<? extends Source> clazz) { ++ return (StaxSource.class.equals(clazz) || (jaxp14Available && Jaxp14StaxHandler.isStaxSourceClass(clazz))); + } + + // Stax Result +@@ -343,6 +352,10 @@ public abstract class StaxUtils { + return source instanceof StAXSource; + } + ++ private static boolean isStaxSourceClass(Class<? extends Source> clazz) { ++ return StAXSource.class.equals(clazz); ++ } ++ + private static boolean isStaxResult(Result result) { + return result instanceof StAXResult; + } +diff --git a/projects/org.springframework.web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java b/projects/org.springframework.web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java +index 4ba1aac..15b7d8e 100644 +--- a/projects/org.springframework.web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java ++++ b/projects/org.springframework.web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java +@@ -1,5 +1,5 @@ + /* +- * Copyright 2002-2010 the original author or authors. ++ * Copyright 2002-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. +@@ -19,21 +19,40 @@ package org.springframework.http.converter.xml; + import java.io.ByteArrayInputStream; + import java.io.ByteArrayOutputStream; + import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import javax.xml.parsers.DocumentBuilder; ++import javax.xml.parsers.DocumentBuilderFactory; ++import javax.xml.parsers.ParserConfigurationException; ++import javax.xml.stream.XMLInputFactory; ++import javax.xml.stream.XMLStreamException; ++import javax.xml.stream.XMLStreamReader; + import javax.xml.transform.Result; + import javax.xml.transform.Source; + import javax.xml.transform.TransformerException; ++import javax.xml.transform.TransformerFactory; + import javax.xml.transform.dom.DOMResult; + import javax.xml.transform.dom.DOMSource; + import javax.xml.transform.sax.SAXSource; + import javax.xml.transform.stream.StreamResult; + import javax.xml.transform.stream.StreamSource; + ++import org.w3c.dom.Document; + import org.xml.sax.InputSource; ++import org.xml.sax.SAXException; ++import org.xml.sax.XMLReader; ++import org.xml.sax.helpers.XMLReaderFactory; + + import org.springframework.http.HttpHeaders; ++import org.springframework.http.HttpInputMessage; ++import org.springframework.http.HttpOutputMessage; ++import org.springframework.http.MediaType; ++import org.springframework.http.converter.AbstractHttpMessageConverter; + import org.springframework.http.converter.HttpMessageConversionException; + import org.springframework.http.converter.HttpMessageNotReadableException; + import org.springframework.http.converter.HttpMessageNotWritableException; ++import org.springframework.util.StreamUtils; ++import org.springframework.util.xml.StaxUtils; + + /** + * Implementation of {@link org.springframework.http.converter.HttpMessageConverter} +@@ -42,55 +61,154 @@ import org.springframework.http.converter.HttpMessageNotWritableException; + * @author Arjen Poutsma + * @since 3.0 + */ +-public class SourceHttpMessageConverter<T extends Source> extends AbstractXmlHttpMessageConverter<T> { ++public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMessageConverter<T> { + +- @Override ++ private final TransformerFactory transformerFactory = TransformerFactory.newInstance(); ++ ++ private boolean processExternalEntities = false; ++ ++ /** ++ * Sets the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes} ++ * to {@code text/xml} and {@code application/xml}, and {@code application/*-xml}. ++ */ ++ public SourceHttpMessageConverter() { ++ super(MediaType.APPLICATION_XML, MediaType.TEXT_XML, new MediaType("application", "*+xml")); ++ } ++ ++ ++ /** ++ * Indicates whether external XML entities are processed when converting ++ * to a Source. ++ * <p>Default is {@code false}, meaning that external entities are not resolved. ++ */ ++ public void setProcessExternalEntities(boolean processExternalEntities) { ++ this.processExternalEntities = processExternalEntities; ++ } ++ ++ @Override + public boolean supports(Class<?> clazz) { +- return DOMSource.class.equals(clazz) || SAXSource.class.equals(clazz) || StreamSource.class.equals(clazz) || +- Source.class.equals(clazz); ++ return DOMSource.class.equals(clazz) || SAXSource.class.equals(clazz) ++ || StreamSource.class.equals(clazz) || Source.class.equals(clazz); + } + ++ @Override ++ protected T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage) ++ throws IOException, HttpMessageNotReadableException { ++ ++ InputStream body = inputMessage.getBody(); ++ if (DOMSource.class.equals(clazz)) { ++ return (T) readDOMSource(body); ++ } ++ else if (StaxUtils.isStaxSourceClass(clazz)) { ++ return (T) readStAXSource(body); ++ } ++ else if (SAXSource.class.equals(clazz)) { ++ return (T) readSAXSource(body); ++ } ++ else if (StreamSource.class.equals(clazz) || Source.class.equals(clazz)) { ++ return (T) readStreamSource(body); ++ } ++ else { ++ throw new HttpMessageConversionException("Could not read class [" + clazz + ++ "]. Only DOMSource, SAXSource, and StreamSource are supported."); ++ } ++ } ++ ++ private DOMSource readDOMSource(InputStream body) throws IOException { ++ try { ++ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); ++ documentBuilderFactory.setNamespaceAware(true); ++ documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", processExternalEntities); ++ DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); ++ Document document = documentBuilder.parse(body); ++ return new DOMSource(document); ++ } ++ catch (ParserConfigurationException ex) { ++ throw new HttpMessageNotReadableException("Could not set feature: " + ex.getMessage(), ex); ++ } ++ catch (SAXException ex) { ++ throw new HttpMessageNotReadableException("Could not parse document: " + ex.getMessage(), ex); ++ } ++ } ++ ++ private SAXSource readSAXSource(InputStream body) throws IOException { ++ try { ++ XMLReader reader = XMLReaderFactory.createXMLReader(); ++ reader.setFeature("http://xml.org/sax/features/external-general-entities", processExternalEntities); ++ byte[] bytes = StreamUtils.copyToByteArray(body); ++ return new SAXSource(reader, new InputSource(new ByteArrayInputStream(bytes))); ++ } ++ catch (SAXException ex) { ++ throw new HttpMessageNotReadableException("Could not parse document: " + ex.getMessage(), ex); ++ } ++ } ++ ++ private Source readStAXSource(InputStream body) { ++ try { ++ XMLInputFactory inputFactory = XMLInputFactory.newFactory(); ++ inputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", processExternalEntities); ++ XMLStreamReader streamReader = inputFactory.createXMLStreamReader(body); ++ return StaxUtils.createStaxSource(streamReader); ++ } ++ catch (XMLStreamException ex) { ++ throw new HttpMessageNotReadableException("Could not parse document: " + ex.getMessage(), ex); ++ } ++ } ++ ++ private StreamSource readStreamSource(InputStream body) throws IOException { ++ byte[] bytes = StreamUtils.copyToByteArray(body); ++ return new StreamSource(new ByteArrayInputStream(bytes)); ++ } ++ + @Override +- @SuppressWarnings("unchecked") +- protected T readFromSource(Class clazz, HttpHeaders headers, Source source) throws IOException { +- try { +- if (DOMSource.class.equals(clazz)) { +- DOMResult domResult = new DOMResult(); +- transform(source, domResult); +- return (T) new DOMSource(domResult.getNode()); +- } +- else if (SAXSource.class.equals(clazz)) { +- ByteArrayInputStream bis = transformToByteArrayInputStream(source); +- return (T) new SAXSource(new InputSource(bis)); ++ protected Long getContentLength(T t, MediaType contentType) { ++ if (t instanceof DOMSource) { ++ try { ++ CountingOutputStream os = new CountingOutputStream(); ++ transform(t, new StreamResult(os)); ++ return os.count; + } +- else if (StreamSource.class.equals(clazz) || Source.class.equals(clazz)) { +- ByteArrayInputStream bis = transformToByteArrayInputStream(source); +- return (T) new StreamSource(bis); +- } +- else { +- throw new HttpMessageConversionException("Could not read class [" + clazz + +- "]. Only DOMSource, SAXSource, and StreamSource are supported."); ++ catch (TransformerException ex) { ++ // ignore + } + } +- catch (TransformerException ex) { +- throw new HttpMessageNotReadableException("Could not transform from [" + source + "] to [" + clazz + "]", +- ex); +- } +- } +- +- private ByteArrayInputStream transformToByteArrayInputStream(Source source) throws TransformerException { +- ByteArrayOutputStream bos = new ByteArrayOutputStream(); +- transform(source, new StreamResult(bos)); +- return new ByteArrayInputStream(bos.toByteArray()); ++ return null; + } + +- @Override +- protected void writeToResult(T t, HttpHeaders headers, Result result) throws IOException { ++ @Override ++ protected void writeInternal(T t, HttpOutputMessage outputMessage) ++ throws IOException, HttpMessageNotWritableException { + try { ++ Result result = new StreamResult(outputMessage.getBody()); + transform(t, result); + } + catch (TransformerException ex) { +- throw new HttpMessageNotWritableException("Could not transform [" + t + "] to [" + result + "]", ex); ++ throw new HttpMessageNotWritableException("Could not transform [" + t + "] to output message", ex); ++ } ++ } ++ ++ private void transform(Source source, Result result) throws TransformerException { ++ this.transformerFactory.newTransformer().transform(source, result); ++ } ++ ++ ++ private static class CountingOutputStream extends OutputStream { ++ ++ private long count = 0; ++ ++ @Override ++ public void write(int b) throws IOException { ++ count++; ++ } ++ ++ @Override ++ public void write(byte[] b) throws IOException { ++ count += b.length; ++ } ++ ++ @Override ++ public void write(byte[] b, int off, int len) throws IOException { ++ count += len; + } + } + +diff --git a/projects/org.springframework.web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java b/projects/org.springframework.web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java +index bb20f8a..8d47c22 100644 +--- a/projects/org.springframework.web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java ++++ b/projects/org.springframework.web/src/test/java/org/springframework/http/converter/xml/SourceHttpMessageConverterTests.java +@@ -1,5 +1,5 @@ + /* +- * Copyright 2002-2010 the original author or authors. ++ * Copyright 2002-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. +@@ -16,35 +16,60 @@ + + package org.springframework.http.converter.xml; + ++import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; ++import static org.junit.Assert.*; ++import static org.junit.Assert.assertNotEquals; ++ ++import java.io.IOException; ++import java.io.InputStream; + import java.io.InputStreamReader; ++import java.io.StringReader; + import java.nio.charset.Charset; ++ + import javax.xml.parsers.DocumentBuilderFactory; ++import javax.xml.stream.XMLStreamException; ++import javax.xml.stream.XMLStreamReader; + import javax.xml.transform.Source; + import javax.xml.transform.dom.DOMSource; + import javax.xml.transform.sax.SAXSource; ++import javax.xml.transform.stax.StAXSource; + import javax.xml.transform.stream.StreamSource; + +-import static org.custommonkey.xmlunit.XMLAssert.*; + import org.junit.Before; + import org.junit.Test; +-import org.w3c.dom.Document; +-import org.w3c.dom.Element; +-import org.xml.sax.InputSource; + ++import org.springframework.core.io.ClassPathResource; ++import org.springframework.core.io.Resource; + import org.springframework.http.MediaType; + import org.springframework.http.MockHttpInputMessage; + import org.springframework.http.MockHttpOutputMessage; + import org.springframework.util.FileCopyUtils; ++import org.w3c.dom.Document; ++import org.w3c.dom.Element; ++import org.xml.sax.InputSource; ++import org.xml.sax.SAXException; ++import org.xml.sax.XMLReader; ++import org.xml.sax.helpers.DefaultHandler; + +-/** @author Arjen Poutsma */ +-@SuppressWarnings("unchecked") ++/** ++ * @author Arjen Poutsma ++ */ + public class SourceHttpMessageConverterTests { + ++ private static final String BODY = "<root>Hello World</root>"; ++ + private SourceHttpMessageConverter<Source> converter; + ++ private String bodyExternal; ++ + @Before +- public void setUp() { ++ public void setUp() throws IOException { + converter = new SourceHttpMessageConverter<Source>(); ++ Resource external = new ClassPathResource("external.txt", getClass()); ++ ++ bodyExternal = "<!DOCTYPE root [" + ++ " <!ELEMENT root ANY >\n" + ++ " <!ENTITY ext SYSTEM \"" + external.getURI() + "\" >]><root>&ext;</root>"; + } + + @Test +@@ -62,45 +87,100 @@ public class SourceHttpMessageConverterTests { + + @Test + public void readDOMSource() throws Exception { +- String body = "<root>Hello World</root>"; +- MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); ++ MockHttpInputMessage inputMessage = new MockHttpInputMessage(BODY.getBytes("UTF-8")); ++ inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); ++ DOMSource result = (DOMSource) converter.read(DOMSource.class, inputMessage); ++ Document document = (Document) result.getNode(); ++ assertEquals("Invalid result", "root", document.getDocumentElement().getLocalName()); ++ } ++ ++ @Test ++ public void readDOMSourceExternal() throws Exception { ++ MockHttpInputMessage inputMessage = new MockHttpInputMessage(bodyExternal.getBytes("UTF-8")); + inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); + DOMSource result = (DOMSource) converter.read(DOMSource.class, inputMessage); + Document document = (Document) result.getNode(); + assertEquals("Invalid result", "root", document.getDocumentElement().getLocalName()); ++ assertNotEquals("Invalid result", "Foo Bar", document.getDocumentElement().getTextContent()); + } + + @Test + public void readSAXSource() throws Exception { +- String body = "<root>Hello World</root>"; +- MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); ++ MockHttpInputMessage inputMessage = new MockHttpInputMessage(BODY.getBytes("UTF-8")); + inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); + SAXSource result = (SAXSource) converter.read(SAXSource.class, inputMessage); + InputSource inputSource = result.getInputSource(); + String s = FileCopyUtils.copyToString(new InputStreamReader(inputSource.getByteStream())); +- assertXMLEqual("Invalid result", body, s); ++ assertXMLEqual("Invalid result", BODY, s); + } + + @Test ++ public void readSAXSourceExternal() throws Exception { ++ MockHttpInputMessage inputMessage = new MockHttpInputMessage(bodyExternal.getBytes("UTF-8")); ++ inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); ++ SAXSource result = (SAXSource) converter.read(SAXSource.class, inputMessage); ++ InputSource inputSource = result.getInputSource(); ++ XMLReader reader = result.getXMLReader(); ++ reader.setContentHandler(new DefaultHandler() { ++ @Override ++ public void characters(char[] ch, int start, int length) throws SAXException { ++ String s = new String(ch, start, length); ++ assertNotEquals("Invalid result", "Foo Bar", s); ++ } ++ }); ++ reader.parse(inputSource); ++ } ++ ++ @Test ++ public void readStAXSource() throws Exception { ++ MockHttpInputMessage inputMessage = new MockHttpInputMessage(BODY.getBytes("UTF-8")); ++ inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); ++ StAXSource result = (StAXSource) converter.read(StAXSource.class, inputMessage); ++ XMLStreamReader streamReader = result.getXMLStreamReader(); ++ assertTrue(streamReader.hasNext()); ++ streamReader.nextTag(); ++ String s = streamReader.getLocalName(); ++ assertEquals("root", s); ++ s = streamReader.getElementText(); ++ assertEquals("Hello World", s); ++ streamReader.close(); ++ } ++ ++ @Test ++ public void readStAXSourceExternal() throws Exception { ++ MockHttpInputMessage inputMessage = new MockHttpInputMessage(bodyExternal.getBytes("UTF-8")); ++ inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); ++ StAXSource result = (StAXSource) converter.read(StAXSource.class, inputMessage); ++ XMLStreamReader streamReader = result.getXMLStreamReader(); ++ assertTrue(streamReader.hasNext()); ++ streamReader.next(); ++ streamReader.next(); ++ String s = streamReader.getLocalName(); ++ assertEquals("root", s); ++ s = streamReader.getElementText(); ++ assertNotEquals("Foo Bar", s); ++ streamReader.close(); ++ } ++ ++ ++ @Test + public void readStreamSource() throws Exception { +- String body = "<root>Hello World</root>"; +- MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); ++ MockHttpInputMessage inputMessage = new MockHttpInputMessage(BODY.getBytes("UTF-8")); + inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); + StreamSource result = (StreamSource) converter.read(StreamSource.class, inputMessage); + String s = FileCopyUtils.copyToString(new InputStreamReader(result.getInputStream())); +- assertXMLEqual("Invalid result", body, s); ++ assertXMLEqual("Invalid result", BODY, s); + } + + @Test + public void readSource() throws Exception { +- String body = "<root>Hello World</root>"; +- MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes("UTF-8")); ++ MockHttpInputMessage inputMessage = new MockHttpInputMessage(BODY.getBytes("UTF-8")); + inputMessage.getHeaders().setContentType(new MediaType("application", "xml")); + converter.read(Source.class, inputMessage); + } + + @Test +- public void write() throws Exception { ++ public void writeDOMSource() throws Exception { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + Document document = documentBuilderFactory.newDocumentBuilder().newDocument(); +@@ -115,7 +195,34 @@ public class SourceHttpMessageConverterTests { + outputMessage.getBodyAsString(Charset.forName("UTF-8"))); + assertEquals("Invalid content-type", new MediaType("application", "xml"), + outputMessage.getHeaders().getContentType()); ++ assertEquals("Invalid content-length", outputMessage.getBodyAsBytes().length, ++ outputMessage.getHeaders().getContentLength()); + } + ++ @Test ++ public void writeSAXSource() throws Exception { ++ String xml = "<root>Hello World</root>"; ++ SAXSource saxSource = new SAXSource(new InputSource(new StringReader(xml))); ++ ++ MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); ++ converter.write(saxSource, null, outputMessage); ++ assertXMLEqual("Invalid result", "<root>Hello World</root>", ++ outputMessage.getBodyAsString(Charset.forName("UTF-8"))); ++ assertEquals("Invalid content-type", new MediaType("application", "xml"), ++ outputMessage.getHeaders().getContentType()); ++ } ++ ++ @Test ++ public void writeStreamSource() throws Exception { ++ String xml = "<root>Hello World</root>"; ++ StreamSource streamSource = new StreamSource(new StringReader(xml)); ++ ++ MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); ++ converter.write(streamSource, null, outputMessage); ++ assertXMLEqual("Invalid result", "<root>Hello World</root>", ++ outputMessage.getBodyAsString(Charset.forName("UTF-8"))); ++ assertEquals("Invalid content-type", new MediaType("application", "xml"), ++ outputMessage.getHeaders().getContentType()); ++ } + + } +diff --git a/projects/org.springframework.web/src/test/resources/org/springframework/http/converter/xml/external.txt b/projects/org.springframework.web/src/test/resources/org/springframework/http/converter/xml/external.txt +new file mode 100644 +index 0000000..76c7ac2 +--- /dev/null ++++ b/projects/org.springframework.web/src/test/resources/org/springframework/http/converter/xml/external.txt +@@ -0,0 +1 @@ ++Foo Bar diff --git a/debian/patches/CVE-2013-6430.patch b/debian/patches/CVE-2013-6430.patch new file mode 100644 index 0000000..7c4c362 --- /dev/null +++ b/debian/patches/CVE-2013-6430.patch @@ -0,0 +1,151 @@ +From: Markus Koschany <a...@gambaru.de> +Date: Thu, 23 Jan 2014 00:03:13 +0100 +Subject: CVE-2013-6430 + +Bug: http://bugs.debian.org/735420 +--- + .../springframework/web/util/JavaScriptUtils.java | 35 ++++++++--- + .../web/util/JavaScriptUtilsTests.java | 67 ++++++++++++++++++++++ + 2 files changed, 95 insertions(+), 7 deletions(-) + create mode 100644 projects/org.springframework.web/src/test/java/org/springframework/web/util/JavaScriptUtilsTests.java + +diff --git a/projects/org.springframework.web/src/main/java/org/springframework/web/util/JavaScriptUtils.java b/projects/org.springframework.web/src/main/java/org/springframework/web/util/JavaScriptUtils.java +index b28d398..861b46f 100644 +--- a/projects/org.springframework.web/src/main/java/org/springframework/web/util/JavaScriptUtils.java ++++ b/projects/org.springframework.web/src/main/java/org/springframework/web/util/JavaScriptUtils.java +@@ -1,5 +1,5 @@ + /* +- * Copyright 2002-2008 the original author or authors. ++ * Copyright 2002-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. +@@ -21,21 +21,21 @@ package org.springframework.web.util; + * Escapes based on the JavaScript 1.5 recommendation. + * + * <p>Reference: +- * <a href="http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide:Literals#String_Literals"> +- * Core JavaScript 1.5 Guide +- * </a> ++ * <a href="https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Values,_variables,_and_literals#String_literals"> ++ * JavaScript Guide</a> on Mozilla Developer Network. + * + * @author Juergen Hoeller + * @author Rob Harrop ++ * @author Rossen Stoyanchev + * @since 1.1.1 + */ + public class JavaScriptUtils { + + /** +- * Turn special characters into escaped characters conforming to JavaScript. +- * Handles complete character set defined in HTML 4.01 recommendation. ++ * Turn JavaScript special characters into escaped characters. ++ * + * @param input the input string +- * @return the escaped string ++ * @return the string with escaped characters + */ + public static String javaScriptEscape(String input) { + if (input == null) { +@@ -73,6 +73,27 @@ public class JavaScriptUtils { + else if (c == '\f') { + filtered.append("\\f"); + } ++ else if (c == '\b') { ++ filtered.append("\\b"); ++ } ++ // No '\v' in Java, use octal value for VT ascii char ++ else if (c == '\013') { ++ filtered.append("\\v"); ++ } ++ else if (c == '<') { ++ filtered.append("\\u003C"); ++ } ++ else if (c == '>') { ++ filtered.append("\\u003E"); ++ } ++ // Unicode for PS (line terminator in ECMA-262) ++ else if (c == '\u2028') { ++ filtered.append("\\u2028"); ++ } ++ // Unicode for LS (line terminator in ECMA-262) ++ else if (c == '\u2029') { ++ filtered.append("\\u2029"); ++ } + else { + filtered.append(c); + } +diff --git a/projects/org.springframework.web/src/test/java/org/springframework/web/util/JavaScriptUtilsTests.java b/projects/org.springframework.web/src/test/java/org/springframework/web/util/JavaScriptUtilsTests.java +new file mode 100644 +index 0000000..182f18e +--- /dev/null ++++ b/projects/org.springframework.web/src/test/java/org/springframework/web/util/JavaScriptUtilsTests.java +@@ -0,0 +1,67 @@ ++/* ++ * Copyright 2004-2013 the original author or authors. ++ * ++ * Licensed 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.springframework.web.util; ++ ++import static org.junit.Assert.*; ++ ++import java.io.UnsupportedEncodingException; ++ ++import org.junit.Test; ++ ++/** ++ * Test fixture for {@link JavaScriptUtils}. ++ * ++ * @author Rossen Stoyanchev ++ */ ++public class JavaScriptUtilsTests { ++ ++ @Test ++ public void escape() { ++ StringBuilder sb = new StringBuilder(); ++ sb.append('"'); ++ sb.append("'"); ++ sb.append("\\"); ++ sb.append("/"); ++ sb.append("\t"); ++ sb.append("\n"); ++ sb.append("\r"); ++ sb.append("\f"); ++ sb.append("\b"); ++ sb.append("\013"); ++ assertEquals("\\\"\\'\\\\\\/\\t\\n\\n\\f\\b\\v", JavaScriptUtils.javaScriptEscape(sb.toString())); ++ } ++ ++ // SPR-9983 ++ ++ @Test ++ public void escapePsLsLineTerminators() { ++ StringBuilder sb = new StringBuilder(); ++ sb.append('\u2028'); ++ sb.append('\u2029'); ++ String result = JavaScriptUtils.javaScriptEscape(sb.toString()); ++ ++ assertEquals("\\u2028\\u2029", result); ++ } ++ ++ // SPR-9983 ++ ++ @Test ++ public void escapeLessThanGreaterThanSigns() throws UnsupportedEncodingException { ++ assertEquals("\\u003C\\u003E", JavaScriptUtils.javaScriptEscape("<>")); ++ } ++ ++} diff --git a/debian/patches/series b/debian/patches/series index 533ec80..c989a84 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -10,3 +10,5 @@ 0010_velocity_17.diff 0011-java7-compat.patch Add-processExternalEntities-to-JAXB2Marshaller.patch +CVE-2013-6429.patch +CVE-2013-6430.patch -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/libspring-java.git _______________________________________________ pkg-java-commits mailing list pkg-java-commits@lists.alioth.debian.org http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits