Skip to content

Commit dc05854

Browse files
committed
Merge commit from fork
* Check validity of Element Call url host. * Prepare release 25.04.2
1 parent c0a1d8d commit dc05854

File tree

5 files changed

+37
-18
lines changed

5 files changed

+37
-18
lines changed

CHANGES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
Changes in Element X v25.04.2
2+
=============================
3+
4+
Security fixes 🔐
5+
-----------------
6+
- 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)
7+
18
Changes in Element X v25.04.1
29
=============================
310

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Main changes in this version: security fix.
2+
Full changelog: https://github.com/element-hq/element-x-android/releases

features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/CallIntentDataParser.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,26 @@ import javax.inject.Inject
1212

1313
class CallIntentDataParser @Inject constructor() {
1414
private val validHttpSchemes = sequenceOf("https")
15+
private val knownHosts = sequenceOf(
16+
"call.element.io",
17+
)
1518

1619
fun parse(data: String?): String? {
1720
val parsedUrl = data?.let { Uri.parse(data) } ?: return null
1821
val scheme = parsedUrl.scheme
1922
return when {
20-
scheme in validHttpSchemes && parsedUrl.host == "call.element.io" -> parsedUrl
23+
scheme in validHttpSchemes -> parsedUrl
2124
scheme == "element" && parsedUrl.host == "call" -> {
22-
// We use this custom scheme to load arbitrary URLs for other instances of Element Call,
23-
// so we can only verify it's an HTTP/HTTPs URL with a non-empty host
2425
parsedUrl.getUrlParameter()
2526
}
2627
scheme == "io.element.call" && parsedUrl.host == null -> {
27-
// We use this custom scheme to load arbitrary URLs for other instances of Element Call,
28-
// so we can only verify it's an HTTP/HTTPs URL with a non-empty host
2928
parsedUrl.getUrlParameter()
3029
}
3130
// This should never be possible, but we still need to take into account the possibility
3231
else -> null
33-
}?.withCustomParameters()
32+
}
33+
?.takeIf { it.host in knownHosts }
34+
?.withCustomParameters()
3435
}
3536

3637
private fun Uri.getUrlParameter(): Uri? {

features/call/impl/src/test/kotlin/io/element/android/features/call/utils/CallIntentDataParserTest.kt

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ class CallIntentDataParserTest {
4545
doTest("http://call.element.io/some-actual-call?with=parameters", null)
4646
}
4747

48+
@Test
49+
fun `Element Call urls with unknown host returns null`() {
50+
// Check valid host first, should not return null
51+
doTest("https://call.element.io", "https://call.element.io#?appPrompt=false&confineToRoom=true")
52+
// Unknown host should return null
53+
doTest("https://unknown.io", null)
54+
doTest("https://call.unknown.io", null)
55+
doTest("https://call.element.com", null)
56+
doTest("https://call.element.io.tld", null)
57+
}
58+
4859
@Test
4960
fun `Element Call urls will be returned as is`() {
5061
doTest(
@@ -64,7 +75,7 @@ class CallIntentDataParserTest {
6475
@Test
6576
fun `HTTP and HTTPS urls that don't come from EC return null`() {
6677
doTest("http://app.element.io", null)
67-
doTest("https://app.element.io", null, testEmbedded = false)
78+
doTest("https://app.element.io", null)
6879
doTest("http://", null)
6980
doTest("https://", null)
7081
}
@@ -193,20 +204,18 @@ class CallIntentDataParserTest {
193204
)
194205
}
195206

196-
private fun doTest(url: String, expectedResult: String?, testEmbedded: Boolean = true) {
207+
private fun doTest(url: String, expectedResult: String?) {
197208
// Test direct parsing
198209
assertThat(callIntentDataParser.parse(url)).isEqualTo(expectedResult)
199210

200-
if (testEmbedded) {
201-
// Test embedded url, scheme 1
202-
val encodedUrl = URLEncoder.encode(url, "utf-8")
203-
val urlScheme1 = "element://call?url=$encodedUrl"
204-
assertThat(callIntentDataParser.parse(urlScheme1)).isEqualTo(expectedResult)
211+
// Test embedded url, scheme 1
212+
val encodedUrl = URLEncoder.encode(url, "utf-8")
213+
val urlScheme1 = "element://call?url=$encodedUrl"
214+
assertThat(callIntentDataParser.parse(urlScheme1)).isEqualTo(expectedResult)
205215

206-
// Test embedded url, scheme 2
207-
val urlScheme2 = "io.element.call:/?url=$encodedUrl"
208-
assertThat(callIntentDataParser.parse(urlScheme2)).isEqualTo(expectedResult)
209-
}
216+
// Test embedded url, scheme 2
217+
val urlScheme2 = "io.element.call:/?url=$encodedUrl"
218+
assertThat(callIntentDataParser.parse(urlScheme2)).isEqualTo(expectedResult)
210219
}
211220

212221
companion object {

plugins/src/main/kotlin/Versions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ private const val versionYear = 25
3232
private const val versionMonth = 4
3333

3434
// Note: must be in [0,99]
35-
private const val versionReleaseNumber = 1
35+
private const val versionReleaseNumber = 2
3636

3737
object Versions {
3838
const val VERSION_CODE = (2000 + versionYear) * 10_000 + versionMonth * 100 + versionReleaseNumber

0 commit comments

Comments
 (0)