This is an automated email from the ASF dual-hosted git repository.

tballison pushed a commit to branch TIKA-4761-sevenz
in repository https://gitbox.apache.org/repos/asf/tika.git

commit fe82d71fff4b51fd2ccbf4f911fda7d119b579a8
Author: tallison <[email protected]>
AuthorDate: Thu Jun 25 11:05:11 2026 -0400

    TIKA-4761 -- improve sevenz memory limit handling
---
 .../org/apache/tika/parser/pkg/SevenZParser.java   | 46 +++++++++++++++++++++-
 .../apache/tika/parser/pkg/Seven7ParserTest.java   | 22 +++++++++++
 2 files changed, 67 insertions(+), 1 deletion(-)

diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/SevenZParser.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/SevenZParser.java
index 2900dec7b9..3de2916860 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/SevenZParser.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/SevenZParser.java
@@ -23,15 +23,19 @@ import java.io.InputStream;
 import java.util.Collections;
 import java.util.Set;
 
+import org.apache.commons.compress.MemoryLimitException;
 import org.apache.commons.compress.PasswordRequiredException;
 import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
 import org.apache.commons.compress.archivers.sevenz.SevenZFile;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
+import org.apache.tika.config.ConfigDeserializer;
+import org.apache.tika.config.JsonConfig;
 import org.apache.tika.config.TikaComponent;
 import org.apache.tika.exception.EncryptedDocumentException;
 import org.apache.tika.exception.TikaException;
+import org.apache.tika.exception.TikaMemoryLimitException;
 import org.apache.tika.extractor.EmbeddedDocumentExtractor;
 import org.apache.tika.extractor.EmbeddedDocumentUtil;
 import org.apache.tika.io.TemporaryResources;
@@ -60,6 +64,37 @@ public class SevenZParser extends AbstractArchiveParser {
 
     private static final Set<MediaType> SUPPORTED_TYPES = 
Collections.singleton(SEVENZ);
 
+    /**
+     * Configuration class for JSON deserialization.
+     */
+    public static class Config {
+        // Cap the LZMA/LZMA2 dictionary allocation. A tiny 7z can declare a 
huge dictionary
+        // (a single header byte), forcing an eager multi-GiB allocation and 
OOMing the parser.
+        // Mirrors CompressorParser's default.
+        private int memoryLimitInKb = 100000;
+
+        public int getMemoryLimitInKb() {
+            return memoryLimitInKb;
+        }
+
+        public void setMemoryLimitInKb(int memoryLimitInKb) {
+            this.memoryLimitInKb = memoryLimitInKb;
+        }
+    }
+
+    private Config defaultConfig = new Config();
+
+    public SevenZParser() {
+    }
+
+    public SevenZParser(Config config) {
+        this.defaultConfig = config;
+    }
+
+    public SevenZParser(JsonConfig jsonConfig) {
+        this(ConfigDeserializer.buildConfig(jsonConfig, Config.class));
+    }
+
     @Override
     public Set<MediaType> getSupportedTypes(ParseContext context) {
         return SUPPORTED_TYPES;
@@ -78,7 +113,10 @@ public class SevenZParser extends AbstractArchiveParser {
 
         SevenZFile sevenZFile;
         try {
-            SevenZFile.Builder builder = new 
SevenZFile.Builder().setFile(tis.getFile());
+            // Use setMaxMemoryLimitKiB (direct KiB); setMaxMemoryLimitKb 
divides the arg by 1024.
+            SevenZFile.Builder builder = new SevenZFile.Builder()
+                    .setFile(tis.getFile())
+                    .setMaxMemoryLimitKiB(defaultConfig.getMemoryLimitInKb());
             if (password == null) {
                 sevenZFile = builder.get();
             } else {
@@ -86,6 +124,10 @@ public class SevenZParser extends AbstractArchiveParser {
             }
         } catch (PasswordRequiredException e) {
             throw new EncryptedDocumentException(e);
+        } catch (MemoryLimitException e) {
+            // The limit can be exceeded at open time (assertValidity) as well 
as lazily on the
+            // first getNextEntry() when the LZMA/LZMA2 dictionary is 
allocated.
+            throw new TikaMemoryLimitException(e.getMessage());
         }
 
         metadata.set(Metadata.CONTENT_TYPE, SEVENZ.toString());
@@ -106,6 +148,8 @@ public class SevenZParser extends AbstractArchiveParser {
             }
         } catch (PasswordRequiredException e) {
             throw new EncryptedDocumentException(e);
+        } catch (MemoryLimitException e) {
+            throw new TikaMemoryLimitException(e.getMessage());
         } finally {
             sevenZFile.close();
             xhtml.endDocument();
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/test/java/org/apache/tika/parser/pkg/Seven7ParserTest.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/test/java/org/apache/tika/parser/pkg/Seven7ParserTest.java
index 2198bafd01..32a29d9df0 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/test/java/org/apache/tika/parser/pkg/Seven7ParserTest.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/test/java/org/apache/tika/parser/pkg/Seven7ParserTest.java
@@ -19,13 +19,16 @@ package org.apache.tika.parser.pkg;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.junit.jupiter.api.Test;
 import org.xml.sax.ContentHandler;
 
+import org.apache.tika.exception.TikaMemoryLimitException;
 import org.apache.tika.io.TikaInputStream;
 import org.apache.tika.metadata.Metadata;
+import org.apache.tika.parser.ParseContext;
 import org.apache.tika.sax.BodyContentHandler;
 
 /**
@@ -71,4 +74,23 @@ public class Seven7ParserTest extends AbstractPkgTest {
             assertTrue(mod.startsWith("20"), "Modified at " + mod);
         }
     }
+
+    /**
+     * A 7z whose declared LZMA2 dictionary exceeds the configured limit must 
fail with a
+     * {@link TikaMemoryLimitException} rather than eagerly allocating the 
dictionary (which a
+     * tiny crafted 7z can drive to multiple GiB, OOMing the parser). The 
default-limit control
+     * is {@link #testEmbedded()} above, which would fail if the limit were 
applied in the wrong
+     * unit (e.g. via setMaxMemoryLimitKb, which divides the argument by 1024).
+     */
+    @Test
+    public void testMemoryLimit() throws Exception {
+        SevenZParser.Config config = new SevenZParser.Config();
+        config.setMemoryLimitInKb(1); // 1 KiB, smaller than any real LZMA2 
dictionary
+        SevenZParser parser = new SevenZParser(config);
+
+        try (TikaInputStream tis = 
getResourceAsStream("/test-documents/test-documents.7z")) {
+            assertThrows(TikaMemoryLimitException.class, () -> 
parser.parse(tis,
+                    new BodyContentHandler(), new Metadata(), new 
ParseContext()));
+        }
+    }
 }

Reply via email to