Merge commit from fork
* Check validity of Element Call url host. * Prepare release 25.04.2
This commit is contained in:
parent
be1c9b793b
commit
dc64d9cf74
5 changed files with 37 additions and 18 deletions
|
|
@ -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
|
Changes in Element X v25.04.1
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
|
|
|
||||||
2
fastlane/metadata/android/en-US/changelogs/202504020.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/202504020.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
Main changes in this version: security fix.
|
||||||
|
Full changelog: https://github.com/element-hq/element-x-android/releases
|
||||||
|
|
@ -12,25 +12,26 @@ import javax.inject.Inject
|
||||||
|
|
||||||
class CallIntentDataParser @Inject constructor() {
|
class CallIntentDataParser @Inject constructor() {
|
||||||
private val validHttpSchemes = sequenceOf("https")
|
private val validHttpSchemes = sequenceOf("https")
|
||||||
|
private val knownHosts = sequenceOf(
|
||||||
|
"call.element.io",
|
||||||
|
)
|
||||||
|
|
||||||
fun parse(data: String?): String? {
|
fun parse(data: String?): String? {
|
||||||
val parsedUrl = data?.let { Uri.parse(data) } ?: return null
|
val parsedUrl = data?.let { Uri.parse(data) } ?: return null
|
||||||
val scheme = parsedUrl.scheme
|
val scheme = parsedUrl.scheme
|
||||||
return when {
|
return when {
|
||||||
scheme in validHttpSchemes && parsedUrl.host == "call.element.io" -> parsedUrl
|
scheme in validHttpSchemes -> parsedUrl
|
||||||
scheme == "element" && parsedUrl.host == "call" -> {
|
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()
|
parsedUrl.getUrlParameter()
|
||||||
}
|
}
|
||||||
scheme == "io.element.call" && parsedUrl.host == null -> {
|
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()
|
parsedUrl.getUrlParameter()
|
||||||
}
|
}
|
||||||
// This should never be possible, but we still need to take into account the possibility
|
// This should never be possible, but we still need to take into account the possibility
|
||||||
else -> null
|
else -> null
|
||||||
}?.withCustomParameters()
|
}
|
||||||
|
?.takeIf { it.host in knownHosts }
|
||||||
|
?.withCustomParameters()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Uri.getUrlParameter(): Uri? {
|
private fun Uri.getUrlParameter(): Uri? {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,17 @@ class CallIntentDataParserTest {
|
||||||
doTest("http://call.element.io/some-actual-call?with=parameters", null)
|
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
|
@Test
|
||||||
fun `Element Call urls will be returned as is`() {
|
fun `Element Call urls will be returned as is`() {
|
||||||
doTest(
|
doTest(
|
||||||
|
|
@ -64,7 +75,7 @@ class CallIntentDataParserTest {
|
||||||
@Test
|
@Test
|
||||||
fun `HTTP and HTTPS urls that don't come from EC return null`() {
|
fun `HTTP and HTTPS urls that don't come from EC return null`() {
|
||||||
doTest("http://app.element.io", 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("http://", null)
|
||||||
doTest("https://", null)
|
doTest("https://", null)
|
||||||
}
|
}
|
||||||
|
|
@ -193,20 +204,18 @@ class CallIntentDataParserTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doTest(url: String, expectedResult: String?, testEmbedded: Boolean = true) {
|
private fun doTest(url: String, expectedResult: String?) {
|
||||||
// Test direct parsing
|
// Test direct parsing
|
||||||
assertThat(callIntentDataParser.parse(url)).isEqualTo(expectedResult)
|
assertThat(callIntentDataParser.parse(url)).isEqualTo(expectedResult)
|
||||||
|
|
||||||
if (testEmbedded) {
|
// Test embedded url, scheme 1
|
||||||
// Test embedded url, scheme 1
|
val encodedUrl = URLEncoder.encode(url, "utf-8")
|
||||||
val encodedUrl = URLEncoder.encode(url, "utf-8")
|
val urlScheme1 = "element://call?url=$encodedUrl"
|
||||||
val urlScheme1 = "element://call?url=$encodedUrl"
|
assertThat(callIntentDataParser.parse(urlScheme1)).isEqualTo(expectedResult)
|
||||||
assertThat(callIntentDataParser.parse(urlScheme1)).isEqualTo(expectedResult)
|
|
||||||
|
|
||||||
// Test embedded url, scheme 2
|
// Test embedded url, scheme 2
|
||||||
val urlScheme2 = "io.element.call:/?url=$encodedUrl"
|
val urlScheme2 = "io.element.call:/?url=$encodedUrl"
|
||||||
assertThat(callIntentDataParser.parse(urlScheme2)).isEqualTo(expectedResult)
|
assertThat(callIntentDataParser.parse(urlScheme2)).isEqualTo(expectedResult)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ private const val versionYear = 25
|
||||||
private const val versionMonth = 4
|
private const val versionMonth = 4
|
||||||
|
|
||||||
// Note: must be in [0,99]
|
// Note: must be in [0,99]
|
||||||
private const val versionReleaseNumber = 1
|
private const val versionReleaseNumber = 2
|
||||||
|
|
||||||
object Versions {
|
object Versions {
|
||||||
const val VERSION_CODE = (2000 + versionYear) * 10_000 + versionMonth * 100 + versionReleaseNumber
|
const val VERSION_CODE = (2000 + versionYear) * 10_000 + versionMonth * 100 + versionReleaseNumber
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue