taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit d96795022adbaaa91e18bffa3995a0359a72d24f
parent f2799b40989bd8bbf685d788810edcf8a2b2f7b0
Author: Florian Dold <florian@dold.me>
Date:   Tue, 25 Nov 2025 18:16:19 +0100

wallet-core: fix handling of slashes in addExchange

There is now also a harness test for this.

Diffstat:
Mpackages/taler-harness/src/integrationtests/test-exchange-management.ts | 32+++++++++++++++++++++++++++++++-
Mpackages/taler-util/src/types-taler-wallet.ts | 1+
Mpackages/taler-wallet-core/src/wallet.ts | 34++++++++++++++++------------------
3 files changed, 48 insertions(+), 19 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-exchange-management.ts b/packages/taler-harness/src/integrationtests/test-exchange-management.ts @@ -17,9 +17,10 @@ /** * Imports. */ +import { j2s } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState } from "../harness/harness.js"; import { createSimpleTestkudosEnvironmentV3 } from "../harness/environments.js"; +import { GlobalTestState } from "../harness/harness.js"; /** * Test if the wallet handles outdated exchange versions correctly. @@ -77,6 +78,35 @@ export async function runExchangeManagementTest( ); t.assertDeepEqual(exchangesListResult4.exchanges.length, 1); + + await walletClient.call(WalletApiOperation.DeleteExchange, { + exchangeBaseUrl: exchange.baseUrl, + }); + + const url = new URL(exchange.baseUrl); + + { + await t.assertThrowsAsync(async () => { + await walletClient.call(WalletApiOperation.AddExchange, { + uri: `${url.hostname}:${url.port}`, + }); + }); + } + + { + const err = await t.assertThrowsTalerErrorAsync(async () => { + const res = await walletClient.call(WalletApiOperation.AddExchange, { + uri: `${url.hostname}:${url.port}`, + allowCompletion: true, + }); + }); + console.log(j2s(err)); + // Tries to complete to https://, but we use http in the local test. + t.assertDeepEqual( + (err.errorDetail as any).innerError.requestUrl, + "https://localhost:8081/keys", + ); + } } runExchangeManagementTest.suites = ["wallet", "exchange"]; diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -2213,6 +2213,7 @@ export interface AddExchangeResponse { export const codecForAddExchangeRequest = (): Codec<AddExchangeRequest> => buildCodecForObject<AddExchangeRequest>() + .property("allowCompletion", codecOptional(codecForBoolean())) .property("exchangeBaseUrl", codecOptional(codecForCanonBaseUrl())) .property("uri", codecOptional(codecForString())) .property("forceUpdate", codecOptional(codecForBoolean())) diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts @@ -1047,20 +1047,28 @@ async function handleAddExchange( ); exchangeBaseUrl = req.exchangeBaseUrl; } else if (req.uri) { - if (req.uri.startsWith("http")) { - const canonUrl = canonicalizeBaseUrl(req.uri); - if (req.uri != canonUrl) { - throw Error("exchange base URL must be canonicalized"); - } - exchangeBaseUrl = req.uri; - } else if (req.uri.startsWith("taler")) { + if (req.uri.startsWith("taler")) { const p = parseTalerUri(req.uri); if (p?.type !== TalerUriAction.AddExchange) { throw Error("invalid taler://add-exchange/ URI"); } exchangeBaseUrl = p.exchangeBaseUrl; + } else if (req.allowCompletion) { + const completeRes = await handleCompleteExchangeBaseUrl(wex, { + url: req.uri, + }); + if (completeRes.status != "ok") { + throw TalerError.fromUncheckedDetail(completeRes.error); + } + exchangeBaseUrl = completeRes.completion; + } else if (req.uri.startsWith("http")) { + const canonUrl = canonicalizeBaseUrl(req.uri); + if (req.uri != canonUrl) { + throw Error("exchange base URL must be canonicalized"); + } + exchangeBaseUrl = req.uri; } else { - throw Error("AddExchangeRequest.uri must be http(s) or taler URI"); + throw Error("AddExchangeRequest.uri must be http(s):// or taler://"); } } else { throw Error( @@ -1068,16 +1076,6 @@ async function handleAddExchange( ); } - if (req.allowCompletion) { - const completeRes = await handleCompleteExchangeBaseUrl(wex, { - url: exchangeBaseUrl, - }); - if (completeRes.status != "ok") { - throw TalerError.fromUncheckedDetail(completeRes.error); - } - exchangeBaseUrl = completeRes.completion; - } - await fetchFreshExchange(wex, exchangeBaseUrl, {}); // Exchange has been explicitly added upon user request. // Thus, we mark it as "used".