Merge pull request #5210 from adangel/issue-5059-core-xml-cdata

[core] Fix PMD's XMLRenderer to escape CDATA
This commit is contained in:
Juan Martín Sotuyo Dodero 2024-09-12 09:42:57 -03:00 committed by GitHub
commit 77b763e321
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 51 additions and 1 deletions

View File

@ -20,6 +20,8 @@ This is a {{ site.pmd.release_type }} release.
(ApexCRUDViolation, CognitiveComplexity, OperationWithLimitsInLoop) (ApexCRUDViolation, CognitiveComplexity, OperationWithLimitsInLoop)
* [#5163](https://github.com/pmd/pmd/issues/5163): \[apex] Parser error when using toLabel in SOSL query * [#5163](https://github.com/pmd/pmd/issues/5163): \[apex] Parser error when using toLabel in SOSL query
* [#5182](https://github.com/pmd/pmd/issues/5182): \[apex] Parser error when using GROUPING in a SOQL query * [#5182](https://github.com/pmd/pmd/issues/5182): \[apex] Parser error when using GROUPING in a SOQL query
* core
* [#5059](https://github.com/pmd/pmd/issues/5059): \[core] xml output doesn't escape CDATA inside its own CDATA
* java * java
* [#5190](https://github.com/pmd/pmd/issues/5190): \[java] NPE in type inference * [#5190](https://github.com/pmd/pmd/issues/5190): \[java] NPE in type inference

View File

@ -196,7 +196,14 @@ public class XMLRenderer extends AbstractIncrementingRenderer {
xmlWriter.writeAttribute("filename", determineFileName(pe.getFileId())); xmlWriter.writeAttribute("filename", determineFileName(pe.getFileId()));
xmlWriter.writeAttribute("msg", pe.getMsg()); xmlWriter.writeAttribute("msg", pe.getMsg());
writeNewLine(); writeNewLine();
xmlWriter.writeCData(pe.getDetail());
// in case the message contains itself some CDATA sections, they need to be handled
// in order to not produce invalid XML...
String detail = pe.getDetail();
// split "]]>" into "]]" and ">" into two cdata sections
detail = detail.replace("]]>", "]]]]><![CDATA[>");
xmlWriter.writeCData(detail);
writeNewLine(); writeNewLine();
xmlWriter.writeEndElement(); xmlWriter.writeEndElement();
} }

View File

@ -7,6 +7,7 @@ package net.sourceforge.pmd.cpd;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -15,6 +16,7 @@ import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.Collections; import java.util.Collections;
import javax.xml.XMLConstants; import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParser;
@ -316,6 +318,29 @@ class XMLRendererTest {
assertEquals(processingError.getDetail(), textContent); assertEquals(processingError.getDetail(), textContent);
} }
/**
* Note, that CPD's processing error isn't output as a CDATA section at the moment.
* This test just makes sure, the XML is valid, in case {@code <error>} is changed into CDATA.
* Currently, {@code >>]} is automatically escaped into {@code ]]&gt;}.
*
* @see <a href="https://github.com/pmd/pmd/issues/5059">[core] xml output doesn't escape CDATA inside its own CDATA</a>
*/
@Test
void cdataSectionInError() throws Exception {
FileId fileId = FileId.fromPathLikeString("file1.txt");
Report.ProcessingError processingError = new Report.ProcessingError(
new LexException(2, 1, fileId, "test exception", new RuntimeException("Invalid source: '<![CDATA[ ... ]]> ...'")),
fileId);
CPDReportRenderer renderer = new XMLRenderer();
StringWriter sw = new StringWriter();
renderer.render(CpdTestUtils.makeReport(Collections.emptyList(), Collections.emptyMap(), Collections.singletonList(processingError)), sw);
String report = sw.toString();
assertReportIsValidSchema(report);
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
assertDoesNotThrow(() -> documentBuilder.parse(new InputSource(new StringReader(report))));
}
private static void assertReportIsValidSchema(String report) throws SAXException, ParserConfigurationException, IOException { private static void assertReportIsValidSchema(String report) throws SAXException, ParserConfigurationException, IOException {
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(new StreamSource(XMLRenderer.class.getResourceAsStream("/cpd-report_1_0_0.xsd"))); Schema schema = schemaFactory.newSchema(new StreamSource(XMLRenderer.class.getResourceAsStream("/cpd-report_1_0_0.xsd")));

View File

@ -4,6 +4,7 @@
package net.sourceforge.pmd.renderers; package net.sourceforge.pmd.renderers;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
@ -15,6 +16,7 @@ import java.io.StringReader;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Path; import java.nio.file.Path;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -26,6 +28,7 @@ import org.xml.sax.InputSource;
import net.sourceforge.pmd.FooRule; import net.sourceforge.pmd.FooRule;
import net.sourceforge.pmd.PMDVersion; import net.sourceforge.pmd.PMDVersion;
import net.sourceforge.pmd.internal.util.IOUtil; import net.sourceforge.pmd.internal.util.IOUtil;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.document.FileId; import net.sourceforge.pmd.lang.document.FileId;
import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.FileLocation;
import net.sourceforge.pmd.lang.document.TextRange2d; import net.sourceforge.pmd.lang.document.TextRange2d;
@ -161,6 +164,19 @@ class XMLRendererTest extends AbstractRendererTest {
}); });
} }
/**
* @see <a href="https://github.com/pmd/pmd/issues/5059">[core] xml output doesn't escape CDATA inside its own CDATA</a>
*/
@Test
void cdataSectionInError() throws Exception {
ProcessingError processingError = new ProcessingError(new ParseException("Invalid source: '<![CDATA[ ... ]]> ...'"),
FileId.fromPathLikeString("dummy.txt"));
String result = renderReport(getRenderer(), it -> it.onError(processingError), StandardCharsets.UTF_8);
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
assertDoesNotThrow(() -> documentBuilder.parse(new InputSource(new StringReader(result))));
}
private String renderTempFile(Renderer renderer, Report report, Charset expectedCharset) throws IOException { private String renderTempFile(Renderer renderer, Report report, Charset expectedCharset) throws IOException {
File reportFile = folder.resolve("report.out").toFile(); File reportFile = folder.resolve("report.out").toFile();