commit d5b68895e9545d3482eaf7ac5f38069dcb111a3a
parent 13c6ede78f2ecdb1336f5b5863fc52c3ea123752
Author: Florian Dold <dold@taler.net>
Date: Thu, 30 Jul 2026 14:19:01 +0200
wallet-core: reject replacing a bank account that does not exist
An unknown replaceBankAccountId silently created an account under the
client's ID instead of failing, so a stale ID in the client went unnoticed.
Diffstat:
3 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/packages/taler-harness/src/integrationtests/test-known-accounts.ts b/packages/taler-harness/src/integrationtests/test-known-accounts.ts
@@ -17,7 +17,11 @@
/**
* Imports.
*/
-import { j2s, TalerCorebankApiClient } from "@gnu-taler/taler-util";
+import {
+ j2s,
+ TalerCorebankApiClient,
+ TalerErrorCode,
+} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
useSharedTestkudosEnvironment,
@@ -122,6 +126,28 @@ export async function runKnownAccountsTest(t: GlobalTestState) {
console.log(`accounts after replace: ${j2s(accts2)}`);
t.assertDeepEqual(accts2.accounts.length, 3);
}
+
+ // Replacing an account that does not exist must fail instead of creating
+ // one under the requested ID.
+ {
+ const err = await t.assertThrowsTalerErrorAsync(async () =>
+ walletClient.call(WalletApiOperation.AddBankAccount, {
+ replaceBankAccountId: "acct:does-not-exist",
+ paytoUri: "payto://iban/CH4308307000289537312",
+ label: "Nope",
+ currencies: ["CHF"],
+ }),
+ );
+ t.assertTrue(
+ err.errorDetail.code === TalerErrorCode.WALLET_BANK_ACCOUNT_NOT_FOUND,
+ );
+
+ const accts2 = await walletClient.call(
+ WalletApiOperation.ListBankAccounts,
+ {},
+ );
+ t.assertDeepEqual(accts2.accounts.length, 3);
+ }
}
runKnownAccountsTest.suites = ["wallet", "shared-env"];
diff --git a/packages/taler-wallet-core/src/requests.ts b/packages/taler-wallet-core/src/requests.ts
@@ -1041,6 +1041,17 @@ async function handleAddBankAccount(
let myId: string;
const oldAcct = await tx.getBankAccountByPaytoUri(req.paytoUri);
if (req.replaceBankAccountId) {
+ // Without this check, replacing an account that does not exist would
+ // create one under the client's ID instead of failing, which hides a
+ // stale ID in the client and mints accounts with foreign ID formats.
+ const toReplace = await tx.getBankAccount(req.replaceBankAccountId);
+ if (!toReplace) {
+ throw TalerError.fromDetail(
+ TalerErrorCode.WALLET_BANK_ACCOUNT_NOT_FOUND,
+ { bankAccountId: req.replaceBankAccountId },
+ `bank account ${req.replaceBankAccountId} not found, cannot replace it`,
+ );
+ }
myId = req.replaceBankAccountId;
} else if (oldAcct) {
myId = oldAcct.bankAccountId;
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -1887,6 +1887,7 @@ export const walletApiExpectedErrors = {
],
[WalletApiOperation.AddBankAccount]: [
TalerErrorCode.GENERIC_PAYTO_URI_MALFORMED,
+ TalerErrorCode.WALLET_BANK_ACCOUNT_NOT_FOUND,
],
// --- Tokens (discounts and subscriptions) -------------------------------