fix(komga): add safeguards for malformed epub

Closes: #1386
This commit is contained in:
Gauthier Roebroeck 2024-01-15 10:02:08 +08:00
parent 966a5d3580
commit 270a50c288
3 changed files with 13 additions and 5 deletions

View file

@ -24,3 +24,4 @@ class PathContainedInPath(message: String, code: String = "") : CodedException(m
class UserEmailAlreadyExistsException(message: String, code: String = "") : CodedException(message, code) class UserEmailAlreadyExistsException(message: String, code: String = "") : CodedException(message, code)
class BookConversionException(message: String) : Exception(message) class BookConversionException(message: String) : Exception(message)
class ComicRackListException(message: String, code: String = "") : CodedException(message, code) class ComicRackListException(message: String, code: String = "") : CodedException(message, code)
class EntryNotFoundException(message: String) : Exception(message)

View file

@ -4,6 +4,7 @@ import mu.KotlinLogging
import org.apache.commons.compress.archivers.ArchiveEntry import org.apache.commons.compress.archivers.ArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipFile import org.apache.commons.compress.archivers.zip.ZipFile
import org.gotson.komga.domain.model.BookPage import org.gotson.komga.domain.model.BookPage
import org.gotson.komga.domain.model.EntryNotFoundException
import org.gotson.komga.domain.model.EpubTocEntry import org.gotson.komga.domain.model.EpubTocEntry
import org.gotson.komga.domain.model.MediaFile import org.gotson.komga.domain.model.MediaFile
import org.gotson.komga.domain.model.R2Locator import org.gotson.komga.domain.model.R2Locator
@ -33,7 +34,8 @@ class EpubExtractor(
*/ */
fun getEntryStream(path: Path, entryName: String): ByteArray = fun getEntryStream(path: Path, entryName: String): ByteArray =
ZipFile(path.toFile()).use { zip -> ZipFile(path.toFile()).use { zip ->
zip.getInputStream(zip.getEntry(entryName)).use { it.readBytes() } zip.getEntry(entryName)?.let { entry -> zip.getInputStream(entry).use { it.readBytes() } }
?: throw EntryNotFoundException("Entry does not exist: $entryName")
} }
fun isEpub(path: Path): Boolean = fun isEpub(path: Path): Boolean =
@ -173,17 +175,17 @@ class EpubExtractor(
readingOrder.map { readingOrder.map {
R2Locator( R2Locator(
href = it.fileName, href = it.fileName,
type = it.mediaType!!, type = it.mediaType ?: "application/octet-stream",
locations = R2Locator.Location(progression = 0F, position = startPosition++), locations = R2Locator.Location(progression = 0F, position = startPosition++),
) )
} }
} else { } else {
readingOrder.flatMap { file -> readingOrder.flatMap { file ->
val positionCount = maxOf(1, ceil(file.fileSize!! / 1024.0).roundToInt()) val positionCount = maxOf(1, ceil((file.fileSize ?: 0) / 1024.0).roundToInt())
(0 until positionCount).map { p -> (0 until positionCount).map { p ->
R2Locator( R2Locator(
href = file.fileName, href = file.fileName,
type = file.mediaType!!, type = file.mediaType ?: "application/octet-stream",
locations = R2Locator.Location(progression = p.toFloat() / positionCount, position = startPosition++), locations = R2Locator.Location(progression = p.toFloat() / positionCount, position = startPosition++),
) )
} }

View file

@ -19,6 +19,7 @@ import org.gotson.komga.domain.model.BookSearchWithReadProgress
import org.gotson.komga.domain.model.BookWithMedia import org.gotson.komga.domain.model.BookWithMedia
import org.gotson.komga.domain.model.Dimension import org.gotson.komga.domain.model.Dimension
import org.gotson.komga.domain.model.DomainEvent import org.gotson.komga.domain.model.DomainEvent
import org.gotson.komga.domain.model.EntryNotFoundException
import org.gotson.komga.domain.model.ImageConversionException import org.gotson.komga.domain.model.ImageConversionException
import org.gotson.komga.domain.model.KomgaUser import org.gotson.komga.domain.model.KomgaUser
import org.gotson.komga.domain.model.MarkSelectedPreference import org.gotson.komga.domain.model.MarkSelectedPreference
@ -701,7 +702,11 @@ class BookController(
if (!isFont) principal!!.user.checkContentRestriction(book) if (!isFont) principal!!.user.checkContentRestriction(book)
val res = media.files.firstOrNull { it.fileName == resourceName } ?: throw ResponseStatusException(HttpStatus.NOT_FOUND) val res = media.files.firstOrNull { it.fileName == resourceName } ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
val bytes = bookAnalyzer.getFileContent(BookWithMedia(book, media), resourceName) val bytes = try {
bookAnalyzer.getFileContent(BookWithMedia(book, media), resourceName)
} catch (e: EntryNotFoundException) {
throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
return ResponseEntity.ok() return ResponseEntity.ok()
.headers( .headers(