commit f350a371605a98cdad36026dcc699ae844782584
parent 75a69a3646ad58a5df37abd784fe015b43adf0b0
Author: Florian Dold <dold@taler.net>
Date: Tue, 21 Jul 2026 20:17:10 +0200
util: test that codecForURLString validates its value
Diffstat:
1 file changed, 44 insertions(+), 0 deletions(-)
diff --git a/packages/taler-util/src/types-taler-common.test.ts b/packages/taler-util/src/types-taler-common.test.ts
@@ -17,6 +17,7 @@
import assert from "node:assert";
import { test } from "node:test";
import { Codec } from "./codec.js";
+import * as bankIntegration from "./types-taler-bank-integration.js";
import * as common from "./types-taler-common.js";
// Resolved dynamically so this file compiles before the codec exists.
@@ -84,3 +85,46 @@ test("the corebank codec rejects a merchant token listing", (t) => {
const codec = codecForBankTokenInfoList();
assert.throws(() => codec.decode(merchantTokenList));
});
+
+test("codecForURLString validates that the value is an http(s) URL", (t) => {
+ const codec = common.codecForURLString();
+ for (const ok of [
+ "https://exchange.example/",
+ "http://localhost:8081/",
+ "https://merchant.example/orders/1?token=x",
+ ]) {
+ assert.strictEqual(codec.decode(ok), ok);
+ }
+ // These fields are opened by the browser or fetched over HTTP, so a URL
+ // that parses is not enough -- the scheme has to be one we would follow.
+ for (const bad of [
+ "javascript:alert(document.cookie)//",
+ "data:text/html,<script>alert(1)</script>",
+ "file:///etc/passwd",
+ "not a url at all",
+ "/relative/path",
+ ]) {
+ assert.throws(() => codec.decode(bad), `accepted ${bad}`);
+ }
+ assert.throws(() => codec.decode(42 as any));
+});
+
+test("a hostile confirm_transfer_url is rejected at the wire boundary", (t) => {
+ // The web extension assigns this field to document.location.href.
+ const status = {
+ status: "selected",
+ amount: "KUDOS:1",
+ currency: "KUDOS",
+ card_fees: "KUDOS:0",
+ sender_wire: "payto://iban/DE75512108001245126199",
+ suggested_exchange: "https://exchange.example/",
+ confirm_transfer_url: "javascript:alert(document.cookie)//",
+ wire_types: ["iban"],
+ aborted: false,
+ selection_done: true,
+ transfer_done: false,
+ };
+ assert.throws(() =>
+ bankIntegration.codecForBankWithdrawalOperationStatus().decode(status),
+ );
+});