commit dc9ac3bdaf8525ae458ac38905e34b159b1628a1
parent e3b2f049795121bcd4e99cf07f221f0207cdac21
Author: Florian Dold <dold@taler.net>
Date: Tue, 21 Jul 2026 11:07:07 +0200
util: test taler URI components containing control characters
Diffstat:
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/packages/taler-util/src/taleruri-roundtrip.test.ts b/packages/taler-util/src/taleruri-roundtrip.test.ts
@@ -30,7 +30,11 @@ test("pay URI: a component with structural characters round-trips", (t) => {
for (const orderId of ["a/b", "a?c=evil", "a#b", "a&b", "plain-01", "a b"]) {
const s = TalerUris.stringify(payUri(orderId));
const r = TalerUris.parse(s);
- assert.strictEqual(r.tag, "ok", `parse failed for orderId ${orderId}: ${s}`);
+ assert.strictEqual(
+ r.tag,
+ "ok",
+ `parse failed for orderId ${orderId}: ${s}`,
+ );
const v = Result.unpack(r) as any;
assert.strictEqual(v.orderId, orderId, `orderId not preserved: ${s}`);
// the merchant host must survive intact
@@ -62,6 +66,36 @@ test("withdrawal-transfer-result URI round-trips", (t) => {
});
test("withdrawal-transfer-result URI requires a ref", (t) => {
- const r = TalerUris.parse("taler://withdrawal-transfer-result?status=success");
+ const r = TalerUris.parse(
+ "taler://withdrawal-transfer-result?status=success",
+ );
assert.strictEqual(r.tag, "error");
});
+
+test("pay URI: control characters cannot erase the merchant host", (t) => {
+ // The WHATWG URL parser strips tab, LF and CR before parsing, so a
+ // component of ".." followed by one of them is not caught by the dot-segment
+ // guard and is resolved away together with the preceding path segment.
+ for (const orderId of ["..\t", "..\n", "..\r", "a\tb", "x\ny"]) {
+ const s = TalerUris.stringify(payUri(orderId));
+ const r = TalerUris.parse(s);
+ assert.strictEqual(r.tag, "ok", `parse failed: ${JSON.stringify(s)}`);
+ const v = Result.unpack(r) as any;
+ assert.strictEqual(v.merchantBaseUrl, "https://m.example.com/", s);
+ assert.strictEqual(v.orderId, orderId, `orderId not preserved: ${s}`);
+ }
+});
+
+test("pay URI: an instance sub-path survives a hostile order id", (t) => {
+ const s = TalerUris.stringify({
+ type: TalerUriAction.Pay,
+ merchantBaseUrl: "https://shop.example.com/instances/alice/" as any,
+ orderId: "..\t",
+ sessionId: "sess",
+ });
+ const v = Result.unpack(TalerUris.parse(s)) as any;
+ assert.strictEqual(
+ v.merchantBaseUrl,
+ "https://shop.example.com/instances/alice/",
+ );
+});