Harden Android shared-vault import intents

This commit is contained in:
Joe Julian
2026-04-16 21:33:40 -07:00
parent 14f22b4ebf
commit 92a7853258
2 changed files with 54 additions and 3 deletions
+3
View File
@@ -45,6 +45,9 @@
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/octet-stream" />
<data android:mimeType="application/x-keepass2" />
<data android:mimeType="application/vnd.keepass" />
<data android:scheme="content" android:pathPattern=".*\\.kdbx" />
<data android:scheme="file" android:pathPattern=".*\\.kdbx" />
</intent-filter>
@@ -1,6 +1,7 @@
package org.julianfamily.keepassgo;
import android.app.Activity;
import android.content.ClipData;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -10,9 +11,11 @@ import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
public final class SharedVaultImportActivity extends Activity {
private static final String TAG = "KeePassGOImport";
@@ -36,6 +39,7 @@ public final class SharedVaultImportActivity extends Activity {
}
private void handleIntent(Intent intent) {
logIntent(intent);
Uri uri = resolveSharedUri(intent);
if (uri == null) {
Log.i(TAG, "no shared vault URI on intent");
@@ -55,10 +59,29 @@ public final class SharedVaultImportActivity extends Activity {
}
String action = intent.getAction();
if (Intent.ACTION_SEND.equals(action)) {
return intent.getParcelableExtra(Intent.EXTRA_STREAM);
Uri extraStream = intent.getParcelableExtra(Intent.EXTRA_STREAM);
if (extraStream != null) {
return extraStream;
}
}
if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
ArrayList<Uri> streams = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
if (streams != null && !streams.isEmpty()) {
return streams.get(0);
}
}
if (Intent.ACTION_VIEW.equals(action)) {
return intent.getData();
Uri data = intent.getData();
if (data != null) {
return data;
}
}
ClipData clipData = intent.getClipData();
if (clipData != null && clipData.getItemCount() > 0) {
Uri clipUri = clipData.getItemAt(0).getUri();
if (clipUri != null) {
return clipUri;
}
}
return null;
}
@@ -69,7 +92,7 @@ public final class SharedVaultImportActivity extends Activity {
throw new IOException("failed to create " + dir.getAbsolutePath());
}
File pendingFile = new File(dir, "pending-shared-vault.kdbx");
try (InputStream in = getContentResolver().openInputStream(uri)) {
try (InputStream in = openSharedInputStream(uri)) {
if (in == null) {
throw new IOException("failed to open shared vault stream");
}
@@ -88,6 +111,17 @@ public final class SharedVaultImportActivity extends Activity {
}
}
private InputStream openSharedInputStream(Uri uri) throws IOException {
if ("file".equalsIgnoreCase(uri.getScheme())) {
String path = uri.getPath();
if (path == null || path.trim().isEmpty()) {
throw new IOException("file URI is missing a path");
}
return new FileInputStream(new File(path));
}
return getContentResolver().openInputStream(uri);
}
private String resolveDisplayName(Uri uri) {
String displayName = queryDisplayName(uri);
if (!displayName.isEmpty()) {
@@ -123,6 +157,20 @@ public final class SharedVaultImportActivity extends Activity {
return "";
}
private void logIntent(Intent intent) {
if (intent == null) {
return;
}
Log.i(TAG, "intent action=" + intent.getAction()
+ " type=" + intent.getType()
+ " data=" + intent.getData()
+ " flags=0x" + Integer.toHexString(intent.getFlags()));
ClipData clipData = intent.getClipData();
if (clipData != null) {
Log.i(TAG, "intent clip items=" + clipData.getItemCount());
}
}
private void launchMainActivity() {
Intent launch = new Intent();
launch.setClassName(this, "org.gioui.GioActivity");