diff --git a/README.md b/README.md index ffe967342..4474d38f3 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Maven Usage org.cyclonedx cyclonedx-core-java - 10.1.0 + 11.0.1 ``` diff --git a/pom.xml b/pom.xml index f224e5aa0..3ccfbb540 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.cyclonedx cyclonedx-core-java jar - 11.1.0-SNAPSHOT + 11.0.1-SNAPSHOT CycloneDX Core (Java) The CycloneDX core module provides a model representation of the BOM along with utilities to assist in creating, parsing, and validating BOMs. diff --git a/src/main/java/org/cyclonedx/CycloneDxSchema.java b/src/main/java/org/cyclonedx/CycloneDxSchema.java index 94a796ad8..1e942272a 100644 --- a/src/main/java/org/cyclonedx/CycloneDxSchema.java +++ b/src/main/java/org/cyclonedx/CycloneDxSchema.java @@ -271,6 +271,9 @@ private Schema getXmlSchema16() throws SAXException { public Schema getXmlSchema(InputStream... inputStreams) throws SAXException { final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + schemaFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); final Source[] schemaFiles = new Source[inputStreams.length]; for (int i = 0; i < inputStreams.length; i++) { schemaFiles[i] = new StreamSource(inputStreams[i]); diff --git a/src/main/java/org/cyclonedx/parsers/XmlParser.java b/src/main/java/org/cyclonedx/parsers/XmlParser.java index 119e5fd69..fb482ba43 100644 --- a/src/main/java/org/cyclonedx/parsers/XmlParser.java +++ b/src/main/java/org/cyclonedx/parsers/XmlParser.java @@ -338,6 +338,7 @@ private Document createSecureDocument(InputSource in) throws ParserConfiguration DocumentBuilderFactory df = DocumentBuilderFactory.newInstance(); df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + df.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); DocumentBuilder builder = df.newDocumentBuilder(); return builder.parse(in); } diff --git a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java index bf041845b..fda392819 100644 --- a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java +++ b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java @@ -18,12 +18,11 @@ */ package org.cyclonedx; -import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.cyclonedx.exception.ParseException; import org.cyclonedx.generators.BomGeneratorFactory; import org.cyclonedx.generators.json.BomJsonGenerator; -import org.cyclonedx.generators.xml.*; +import org.cyclonedx.generators.xml.BomXmlGenerator; import org.cyclonedx.model.Attribute; import org.cyclonedx.model.Bom; import org.cyclonedx.model.Component; @@ -46,20 +45,26 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Document; + import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.stream.Stream; -import java.util.Objects; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class BomXmlGeneratorTest { @@ -670,9 +675,9 @@ public void testIssue408Regression_externalReferenceBom() throws Exception { @Test public void testXxeProtection() { - assertThrows(ParseException.class, () -> { - createCommonBomXml("/security/xxe-protection.xml"); - }); + assertThatExceptionOfType(ParseException.class) + .isThrownBy(() -> createCommonBomXml("/security/xxe-protection.xml")) + .withMessageContaining("not allowed due to restriction set by the accessExternalDTD property"); } @Test diff --git a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java index c444fc5a5..0074cdbd3 100644 --- a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java +++ b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java @@ -18,13 +18,14 @@ */ package org.cyclonedx.parsers; +import org.apache.commons.io.IOUtils; import org.cyclonedx.Version; +import org.cyclonedx.exception.ParseException; import org.cyclonedx.model.Bom; import org.cyclonedx.model.Component; import org.cyclonedx.model.Component.Type; import org.cyclonedx.model.Dependency; import org.cyclonedx.model.ExternalReference; -import org.cyclonedx.model.License; import org.cyclonedx.model.LicenseChoice; import org.cyclonedx.model.OrganizationalEntity; import org.cyclonedx.model.Pedigree; @@ -65,13 +66,16 @@ import org.cyclonedx.model.license.Acknowledgement; import org.cyclonedx.model.license.Expression; import org.junit.jupiter.api.Test; + import java.io.File; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -746,4 +750,19 @@ public void testIssue492Regression() throws Exception { final Bom bom = getXmlBom("regression/issue492.xml"); assertEquals(2, bom.getMetadata().getTools().size()); } + + @Test + void validateShouldNotBeVulnerableToXxe() throws Exception { + final byte[] bomBytes; + try (final InputStream bomInputStream = getClass().getResourceAsStream("/security/xxe-protection.xml")) { + assertThat(bomInputStream).isNotNull(); + bomBytes = IOUtils.toByteArray(bomInputStream); + } + + final List validationFailures = new XmlParser().validate(bomBytes); + assertThat(validationFailures).extracting(Throwable::getMessage).allSatisfy( + failureMessage -> assertThat(failureMessage) + .contains("not allowed due to restriction set by the accessExternalDTD property")); + } + } diff --git a/src/test/resources/security/xxe-protection.xml b/src/test/resources/security/xxe-protection.xml index f91bdcb49..3de89c238 100644 --- a/src/test/resources/security/xxe-protection.xml +++ b/src/test/resources/security/xxe-protection.xml @@ -1,5 +1,5 @@ - %sp; %param1; %exfil;]> + %sp; %param1; %exfil;]>