Merge commit from fork

* Check validity of Element Call url host.

* Prepare release 25.04.2
This commit is contained in:
Benoit Marty
2025-04-17 11:39:38 +02:00
committed by GitHub
parent 5d3de494d7
commit 4c21f48661
5 changed files with 37 additions and 18 deletions

View File

@@ -1,3 +1,10 @@
Changes in Element X v25.04.2
=============================
Security fixes 🔐
-----------------
- Fix for [GHSA-m5px-pwq3-4p5m](https://github.com/element-hq/element-x-android/security/advisories/GHSA-m5px-pwq3-4p5m) / [CVE-2025-27599](https://www.cve.org/CVERecord?id=CVE-2025-27599)
Changes in Element X v25.04.1
=============================

View File

@@ -0,0 +1,2 @@
Main changes in this version: security fix.
Full changelog: https://github.com/element-hq/element-x-android/releases

View File

@@ -12,25 +12,26 @@ import javax.inject.Inject
class CallIntentDataParser @Inject constructor() {
private val validHttpSchemes = sequenceOf("https")
private val knownHosts = sequenceOf(
"call.element.io",
)
fun parse(data: String?): String? {
val parsedUrl = data?.let { Uri.parse(data) } ?: return null
val scheme = parsedUrl.scheme
return when {
scheme in validHttpSchemes && parsedUrl.host == "call.element.io" -> parsedUrl
scheme in validHttpSchemes -> parsedUrl
scheme == "element" && parsedUrl.host == "call" -> {
// We use this custom scheme to load arbitrary URLs for other instances of Element Call,
// so we can only verify it's an HTTP/HTTPs URL with a non-empty host
parsedUrl.getUrlParameter()
}
scheme == "io.element.call" && parsedUrl.host == null -> {
// We use this custom scheme to load arbitrary URLs for other instances of Element Call,
// so we can only verify it's an HTTP/HTTPs URL with a non-empty host
parsedUrl.getUrlParameter()
}
// This should never be possible, but we still need to take into account the possibility
else -> null
}?.withCustomParameters()
}
?.takeIf { it.host in knownHosts }
?.withCustomParameters()
}
private fun Uri.getUrlParameter(): Uri? {

View File

@@ -45,6 +45,17 @@ class CallIntentDataParserTest {
doTest("http://call.element.io/some-actual-call?with=parameters", null)
}
@Test
fun `Element Call urls with unknown host returns null`() {
// Check valid host first, should not return null
doTest("https://call.element.io", "https://call.element.io#?appPrompt=false&confineToRoom=true")
// Unknown host should return null
doTest("https://unknown.io", null)
doTest("https://call.unknown.io", null)
doTest("https://call.element.com", null)
doTest("https://call.element.io.tld", null)
}
@Test
fun `Element Call urls will be returned as is`() {
doTest(
@@ -64,7 +75,7 @@ class CallIntentDataParserTest {
@Test
fun `HTTP and HTTPS urls that don't come from EC return null`() {
doTest("http://app.element.io", null)
doTest("https://app.element.io", null, testEmbedded = false)
doTest("https://app.element.io", null)
doTest("http://", null)
doTest("https://", null)
}
@@ -193,11 +204,10 @@ class CallIntentDataParserTest {
)
}
private fun doTest(url: String, expectedResult: String?, testEmbedded: Boolean = true) {
private fun doTest(url: String, expectedResult: String?) {
// Test direct parsing
assertThat(callIntentDataParser.parse(url)).isEqualTo(expectedResult)
if (testEmbedded) {
// Test embedded url, scheme 1
val encodedUrl = URLEncoder.encode(url, "utf-8")
val urlScheme1 = "element://call?url=$encodedUrl"
@@ -207,7 +217,6 @@ class CallIntentDataParserTest {
val urlScheme2 = "io.element.call:/?url=$encodedUrl"
assertThat(callIntentDataParser.parse(urlScheme2)).isEqualTo(expectedResult)
}
}
companion object {
const val VALID_CALL_URL_WITH_PARAM = "https://call.element.io/some-actual-call?with=parameters"

View File

@@ -32,7 +32,7 @@ private const val versionYear = 25
private const val versionMonth = 4
// Note: must be in [0,99]
private const val versionReleaseNumber = 1
private const val versionReleaseNumber = 2
object Versions {
const val VERSION_CODE = (2000 + versionYear) * 10_000 + versionMonth * 100 + versionReleaseNumber