diff --git a/History.md b/History.md index d91af9a5..f11df7d0 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,6 @@ +- #722 +- #774 + 2.16.3 ======= 2025-03-03 diff --git a/Mustang-CLI/src/main/java/org/mustangproject/commandline/Main.java b/Mustang-CLI/src/main/java/org/mustangproject/commandline/Main.java index 275da445..5ab757fe 100755 --- a/Mustang-CLI/src/main/java/org/mustangproject/commandline/Main.java +++ b/Mustang-CLI/src/main/java/org/mustangproject/commandline/Main.java @@ -307,12 +307,13 @@ public class Main { // Plain Java // based on https://mkyong.com/java/how-to-convert-inputstream-to-string-in-java/ private static String convertInputStreamToString(InputStream is) { - int DEFAULT_BUFFER_SIZE = 8192; - ByteArrayOutputStream result = new ByteArrayOutputStream(); - byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; - int length; - try { - while ((length = is.read(buffer)) != -1) { + try (InputStream inputStream = is) { + int DEFAULT_BUFFER_SIZE = 8192; + ByteArrayOutputStream result = new ByteArrayOutputStream(); + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + int length; + + while ((length = inputStream.read(buffer)) != -1) { result.write(buffer, 0, length); } @@ -320,11 +321,10 @@ public class Main { return result.toString(StandardCharsets.UTF_8.name()); } catch (IOException e) { e.printStackTrace(); + return null; + // Java 10 + // return result.toString(StandardCharsets.UTF_8); } - return null; - // Java 10 - // return result.toString(StandardCharsets.UTF_8); - } /*** diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..38dd608d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,29 @@ +# Security Policy + +## Supported Versions + +The following versions are currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 2.x.x | :white_check_mark: | +| < 2.0 | :x: | + +## Reporting a Vulnerability + +Feel free to submit issues to info at mustangproject.org with [security] indicated in the subject. +We may ask back questions but we usually open (or communicate about) an issue (potentially in a private location you would be provided with access to) and decide on the severity within two working days. + +Please indicate +* a proof of concept, if possible +* If any of the information you submit, e.g. an invoice which can not be [anonymized](https://github.com/ZUGFeRD/einvoice-anonymizer), is confidential +* A quick justification why you require a fix in a older version than he most up to date one, if you can not update to the most recent version +* If you require encrypted communication (our GPG fingerprint will likely be 68F4 2269 8165 F0F5 63CA A13B 7CB7 1548 B596 66A3) + + +## After your Report + +We try to fix critical issues in less than a week, and release a fixed version in less than two weeks. + +Thank you for keeping our software safe! + diff --git a/library/src/main/java/org/mustangproject/Invoice.java b/library/src/main/java/org/mustangproject/Invoice.java index 7e011986..c1ebc382 100644 --- a/library/src/main/java/org/mustangproject/Invoice.java +++ b/library/src/main/java/org/mustangproject/Invoice.java @@ -158,7 +158,6 @@ public class Invoice implements IExportableTransaction { */ public Invoice setCorrection(String number) { setInvoiceReferencedDocumentID(number); - addInvoiceReferencedDocument(new ReferencedDocument(number)); documentCode = DocumentCodeTypeConstants.CORRECTEDINVOICE; return this; } diff --git a/library/src/main/java/org/mustangproject/XMLTools.java b/library/src/main/java/org/mustangproject/XMLTools.java index e5e297f1..5905fdae 100644 --- a/library/src/main/java/org/mustangproject/XMLTools.java +++ b/library/src/main/java/org/mustangproject/XMLTools.java @@ -220,7 +220,8 @@ public class XMLTools extends XMLWriter { } public static byte[] getBytesFromStream(InputStream fileinput) throws IOException { - return IOUtils.toByteArray (fileinput); + // Stream closing responsibility is with the caller + return IOUtils.toByteArray(fileinput); } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java index 7347d422..eb3ff49c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java @@ -32,6 +32,7 @@ import java.util.Map; import org.mustangproject.EStandard; import org.mustangproject.FileAttachment; +import org.mustangproject.ReferencedDocument; import org.mustangproject.XMLTools; public class OXPullProvider extends ZUGFeRD2PullProvider { @@ -460,7 +461,7 @@ public class OXPullProvider extends ZUGFeRD2PullProvider { xml += ""; } if (trans.getInvoiceReferencedDocuments() != null) { - for (var doc : trans.getInvoiceReferencedDocuments()) { + for (ReferencedDocument doc : trans.getInvoiceReferencedDocuments()) { xml += "" + "" + XMLTools.encodeXML(doc.getIssuerAssignedID()) + ""; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ValidationLogVisualizer.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ValidationLogVisualizer.java index 686daa7c..7e7d8a17 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ValidationLogVisualizer.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ValidationLogVisualizer.java @@ -1,5 +1,13 @@ package org.mustangproject.ZUGFeRD; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringReader; import org.apache.fop.apps.*; import org.apache.fop.apps.io.ResourceResolverFactory; import org.apache.fop.configuration.Configuration; @@ -10,11 +18,12 @@ import org.mustangproject.ClasspathResolverURIAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.xml.XMLConstants; import javax.xml.transform.*; import javax.xml.transform.sax.SAXResult; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; -import java.io.*; + import java.nio.charset.StandardCharsets; public class ValidationLogVisualizer { @@ -70,7 +79,7 @@ public class ValidationLogVisualizer { return baos.toString(StandardCharsets.UTF_8); } - public void toPDF(String xmlLogfileContent, String pdfFilename) { + public byte[] createPDFBytes(String xmlLogfileContent) { // the writing part @@ -111,13 +120,19 @@ public class ValidationLogVisualizer { // Step 2: Set up output stream. // Note: Using BufferedOutputStream for performance reasons (helpful with FileOutputStreams). - try (OutputStream out = new BufferedOutputStream(new FileOutputStream(pdfFilename))) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (OutputStream out = new BufferedOutputStream(baos)) { // Step 3: Construct fop with desired output format Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent, out); // Step 4: Setup JAXP using identity transformer TransformerFactory factory = TransformerFactory.newInstance(); + + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); Transformer transformer = factory.newTransformer(); // identity transformer // Step 5: Setup input and output for XSLT transformation @@ -133,6 +148,20 @@ public class ValidationLogVisualizer { } catch (FOPException | IOException | TransformerException e) { LOGGER.error("Failed to create PDF", e); } + return baos.toByteArray(); + } + + public byte[] toPDF(String xmlLogfileContent) { + return createPDFBytes(xmlLogfileContent); + } + + public void toPDF(String xmlLogfileContent, String pdfFilename) { + byte[] pdfData = createPDFBytes(xmlLogfileContent); + try (FileOutputStream fos = new FileOutputStream(pdfFilename)) { + fos.write(pdfData); + } catch (IOException e) { + LOGGER.error("Failed to write PDF to file", e); + } } private static class ClasspathResourceURIResolver implements URIResolver { diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index da9a34b2..cce7012c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -45,6 +45,7 @@ import org.dom4j.io.OutputFormat; import org.dom4j.io.XMLWriter; import org.mustangproject.FileAttachment; import org.mustangproject.IncludedNote; +import org.mustangproject.ReferencedDocument; import org.mustangproject.XMLTools; import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants; import org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants; @@ -343,8 +344,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { boolean hasDueDate = trans.getDueDate() != null; final SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); - String exemptionReason = ""; - if (trans.getPaymentTermDescription() != null) { paymentTermsDescription = XMLTools.encodeXML(trans.getPaymentTermDescription()); } @@ -410,9 +409,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { if (currentItem.getId() != null) { lineIDStr = currentItem.getId(); } - if (currentItem.getProduct().getTaxExemptionReason() != null) { - exemptionReason = "" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + ""; - } final LineCalculator lc = new LineCalculator(currentItem); if ((getProfile() != Profiles.getByName("Minimum")) && (getProfile() != Profiles.getByName("BasicWL"))) { xml += "" + @@ -533,9 +529,11 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { + "" + "" + "" - + "VAT" - + exemptionReason - + "" + currentItem.getProduct().getTaxCategoryCode() + ""; + + "VAT"; + if (currentItem.getProduct().getTaxExemptionReason() != null) { + xml += "" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + ""; + } + xml += "" + currentItem.getProduct().getTaxCategoryCode() + ""; if (!currentItem.getProduct().getTaxCategoryCode().equals(TaxCategoryCodeTypeConstants.UNTAXEDSERVICE)) { xml += "" + vatFormat(currentItem.getProduct().getVATPercent()) + ""; @@ -905,7 +903,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { xml += ""; } if (trans.getInvoiceReferencedDocuments() != null) { - for (var doc : trans.getInvoiceReferencedDocuments()) { + for (ReferencedDocument doc : trans.getInvoiceReferencedDocuments()) { xml += "" + "" + XMLTools.encodeXML(doc.getIssuerAssignedID()) + ""; diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java index c3b9299b..e128f0a6 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromPDFA.java @@ -90,9 +90,10 @@ public class ZUGFeRDExporterFromPDFA implements IZUGFeRDExporter { protected byte[] inputstreamToByteArray(InputStream fileInputStream) throws IOException { byte[] bytes = new byte[fileInputStream.available()]; - DataInputStream dataInputStream = new DataInputStream(fileInputStream); - dataInputStream.readFully(bytes); - return bytes; + try (DataInputStream dataInputStream = new DataInputStream(fileInputStream)) { + dataInputStream.readFully(bytes); + return bytes; + } } /*** diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index eb063440..3015f91c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -138,9 +138,9 @@ public class ZUGFeRDInvoiceImporter { return; } - final InputStream XMP = doc.getDocumentCatalog().getMetadata().exportXMPMetadata(); - - xmpString = new String(XMLTools.getBytesFromStream(XMP), StandardCharsets.UTF_8); + try (final InputStream XMP = doc.getDocumentCatalog().getMetadata().exportXMPMetadata()) { + xmpString = new String(XMLTools.getBytesFromStream(XMP), StandardCharsets.UTF_8); + } final PDEmbeddedFilesNameTreeNode etn = names.getEmbeddedFiles(); if (etn == null) { @@ -260,12 +260,23 @@ public class ZUGFeRDInvoiceImporter { private void setDocument() throws ParserConfigurationException, IOException, SAXException, ParseException { final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - dbf.setExpandEntityReferences(false); - dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + //REDHAT + //https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf + dbf.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, true); + dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + + //OWASP + //https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + // Disable external DTDs as well + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks" + dbf.setXIncludeAware(false); + dbf.setExpandEntityReferences(false); + dbf.setNamespaceAware(true); final DocumentBuilder builder = dbf.newDocumentBuilder(); final ByteArrayInputStream is = new ByteArrayInputStream(rawXML); /// is.skip(guessBOMSize(is)); diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java index e133b028..da340ad1 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java @@ -102,12 +102,23 @@ public class ZUGFeRDVisualizer { String cioSignature = "SCRDMCCBDACIOMessageStructure"; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - dbf.setExpandEntityReferences(false); - dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + //REDHAT + //https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf + dbf.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, true); + dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + + //OWASP + //https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + // Disable external DTDs as well + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks" + dbf.setXIncludeAware(false); + dbf.setExpandEntityReferences(false); + dbf.setNamespaceAware(true); try { DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(new InputSource(fis)); @@ -131,8 +142,9 @@ public class ZUGFeRDVisualizer { public String visualize(String xmlFilename, Language lang) throws IOException, TransformerException, ParserConfigurationException { - FileInputStream fis = new FileInputStream(xmlFilename); - return visualize(fis, lang); + try (FileInputStream fis = new FileInputStream(xmlFilename)) { + return visualize(fis, lang); + } } public String visualize(InputStream inputXml, Language lang) @@ -222,12 +234,14 @@ public class ZUGFeRDVisualizer { protected String toFOP(String xmlFilename) throws IOException, TransformerException, ParserConfigurationException { - - FileInputStream fis = new FileInputStream(xmlFilename); - EStandard theStandard = findOutStandardFromRootNode(fis); - fis = new FileInputStream(xmlFilename);//rewind :-( - - return toFOP(fis, theStandard); + EStandard theStandard; + try (FileInputStream fis = new FileInputStream(xmlFilename)) { + theStandard = findOutStandardFromRootNode(fis); + } + + try (FileInputStream fis = new FileInputStream(xmlFilename)) { + return toFOP(fis, theStandard); + } } protected String toFOP(InputStream is, EStandard theStandard) @@ -356,6 +370,10 @@ public class ZUGFeRDVisualizer { // Step 4: Setup JAXP using identity transformer TransformerFactory factory = TransformerFactory.newInstance(); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); Transformer transformer = factory.newTransformer(); // identity transformer // Step 5: Setup input and output for XSLT transformation diff --git a/library/src/main/resources/stylesheets/xr-mapping.xsl b/library/src/main/resources/stylesheets/xr-mapping.xsl index be261921..064e4e43 100644 --- a/library/src/main/resources/stylesheets/xr-mapping.xsl +++ b/library/src/main/resources/stylesheets/xr-mapping.xsl @@ -398,7 +398,7 @@ BT-64 - + BT-65 @@ -486,7 +486,7 @@ BT-75 - + BT-76 diff --git a/library/src/main/resources/stylesheets/xrechnung-html.de.ids.xsl b/library/src/main/resources/stylesheets/xrechnung-html.de.ids.xsl index ab453b51..1c02ca63 100644 --- a/library/src/main/resources/stylesheets/xrechnung-html.de.ids.xsl +++ b/library/src/main/resources/stylesheets/xrechnung-html.de.ids.xsl @@ -1193,7 +1193,7 @@ function downloadData (element_id) {
-
Postfach (BT-51):
+
Adresszusatz (BT-51):
@@ -1258,7 +1258,7 @@ function downloadData (element_id) {
-
Postfach (BT-36):
+
Adresszusatz (BT-36):
@@ -1998,7 +1998,7 @@ function downloadData (element_id) {
-
Postfach (BT-65):
+
Adresszusatz (BT-65):
@@ -2111,7 +2111,7 @@ function downloadData (element_id) {
-
Postfach (BT-76):
+
Adresszusatz (BT-76):
diff --git a/library/src/main/resources/stylesheets/xrechnung-html.de.xsl b/library/src/main/resources/stylesheets/xrechnung-html.de.xsl index e2a2e930..052cb544 100644 --- a/library/src/main/resources/stylesheets/xrechnung-html.de.xsl +++ b/library/src/main/resources/stylesheets/xrechnung-html.de.xsl @@ -13,7 +13,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -166,7 +166,7 @@ - + @@ -189,7 +189,7 @@ - + diff --git a/library/src/main/resources/stylesheets/xrechnung-html.xsl b/library/src/main/resources/stylesheets/xrechnung-html.xsl index 54a0de84..6309f380 100644 --- a/library/src/main/resources/stylesheets/xrechnung-html.xsl +++ b/library/src/main/resources/stylesheets/xrechnung-html.xsl @@ -122,7 +122,7 @@
-
Postfach:
+
Adresszusatz:
@@ -179,7 +179,7 @@
-
Postfach:
+
Adresszusatz:
@@ -892,7 +892,7 @@
-
Postfach:
+
Adresszusatz:
@@ -998,7 +998,7 @@
-
Postfach:
+
Adresszusatz:
diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java index f819db12..9902a05c 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java @@ -180,6 +180,36 @@ public class XRTest extends TestCase { } } + + public void testTaxExemptionReasonIssue() { + String orgname = "Test company"; + String number = "123"; + String amountStr = "1.00"; + BigDecimal amount = new BigDecimal(amountStr); + byte[] b = {12, 13}; + + Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()) + .setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").setEmail("sender@example.com").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test", "+49123456789", "test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890", "COBADEFXXX").setAccountName("kontoInhaber"))) + .setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").setEmail("recipient@sample.org")) + .setReferenceNumber("991-01484-64")//leitweg-id + // not using any VAT, this is also a test of zero-rated goods: + .setNumber(number) + .addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO).setTaxCategoryCode("E").setTaxExemptionReason("Kleinunternehmer"), amount, new BigDecimal(1.0))) + .addItem(new Item(new Product("Testprodukt2", "", "C62", BigDecimal.ZERO).setTaxCategoryCode("S"), amount, new BigDecimal(1.0))) + .setPayee( new TradeParty().setName("VR Factoring GmbH").setID("DE813838785").setLegalOrganisation(new LegalOrganisation("391200LDDFJDMIPPMZ54", "0199"))); + + + + ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider(); + + zf2p.setProfile(Profiles.getByName("XRechnung")); + zf2p.generateXML(i); + String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8); + assertThat(theXML).valueByXPath("count(//*[local-name()='ExemptionReason'])") + .asInt() + .isEqualTo(2); + } + public void testApplicablePercentInUntaxedService() { diff --git a/library/src/test/resources/factur-x-vis-extended.de.html b/library/src/test/resources/factur-x-vis-extended.de.html index ed5a147d..4ef990b5 100644 --- a/library/src/test/resources/factur-x-vis-extended.de.html +++ b/library/src/test/resources/factur-x-vis-extended.de.html @@ -762,7 +762,7 @@
KUNDENWEG 88
-
Postfach:
+
Adresszusatz:
@@ -826,7 +826,7 @@
BAHNHOFSTRASSE 99
-
Postfach:
+
Adresszusatz:
@@ -2203,7 +2203,7 @@
HAUPTSTRASSE 44
-
Postfach:
+
Adresszusatz:
diff --git a/validator/src/main/java/org/mustangproject/validator/Validator.java b/validator/src/main/java/org/mustangproject/validator/Validator.java index 545ee574..92e38228 100644 --- a/validator/src/main/java/org/mustangproject/validator/Validator.java +++ b/validator/src/main/java/org/mustangproject/validator/Validator.java @@ -61,6 +61,10 @@ public abstract class Validator { Source xmlData = new StreamSource(new ByteArrayInputStream(xmlRawData)); SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); try { + schemaFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + schemaFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); + schemaFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); Schema schema = schemaFactory.newSchema(schemaFile); javax.xml.validation.Validator validator = schema.newValidator(); validator.validate(xmlData); diff --git a/validator/src/main/java/org/mustangproject/validator/XMLValidator.java b/validator/src/main/java/org/mustangproject/validator/XMLValidator.java index 58dba911..cf911dc6 100644 --- a/validator/src/main/java/org/mustangproject/validator/XMLValidator.java +++ b/validator/src/main/java/org/mustangproject/validator/XMLValidator.java @@ -150,13 +150,23 @@ public class XMLValidator extends Validator { */ final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); // otherwise we can not act namespace independently, i.e. use - // document.getElementsByTagNameNS("*",... - dbf.setExpandEntityReferences(false); - dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + //REDHAT + //https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf + dbf.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, true); + dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + + //OWASP + //https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + // Disable external DTDs as well + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks" + dbf.setXIncludeAware(false); + dbf.setExpandEntityReferences(false); + dbf.setNamespaceAware(true); final DocumentBuilder db = dbf.newDocumentBuilder(); final InputSource is = new InputSource(new StringReader(zfXML)); diff --git a/validator/src/main/java/org/mustangproject/validator/ZUGFeRDValidator.java b/validator/src/main/java/org/mustangproject/validator/ZUGFeRDValidator.java index cdab1874..1b328cd0 100644 --- a/validator/src/main/java/org/mustangproject/validator/ZUGFeRDValidator.java +++ b/validator/src/main/java/org/mustangproject/validator/ZUGFeRDValidator.java @@ -143,12 +143,23 @@ public class ZUGFeRDValidator { String xmlAsString = null; try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - dbf.setExpandEntityReferences(false); - dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + //REDHAT + //https://www.blackhat.com/docs/us-15/materials/us-15-Wang-FileCry-The-New-Age-Of-XXE-java-wp.pdf + dbf.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, true); + dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + + //OWASP + //https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + // Disable external DTDs as well + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks" + dbf.setXIncludeAware(false); + dbf.setExpandEntityReferences(false); + dbf.setNamespaceAware(true); DocumentBuilder db = dbf.newDocumentBuilder(); content = XMLTools.removeBOM(content); @@ -301,6 +312,7 @@ public class ZUGFeRDValidator { XMLWriter writer = new XMLWriter(sw, format); try { writer.write(document); + writer.close(); } catch (Exception e) { LOGGER.error(e.getMessage()); }