taler-typescript-core

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

commit 4a9cb7196f9bb85bc8e2b3e46f3db57ed5b103db
parent 4c93cec5fbdf99ecac1c06a7298bf3d1cbfcb106
Author: Florian Dold <dold@taler.net>
Date:   Wed, 22 Jul 2026 00:28:26 +0200

wallet: match token domains against the merchant correctly

A domain without a wildcard cleared the warning for every host it did not
equal, so a single plain entry in expected_domains or trusted_domains made
the token spendable anywhere.  Wildcards compared the suffix without its
separating dot, letting "eviltaler.net" match "*.taler.net", and the
syntax check rejected hyphens, which are legal in host names.

Matching now happens in one helper that anchors the wildcard suffix at a
label boundary.

Diffstat:
Mpackages/taler-wallet-core/src/tokenSelection.ts | 35++++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/packages/taler-wallet-core/src/tokenSelection.ts b/packages/taler-wallet-core/src/tokenSelection.ts @@ -80,6 +80,25 @@ export enum TokenMerchantVerificationResult { } /** + * Check whether a host matches a single entry of `trusted_domains' or + * `expected_domains'. + * + * A "*." prefix matches the domain itself and any of its subdomains. The + * match is anchored at a label boundary, so "*.taler.net" covers + * "shop.taler.net" and "taler.net", but not "eviltaler.net". + */ +function matchesTokenDomain(domain: string, host: string): boolean { + if (domain === host) { + return true; + } + if (domain.startsWith("*.")) { + const suffix = domain.slice(2); + return host === suffix || host.endsWith(`.${suffix}`); + } + return false; +} + +/** * Verify that merchant URL matches `trusted_domains' or * `expected_domains' in the token family. * @@ -126,18 +145,16 @@ export function verifyTokenMerchant( } } - var warning = true; - const regex = new RegExp("^(\\*\\.)?([\\w\\d]+\\.)+[\\w\\d]+$"); + let warning = true; + const regex = new RegExp( + "^(\\*\\.)?([a-z0-9]([a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?$", + ); for (let domain of domains) { domain = domain.toLowerCase(); if (!regex.test(domain)) throw new Error("assertion failed"); - if (warning) { - // If the two domains match exactly, no warning. - // If the domain has a wildcard, do multi-level matching. - warning = - domain !== merchantDomain && - domain.startsWith("*.") && - !merchantDomain.endsWith(domain.slice(2)); + if (matchesTokenDomain(domain, merchantDomain)) { + warning = false; + break; } }