From 39cd02a15a4d5889d9370f5e0b2e97e8f2fcead5 Mon Sep 17 00:00:00 2001 From: MiMoHo <37556964+MiMoHo@users.noreply.github.com> Date: Fri, 10 Jul 2026 23:30:43 +0200 Subject: [PATCH] Mark copied secret custom fields as sensitive Secret custom fields are masked in the details view just like the account password, but their value was copied to the clipboard without the sensitive flag. On Android 13+ this let the value appear in the clipboard preview and be retained in clipboard history. Add CustomField.isSensitive (true only for secret fields) and use it at the custom-field copy actions, so secret fields are flagged with ClipDescription.EXTRA_IS_SENSITIVE like the password field already is. Assisted-by: ClaudeCode:claude-opus-4-8 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../data/password/CustomField.kt | 8 +++++ .../ui/components/PasswordItem.kt | 6 ++-- .../data/password/CustomFieldTest.kt | 32 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 app/src/test/java/com/hegocre/nextcloudpasswords/data/password/CustomFieldTest.kt diff --git a/app/src/main/java/com/hegocre/nextcloudpasswords/data/password/CustomField.kt b/app/src/main/java/com/hegocre/nextcloudpasswords/data/password/CustomField.kt index e93950e4a..e73eb6c30 100644 --- a/app/src/main/java/com/hegocre/nextcloudpasswords/data/password/CustomField.kt +++ b/app/src/main/java/com/hegocre/nextcloudpasswords/data/password/CustomField.kt @@ -15,6 +15,14 @@ data class CustomField( val type: String, val value: String ) { + /** + * Whether this field holds a secret value. Secret fields are masked in the UI and + * should be marked as sensitive when copied, so the system keeps them out of the + * clipboard preview and history (Android 13+), just like the account password. + */ + val isSensitive: Boolean + get() = type == TYPE_SECRET + companion object { const val TYPE_TEXT = "text" const val TYPE_SECRET = "secret" diff --git a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt index e578b1c5f..425273f09 100644 --- a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt +++ b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt @@ -347,7 +347,7 @@ fun PasswordItemContent( }, trailingIcon = { IconButton(onClick = { - context.copyToClipboard(customField.value) + context.copyToClipboard(customField.value, isSensitive = customField.isSensitive) Toast.makeText( context, String.format(copiedText, customField.label), @@ -378,7 +378,7 @@ fun PasswordItemContent( }, trailingIcon = { IconButton(onClick = { - context.copyToClipboard(customField.value) + context.copyToClipboard(customField.value, isSensitive = customField.isSensitive) Toast.makeText( context, String.format(copiedText, customField.label), @@ -409,7 +409,7 @@ fun PasswordItemContent( }, trailingIcon = { IconButton(onClick = { - context.copyToClipboard(customField.value) + context.copyToClipboard(customField.value, isSensitive = customField.isSensitive) Toast.makeText( context, String.format(copiedText, customField.label), diff --git a/app/src/test/java/com/hegocre/nextcloudpasswords/data/password/CustomFieldTest.kt b/app/src/test/java/com/hegocre/nextcloudpasswords/data/password/CustomFieldTest.kt new file mode 100644 index 000000000..d278c687b --- /dev/null +++ b/app/src/test/java/com/hegocre/nextcloudpasswords/data/password/CustomFieldTest.kt @@ -0,0 +1,32 @@ +package com.hegocre.nextcloudpasswords.data.password + +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +/** + * Unit test for [CustomField.isSensitive], which decides whether a custom field value + * must be flagged as sensitive when copied to the clipboard. + */ +class CustomFieldTest { + private fun field(type: String) = CustomField(label = "label", type = type, value = "value") + + @Test + fun secretFieldIsSensitive() { + assertTrue(field(CustomField.TYPE_SECRET).isSensitive) + } + + @Test + fun nonSecretFieldsAreNotSensitive() { + assertFalse(field(CustomField.TYPE_TEXT).isSensitive) + assertFalse(field(CustomField.TYPE_EMAIL).isSensitive) + assertFalse(field(CustomField.TYPE_URL).isSensitive) + assertFalse(field(CustomField.TYPE_FILE).isSensitive) + assertFalse(field(CustomField.TYPE_DATA).isSensitive) + } + + @Test + fun unknownTypeIsNotSensitive() { + assertFalse(field("something-else").isSensitive) + } +}