ensure imported keystore labels are truncated and unique
What changed, and why it matters
This commit fixes a bug in Sparrow Wallet's import of wallet labels. Previously, labels for multi-signature participants (keystores) could be too long or identical to each other, which could make the wallet file impossible to reopen. The change now truncates long labels and rejects duplicates during import, preventing the wallet from becoming unopenable.
Users importing wallet labels from untrusted or shared sources should upgrade to a version containing this commit to avoid wallet files becoming unopenable. Review backups before importing labels from external files.
Security signals we found
Denial-of-service via malformed imported label causing wallet file to become unopenable
Input validation added for imported keystore labels
Length truncation and uniqueness enforcement for keystore labels
Evidence from the diff
WalletLabels.java previously set keystore labels directly from imported label data without enforcing Keystore.MAX_LABEL_LENGTH or uniqueness. The patch introduces updateKeystoreLabel(), which truncates labels exceeding the max length and checks wallet.containsDuplicateKeystoreLabels(); if a duplicate would result, it reverts to the previous label and skips recording the change. This prevents wallet file deserialization/loading failures caused by invalid keystore labels.
Changed components
src/main/java/com/sparrowwallet/sparrow/io/WalletLabels.javaWallet import (WalletLabels)Keystore label handlingInspect captured patch +17 / −6
diff --git a/src/main/java/com/sparrowwallet/sparrow/io/WalletLabels.java b/src/main/java/com/sparrowwallet/sparrow/io/WalletLabels.java
index 7ae56c0..a6fc28a 100644
--- a/src/main/java/com/sparrowwallet/sparrow/io/WalletLabels.java
+++ b/src/main/java/com/sparrowwallet/sparrow/io/WalletLabels.java
@@ -226,9 +226,7 @@ public class WalletLabels implements WalletImport, WalletExport {
if(label.type == Type.xpub) {
for(Keystore keystore : wallet.getKeystores()) {
if(keystore.getExtendedPublicKey() != null && keystore.getExtendedPublicKey().toString().equals(label.ref)) {
- keystore.setLabel(label.label);
- List<Keystore> changedKeystores = changedWalletKeystores.computeIfAbsent(wallet, w -> new ArrayList<>());
- changedKeystores.add(keystore);
+ updateKeystoreLabel(wallet, keystore, label.label, changedWalletKeystores);
}
}
}
@@ -236,9 +234,7 @@ public class WalletLabels implements WalletImport, WalletExport {
if(label.type == Type.spscan) {
for(Keystore keystore : wallet.getKeystores()) {
if(keystore.getSilentPaymentScanAddress() != null && keystore.getSilentPaymentScanAddress().toKeyString().equals(label.ref)) {
- keystore.setLabel(label.label);
- List<Keystore> changedKeystores = changedWalletKeystores.computeIfAbsent(wallet, w -> new ArrayList<>());
- changedKeystores.add(keystore);
+ updateKeystoreLabel(wallet, keystore, label.label, changedWalletKeystores);
}
}
}
@@ -327,6 +323,21 @@ public class WalletLabels implements WalletImport, WalletExport {
return walletForms.get(0).getWallet();
}
+ private static void updateKeystoreLabel(Wallet wallet, Keystore keystore, String label, Map<Wallet, List<Keystore>> changedWalletKeystores) {
+ //Keystore labels are length constrained and must be unique, and an invalid label renders the wallet file unopenable
+ String previousLabel = keystore.getLabel();
+ keystore.setLabel(label.length() > Keystore.MAX_LABEL_LENGTH ? label.substring(0, Keystore.MAX_LABEL_LENGTH) : label);
+
+ if(wallet.containsDuplicateKeystoreLabels()) {
+ log.warn("Not importing keystore label of " + label + " for " + previousLabel + " as it duplicates another keystore label");
+ keystore.setLabel(previousLabel);
+ return;
+ }
+
+ List<Keystore> changedKeystores = changedWalletKeystores.computeIfAbsent(wallet, w -> new ArrayList<>());
+ changedKeystores.add(keystore);
+ }
+
private static void updateHashIndexEntryLabel(Label label, Entry entry) {
if(entry instanceof HashIndexEntry hashIndexEntry) {
BlockTransactionHashIndex reference = hashIndexEntry.getHashIndex();
Why this scored 49/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.