Skip to content

Commit 11a1737

Browse files
authored
Prevent infinite recursion in format sniffing (#476)
1 parent 82b1238 commit 11a1737

File tree

4 files changed

+79
-135
lines changed

4 files changed

+79
-135
lines changed

readium/shared/src/main/java/org/readium/r2/shared/util/asset/AssetSniffer.kt

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,20 @@ internal class AssetSniffer(
4646
source: Either<Resource, Container<Resource>>,
4747
hints: FormatHints
4848
): Try<Asset, SniffError> {
49-
val initialDescription = Format(
50-
specification = FormatSpecification(emptySet()),
51-
mediaType = MediaType.BINARY,
52-
fileExtension = FileExtension("")
53-
)
49+
val initialFormat = formatSniffer
50+
.sniffHints(hints)
51+
?: Format(
52+
specification = FormatSpecification(emptySet()),
53+
mediaType = MediaType.BINARY,
54+
fileExtension = FileExtension("")
55+
)
5456

5557
val cachingSource: Either<Readable, Container<Readable>> = when (source) {
5658
is Either.Left -> Either.Left(CachingReadable(source.value))
5759
is Either.Right -> Either.Right(CachingContainer(source.value))
5860
}
5961

60-
val asset = doSniff(initialDescription, source, cachingSource, hints)
62+
val asset = sniffContent(initialFormat, source, cachingSource, hints, forceRefine = false)
6163
.getOrElse { return Try.failure(SniffError.Reading(it)) }
6264

6365
return asset
@@ -66,45 +68,39 @@ internal class AssetSniffer(
6668
?: Try.failure(SniffError.NotRecognized)
6769
}
6870

69-
private suspend fun doSniff(
71+
private suspend fun sniffContent(
7072
format: Format,
7173
source: Either<Resource, Container<Resource>>,
7274
cache: Either<Readable, Container<Readable>>,
73-
hints: FormatHints
75+
hints: FormatHints,
76+
forceRefine: Boolean
7477
): Try<Asset, ReadError> {
75-
formatSniffer
76-
.sniffHints(format, hints)
77-
.takeIf { it.conformsTo(format) }
78-
?.takeIf { it != format }
79-
?.let { return doSniff(it, source, cache, hints) }
80-
8178
when (cache) {
8279
is Either.Left ->
8380
formatSniffer
8481
.sniffBlob(format, cache.value)
8582
.getOrElse { return Try.failure(it) }
86-
.takeIf { it.conformsTo(format) }
87-
?.takeIf { it != format }
88-
?.let { return doSniff(it, source, cache, hints) }
83+
.takeIf { !forceRefine || it.refines(format) }
84+
?.let { return sniffContent(it, source, cache, hints, forceRefine = true) }
8985

9086
is Either.Right ->
9187
formatSniffer
9288
.sniffContainer(format, cache.value)
9389
.getOrElse { return Try.failure(it) }
94-
.takeIf { it.conformsTo(format) }
95-
?.takeIf { it != format }
96-
?.let { return doSniff(it, source, cache, hints) }
90+
.takeIf { !forceRefine || it.refines(format) }
91+
?.let { return sniffContent(it, source, cache, hints, forceRefine = true) }
9792
}
9893

9994
if (source is Either.Left) {
10095
tryOpenArchive(format, source.value)
10196
.getOrElse { return Try.failure(it) }
10297
?.let {
103-
return doSniff(
98+
return sniffContent(
10499
it.format,
105100
Either.Right(it.container),
106101
Either.Right(CachingContainer(it.container)),
107-
hints
102+
hints,
103+
forceRefine = true
108104
)
109105
}
110106
}

readium/shared/src/main/java/org/readium/r2/shared/util/format/Format.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ public data class Format(
2525

2626
internal fun conformsTo(other: Format): Boolean =
2727
specification.conformsTo(other.specification)
28+
29+
internal fun refines(other: Format) =
30+
specification != other.specification &&
31+
specification.conformsToAll(other.specification.specifications)
2832
}
2933

3034
@JvmInline

0 commit comments

Comments
 (0)