package community.yuvomi.mobile import android.content.Context import android.net.Uri import android.view.View import android.view.ViewGroup import android.webkit.WebResourceRequest import android.webkit.WebView import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class BrowserSecurityTest { @Test fun blocksCrossOriginPostAndSubresourceBeforeNetwork() { val context = ApplicationProvider.getApplicationContext() context.getSharedPreferences("server", Context.MODE_PRIVATE).edit() .putString("origin", "https://family.example").commit() ActivityScenario.launch(MainActivity::class.java).use { scenario -> lateinit var web: WebView lateinit var client: android.webkit.WebViewClient scenario.onActivity { web = requireNotNull(findWeb(it.window.decorView)) client = web.webViewClient } // Interception runs off the UI thread in WebView. POST navigations do // not reliably call shouldOverrideUrlLoading, so this is a separate gate. for (mainFrame in listOf(true, false)) { val response = client.shouldInterceptRequest( web, Request("https://evil.example/collect", mainFrame), ) assertNotNull(response) assertEquals(403, response!!.statusCode) } assertNull( client.shouldInterceptRequest(web, Request("https://family.example/tasks", true)), ) } } private fun findWeb(view: View): WebView? { if (view is WebView) return view if (view is ViewGroup) { for (i in 0 until view.childCount) { findWeb(view.getChildAt(i))?.let { return it } } } return null } private class Request(private val value: String, private val mainFrame: Boolean) : WebResourceRequest { override fun getUrl(): Uri = Uri.parse(value) override fun isForMainFrame() = mainFrame override fun isRedirect() = false override fun hasGesture() = true override fun getMethod() = "POST" override fun getRequestHeaders(): MutableMap = mutableMapOf() } }