From 0bf29e59bcbba7174c9694f9c2b60e9d8576b3e5 Mon Sep 17 00:00:00 2001 From: Tomas Hofman Date: Mon, 22 Jul 2019 22:59:13 +0200 Subject: [PATCH] Escaping CDATA end tags Fixes #4392 Signed-off-by: Tomas Hofman --- .../html_basic/HtmlResponseWriter.java | 82 ++++++++++++++++++- .../html_basic/HtmlResponseWriterTest.java | 42 ++++++++++ 2 files changed, 120 insertions(+), 4 deletions(-) diff --git a/impl/src/main/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriter.java b/impl/src/main/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriter.java index d3cc8f4a55..9ebf679cfb 100644 --- a/impl/src/main/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriter.java +++ b/impl/src/main/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriter.java @@ -867,7 +867,12 @@ public void writeText(char text) throws IOException { closeStartIfNecessary(); if (dontEscape) { - writer.write(text); + if (writingCdata) { + charHolder[0] = text; + writeUnescapedCData(charHolder, 0, 1); + } else { + writer.write(text); + } } else if (isPartial || !writingCdata) { charHolder[0] = text; HtmlUtils.writeText(writer, escapeUnicode, escapeIso, buffer, charHolder); @@ -906,7 +911,11 @@ public void writeText(char text[]) throws IOException { closeStartIfNecessary(); if (dontEscape) { - writer.write(text); + if (writingCdata) { + writeUnescapedCData(text, 0, text.length); + } else { + writer.write(text); + } } else if (isPartial || !writingCdata) { HtmlUtils.writeText(writer, escapeUnicode, escapeIso, buffer, text); } else { // if writingCdata @@ -943,7 +952,11 @@ public void writeText(Object text, String componentPropertyName) String textStr = text.toString(); if (dontEscape) { - writer.write(textStr); + if (writingCdata) { + writeUnescapedCData(textStr.toCharArray(), 0, textStr.length()); + } else { + writer.write(textStr); + } } else if (isPartial || !writingCdata) { ensureTextBufferCapacity(textStr); HtmlUtils.writeText(writer, @@ -1006,7 +1019,11 @@ public void writeText(char text[], int off, int len) if (len == 0) return; if (dontEscape) { - writer.write(text, off, len); + if (writingCdata) { + writeUnescapedCData(text, off, len); + } else { + writer.write(text, off, len); + } } else if (isPartial || !writingCdata) { HtmlUtils.writeText(writer, escapeUnicode, escapeIso, buffer, text, off, len); } else { // if (writingCdata) @@ -1405,4 +1422,61 @@ private void flushBuffer() throws IOException { writer.write(cdataBuffer, 0, cdataBufferLength); cdataBufferLength = 0; } + + /** + * When writing un-escaped CDATA, "]]>" sequence still has to be escaped by breaking CDATA block. + */ + private void writeUnescapedCData(char[] cbuf, int offset, int length) throws IOException { + // single char case + if (length == 1) { + if (cbuf[offset] == ']') { + appendBuffer(ESCAPEDSINGLEBRACKET); + } else { + appendBuffer(cbuf[offset]); + } + flushBuffer(); + return; + } + + // two char case + if (length == 2) { + if (cbuf[offset] == ']' && cbuf[offset + 1] == ']') { + appendBuffer(ESCAPEDSINGLEBRACKET); + appendBuffer(ESCAPEDSINGLEBRACKET); + } else { + appendBuffer(cbuf[offset]); + appendBuffer(cbuf[offset + 1]); + } + flushBuffer(); + return; + } + + // > 2 char case + boolean last = false; + for (int i = offset; i < length - 2; i++) { + if (cbuf[i] == ']' && cbuf[i + 1] == ']' && cbuf[i + 2] == '>') { + appendBuffer(ESCAPEDEND); + i += 2; + } else { + appendBuffer(cbuf[i]); + } + if (i == (offset + length - 1)) { + last = true; + } + } + // if we didn't look at the last characters, look at them now + if (!last) { + if (cbuf[offset + length - 2] == ']') { + appendBuffer(ESCAPEDSINGLEBRACKET); + } else { + appendBuffer(cbuf[offset + length - 2]); + } + if (cbuf[offset + length - 1] == ']') { + appendBuffer(ESCAPEDSINGLEBRACKET); + } else { + appendBuffer(cbuf[offset + length - 1]); + } + } + flushBuffer(); + } } diff --git a/impl/src/test/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriterTest.java b/impl/src/test/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriterTest.java index cc3c7df405..6502c8f766 100644 --- a/impl/src/test/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriterTest.java +++ b/impl/src/test/java/com/sun/faces/renderkit/html_basic/HtmlResponseWriterTest.java @@ -141,4 +141,46 @@ public void testCDATAWithXHTML() throws Exception { responseWriter.flush(); assertEquals(expected, stringWriter.toString()); } + + /** + * Test CDATA escaping. + */ + @Test + public void testUnescapedCDATA() throws Exception { + String expectedStart = ""; + + // single char + StringWriter stringWriter = new StringWriter(); + HtmlResponseWriter responseWriter = new HtmlResponseWriter(stringWriter, "application/xhtml+xml", "UTF-8"); + responseWriter.startElement("style", null); + responseWriter.startCDATA(); + responseWriter.writeText(']', null); + responseWriter.endCDATA(); + responseWriter.endElement("style"); + responseWriter.flush(); + assertEquals(expectedStart + "]]]>abc", null); + responseWriter.endCDATA(); + responseWriter.endElement("style"); + responseWriter.flush(); + assertEquals(expectedStart + "abc]]]>abc" + expectedEnd, stringWriter.toString()); + } }