From ca9eff5efe56e4ac1d65fda6e3d91dc6235986ac Mon Sep 17 00:00:00 2001 From: theopensourceguy Date: Tue, 13 Aug 2019 11:12:57 +0200 Subject: Obfuscate window tags for JS injection * Generates a random prefix at startup * Obfuscates tags based on their hashCode and a salt generated at startup as well as the generated prefix * Name mappings are logged in debug mode --- .../com/pitchedapps/frost/injectors/JsInjector.kt | 40 +++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt index 13032479..5d8c55e6 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt @@ -16,10 +16,14 @@ */ package com.pitchedapps.frost.injectors +import android.util.Log import android.webkit.WebView +import com.pitchedapps.frost.BuildConfig +import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.web.FrostWebViewClient import org.apache.commons.text.StringEscapeUtils import java.util.Locale +import kotlin.random.Random class JsBuilder { private val css = StringBuilder() @@ -38,7 +42,7 @@ class JsBuilder { } fun single(tag: String): JsBuilder { - this.tag = "_frost_${tag.toLowerCase(Locale.CANADA)}" + this.tag = TagObfuscator.obfuscateTag(tag) return this } @@ -106,4 +110,38 @@ fun FrostWebViewClient.jsInject(vararg injectors: InjectorContract) = web.jsInje class JsInjector(val function: String) : InjectorContract { override fun inject(webView: WebView) = webView.evaluateJavascript(function, null) +} + +/** + * Helper object to obfuscate window tags for JS injection. + */ +private object TagObfuscator { + + fun obfuscateTag(tag: String) : String { + val rnd = Random(tag.hashCode() + salt) + val obfuscated = StringBuilder() + .append(prefix) + .append(randomChars(rnd, tag.length)) + L._d { "TagObfuscator: Obfuscating tag '$tag' to '$obfuscated'" } + //if (BuildConfig.DEBUG) { + // return "_frost_${tag.toLowerCase(Locale.CANADA)}" + //} else + return obfuscated.toString() + } + + private val salt by lazy { System.currentTimeMillis() } + + private val prefix by lazy { + val rnd = Random(System.currentTimeMillis()) + val length = rnd.nextInt(10, 16) + randomChars(rnd, length) + } + + private fun randomChars(random: Random, count: Int) : String { + val result = StringBuilder() + for (i in 1..count) { + result.append('a' + random.nextInt(0, 26)) + } + return result.toString() + } } \ No newline at end of file -- cgit v1.2.3 From 06408157dfde2f40c6368c5ab03e46479428f566 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 13 Aug 2019 23:49:04 -0700 Subject: Use one stringbuilder per tag creation and add test --- .../com/pitchedapps/frost/injectors/JsInjector.kt | 37 ++++++++++------------ .../frost/injectors/TagObfuscatorTest.kt | 22 +++++++++++++ 2 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 app/src/test/kotlin/com/pitchedapps/frost/injectors/TagObfuscatorTest.kt diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt index 5d8c55e6..eed2f819 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt @@ -16,13 +16,11 @@ */ package com.pitchedapps.frost.injectors -import android.util.Log import android.webkit.WebView -import com.pitchedapps.frost.BuildConfig +import androidx.annotation.VisibleForTesting import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.web.FrostWebViewClient import org.apache.commons.text.StringEscapeUtils -import java.util.Locale import kotlin.random.Random class JsBuilder { @@ -115,33 +113,30 @@ class JsInjector(val function: String) : InjectorContract { /** * Helper object to obfuscate window tags for JS injection. */ -private object TagObfuscator { +@VisibleForTesting +internal object TagObfuscator { - fun obfuscateTag(tag: String) : String { + fun obfuscateTag(tag: String): String { val rnd = Random(tag.hashCode() + salt) - val obfuscated = StringBuilder() - .append(prefix) - .append(randomChars(rnd, tag.length)) - L._d { "TagObfuscator: Obfuscating tag '$tag' to '$obfuscated'" } - //if (BuildConfig.DEBUG) { - // return "_frost_${tag.toLowerCase(Locale.CANADA)}" - //} else - return obfuscated.toString() + val obfuscated = buildString { + append(prefix) + append('_') + appendRandomChars(rnd, 16) + } + L.v { "TagObfuscator: Obfuscating tag '$tag' to '$obfuscated'" } + return obfuscated } - private val salt by lazy { System.currentTimeMillis() } + private val salt: Long = System.currentTimeMillis() - private val prefix by lazy { + private val prefix: String by lazy { val rnd = Random(System.currentTimeMillis()) - val length = rnd.nextInt(10, 16) - randomChars(rnd, length) + buildString { appendRandomChars(rnd, 8) } } - private fun randomChars(random: Random, count: Int) : String { - val result = StringBuilder() + private fun Appendable.appendRandomChars(random: Random, count: Int) { for (i in 1..count) { - result.append('a' + random.nextInt(0, 26)) + append('a' + random.nextInt(26)) } - return result.toString() } } \ No newline at end of file diff --git a/app/src/test/kotlin/com/pitchedapps/frost/injectors/TagObfuscatorTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/injectors/TagObfuscatorTest.kt new file mode 100644 index 00000000..5c316a4a --- /dev/null +++ b/app/src/test/kotlin/com/pitchedapps/frost/injectors/TagObfuscatorTest.kt @@ -0,0 +1,22 @@ +package com.pitchedapps.frost.injectors + +import java.util.UUID +import kotlin.test.Test +import kotlin.test.assertEquals + +class TagObfuscatorTest { + + /** + * The same key should result in the same tag per session + */ + @Test + fun consistentTags() { + val keys = generateSequence { UUID.randomUUID().toString() }.take(10).toSet() + val tags = keys.map { + val tag = generateSequence { TagObfuscator.obfuscateTag(it) }.take(10).toSet() + assertEquals(1, tag.size, "Key $it produced multiple tags: $tag") + tag.first() + } + assertEquals(keys.size, tags.size, "Key set and tag set have different sizes") + } +} -- cgit v1.2.3 From da3f1bb99df7f75e6adc62aa8aae15716033789b Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 13 Aug 2019 23:57:10 -0700 Subject: Move web only to experimental --- app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt | 7 ------- app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt | 7 +++++++ app/src/main/res/values/strings_pref_behaviour.xml | 5 ----- app/src/main/res/values/strings_pref_experimental.xml | 6 ++++++ app/src/main/res/xml/frost_changelog.xml | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt index 1ab53a56..ba5b839b 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt @@ -83,13 +83,6 @@ fun SettingsActivity.getBehaviourPrefs(): KPrefAdapterBuilder.() -> Unit = { descRes = R.string.exit_confirmation_desc } - checkbox(R.string.web_only, Prefs::webOnly, { - Prefs.webOnly = it - shouldRestartMain() - }) { - descRes = R.string.web_only_desc - } - checkbox(R.string.analytics, Prefs::analytics, { Prefs.analytics = it }) { descRes = R.string.analytics_desc } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt index 7aac7526..d0963665 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt @@ -43,6 +43,13 @@ fun SettingsActivity.getExperimentalPrefs(): KPrefAdapterBuilder.() -> Unit = { // Experimental content starts here ------------------ + checkbox(R.string.web_only, Prefs::webOnly, { + Prefs.webOnly = it + shouldRestartMain() + }) { + descRes = R.string.web_only_desc + } + // Experimental content ends here -------------------- checkbox(R.string.verbose_logging, Prefs::verboseLogging, { diff --git a/app/src/main/res/values/strings_pref_behaviour.xml b/app/src/main/res/values/strings_pref_behaviour.xml index d7043aa7..32188698 100644 --- a/app/src/main/res/values/strings_pref_behaviour.xml +++ b/app/src/main/res/values/strings_pref_behaviour.xml @@ -19,11 +19,6 @@ When loading a message thread, trigger a scroll to the bottom of the page rather than loading the page as is. Enable PIP Enable picture in picture videos - - Web Only - Having troubles? Enable to use web exclusive features. All parsing and background services will be disabled. - Leave web only mode - Currently in web only mode. Would you like to disable it to continue? Autoplay Settings Open Facebook\'s auto play settings. Note that it must be disabled for PIP to work. Exit Confirmation diff --git a/app/src/main/res/values/strings_pref_experimental.xml b/app/src/main/res/values/strings_pref_experimental.xml index 95d54ff2..fad29ff6 100644 --- a/app/src/main/res/values/strings_pref_experimental.xml +++ b/app/src/main/res/values/strings_pref_experimental.xml @@ -8,4 +8,10 @@ Enable verbose logging to help with crash reports. Logging will only be sent once an error is encountered, so repeat the issue to notify the dev. This will automatically be disabled if the app restarts. Restart Frost Launch a cold restart for the application. + + + Web Only + Having troubles? Enable to use web exclusive features. All parsing and background services will be disabled. + Leave web only mode + Currently in web only mode. Would you like to disable it to continue? \ No newline at end of file diff --git a/app/src/main/res/xml/frost_changelog.xml b/app/src/main/res/xml/frost_changelog.xml index 3c576a9e..6494fdd1 100644 --- a/app/src/main/res/xml/frost_changelog.xml +++ b/app/src/main/res/xml/frost_changelog.xml @@ -13,7 +13,7 @@ - + -- cgit v1.2.3