commit 4f6b3258eb3ebca62080507c1af70c4d41725d1e
parent f20bc8bd00efaa270be937432afccf9fae1403ff
Author: Christian Grothoff <christian@grothoff.org>
Date: Wed, 5 Aug 2026 22:17:10 +0200
fix payto URI mismatch where REGEXP_REPLACE failed to properly strip receiver-name as intended
Diffstat:
2 files changed, 59 insertions(+), 4 deletions(-)
diff --git a/src/backenddb/insert_transfer.sql b/src/backenddb/insert_transfer.sql
@@ -35,13 +35,16 @@ BEGIN
out_conflict=FALSE;
+-- NOTE: compare the payto URIs with any query parameters (such as
+-- 'receiver-name') stripped. SPLIT_PART is used instead of REGEXP_REPLACE
+-- because the previous regular expression ('\\?.*') matched an *optional*
+-- backslash followed by anything, i.e. it matched at offset 0 and reduced
+-- every URI to the empty string, making this condition always true.
SELECT account_serial
INTO my_account_serial
FROM merchant_accounts
- WHERE REGEXP_REPLACE(payto_uri,
- '\\?.*','')
- =REGEXP_REPLACE(in_credited_account_payto,
- '\\?.*','');
+ WHERE SPLIT_PART(payto_uri,'?',1)
+ =SPLIT_PART(in_credited_account_payto,'?',1);
IF NOT FOUND
THEN
out_no_account=TRUE;
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
@@ -4431,6 +4431,54 @@ test_insert_transfer (const struct InstanceData *instance,
/**
+ * Tests that inserting a transfer crediting a payto URI which belongs to
+ * no account of the instance is reported via the @e no_account flag,
+ * instead of being silently attributed to an arbitrary account.
+ *
+ * Regression test: the account lookup in insert_transfer.sql compared
+ * REGEXP_REPLACE(payto_uri,'\\?.*',''), a pattern which matches an
+ * *optional* backslash followed by anything. It therefore matched at
+ * offset 0, reduced every URI to the empty string, and made the
+ * comparison unconditionally true.
+ *
+ * @param instance the instance to use.
+ * @param transfer the transfer to attempt to insert.
+ * @return 0 on success, 1 otherwise.
+ */
+static int
+test_insert_transfer_unknown_account (
+ const struct InstanceData *instance,
+ const struct TransferData *transfer)
+{
+ bool no_account = false;
+ bool conflict = false;
+ struct TALER_FullPayto unknown = {
+ .full_payto = (char *) "payto://iban/DE/NO-SUCH-ACCOUNT"
+ "?receiver-name=Not+My+Account"
+ };
+
+ TEST_SET_INSTANCE (instance->instance.id,
+ GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
+ TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
+ TALER_MERCHANTDB_insert_transfer (
+ pg,
+ instance->instance.id,
+ transfer->exchange_url,
+ &transfer->wtid,
+ &transfer->data.total_amount,
+ unknown,
+ transfer->confirmed,
+ &no_account,
+ &conflict),
+ "Insert transfer for unknown account failed\n");
+ TEST_COND_RET_ON_FAIL (no_account,
+ "Insert transfer accepted a payto URI belonging to "
+ "no account of the instance\n");
+ return 0;
+}
+
+
+/**
* Tests linking a deposit to a transfer.
*
* @param instance the instance that the deposit and transfer are for.
@@ -4681,6 +4729,10 @@ run_test_transfers (struct TestTransfers_Closure *cls)
TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
&cls->order,
GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
+ /* A payto URI that matches no account must be rejected, not silently
+ attributed to some arbitrary account of the instance. */
+ TEST_RET_ON_FAIL (test_insert_transfer_unknown_account (&cls->instance,
+ &cls->transfers[0]));
/* Insert the transfer */
TEST_RET_ON_FAIL (test_insert_transfer (&cls->instance,
&cls->account,