From 123ab642f77e78c228b8f584503d7862fcf54ef6 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Wed, 6 Jan 2021 16:09:20 +0200 Subject: [PATCH] For #9338 - Introduce SafeUrl to strip unwanted uri schemes Interested clients can overwrite "mozac_url_schemes_blocklist" with a custom list of uri schemes that will be recursively removed from the front of the uri. --- .../components/support/utils/SafeUrl.kt | 33 +++++++++++ .../utils/src/main/res/values/arrays.xml | 10 ++++ .../components/support/utils/SafeUrlTest.kt | 55 +++++++++++++++++++ docs/changelog.md | 3 + 4 files changed, 101 insertions(+) create mode 100644 components/support/utils/src/main/java/mozilla/components/support/utils/SafeUrl.kt create mode 100644 components/support/utils/src/main/res/values/arrays.xml create mode 100644 components/support/utils/src/test/java/mozilla/components/support/utils/SafeUrlTest.kt diff --git a/components/support/utils/src/main/java/mozilla/components/support/utils/SafeUrl.kt b/components/support/utils/src/main/java/mozilla/components/support/utils/SafeUrl.kt new file mode 100644 index 00000000000..d64c73e0c17 --- /dev/null +++ b/components/support/utils/src/main/java/mozilla/components/support/utils/SafeUrl.kt @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.support.utils + +import android.content.Context + +/** + * Collection of methods used for ensuring the validity and security of URLs. + */ +object SafeUrl { + fun stripUnsafeUrlSchemes(context: Context, unsafeText: CharSequence?): String? { + val urlSchemesBlocklist = context.resources.getStringArray(R.array.mozac_url_schemes_blocklist) + var safeUrl = unsafeText.toString() + if (safeUrl.isEmpty()) { + return safeUrl + } + + @Suppress("ControlFlowWithEmptyBody", "EmptyWhileBlock") + while (urlSchemesBlocklist.find { + if (safeUrl.startsWith(it, true)) { + safeUrl = safeUrl.replaceFirst(Regex(it, RegexOption.IGNORE_CASE), "") + true + } else { + false + } + } != null) { + } + + return safeUrl + } +} diff --git a/components/support/utils/src/main/res/values/arrays.xml b/components/support/utils/src/main/res/values/arrays.xml new file mode 100644 index 00000000000..c9b092ee300 --- /dev/null +++ b/components/support/utils/src/main/res/values/arrays.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/components/support/utils/src/test/java/mozilla/components/support/utils/SafeUrlTest.kt b/components/support/utils/src/test/java/mozilla/components/support/utils/SafeUrlTest.kt new file mode 100644 index 00000000000..deb57501046 --- /dev/null +++ b/components/support/utils/src/test/java/mozilla/components/support/utils/SafeUrlTest.kt @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.support.utils + +import android.content.Context +import android.content.res.Resources +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.mock +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.doReturn + +@RunWith(AndroidJUnit4::class) +class SafeUrlTest { + @Test + fun `WHEN schemes blocklist is empty THEN stripUnsafeUrlSchemes should return the initial String`() { + val resources = mock() + val context = mock() + doReturn(resources).`when`(context).resources + doReturn(emptyArray()).`when`(resources).getStringArray(R.array.mozac_url_schemes_blocklist) + + val result = SafeUrl.stripUnsafeUrlSchemes(context, "unsafeText") + + assertEquals("unsafeText", result) + } + + @Test + fun `WHEN schemes blocklist contains items not found in the argument THEN stripUnsafeUrlSchemes should return the initial String`() { + val resources = mock() + val context = mock() + doReturn(resources).`when`(context).resources + doReturn(arrayOf("alien")).`when`(resources).getStringArray(R.array.mozac_url_schemes_blocklist) + + val result = SafeUrl.stripUnsafeUrlSchemes(context, "thisIsAnOkText") + + assertEquals("thisIsAnOkText", result) + } + + @Test + fun `WHEN schemes blocklist contains items found in the argument THEN stripUnsafeUrlSchemes should recursively remove them from the front`() { + val resources = mock() + val context = mock() + doReturn(resources).`when`(context).resources + doReturn(arrayOf("one", "two")).`when`(resources).getStringArray(R.array.mozac_url_schemes_blocklist) + + val result = SafeUrl.stripUnsafeUrlSchemes(context, "two" + "one" + "one" + "two" + "safeText") + assertEquals("safeText", result) + + val result2 = SafeUrl.stripUnsafeUrlSchemes(context, "one" + "two" + "one" + "two" + "safeText" + "one") + assertEquals("safeText" + "one", result2) + } +} diff --git a/docs/changelog.md b/docs/changelog.md index f2a4db56773..7d9e2a9d3ed 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -12,6 +12,9 @@ permalink: /changelog/ * [Gecko](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Gecko.kt) * [Configuration](https://github.com/mozilla-mobile/android-components/blob/master/.config.yml) +* **support-utils**: + * 🌟 Added SafeUrl#stripUnsafeUrlSchemes that can cleanup unwanted uri schemes. Interested clients can specify what these are by overwriting "mozac_url_schemes_blocklist". + * **feature-prompts**: * 🚒 Bug fixed [issue #9229](https://github.com/mozilla-mobile/android-components/issues/9229) - Dismiss SelectLoginPrompt from the current tab when opening a new one ensuring the new one can show it's own. When returning to the previous tab users should focus a login field to see the SelectLoginPrompt again.