Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 80462c37 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

FRP bypass defense in the Settings App for SPA

Over the last few years, there have been a number of
Factory Reset Protection bypass bugs in the SUW flow.
It's unlikely to defense all points from individual apps.

Therefore, we decide to block some critical pages when
user doesn't complete the SUW flow.

Fix: 280154358
Test: Unit test
Change-Id: I06e73386711d5ad13c89d033cf0fe3164781c0ef
parent 6a2f6960
Loading
Loading
Loading
Loading
+20 −0
Original line number Original line Diff line number Diff line
@@ -22,14 +22,34 @@ import android.content.Intent
import android.os.RemoteException
import android.os.RemoteException
import android.os.UserHandle
import android.os.UserHandle
import android.util.Log
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
import com.android.settingslib.spa.framework.BrowseActivity
import com.android.settingslib.spa.framework.BrowseActivity
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.util.SESSION_BROWSE
import com.android.settingslib.spa.framework.util.SESSION_BROWSE
import com.android.settingslib.spa.framework.util.SESSION_EXTERNAL
import com.android.settingslib.spa.framework.util.SESSION_EXTERNAL
import com.android.settingslib.spa.framework.util.appendSpaParams
import com.android.settingslib.spa.framework.util.appendSpaParams
import com.google.android.setupcompat.util.WizardManagerHelper


class SpaActivity : BrowseActivity() {
class SpaActivity : BrowseActivity() {
    override fun isPageEnabled(page: SettingsPage) =
        super.isPageEnabled(page) && !isSuwAndPageBlocked(page.sppName)

    companion object {
    companion object {
        private const val TAG = "SpaActivity"
        private const val TAG = "SpaActivity"

        /** The pages that blocked from SUW. */
        private val SuwBlockedPages = setOf(AppInfoSettingsProvider.name)

        @VisibleForTesting
        fun Context.isSuwAndPageBlocked(name: String): Boolean =
            if (name in SuwBlockedPages && !WizardManagerHelper.isDeviceProvisioned(this)) {
                Log.w(TAG, "$name blocked before SUW completed.");
                true
            } else {
                false
            }

        @JvmStatic
        @JvmStatic
        fun Context.startSpaActivity(destination: String) {
        fun Context.startSpaActivity(destination: String) {
            val intent = Intent(this, SpaActivity::class.java)
            val intent = Intent(this, SpaActivity::class.java)
+47 −9
Original line number Original line Diff line number Diff line
@@ -21,33 +21,71 @@ import android.content.Intent
import android.net.Uri
import android.net.Uri
import android.os.UserHandle
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.settings.spa.SpaActivity.Companion.isSuwAndPageBlocked
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.SpaActivity.Companion.startSpaActivityForApp
import com.android.settings.spa.SpaActivity.Companion.startSpaActivityForApp
import com.android.settings.spa.app.AllAppListPageProvider
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
import com.android.settingslib.spa.framework.util.KEY_DESTINATION
import com.android.settingslib.spa.framework.util.KEY_DESTINATION
import com.google.android.setupcompat.util.WizardManagerHelper
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoSession
import org.mockito.junit.MockitoJUnit
import org.mockito.quality.Strictness
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever


@RunWith(AndroidJUnit4::class)
@RunWith(AndroidJUnit4::class)
class SpaActivityTest {
class SpaActivityTest {
    @get:Rule
    private lateinit var mockSession: MockitoSession
    val mockito: MockitoRule = MockitoJUnit.rule()


    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    @Mock
    private lateinit var context: Context
    private lateinit var context: Context


    @Before
    @Before
    fun setUp() {
    fun setUp() {
        `when`(context.applicationContext.packageName).thenReturn("com.android.settings")
        mockSession = ExtendedMockito.mockitoSession()
            .initMocks(this)
            .mockStatic(WizardManagerHelper::class.java)
            .strictness(Strictness.LENIENT)
            .startMocking()
        whenever(context.applicationContext).thenReturn(context)
    }

    @After
    fun tearDown() {
        mockSession.finishMocking()
    }

    @Test
    fun isSuwAndPageBlocked_regularPage_notBlocked() {
        val isBlocked = context.isSuwAndPageBlocked(AllAppListPageProvider.name)

        assertThat(isBlocked).isFalse()
    }

    @Test
    fun isSuwAndPageBlocked_blocklistedPageInSuw_blocked() {
        whenever(WizardManagerHelper.isDeviceProvisioned(context)).thenReturn(false)

        val isBlocked = context.isSuwAndPageBlocked(AppInfoSettingsProvider.name)

        assertThat(isBlocked).isTrue()
    }

    @Test
    fun isSuwAndPageBlocked_blocklistedPageNotInSuw_notBlocked() {
        whenever(WizardManagerHelper.isDeviceProvisioned(context)).thenReturn(true)

        val isBlocked = context.isSuwAndPageBlocked(AppInfoSettingsProvider.name)

        assertThat(isBlocked).isFalse()
    }
    }


    @Test
    @Test