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

tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new f52c779fe3 TIKA-4985: keep parsing a RAR past an entry junrar fails to 
extract (#3377)
f52c779fe3 is described below

commit f52c779fe3a6511754f36501cef4bc8d81bef094
Author: Tim Allison <[email protected]>
AuthorDate: Wed Oct 21 08:52:56 2026 -0400

    TIKA-4985: keep parsing a RAR past an entry junrar fails to extract (#3377)
    
    * TIKA-4985: keep parsing a RAR past an entry junrar fails to extract
    
      junrar 8.1.2 reports a truncated, corrupt or password-protected entry as 
an
      IOException on the entry's stream, which escaped as a container exception 
and
      lost the rest of the archive. End the entry at the failure instead, keep 
what
      was extracted, and record the failure as an embedded-stream exception.
    
      Co-Authored-By: Claude Opus 5.5 <[email protected]>
    
    * TIKA-4985: name the failed RAR entry; assert test entries by path
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 5.5 <[email protected]>
    Co-authored-by: Tilman Hausherr <[email protected]>
---
 CHANGES.txt                                        | 12 ++++++--
 .../java/org/apache/tika/parser/pkg/RarParser.java | 30 +++++++++++++++++++-
 .../org/apache/tika/parser/pkg/RarParserTest.java  | 32 ++++++++++++++++++++++
 3 files changed, 71 insertions(+), 3 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index f3df1b316f..ef14a82210 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
 Release 4.2.0 - unreleased
 
+  BREAKING CHANGES AND DEPRECATIONS
    * Extract stored line and paragraph counts from legacy Microsoft Word
      (.doc) documents. The existing metadata properties are now populated
      when these values are available in the document (TIKA-4694).
@@ -12,8 +13,6 @@ Release 4.2.0 - unreleased
      parent directory of the study file, so pass a file-backed TikaInputStream
      (TikaInputStream.get(Path)) (TIKA-4986).
 
-  BREAKING CHANGES AND DEPRECATIONS
-
    * Unpack output is a Frictionless Data Package by default on tika-server 
/unpack,
      tika-app -z/-Z and pipes UNPACK; unpack-config.outputFormat=REGULAR
      (--unpack-format=REGULAR) restores the old layout (TIKA-4681). A document 
without
@@ -318,6 +317,15 @@ Release 4.2.0 - unreleased
 
   OTHER CHANGES
 
+   * A RAR entry junrar fails to extract (truncated archive, bad CRC, missing 
password)
+     is now recorded as tk:exception:embedded-stream-exception on the 
container; the
+     entry still yields what was extracted before the failure, and the rest of 
the
+     archive is still parsed (junrar 8.1.2, TIKA-4985).
+
+   * ISArchiveParser no longer reads every ISA-Tab document's investigation 
and assay files
+     from the directory of the first document it parsed; concurrent parses no 
longer share
+     the study file name (TIKA-4986).
+
    * JSoupParser emits CDATA text again (TIKA-4963).
 
    * OutlookExtractor falls back to the Date: header when a .msg has no MAPI 
date
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/RarParser.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/RarParser.java
index 667c983612..377667da03 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/RarParser.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/main/java/org/apache/tika/parser/pkg/RarParser.java
@@ -17,12 +17,14 @@
 package org.apache.tika.parser.pkg;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Collections;
 import java.util.Set;
 
 import com.github.junrar.Archive;
 import com.github.junrar.exception.RarException;
 import com.github.junrar.rarfile.FileHeader;
+import org.apache.commons.io.input.ProxyInputStream;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
@@ -91,11 +93,17 @@ public class RarParser implements Parser {
                     AbstractArchiveParser.setLocalTime(entrydata, 
FileSystem.CREATED, header.getCTime());
                     AbstractArchiveParser.setLocalTime(entrydata, 
FileSystem.MODIFIED, header.getMTime());
                     AbstractArchiveParser.setLocalTime(entrydata, 
FileSystem.ACCESSED, header.getATime());
-                    try (TikaInputStream rarTis = 
TikaInputStream.get(rar.getInputStream(header))) {
+                    EntryStream entryStream = new 
EntryStream(rar.getInputStream(header));
+                    try (TikaInputStream rarTis = 
TikaInputStream.get(entryStream)) {
                         if (extractor.shouldParseEmbedded(entrydata, context)) 
{
                             extractor.parseEmbedded(rarTis, handler, 
entrydata, context, true);
                         }
                     }
+                    if (entryStream.failure != null) {
+                        EmbeddedDocumentUtil.recordEmbeddedStreamException(
+                                new IOException("RAR entry " + 
header.getFileName(),
+                                        entryStream.failure), metadata, 
context);
+                    }
                 }
 
                 header = rar.nextFileHeader();
@@ -112,4 +120,24 @@ public class RarParser implements Parser {
 
         xhtml.endDocument();
     }
+
+    /**
+     * junrar 8.1.2+ reports a failed entry (truncated archive, bad CRC, 
missing password) as an
+     * IOException on the entry's stream. End the entry there instead, as 
junrar 8.1.1 did, so
+     * the bytes it delivered are still parsed and the rest of the archive is 
reached.
+     */
+    private static class EntryStream extends ProxyInputStream {
+        private IOException failure;
+
+        EntryStream(InputStream in) {
+            super(in);
+        }
+
+        @Override
+        protected void handleIOException(IOException e) {
+            if (failure == null) {
+                failure = e;
+            }
+        }
+    }
 }
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/test/java/org/apache/tika/parser/pkg/RarParserTest.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/test/java/org/apache/tika/parser/pkg/RarParserTest.java
index 89380866b5..fc0e601e52 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/test/java/org/apache/tika/parser/pkg/RarParserTest.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pkg-module/src/test/java/org/apache/tika/parser/pkg/RarParserTest.java
@@ -22,12 +22,19 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
 import org.xml.sax.ContentHandler;
 
 import org.apache.tika.exception.EncryptedDocumentException;
 import org.apache.tika.io.TikaInputStream;
 import org.apache.tika.metadata.Metadata;
+import org.apache.tika.metadata.TikaCoreProperties;
 import org.apache.tika.parser.ParseContext;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.parser.PasswordProvider;
@@ -92,6 +99,31 @@ public class RarParserTest extends AbstractPkgTest {
         assertContains("test-documents/testXML.xml", content);
     }
 
+    @Test
+    public void testCorruptEntryKeepsLaterEntries(@TempDir Path tmp) throws 
Exception {
+        byte[] rar;
+        try (TikaInputStream tis = 
getResourceAsStream("/test-documents/test-documents.rar")) {
+            rar = IOUtils.toByteArray(tis);
+        }
+        // testEXCEL.xls, the first entry, has its packed data at 80..2885
+        rar[1500] ^= 0x5A;
+        Path corrupt = tmp.resolve("corrupt.rar");
+        Files.write(corrupt, rar);
+
+        List<Metadata> metadataList = getRecursiveMetadata(corrupt, new 
ParseContext(), false);
+        assertContains("testEXCEL.xls",
+                
metadataList.get(0).get(TikaCoreProperties.TIKA_META_EXCEPTION_EMBEDDED_STREAM));
+        assertContains("test-documents/testXML.xml",
+                metadataList.get(0).get(TikaCoreProperties.TIKA_CONTENT));
+        assertTrue(hasEmbeddedPath(metadataList, "/testEXCEL.xls"));
+        assertTrue(hasEmbeddedPath(metadataList, "/testXML.xml"));
+    }
+
+    private static boolean hasEmbeddedPath(List<Metadata> metadataList, String 
path) {
+        return metadataList.stream()
+                .anyMatch(m -> 
path.equals(m.get(TikaCoreProperties.EMBEDDED_RESOURCE_PATH)));
+    }
+
     /**
      * Note - we don't currently support Encrypted RAR files,
      * so all we can do is throw a helpful exception

Reply via email to