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:
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;
}
}