fix(cbx): "Archive too large" error for invalid/corrupt CBX files (#2205)

Signed-off-by: Balázs Szücs <bszucs1209@gmail.com>
This commit is contained in:
Balázs Szücs 2026-01-08 23:18:40 +01:00 committed by GitHub
parent 15de049f3d
commit 14dcf552bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 30 additions and 5 deletions

View File

@ -57,7 +57,14 @@ public class CbxReaderService {
try {
long maxCacheSizeBytes = mbToBytes(appSettingService.getAppSettings().getCbxCacheSizeInMb());
long estimatedSize = estimateArchiveSize(cbzPath);
if (estimatedSize > maxCacheSizeBytes) {
if (estimatedSize == -1) {
long fileSize = Files.size(cbzPath);
if (fileSize > maxCacheSizeBytes) {
log.warn("Cache skipped: Physical file size {} exceeds max cache size {} (Estimation failed)", fileSize, maxCacheSizeBytes);
throw ApiError.CACHE_TOO_LARGE.createException();
}
} else if (estimatedSize > maxCacheSizeBytes) {
log.warn("Cache skipped: Estimated archive size {} exceeds max cache size {}", estimatedSize, maxCacheSizeBytes);
throw ApiError.CACHE_TOO_LARGE.createException();
}
@ -78,7 +85,7 @@ public class CbxReaderService {
}
} catch (IOException e) {
log.error("Failed to cache CBZ for book {}", bookId, e);
return List.of();
throw ApiError.FILE_READ_ERROR.createException("Failed to read archive: " + e.getMessage());
}
try (var stream = Files.list(cacheDir)) {
@ -360,7 +367,7 @@ public class CbxReaderService {
} catch (Exception e) {
log.warn("Failed to estimate archive size for {}", cbxPath, e);
}
return Long.MAX_VALUE;
return -1;
}
private long estimateCbzArchiveSize(Path cbxPath) throws IOException {
@ -384,7 +391,7 @@ public class CbxReaderService {
}
log.warn("Unable to estimate archive size for {} with any supported encoding", cbxPath);
return Long.MAX_VALUE;
return -1;
}
private long estimateCbzWithEncoding(Path cbxPath, Charset charset, boolean useFastPath) throws IOException {
@ -405,7 +412,7 @@ public class CbxReaderService {
total += (size >= 0) ? size : entry.getCompressedSize();
}
}
return total > 0 ? total : Long.MAX_VALUE;
return total > 0 ? total : -1;
}
}

View File

@ -1,6 +1,8 @@
package com.adityachandel.booklore.service.reader;
import com.adityachandel.booklore.exception.APIException;
import com.adityachandel.booklore.model.dto.settings.AppSettings;
import com.adityachandel.booklore.exception.ApiError;
import com.adityachandel.booklore.model.entity.BookEntity;
import com.adityachandel.booklore.model.entity.LibraryPathEntity;
import com.adityachandel.booklore.repository.BookRepository;
@ -167,6 +169,22 @@ class CbxReaderServiceTest {
"Page count should be exactly 130 (actual comic pages only)");
}
@Test
void getAvailablePages_whenArchiveIsCorrupt_shouldThrowFileReadError() throws IOException {
Path corruptCbz = tempDir.resolve("corrupt.cbz");
Files.writeString(corruptCbz, "This is not a zip file");
testBook.setFileName(corruptCbz.getFileName().toString());
testBook.getLibraryPath().setPath(tempDir.toString());
APIException exception = assertThrows(APIException.class, () -> service.getAvailablePages(bookId));
assertNotEquals(ApiError.CACHE_TOO_LARGE.getMessage(), exception.getMessage(),
"Should not throw CACHE_TOO_LARGE for a corrupt file");
assertTrue(exception.getMessage().startsWith("Error reading files from path"),
"Should throw FILE_READ_ERROR (message starts with 'Error reading files from path'), actual: '" + exception.getMessage() + "'");
}
private void createTestCbzWithMacOsFiles(File cbzFile) throws IOException {
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(cbzFile))) {
for (int i = 1; i <= 130; i++) {