package com.peachpasswords.passkeys import android.app.Activity import android.content.Intent import android.view.WindowManager import androidx.test.core.app.ActivityScenario import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import androidx.test.runner.lifecycle.ActivityLifecycleCallback import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry import androidx.test.runner.lifecycle.Stage import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith /** * Real-device coverage for audit H-05. * * Passkey selection drives the WebAuthn assertion/registration flow against * vault-resident private keys, so the selection window must set FLAG_SECURE. * Debug builds are secure by default; only an explicit `qa_disable_secure_flag` * intent extra bypasses it. * * The activity auto-finishes on an empty test intent (completeSelection sets * an exception result and calls finish in onCreate), so we capture FLAG_SECURE * at CREATED via an ActivityLifecycleCallback rather than sampling onActivity. */ @RunWith(AndroidJUnit4::class) class PasskeySelectionFlagSecureTest { @Test fun selectionWindowActuallyEnablesFlagSecure() { val context = InstrumentationRegistry.getInstrumentation().targetContext assertTrue( "target must be release or its correctly suffixed debug id", context.packageName == "com.peachpasswords" || context.packageName == "com.peachpasswords.debug", ) // Capture FLAG_SECURE at CREATED, before the activity may finish. val flagSecureObserved = AtomicBoolean(false) val createdLatch = CountDownLatch(1) val monitor = ActivityLifecycleMonitorRegistry.getInstance() val callback = ActivityLifecycleCallback { activity: Activity?, stage: Stage -> if (activity is PasskeyCredentialSelectionActivity && stage == Stage.CREATED) { flagSecureObserved.set( activity.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE != 0, ) createdLatch.countDown() } } monitor.addLifecycleCallback(callback) try { val intent = Intent(context, PasskeyCredentialSelectionActivity::class.java) ActivityScenario.launch(intent).use { /* scenario closes automatically */ } assertTrue( "passkey selection activity did not reach CREATED within timeout", createdLatch.await(5, TimeUnit.SECONDS), ) assertTrue( "passkey selection window must set FLAG_SECURE at creation", flagSecureObserved.get(), ) } finally { monitor.removeLifecycleCallback(callback) } } }