handle import of samourai wallet backup file with extraneous appended data
What changed, and why it matters
This commit changes how Sparrow Wallet imports a Samourai wallet backup file. Previously, if the backup file contained any extra characters after the closing JSON brace, the import would fail. The new code tries to parse the file as-is, and if that fails, it trims everything after the first closing brace and tries again. This is a robustness fix for malformed or padded backup files, not a clear-cut security patch. It could, in theory, hide malicious trailing content from the parser, but there is no direct evidence in the commit that this trailing content is dangerous or that the change is intended to address a security vulnerability.
Treat as a robustness improvement unless additional context shows the appended data was exploitable. If reviewing for security, verify whether the trimmed trailing bytes could affect downstream parsing, seed extraction, or backup integrity. Consider logging or warning the user when extraneous data is detected and trimmed, rather than silently discarding it.
Security signals we found
Truncates input after first closing brace on parse failure, which could mask appended data
No validation, logging, or rejection of the trimmed trailing content
Change is in wallet import code handling encrypted/decrypted backup payloads
Commit message frames the change as handling 'extraneous appended data', not as a security fix
Evidence from the diff
The diff refactors JSON parsing in Samourai.java into a new parseJsonInput() method. The method first attempts a normal Gson parse of the full input. On JsonParseException, it finds the first ‘}’ character, truncates the input after it, and re-parses. This allows Samourai backup files with extraneous appended data to import successfully. The change is defensive/robustness-oriented. It does not add validation of the truncated trailing data, nor does it log or reject it. There is no explicit security context in the commit message or diff, and no verified references were supplied.
Changed components
src/main/java/com/sparrowwallet/sparrow/io/Samourai.javaSamourai wallet backup import featureInspect captured patch +20 / −5
diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Samourai.java b/src/main/java/com/sparrowwallet/sparrow/io/Samourai.java
index 6459c09..0ed872a 100644
--- a/src/main/java/com/sparrowwallet/sparrow/io/Samourai.java
+++ b/src/main/java/com/sparrowwallet/sparrow/io/Samourai.java
@@ -29,10 +29,7 @@ public class Samourai implements KeystoreFileImport {
try {
String input = CharStreams.toString(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
- Gson gson = new Gson();
- Type stringStringMap = new TypeToken<Map<String, JsonElement>>() {
- }.getType();
- Map<String, JsonElement> map = gson.fromJson(input, stringStringMap);
+ Map<String, JsonElement> map = this.parseJsonInput(input);
String payload = input;
if(map.containsKey("payload")) {
@@ -53,7 +50,7 @@ public class Samourai implements KeystoreFileImport {
throw new ImportException("Unsupported backup version: " + version);
}
- SamouraiBackup backup = gson.fromJson(decrypted, SamouraiBackup.class);
+ SamouraiBackup backup = new Gson().fromJson(decrypted, SamouraiBackup.class);
DeterministicSeed seed = new DeterministicSeed(Utils.hexToBytes(backup.wallet.seed), password, 0);
Keystore keystore = Keystore.fromSeed(seed, scriptType.getDefaultDerivation());
keystore.setLabel(getWalletModel().toDisplayString());
@@ -67,6 +64,24 @@ public class Samourai implements KeystoreFileImport {
}
}
+ private Map<String, JsonElement> parseJsonInput(String input) {
+ Gson gson = new Gson();
+ Type stringStringMap = new TypeToken<Map<String, JsonElement>>() {
+ }.getType();
+
+ try {
+ return gson.fromJson(input, stringStringMap);
+ } catch (JsonParseException e) {
+ int closingBracket = input.indexOf('}');
+ if (closingBracket < 0) {
+ throw e;
+ }
+
+ String fixedInput = input.substring(0, closingBracket + 1);
+ return gson.fromJson(fixedInput, stringStringMap);
+ }
+ }
+
@Override
public boolean isKeystoreImportScannable() {
return false;
Why this scored 35/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.