Author: fanningpj
Date: Wed Mar 19 20:38:10 2025
New Revision: 1924476
URL: http://svn.apache.org/viewvc?rev=1924476&view=rev
Log:
[github-775] Allow some OPC compliance checks to be tuned. Thanks to Ken Reese.
This closes #775
Added:
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCComplianceFlags.java
Modified:
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCPackage.java
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/ZipPackage.java
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/PackagePropertiesUnmarshaller.java
poi/trunk/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/compliance/TestOPCComplianceCoreProperties.java
Added:
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCComplianceFlags.java
URL:
http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCComplianceFlags.java?rev=1924476&view=auto
==============================================================================
---
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCComplianceFlags.java
(added)
+++
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCComplianceFlags.java
Wed Mar 19 20:38:10 2025
@@ -0,0 +1,118 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc;
+
+/**
+ * Allows disabling specific OPC compliance rules.
+ * By default, rules M4.2, M4.3, M4.4, and M4.5 are all enforced which will
prevent
+ * non-compliant documents from being parsed.
+ *
+ * Consumers may disable these compliance checks individually or as a whole at
their
+ * own discretion to allow certain non-compliant documents to be parsed.
+ * @since POI 5.4.1
+ */
+public class OPCComplianceFlags {
+
+ /*
+ * Rule M4.2: A format consumer shall consider the use of the Markup
+ * Compatibility namespace to be an error.
+ */
+ protected boolean ENFORCE_M4_2_FORBID_MARKUP_COMPATIBILITY_NAMESPACE;
+
+ /**
+ * Rule M4.3: Producers shall not create a document element that contains
+ * refinements to the Dublin Core elements, except for the two specified in
+ * the schema: <dcterms:created> and <dcterms:modified>
Consumers shall
+ * consider a document element that violates this constraint to be an
error.
+ */
+ protected boolean ENFORCE_M4_3_FORBID_REFINING_DUBLIN_CORE_ELEMENTS;
+
+ /**
+ * Rule M4.4: Producers shall not create a document element that contains
+ * the xml:lang attribute. Consumers shall consider a document element that
+ * violates this constraint to be an error.
+ */
+ protected boolean ENFORCE_M4_4_FORBID_XML_LANG_ATTRIBUTE;
+
+ /*
+ * Rule M4.5: Producers shall not create a document element that contains
+ * the xsi:type attribute, except for a <dcterms:created> or
+ * <dcterms:modified> element where the xsi:type attribute shall be
present
+ * and shall hold the value dcterms:W3CDTF, where dcterms is the namespace
+ * prefix of the Dublin Core namespace. Consumers shall consider a document
+ * element that violates this constraint to be an error.
+ */
+ protected boolean ENFORCE_M4_5_RESTRICT_XSI_TYPE_ATTRIBUTE;
+
+ private OPCComplianceFlags(
+ boolean forbidMarkupCompatibilityNamespace,
+ boolean forbidRefiningDublinCoreElements,
+ boolean forbidXmlLangAttribute,
+ boolean restrictXsiTypeAttribute
+ ) {
+ this.ENFORCE_M4_2_FORBID_MARKUP_COMPATIBILITY_NAMESPACE =
forbidMarkupCompatibilityNamespace;
+ this.ENFORCE_M4_3_FORBID_REFINING_DUBLIN_CORE_ELEMENTS =
forbidRefiningDublinCoreElements;
+ this.ENFORCE_M4_4_FORBID_XML_LANG_ATTRIBUTE = forbidXmlLangAttribute;
+ this.ENFORCE_M4_5_RESTRICT_XSI_TYPE_ATTRIBUTE =
restrictXsiTypeAttribute;
+ }
+
+ public static OPCComplianceFlags enforceAll() {
+ return new OPCComplianceFlags(true, true, true, true);
+ }
+
+ public static OPCComplianceFlags disableAll() {
+ return new OPCComplianceFlags(false, false, false, false);
+ }
+
+ public OPCComplianceFlags setForbidMarkupCompatibilityNamespace(boolean
flag) {
+ ENFORCE_M4_2_FORBID_MARKUP_COMPATIBILITY_NAMESPACE = flag;
+ return this;
+ }
+
+ public OPCComplianceFlags setForbidRefiningDublinCoreElements(boolean
flag) {
+ ENFORCE_M4_3_FORBID_REFINING_DUBLIN_CORE_ELEMENTS = flag;
+ return this;
+ }
+
+ public OPCComplianceFlags setForbidXmlLangAttribute(boolean flag) {
+ ENFORCE_M4_4_FORBID_XML_LANG_ATTRIBUTE = flag;
+ return this;
+ }
+
+ public OPCComplianceFlags setRestrictXsiTypeAttribute(boolean flag) {
+ ENFORCE_M4_5_RESTRICT_XSI_TYPE_ATTRIBUTE = flag;
+ return this;
+ }
+
+ public boolean getForbidMarkupCompatibilityNamespace() {
+ return ENFORCE_M4_2_FORBID_MARKUP_COMPATIBILITY_NAMESPACE;
+ }
+
+ public boolean getForbidRefiningDublinCoreElements() {
+ return ENFORCE_M4_3_FORBID_REFINING_DUBLIN_CORE_ELEMENTS;
+ }
+
+ public boolean getForbidXmlLangAttributes() {
+ return ENFORCE_M4_4_FORBID_XML_LANG_ATTRIBUTE;
+ }
+
+ public boolean getRestrictXsiTypeAttribute() {
+ return ENFORCE_M4_5_RESTRICT_XSI_TYPE_ATTRIBUTE;
+ }
+
+}
Modified:
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCPackage.java
URL:
http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCPackage.java?rev=1924476&r1=1924475&r2=1924476&view=diff
==============================================================================
---
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCPackage.java
(original)
+++
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/OPCPackage.java
Wed Mar 19 20:38:10 2025
@@ -141,6 +141,19 @@ public abstract class OPCPackage impleme
* @throws OpenXML4JRuntimeException if there are issues creating
properties part
*/
OPCPackage(PackageAccess access) {
+ this(access, OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param access Package access.
+ * @param opcComplianceFlags Enable or disable specific OPC compliance
flags.
+ * This is useful to allow parsing of certain
non-compliant documents.
+ * @throws OpenXML4JRuntimeException if there are issues creating
properties part
+ * @since POI 5.4.1
+ */
+ OPCPackage(PackageAccess access, OPCComplianceFlags opcComplianceFlags) {
if (getClass() != ZipPackage.class) {
throw new IllegalArgumentException("PackageBase may not be
subclassed");
}
@@ -148,7 +161,7 @@ public abstract class OPCPackage impleme
final ContentType contentType = newCorePropertiesPart();
// TODO Delocalize specialized marshallers
- this.partUnmarshallers.put(contentType, new
PackagePropertiesUnmarshaller());
+ this.partUnmarshallers.put(contentType, new
PackagePropertiesUnmarshaller(opcComplianceFlags));
this.partMarshallers.put(contentType, new
ZipPackagePropertiesMarshaller());
}
@@ -176,7 +189,24 @@ public abstract class OPCPackage impleme
* occur.
*/
public static OPCPackage open(String path) throws InvalidFormatException {
- return open(path, defaultPackageAccess);
+ return open(path, defaultPackageAccess,
OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * Open a package with read/write permission.
+ *
+ * @param path
+ * The document path.
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @return A Package object, else <b>null</b>.
+ * @throws InvalidFormatException
+ * If the specified file doesn't exist, and a parsing error
+ * occur.
+ * @since POI 5.4.1
+ */
+ public static OPCPackage open(String path, OPCComplianceFlags
opcComplianceFlags) throws InvalidFormatException {
+ return open(path, defaultPackageAccess, opcComplianceFlags);
}
/**
@@ -193,6 +223,23 @@ public abstract class OPCPackage impleme
return open(file, defaultPackageAccess);
}
+ /**
+ * Open a package with read/write permission.
+ *
+ * @param file
+ * The file to open.
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @return A Package object, else <b>null</b>.
+ * @throws InvalidFormatException
+ * If the specified file doesn't exist, and a parsing error
+ * occur.
+ * @since POI 5.4.1
+ */
+ public static OPCPackage open(File file, OPCComplianceFlags
opcComplianceFlags) throws InvalidFormatException {
+ return open(file, defaultPackageAccess, opcComplianceFlags);
+ }
+
/**
* Open a user provided {@link ZipEntrySource} with read-only permission.
* This method can be used to stream data into POI.
@@ -204,22 +251,39 @@ public abstract class OPCPackage impleme
* @throws InvalidFormatException if a parsing error occur.
*/
public static OPCPackage open(ZipEntrySource zipEntry) throws
InvalidFormatException {
- OPCPackage pack = new ZipPackage(zipEntry, PackageAccess.READ);
- try {
- if (pack.partList == null) {
- pack.getParts();
- }
- // pack.originalPackagePath = file.getAbsolutePath();
- return pack;
- } catch (InvalidFormatException | RuntimeException e) {
- // use revert() to free resources when the package is opened
read-only
- pack.revert();
-
- throw e;
- }
+ return open(zipEntry, OPCComplianceFlags.enforceAll());
}
/**
+ * Open a user provided {@link ZipEntrySource} with read-only permission.
+ * This method can be used to stream data into POI.
+ * Opposed to other open variants, the data is read as-is, e.g. there
aren't
+ * any zip-bomb protection put in place.
+ *
+ * @param zipEntry the custom source
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @return A Package object
+ * @throws InvalidFormatException if a parsing error occur.
+ * @since POI 5.4.1
+ */
+ public static OPCPackage open(ZipEntrySource zipEntry, OPCComplianceFlags
opcComplianceFlags) throws InvalidFormatException {
+ OPCPackage pack = new ZipPackage(zipEntry, PackageAccess.READ,
opcComplianceFlags);
+ try {
+ if (pack.partList == null) {
+ pack.getParts();
+ }
+ // pack.originalPackagePath = file.getAbsolutePath();
+ return pack;
+ } catch (InvalidFormatException | RuntimeException e) {
+ // use revert() to free resources when the package is opened
read-only
+ pack.revert();
+
+ throw e;
+ }
+ }
+
+ /**
* Open a package.
*
* @param path
@@ -235,6 +299,28 @@ public abstract class OPCPackage impleme
*/
public static OPCPackage open(String path, PackageAccess access)
throws InvalidFormatException, InvalidOperationException {
+ return open(path, access, OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * Open a package.
+ *
+ * @param path
+ * The document path.
+ * @param access
+ * PackageBase access.
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @return A PackageBase object, else <b>null</b>.
+ * @throws InvalidFormatException
+ * If the specified file doesn't exist, and a parsing error
+ * occur.
+ * @throws InvalidOperationException If the zip file cannot be opened.
+ * @throws InvalidFormatException if the package is not valid.
+ * @since POI 5.4.1
+ */
+ public static OPCPackage open(String path, PackageAccess access,
OPCComplianceFlags opcComplianceFlags)
+ throws InvalidFormatException, InvalidOperationException {
if (StringUtil.isBlank(path)) {
throw new IllegalArgumentException("'path' must be given");
}
@@ -244,7 +330,7 @@ public abstract class OPCPackage impleme
throw new IllegalArgumentException("path must not be a directory");
}
- OPCPackage pack = new ZipPackage(path, access); // NOSONAR
+ OPCPackage pack = new ZipPackage(path, access, opcComplianceFlags); //
NOSONAR
boolean success = false;
if (pack.partList == null && access != PackageAccess.WRITE) {
try {
@@ -275,38 +361,58 @@ public abstract class OPCPackage impleme
*/
public static OPCPackage open(File file, PackageAccess access)
throws InvalidFormatException {
- if (file == null) {
- throw new IllegalArgumentException("'file' must be given");
- }
- if (file.exists() && file.isDirectory()) {
- throw new IllegalArgumentException("file must not be a directory");
- }
-
- final OPCPackage pack;
- try {
- pack = new ZipPackage(file, access); //NOSONAR
- } catch (InvalidOperationException e) {
- throw new InvalidFormatException(e.getMessage(), e);
- }
- try {
- if (pack.partList == null && access != PackageAccess.WRITE) {
- pack.getParts();
- }
- pack.originalPackagePath = file.getAbsolutePath();
- return pack;
- } catch (InvalidFormatException | RuntimeException e) {
- if (access == PackageAccess.READ) {
- pack.revert();
- } else {
- IOUtils.closeQuietly(pack);
- }
- throw e;
- }
+ return open(file, access, OPCComplianceFlags.enforceAll());
}
/**
* Open a package.
*
+ * @param file
+ * The file to open.
+ * @param access
+ * PackageBase access.
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @return A PackageBase object, else <b>null</b>.
+ * @throws InvalidFormatException
+ * If the specified file doesn't exist, and a parsing error
+ * occur.
+ * @since POI 5.4.1
+ */
+ public static OPCPackage open(File file, PackageAccess access,
OPCComplianceFlags opcComplianceFlags)
+ throws InvalidFormatException {
+ if (file == null) {
+ throw new IllegalArgumentException("'file' must be given");
+ }
+ if (file.exists() && file.isDirectory()) {
+ throw new IllegalArgumentException("file must not be a directory");
+ }
+
+ final OPCPackage pack;
+ try {
+ pack = new ZipPackage(file, access, opcComplianceFlags); //NOSONAR
+ } catch (InvalidOperationException e) {
+ throw new InvalidFormatException(e.getMessage(), e);
+ }
+ try {
+ if (pack.partList == null && access != PackageAccess.WRITE) {
+ pack.getParts();
+ }
+ pack.originalPackagePath = file.getAbsolutePath();
+ return pack;
+ } catch (InvalidFormatException | RuntimeException e) {
+ if (access == PackageAccess.READ) {
+ pack.revert();
+ } else {
+ IOUtils.closeQuietly(pack);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Open a package.
+ *
* Note - uses quite a bit more memory than {@link #open(String)}, which
* doesn't need to hold the whole zip file in memory, and can take
advantage
* of native methods
@@ -321,9 +427,32 @@ public abstract class OPCPackage impleme
*/
public static OPCPackage open(InputStream in) throws
InvalidFormatException,
IOException {
+ return open(in, OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * Open a package.
+ *
+ * Note - uses quite a bit more memory than {@link #open(String)}, which
+ * doesn't need to hold the whole zip file in memory, and can take
advantage
+ * of native methods
+ *
+ * @param in
+ * The InputStream to read the package from. The stream is
closed.
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @return A PackageBase object
+ *
+ * @throws InvalidFormatException
+ * Throws if the specified file exist and is not valid.
+ * @throws IOException If reading the stream fails
+ * @since POI 5.4.1
+ */
+ public static OPCPackage open(InputStream in, OPCComplianceFlags
opcComplianceFlags) throws InvalidFormatException,
+ IOException {
final OPCPackage pack;
try {
- pack = new ZipPackage(in, PackageAccess.READ_WRITE);
+ pack = new ZipPackage(in, PackageAccess.READ_WRITE,
opcComplianceFlags);
} catch (InvalidZipException e) {
throw new InvalidFormatException(e.getMessage(), e);
}
@@ -358,9 +487,34 @@ public abstract class OPCPackage impleme
*/
public static OPCPackage open(InputStream in, boolean closeStream) throws
InvalidFormatException,
IOException {
+ return open(in, closeStream, OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * Open a package.
+ *
+ * Note - uses quite a bit more memory than {@link #open(String)}, which
+ * doesn't need to hold the whole zip file in memory, and can take
advantage
+ * of native methods
+ *
+ * @param in
+ * The InputStream to read the package from.
+ * @param closeStream
+ * Whether to close the input stream.
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @return A PackageBase object
+ *
+ * @throws InvalidFormatException
+ * Throws if the specified file exist and is not valid.
+ * @throws IOException If reading the stream fails
+ * @since POI 5.4.1
+ */
+ public static OPCPackage open(InputStream in, boolean closeStream,
OPCComplianceFlags opcComplianceFlags) throws InvalidFormatException,
+ IOException {
final OPCPackage pack;
try {
- pack = new ZipPackage(in, PackageAccess.READ_WRITE, closeStream);
+ pack = new ZipPackage(in, PackageAccess.READ_WRITE, closeStream,
opcComplianceFlags);
} catch (InvalidZipException e) {
throw new InvalidFormatException(e.getMessage(), e);
}
Modified:
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/ZipPackage.java
URL:
http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/ZipPackage.java?rev=1924476&r1=1924475&r2=1924476&view=diff
==============================================================================
---
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/ZipPackage.java
(original)
+++
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/ZipPackage.java
Wed Mar 19 20:38:10 2025
@@ -103,7 +103,17 @@ public final class ZipPackage extends OP
* Constructor. Creates a new, empty ZipPackage.
*/
public ZipPackage() {
- super(defaultPackageAccess);
+ this(OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * Constructor. Creates a new, empty ZipPackage.
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @since POI 5.4.1
+ */
+ public ZipPackage(OPCComplianceFlags opcComplianceFlags) {
+ super(defaultPackageAccess, opcComplianceFlags);
this.zipArchive = null;
try {
@@ -128,7 +138,28 @@ public final class ZipPackage extends OP
* if input stream cannot be opened, read, or closed
*/
ZipPackage(InputStream in, PackageAccess access) throws IOException {
- super(access);
+ this(in, access, OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * Constructor. Opens a Zip based Open XML document from
+ * an InputStream. The InputStream is closed.
+ *
+ * @param in
+ * Zip input stream to load.
+ * @param access
+ * The package access mode.
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @throws IllegalArgumentException
+ * If the specified input stream is not an instance of
+ * ZipInputStream.
+ * @throws IOException
+ * if input stream cannot be opened, read, or closed
+ * @since POI 5.4.1
+ */
+ ZipPackage(InputStream in, PackageAccess access, OPCComplianceFlags
opcComplianceFlags) throws IOException {
+ super(access, opcComplianceFlags);
try (ZipArchiveThresholdInputStream zis = ZipHelper.openZipStream(in))
{
this.zipArchive = new ZipInputStreamZipEntrySource(zis);
}
@@ -152,7 +183,30 @@ public final class ZipPackage extends OP
* @since POI 5.2.5
*/
ZipPackage(InputStream in, PackageAccess access, boolean closeStream)
throws IOException {
- super(access);
+ this(in, access, closeStream, OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * Constructor. Opens a Zip based Open XML document from
+ * an InputStream.
+ *
+ * @param in
+ * Zip input stream to load.
+ * @param access
+ * The package access mode.
+ * @param closeStream
+ * Whether to close the input stream.
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @throws IllegalArgumentException
+ * If the specified input stream is not an instance of
+ * ZipInputStream.
+ * @throws IOException
+ * if input stream cannot be opened, read, or closed
+ * @since POI 5.4.1
+ */
+ ZipPackage(InputStream in, PackageAccess access, boolean closeStream,
OPCComplianceFlags opcComplianceFlags) throws IOException {
+ super(access, opcComplianceFlags);
try (ZipArchiveThresholdInputStream zis = ZipHelper.openZipStream(in,
closeStream)) {
this.zipArchive = new ZipInputStreamZipEntrySource(zis);
}
@@ -168,7 +222,23 @@ public final class ZipPackage extends OP
* @throws InvalidOperationException If the zip file cannot be opened.
*/
ZipPackage(String path, PackageAccess access) throws
InvalidOperationException {
- this(new File(path), access);
+ this(path, access, OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * Constructor. Opens a Zip based Open XML document from a file.
+ *
+ * @param path
+ * The path of the file to open or create.
+ * @param access
+ * The package access mode.
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @throws InvalidOperationException If the zip file cannot be opened.
+ * @since POI 5.4.1
+ */
+ ZipPackage(String path, PackageAccess access, OPCComplianceFlags
opcComplianceFlags) throws InvalidOperationException {
+ this(new File(path), access, opcComplianceFlags);
}
/**
@@ -181,7 +251,23 @@ public final class ZipPackage extends OP
* @throws InvalidOperationException If the zip file cannot be opened.
*/
ZipPackage(File file, PackageAccess access) throws
InvalidOperationException {
- super(access);
+ this(file, access, OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * Constructor. Opens a Zip based Open XML document from a File.
+ *
+ * @param file
+ * The file to open or create.
+ * @param access
+ * The package access mode.
+ * @param opcComplianceFlags
+ * The level of OPC compliance to enforce when reading the
package
+ * @throws InvalidOperationException If the zip file cannot be opened.
+ * @since POI 5.4.1
+ */
+ ZipPackage(File file, PackageAccess access, OPCComplianceFlags
opcComplianceFlags) throws InvalidOperationException {
+ super(access, opcComplianceFlags);
ZipEntrySource ze;
try {
@@ -248,7 +334,24 @@ public final class ZipPackage extends OP
* The package access mode.
*/
ZipPackage(ZipEntrySource zipEntry, PackageAccess access) {
- super(access);
+ this(zipEntry, access, OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * Constructor. Opens a Zip based Open XML document from
+ * a custom ZipEntrySource, typically an open archive
+ * from another system
+ *
+ * @param zipEntry
+ * Zip data to load.
+ * @param access
+ * The package access mode.
+ * @param access
+ * The package access mode.
+ * @since POI 5.4.1
+ */
+ ZipPackage(ZipEntrySource zipEntry, PackageAccess access,
OPCComplianceFlags opcComplianceFlags) {
+ super(access, opcComplianceFlags);
this.zipArchive = zipEntry;
}
Modified:
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/PackagePropertiesUnmarshaller.java
URL:
http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/PackagePropertiesUnmarshaller.java?rev=1924476&r1=1924475&r2=1924476&view=diff
==============================================================================
---
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/PackagePropertiesUnmarshaller.java
(original)
+++
poi/trunk/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/unmarshallers/PackagePropertiesUnmarshaller.java
Wed Mar 19 20:38:10 2025
@@ -28,6 +28,7 @@ import org.apache.poi.openxml4j.opc.Pack
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageProperties;
import org.apache.poi.openxml4j.opc.ZipPackage;
+import org.apache.poi.openxml4j.opc.OPCComplianceFlags;
import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
import org.apache.poi.openxml4j.opc.internal.PartUnmarshaller;
import org.apache.poi.openxml4j.opc.internal.ZipHelper;
@@ -44,6 +45,23 @@ import org.xml.sax.SAXException;
*/
public final class PackagePropertiesUnmarshaller implements PartUnmarshaller {
+ /**
+ * Whether OPC compliance requirements are checked (e.g., M4.2, M4.3,
M4.4, and M4.5)
+ */
+ private OPCComplianceFlags opcComplianceFlags;
+
+ public PackagePropertiesUnmarshaller() {
+ this(OPCComplianceFlags.enforceAll());
+ }
+
+ /**
+ * @param opcComplianceFlags Overrides the default OPC compliance settings
+ * @since POI 5.4.1
+ */
+ public PackagePropertiesUnmarshaller(OPCComplianceFlags
opcComplianceFlags) {
+ this.opcComplianceFlags = opcComplianceFlags;
+ }
+
protected static final String KEYWORD_CATEGORY = "category";
protected static final String KEYWORD_CONTENT_STATUS = "contentStatus";
@@ -234,24 +252,28 @@ public final class PackagePropertiesUnma
*/
public void checkElementForOPCCompliance(Element el)
throws InvalidFormatException {
- // Check the current element
- NamedNodeMap namedNodeMap = el.getAttributes();
- int namedNodeCount = namedNodeMap.getLength();
- for (int i = 0; i < namedNodeCount; i++) {
- Attr attr = (Attr)namedNodeMap.item(0);
-
- if (attr != null &&
XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(attr.getNamespaceURI())) {
- // Rule M4.2
- if
(PackageNamespaces.MARKUP_COMPATIBILITY.equals(attr.getValue())) {
- throw new InvalidFormatException(
- "OPC Compliance error [M4.2]: A format consumer
shall consider the use of the Markup Compatibility namespace to be an error.");
+
+ if (opcComplianceFlags.getForbidMarkupCompatibilityNamespace()) {
+ // Check the current element
+ NamedNodeMap namedNodeMap = el.getAttributes();
+ int namedNodeCount = namedNodeMap.getLength();
+ for (int i = 0; i < namedNodeCount; i++) {
+ Attr attr = (Attr)namedNodeMap.item(0);
+
+ if (attr != null &&
XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(attr.getNamespaceURI())) {
+ // Rule M4.2
+ if
(PackageNamespaces.MARKUP_COMPATIBILITY.equals(attr.getValue())) {
+ throw new InvalidFormatException(
+ "OPC Compliance error [M4.2]: A format
consumer shall consider the use of the Markup Compatibility namespace to be an
error.");
+ }
}
}
}
// Rule M4.3
String elName = el.getLocalName();
- if (PackageProperties.NAMESPACE_DCTERMS.equals(el.getNamespaceURI())) {
+ if (opcComplianceFlags.getForbidRefiningDublinCoreElements() &&
+
PackageProperties.NAMESPACE_DCTERMS.equals(el.getNamespaceURI())) {
if (!(KEYWORD_CREATED.equals(elName) ||
KEYWORD_MODIFIED.equals(elName))) {
throw new InvalidFormatException(
"OPC Compliance error [M4.3]: Producers shall not
create a document element that contains refinements to the Dublin Core
elements, except for the two specified in the schema: <dcterms:created> and
<dcterms:modified> Consumers shall consider a document element that violates
this constraint to be an error.");
@@ -259,13 +281,15 @@ public final class PackagePropertiesUnma
}
// Rule M4.4
- if (el.getAttributeNodeNS(XMLConstants.XML_NS_URI, "lang") != null) {
+ if (opcComplianceFlags.getForbidXmlLangAttributes() &&
+ el.getAttributeNodeNS(XMLConstants.XML_NS_URI, "lang") !=
null) {
throw new InvalidFormatException(
"OPC Compliance error [M4.4]: Producers shall not create a
document element that contains the xml:lang attribute. Consumers shall consider
a document element that violates this constraint to be an error.");
}
// Rule M4.5
- if (PackageProperties.NAMESPACE_DCTERMS.equals(el.getNamespaceURI())) {
+ if (opcComplianceFlags.getRestrictXsiTypeAttribute() &&
+
PackageProperties.NAMESPACE_DCTERMS.equals(el.getNamespaceURI())) {
// DCTerms namespace only use with 'created' and 'modified'
elements
if (!(elName.equals(KEYWORD_CREATED) ||
elName.equals(KEYWORD_MODIFIED))) {
throw new InvalidFormatException("Namespace error : " + elName
Modified:
poi/trunk/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/compliance/TestOPCComplianceCoreProperties.java
URL:
http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/compliance/TestOPCComplianceCoreProperties.java?rev=1924476&r1=1924475&r2=1924476&view=diff
==============================================================================
---
poi/trunk/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/compliance/TestOPCComplianceCoreProperties.java
(original)
+++
poi/trunk/poi-ooxml/src/test/java/org/apache/poi/openxml4j/opc/compliance/TestOPCComplianceCoreProperties.java
Wed Mar 19 20:38:10 2025
@@ -38,6 +38,7 @@ import org.apache.commons.io.output.Unsy
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
import org.apache.poi.openxml4j.opc.ContentTypes;
+import org.apache.poi.openxml4j.opc.OPCComplianceFlags;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
@@ -87,17 +88,26 @@ public final class TestOPCComplianceCore
private static String extractInvalidFormatMessage(String sampleNameSuffix)
throws IOException {
try (InputStream is = openSampleStream("OPCCompliance_CoreProperties_"
+ sampleNameSuffix)) {
InvalidFormatException e =
assertThrows(InvalidFormatException.class,
- () -> OPCPackage.open(is).revert(), "expected OPC compliance
exception was not thrown");
+ () -> OPCPackage.open(is).revert(), "expected OPC
compliance exception was not thrown");
return e.getMessage();
}
}
+ private static void testComplianceFlagOverridePreventsException(
+ String sampleNameSuffix,
+ OPCComplianceFlags opcComplianceFlags
+ ) throws IOException {
+ try (InputStream is = openSampleStream("OPCCompliance_CoreProperties_"
+ sampleNameSuffix)) {
+ assertDoesNotThrow(() -> OPCPackage.open(is,
opcComplianceFlags).revert(), "Unexpected compliance related exception thrown");
+ }
+ }
+
/**
* Test M4.1 rule.
*/
@Test
void testOnlyOneCorePropertiesPart() throws Exception {
- // We have relaxed this check, so we can read the file anyway
+ // We have relaxed this check, so we can read the file anyway
assertDoesNotThrow(() -> {
try (InputStream is =
openSampleStream("OPCCompliance_CoreProperties_OnlyOneCorePropertiesPartFAIL.docx");
OPCPackage pkg = OPCPackage.open(is)) {
@@ -105,21 +115,21 @@ public final class TestOPCComplianceCore
}
}, "M4.1 should be being relaxed");
- // We will use the first core properties, and ignore the others
+ // We will use the first core properties, and ignore the others
- try (InputStream is = openSampleStream("MultipleCoreProperties.docx");
- OPCPackage pkg = OPCPackage.open(is)) {
+ try (InputStream is = openSampleStream("MultipleCoreProperties.docx");
+ OPCPackage pkg = OPCPackage.open(is)) {
- // We can see 2 by type
- assertEquals(2,
pkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size());
- // But only the first one by relationship
- assertEquals(1,
pkg.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES).size());
- // It should be core.xml not the older core1.xml
- assertEquals(
- "/docProps/core.xml",
-
pkg.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES).get(0).getPartName().toString()
- );
- }
+ // We can see 2 by type
+ assertEquals(2,
pkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size());
+ // But only the first one by relationship
+ assertEquals(1,
pkg.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES).size());
+ // It should be core.xml not the older core1.xml
+ assertEquals(
+ "/docProps/core.xml",
+
pkg.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES).get(0).getPartName().toString()
+ );
+ }
}
private static URI createURI(String text) {
@@ -139,8 +149,8 @@ public final class TestOPCComplianceCore
OPCPackage pkg = OPCPackage.open(is);
URI partUri = createURI("/docProps/core2.xml");
InvalidOperationException e =
assertThrows(InvalidOperationException.class, () ->
-
pkg.addRelationship(PackagingURIHelper.createPartName(partUri),
TargetMode.INTERNAL, PackageRelationshipTypes.CORE_PROPERTIES),
- "expected OPC compliance exception was not thrown"
+
pkg.addRelationship(PackagingURIHelper.createPartName(partUri),
TargetMode.INTERNAL, PackageRelationshipTypes.CORE_PROPERTIES),
+ "expected OPC compliance exception was not thrown"
);
assertEquals("OPC Compliance error [M4.1]: can't add another core
properties part ! Use the built-in package method instead.", e.getMessage());
pkg.revert();
@@ -157,8 +167,8 @@ public final class TestOPCComplianceCore
URI partUri = createURI("/docProps/core2.xml");
InvalidOperationException e =
assertThrows(InvalidOperationException.class, () ->
- pkg.createPart(PackagingURIHelper.createPartName(partUri),
ContentTypes.CORE_PROPERTIES_PART),
- "expected OPC compliance exception was not thrown");
+
pkg.createPart(PackagingURIHelper.createPartName(partUri),
ContentTypes.CORE_PROPERTIES_PART),
+ "expected OPC compliance exception was not thrown");
assertEquals("OPC Compliance error [M4.1]: you try to add more
than one core properties relationship in the package !", e.getMessage());
pkg.revert();
}
@@ -211,7 +221,7 @@ public final class TestOPCComplianceCore
/**
* Document with no core properties - testing at the OPC level,
- * saving into a new stream
+ * saving into a new stream
*/
@Test
void testNoCoreProperties_saveNew() throws Exception {
@@ -257,7 +267,7 @@ public final class TestOPCComplianceCore
/**
* Document with no core properties - testing at the OPC level,
- * from a temp-file, saving in-place
+ * from a temp-file, saving in-place
*/
@Test
void testNoCoreProperties_saveInPlace() throws Exception {
@@ -266,7 +276,7 @@ public final class TestOPCComplianceCore
// Copy this into a temp file, so we can play with it
File tmp = getOutputFile("poi-test.opc");
try (FileOutputStream out = new FileOutputStream(tmp);
- InputStream in = openSampleStream(sampleFileName)) {
+ InputStream in = openSampleStream(sampleFileName)) {
IOUtils.copy(in, out);
}
@@ -295,4 +305,50 @@ public final class TestOPCComplianceCore
}
assertTrue(tmp.delete());
}
+
+ /**
+ * Test M4.2 rule not enforced when appropriate OPCComplianceFlag is set.
+ */
+ @Test
+ void testDisableEnforceCompatibilityMarkup() throws IOException {
+ testComplianceFlagOverridePreventsException(
+ "DoNotUseCompatibilityMarkupFAIL.docx",
+
OPCComplianceFlags.enforceAll().setForbidMarkupCompatibilityNamespace(false)
+ );
+ }
+
+ /**
+ * Test M4.3 and M4.5 rules not enforced when appropriate
OPCComplianceFlag is set.
+ */
+ @Test
+ void testDisableEnforceDublinCoreAndLimitedXSI() throws IOException {
+ testComplianceFlagOverridePreventsException(
+ "DCTermsNamespaceLimitedUseFAIL.docx",
+ OPCComplianceFlags.enforceAll() // This test document violates
both M4.3 and M4.5
+ .setRestrictXsiTypeAttribute(false)
+ .setForbidRefiningDublinCoreElements(false)
+ );
+ }
+
+ /**
+ * Test M4.4 rule not enforced when appropriate OPCComplianceFlag is set.
+ */
+ @Test
+ void testDisableEnforceUnauthorizedXMLLangAttribute() throws IOException {
+ testComplianceFlagOverridePreventsException(
+ "UnauthorizedXMLLangAttributeFAIL.docx",
+
OPCComplianceFlags.enforceAll().setForbidXmlLangAttribute(false)
+ );
+ }
+
+ /**
+ * Test M4.5 rule not enforced when appropriate OPCComplianceFlag is set.
+ */
+ @Test
+ void testDisableEnforceLimitedXSITypeAttribute_NotPresent() throws
IOException {
+ testComplianceFlagOverridePreventsException(
+ "LimitedXSITypeAttribute_NotPresentFAIL.docx",
+
OPCComplianceFlags.enforceAll().setRestrictXsiTypeAttribute(false)
+ );
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]