From 1b956e326b952e860bffd34cf9c401d4c3531973 Mon Sep 17 00:00:00 2001 From: "mr.mister123" Date: Fri, 31 Jan 2025 11:18:28 +0100 Subject: [PATCH 01/12] remove CIDSets from XObjects --- .../ZUGFeRD/ZUGFeRDExporterFromA3.java | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java index dea69a6f..87137411 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3.java @@ -58,6 +58,7 @@ import org.apache.pdfbox.pdmodel.font.PDCIDFontType2; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDFontDescriptor; import org.apache.pdfbox.pdmodel.font.PDType0Font; +import org.apache.pdfbox.pdmodel.graphics.PDXObject; import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent; import org.apache.xmpbox.XMPMetadata; import org.apache.xmpbox.schema.AdobePDFSchema; @@ -559,6 +560,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte // https://github.com/ZUGFeRD/mustangproject/issues/249 COSName cidSet = COSName.getPDFName("CIDSet"); + COSName resources = COSName.getPDFName("Resources"); // iterate over all pdf pages @@ -567,29 +569,45 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte PDPage page = (PDPage) object; PDResources res = page.getResources(); - for (COSName fontName : res.getFontNames()) { - try { - PDFont pdFont = res.getFont(fontName); - if (pdFont instanceof PDType0Font) { - PDType0Font typedFont = (PDType0Font) pdFont; - if (typedFont.getDescendantFont() instanceof PDCIDFontType2) { - @SuppressWarnings("unused") - PDCIDFontType2 f = (PDCIDFontType2) typedFont.getDescendantFont(); - PDFontDescriptor fontDescriptor = pdFont.getFontDescriptor(); - - fontDescriptor.getCOSObject().removeItem(cidSet); - } - } - } catch (IOException e) { - throw e; + // Check for fonts in PDXObjects: + for (COSName xObjectName : res.getXObjectNames()) { + PDXObject xObject = res.getXObject(xObjectName); + COSDictionary d = xObject.getCOSObject().getCOSDictionary(resources); + if (d != null) { + PDResources xr = new PDResources(d); + removeCIDSetFromPDResources(cidSet, xr); } - // do stuff with the font } + + // Check for fonts in document-resources: + removeCIDSetFromPDResources(cidSet, res); } } } + private void removeCIDSetFromPDResources(COSName cidSet, PDResources res) throws IOException { + for (COSName fontName : res.getFontNames()) { + try { + PDFont pdFont = res.getFont(fontName); + if (pdFont instanceof PDType0Font) { + PDType0Font typedFont = (PDType0Font) pdFont; + + if (typedFont.getDescendantFont() instanceof PDCIDFontType2) { + @SuppressWarnings("unused") + PDCIDFontType2 f = (PDCIDFontType2) typedFont.getDescendantFont(); + PDFontDescriptor fontDescriptor = pdFont.getFontDescriptor(); + + fontDescriptor.getCOSObject().removeItem(cidSet); + } + } + } catch (IOException e) { + throw e; + } + // do stuff with the font + } + } + protected void prepareDocument() throws IOException { PDDocumentCatalog cat = doc.getDocumentCatalog(); From 1b54d87e4093d549f00df5804de738a585db6552 Mon Sep 17 00:00:00 2001 From: cs Date: Tue, 4 Feb 2025 10:08:25 +0100 Subject: [PATCH 02/12] Fix the double assignment of invoices when using invoice setCorrection --- library/src/main/java/org/mustangproject/Invoice.java | 1 - 1 file changed, 1 deletion(-) diff --git a/library/src/main/java/org/mustangproject/Invoice.java b/library/src/main/java/org/mustangproject/Invoice.java index a03487ae..ce094c6a 100644 --- a/library/src/main/java/org/mustangproject/Invoice.java +++ b/library/src/main/java/org/mustangproject/Invoice.java @@ -154,7 +154,6 @@ public class Invoice implements IExportableTransaction { */ public Invoice setCorrection(String number) { setInvoiceReferencedDocumentID(number); - addInvoiceReferencedDocument(new ReferencedDocument(number)); documentCode = DocumentCodeTypeConstants.CORRECTEDINVOICE; return this; } From 51ebbd8fb1a1b539440d61034b5a20d9529b3640 Mon Sep 17 00:00:00 2001 From: ean Date: Wed, 12 Feb 2025 08:30:47 +0100 Subject: [PATCH 03/12] read position accountingReference To new field ReceivableSpecifiedTradeAccountingAccount --- library/src/main/java/org/mustangproject/Item.java | 8 ++++++++ .../ZUGFeRD/IZUGFeRDExportableItem.java | 4 ++++ .../ZUGFeRD/ZF2ZInvoiceImporterTest.java | 12 ++++++++++++ 3 files changed, 24 insertions(+) diff --git a/library/src/main/java/org/mustangproject/Item.java b/library/src/main/java/org/mustangproject/Item.java index 0688530a..456e465a 100644 --- a/library/src/main/java/org/mustangproject/Item.java +++ b/library/src/main/java/org/mustangproject/Item.java @@ -42,6 +42,7 @@ public class Item implements IZUGFeRDExportableItem { protected ArrayList Allowances = new ArrayList<>(); protected ArrayList Charges = new ArrayList<>(); protected List includedNotes = null; + protected String accountingReference; //protected HashMap attributes = new HashMap<>(); /*** @@ -214,6 +215,8 @@ public class Item implements IZUGFeRDExportableItem { icnm.getAllNodes("AdditionalReferencedDocument").map(ReferencedDocument::fromNode).forEach(this::addAdditionalReference); + icnm.getAsString("ReceivableSpecifiedTradeAccountingAccount").ifPresent(s -> this.accountingReference = s == null ? null : s.trim()); + icnm.getAsNodeMap("BillingSpecifiedPeriod").ifPresent(periodNode -> { Date start = periodNode.getAsNodeMap("StartDateTime").flatMap(dateTimeNode -> dateTimeNode.getNode("DateTimeString")).map(dts -> XMLTools.tryDate(dts)).orElse(null); Date end = periodNode.getAsNodeMap("EndDateTime").flatMap(dateTimeNode -> dateTimeNode.getNode("DateTimeString")).map(dts -> XMLTools.tryDate(dts)).orElse(null); @@ -538,4 +541,9 @@ public class Item implements IZUGFeRDExportableItem { public List getNotesWithSubjectCode() { return includedNotes; } + + @Override + public String getAccountingReference() { + return accountingReference; + } } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java index 45c210ea..6d68cdff 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableItem.java @@ -172,4 +172,8 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{ default List getNotesWithSubjectCode() { return null; } + + default String getAccountingReference() { + return null; + } } diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java index b0454510..f5ecb15a 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java @@ -585,4 +585,16 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { assertEquals("0", zii.importedInvoice.getDuePayable().toPlainString()); } + + @Test + public void test() throws FileNotFoundException, XPathExpressionException, ParseException { + File inputFile = getResourceAsFile("ORDER-X_EX01_ORDER_FULL_DATA-COMFORTorder-x.xml"); + ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(); + zii.doIgnoreCalculationErrors(); + zii.setInputStream(new FileInputStream(inputFile)); + + Invoice invoice = zii.extractInvoice(); + assertEquals(3, invoice.getZFItems().length); + assertEquals("BUYER_ACCOUNTING_REF", invoice.getZFItems()[0].getAccountingReference()); + } } From b446d92b131dbfef0b1468ea069f9499ffc7864e Mon Sep 17 00:00:00 2001 From: langfr Date: Thu, 20 Feb 2025 20:48:22 +0000 Subject: [PATCH 04/12] Do not output Adresszusatz 1 as Postfach. --- library/src/main/resources/stylesheets/xr-mapping.xsl | 4 ++-- .../main/resources/stylesheets/xrechnung-html.de.ids.xsl | 8 ++++---- .../src/main/resources/stylesheets/xrechnung-html.de.xsl | 8 ++++---- library/src/main/resources/stylesheets/xrechnung-html.xsl | 8 ++++---- library/src/test/resources/factur-x-vis-extended.de.html | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) 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/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:
From 64913c2ac805f819a92a81af98c2e55ce8176fa9 Mon Sep 17 00:00:00 2001 From: langfr Date: Fri, 28 Feb 2025 10:54:10 +0000 Subject: [PATCH 05/12] Use the dedicated class instead of var type. --- .../main/java/org/mustangproject/ZUGFeRD/OXPullProvider.java | 3 ++- .../java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) 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/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 79ea800e..9cbf6afa 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.slf4j.Logger; @@ -898,7 +899,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()) + ""; From 31861373f05b4591cc005e3697ded7378437c7b0 Mon Sep 17 00:00:00 2001 From: Peter Hermsdorf Date: Mon, 10 Mar 2025 16:37:59 +0100 Subject: [PATCH 06/12] ExemptionReason is repeated once used Closes Issue #614 --- .../ZUGFeRD/ZUGFeRD2PullProvider.java | 15 +++++----- .../org/mustangproject/ZUGFeRD/XRTest.java | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 79ea800e..f594a56c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -342,8 +342,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()); } @@ -409,9 +407,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 += "" + @@ -532,9 +527,13 @@ 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() + "" + "" + vatFormat(currentItem.getProduct().getVATPercent()) + "" + ""; diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java index ace63df4..6d0a2664 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/XRTest.java @@ -178,6 +178,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(1); + } + private org.mustangproject.Invoice createInvoice(TradeParty recipient) { String orgname = "Test company"; From 7fa2d7012868979a614828cfc80efefa8e1dd77f Mon Sep 17 00:00:00 2001 From: Ivan Pereverziev Date: Thu, 13 Mar 2025 11:26:35 +0100 Subject: [PATCH 07/12] Fix resource leaks in core file processing classes - Add try-with-resources for FileInputStream in ZUGFeRDVisualizer - Fix unclosed InputStream in ZUGFeRDInvoiceImporter with try-with-resources - Add explicit close() call for XMLWriter in ZUGFeRDValidator - Properly close DataInputStream in ZUGFeRDExporterFromPDFA - Fix resource management in Main.convertInputStreamToString --- .../org/mustangproject/commandline/Main.java | 20 +++++++++---------- .../java/org/mustangproject/XMLTools.java | 3 ++- .../ZUGFeRD/ZUGFeRDExporterFromPDFA.java | 7 ++++--- .../ZUGFeRD/ZUGFeRDInvoiceImporter.java | 6 +++--- .../ZUGFeRD/ZUGFeRDVisualizer.java | 19 ++++++++++-------- .../validator/ZUGFeRDValidator.java | 1 + 6 files changed, 31 insertions(+), 25 deletions(-) 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/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/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..d9c48c95 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) { diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java index 6e42b632..e9197e9f 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java @@ -131,8 +131,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 +223,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) diff --git a/validator/src/main/java/org/mustangproject/validator/ZUGFeRDValidator.java b/validator/src/main/java/org/mustangproject/validator/ZUGFeRDValidator.java index cdab1874..c0aa0433 100644 --- a/validator/src/main/java/org/mustangproject/validator/ZUGFeRDValidator.java +++ b/validator/src/main/java/org/mustangproject/validator/ZUGFeRDValidator.java @@ -301,6 +301,7 @@ public class ZUGFeRDValidator { XMLWriter writer = new XMLWriter(sw, format); try { writer.write(document); + writer.close(); } catch (Exception e) { LOGGER.error(e.getMessage()); } From 8127b177fbef7183d8ac8c8dfbba8fc3951e9960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20R=C3=B6schke?= Date: Tue, 18 Mar 2025 08:43:00 +0100 Subject: [PATCH 08/12] Added XEE Protection features (that were missing according to Stackoverflow) --- .../ZUGFeRD/ZUGFeRDInvoiceImporter.java | 17 ++++++++++++++--- .../ZUGFeRD/ZUGFeRDVisualizer.java | 17 ++++++++++++++--- .../mustangproject/validator/XMLValidator.java | 18 ++++++++++++++---- .../validator/ZUGFeRDValidator.java | 17 ++++++++++++++--- 4 files changed, 56 insertions(+), 13 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index eb063440..a1327c0d 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -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 6e42b632..c4ab3051 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)); 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..214c5517 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); From b2917ed742a55384219b4dc3c8b0ae937e948e11 Mon Sep 17 00:00:00 2001 From: itomic Date: Wed, 26 Mar 2025 13:46:25 +0100 Subject: [PATCH 09/12] #771: Added null check to prevent NullPointerException on Product Description --- .../java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 6e23e7f2..af0e2f4c 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -450,7 +450,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { } xml += "" + XMLTools.encodeXML(currentItem.getProduct().getName()) + ""; - if (currentItem.getProduct().getDescription().length() > 0) { + if (currentItem.getProduct().getDescription() != null && currentItem.getProduct().getDescription().length() > 0) { xml += "" + XMLTools.encodeXML(currentItem.getProduct().getDescription()) + ""; From 732c9c94a1c9b72c9cec0b3e98251309e8104c2e Mon Sep 17 00:00:00 2001 From: itomic Date: Wed, 26 Mar 2025 13:52:12 +0100 Subject: [PATCH 10/12] #772: Made Deliver to party name (BT-70) optional to align with specification --- .../java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 6e23e7f2..3a838902 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -140,7 +140,9 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { xml += "" + XMLTools.encodeXML(party.getGlobalID()) + ""; } - xml += "" + XMLTools.encodeXML(party.getName()) + ""; + if (party.getName() != null && !party.getName().isEmpty()) { + xml += "" + XMLTools.encodeXML(party.getName()) + ""; + } if (party.getDescription() != null) { xml += "" + XMLTools.encodeXML(party.getDescription()) + ""; } From 76d54cfb28a6eda3585f28dbb518044769952ea6 Mon Sep 17 00:00:00 2001 From: rechtsanwalt-fortbildung <95717047+rechtsanwalt-fortbildung@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:24:24 +0200 Subject: [PATCH 11/12] fix: capitial letter for ID in listID --- .../java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 6e23e7f2..761dda72 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -458,7 +458,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { if (currentItem.getProduct().getClassifications() != null && currentItem.getProduct().getClassifications().length > 0) { for (IDesignatedProductClassification classification : currentItem.getProduct().getClassifications()) { xml += "" - + " Date: Thu, 3 Apr 2025 18:22:30 +0200 Subject: [PATCH 12/12] Delete temp file on exit; #775 --- .../ZUGFeRD/PDFBoxUpdateMitigation.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/PDFBoxUpdateMitigation.java b/library/src/main/java/org/mustangproject/ZUGFeRD/PDFBoxUpdateMitigation.java index 652051b6..252cd7ba 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/PDFBoxUpdateMitigation.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/PDFBoxUpdateMitigation.java @@ -13,14 +13,14 @@ import org.apache.pdfbox.preflight.parser.PreflightParser; import jakarta.activation.DataSource; -// Copied from PDFBox preflight 2.0.x +// Copied from PDFBox preflight 2.0.x final class ByteArrayDataSource implements DataSource { private ByteArrayOutputStream data; private String type = null; private String name = null; - public ByteArrayDataSource (InputStream is) throws IOException + public ByteArrayDataSource (final InputStream is) throws IOException { data = new ByteArrayOutputStream (); IOUtils.copy (is, data); @@ -36,7 +36,7 @@ final class ByteArrayDataSource implements DataSource * @param type * the type to set */ - public void setType (String type) + public void setType (final String type) { this.type = type; } @@ -45,7 +45,7 @@ final class ByteArrayDataSource implements DataSource * @param name * the name to set */ - public void setName (String name) + public void setName (final String name) { this.name = name; } @@ -70,12 +70,13 @@ final class ByteArrayDataSource implements DataSource // Try to create an API similar to the 2.x one final class PreflightParserHelper { - private static File createTmpFile (InputStream input) throws IOException + private static File createTmpFile (final InputStream input) throws IOException { FileOutputStream fos = null; try { - File tmpFile = File.createTempFile ("mustang-pdf", ".pdf"); + final File tmpFile = File.createTempFile ("mustang-pdf", ".pdf"); + tmpFile.deleteOnExit (); fos = new FileOutputStream (tmpFile); IOUtils.copy (input, fos); return tmpFile; @@ -87,7 +88,7 @@ final class PreflightParserHelper } } - public static PreflightParser createPreflightParser (DataSource dataSource) throws IOException + public static PreflightParser createPreflightParser (final DataSource dataSource) throws IOException { return new PreflightParser (createTmpFile (dataSource.getInputStream ())); }