diff --git a/app/src/main/java/com/m57/hermescontrol/data/update/AppUpdateChecker.kt b/app/src/main/java/com/m57/hermescontrol/data/update/AppUpdateChecker.kt index fb261acd..c9133ca2 100644 --- a/app/src/main/java/com/m57/hermescontrol/data/update/AppUpdateChecker.kt +++ b/app/src/main/java/com/m57/hermescontrol/data/update/AppUpdateChecker.kt @@ -222,7 +222,10 @@ open class AppUpdateChecker( val url = NEXT_LINK.find(link)?.groupValues?.get(1) ?: return null val next = url.toHttpUrlOrNull() ?: throw IOException("GitHub returned an invalid pagination URL") val base = apiBaseUrl.toHttpUrlOrNull() ?: throw IOException("Invalid GitHub API base URL") - if (next.host != base.host || next.encodedPath != base.encodedPath.trimEnd('/') + "/releases") { + val expectedReposPath = base.encodedPath.trimEnd('/') + "/releases" + val isReposPath = next.encodedPath == expectedReposPath + val isRepositoriesPath = REPOSITORIES_RELEASES_PATH.matches(next.encodedPath) + if (next.host != base.host || (!isReposPath && !isRepositoriesPath)) { throw IOException("GitHub returned an untrusted pagination URL") } return next.toString() @@ -340,6 +343,7 @@ open class AppUpdateChecker( const val RELEASE_LIST_PAGE_SIZE = 100 val NEXT_LINK = Regex("<([^>]+)>;\\s*rel=\"next\"") + val REPOSITORIES_RELEASES_PATH = Regex("^/repositories/[0-9]+/releases$") } } diff --git a/app/src/test/java/com/m57/hermescontrol/data/update/AppUpdateCheckerNetworkTest.kt b/app/src/test/java/com/m57/hermescontrol/data/update/AppUpdateCheckerNetworkTest.kt index e149cfcc..93687a75 100644 --- a/app/src/test/java/com/m57/hermescontrol/data/update/AppUpdateCheckerNetworkTest.kt +++ b/app/src/test/java/com/m57/hermescontrol/data/update/AppUpdateCheckerNetworkTest.kt @@ -107,6 +107,24 @@ class AppUpdateCheckerNetworkTest { assertEquals("/repos/Hy4ri/hermes-mobile/releases?per_page=100&page=2", server.takeRequest().path) } + @Test + fun fetchLatestRelease_rcScanAcceptsRepositoriesPaginationFormat() = + runTest { + enqueue(STABLE_BODY) + val nextPage = server.url("/repositories/1270568813/releases?per_page=100&page=2") + enqueue(RELEASES_BODY, headers = mapOf("Link" to "<$nextPage>; rel=\"next\"")) + enqueue( + """[{"tag_name":"v2.0.0-rc.1","prerelease":true,"assets":[{"name":"release.apk","size":5,"browser_download_url":"https://example.com/new.apk"}]}]""", + ) + + val info = checker().fetchLatestRelease(includeReleaseCandidates = true) + + assertEquals("v2.0.0-rc.1", info?.tagName) + assertEquals("/repos/Hy4ri/hermes-mobile/releases/latest", server.takeRequest().path) + assertEquals("/repos/Hy4ri/hermes-mobile/releases?per_page=100&page=1", server.takeRequest().path) + assertEquals("/repositories/1270568813/releases?per_page=100&page=2", server.takeRequest().path) + } + @Test fun fetchLatestRelease_releaseCandidatesStillIgnoreAlphaAndBetaButKeepStable() = runTest {