commit 8d82b2fc77b642784ab5025df33493d141cafca2
parent 35a7cff66291f0cbd41ca89015cc77aa33490590
Author: Florian Dold <florian@dold.me>
Date: Thu, 8 May 2025 20:33:00 +0200
harness: reproducer for form submission bug
Diffstat:
6 files changed, 221 insertions(+), 14 deletions(-)
diff --git a/packages/taler-harness/src/harness/tops.ts b/packages/taler-harness/src/harness/tops.ts
@@ -36,6 +36,7 @@ import {
TalerCorebankApiClient,
TalerCoreBankHttpClient,
TalerExchangeHttpClient,
+ TalerExchangeHttpClient2,
TalerMerchantInstanceHttpClient,
TalerProtocolDuration,
TalerProtocolTimestamp,
@@ -945,6 +946,8 @@ export interface MeasuresTestEnvironment {
decideReset: () => Promise<void>;
challengerPostal: TestfakeChallengerService;
challengerSms: TestfakeChallengerService;
+ officerAcc: OfficerAccount;
+ exchangeClient: TalerExchangeHttpClient2;
}
export async function setupMeasuresTestEnvironment(
@@ -971,7 +974,7 @@ export async function setupMeasuresTestEnvironment(
const merchantClient = new TalerMerchantInstanceHttpClient(
merchant.makeInstanceBaseUrl(),
);
- const exchangeClient = new TalerExchangeHttpClient(exchange.baseUrl, {
+ const exchangeClient = new TalerExchangeHttpClient2(exchange.baseUrl, {
httpClient: harnessHttpLib,
});
@@ -998,11 +1001,7 @@ export async function setupMeasuresTestEnvironment(
});
const submitForm = async (form: string, data: any) => {
- const kycInfoResp = await exchangeClient.checkKycInfo(
- accessToken,
- undefined,
- undefined,
- );
+ const kycInfoResp = await exchangeClient.checkKycInfo(accessToken);
t.assertDeepEqual(kycInfoResp.case, "ok");
t.assertDeepEqual(kycInfoResp.body.requirements[0].form, form);
const requirementId = kycInfoResp.body.requirements[0].id;
@@ -1017,11 +1016,7 @@ export async function setupMeasuresTestEnvironment(
address: any,
) => {
{
- const kycInfoResp = await exchangeClient.checkKycInfo(
- accessToken,
- undefined,
- undefined,
- );
+ const kycInfoResp = await exchangeClient.checkKycInfo(accessToken);
console.log(
`kyc info after my-postal-registration measure`,
j2s(kycInfoResp),
@@ -1113,6 +1108,8 @@ export async function setupMeasuresTestEnvironment(
const dec = await getCurrentDecision();
t.assertDeepEqual(dec.to_investigate, false);
},
+ officerAcc,
+ exchangeClient,
};
}
@@ -1145,7 +1142,7 @@ async function doTriggerReset(
t: GlobalTestState,
args: {
officerAcc: OfficerAccount;
- exchangeClient: TalerExchangeHttpClient;
+ exchangeClient: TalerExchangeHttpClient2;
merchantPaytoHash: string;
},
): Promise<void> {
@@ -1207,7 +1204,7 @@ async function doTriggerMeasure(
t: GlobalTestState,
args: {
officerAcc: OfficerAccount;
- exchangeClient: TalerExchangeHttpClient;
+ exchangeClient: TalerExchangeHttpClient2;
merchantPaytoHash: string;
measure: string;
},
diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts
@@ -1594,6 +1594,24 @@ const allAmlPrograms: TalerKycAml.AmlProgramDefinition[] = [
requiredContext: [],
},
{
+ name: "undefined-new-measures",
+ logic: async (_input, _config) => {
+ const outcome: TalerKycAml.AmlOutcome = {
+ new_measures: "does-not-exist",
+ new_rules: {
+ expiration_time: TalerProtocolTimestamp.never(),
+ rules: [],
+ custom_measures: {},
+ },
+ events: [],
+ };
+ return outcome;
+ },
+ requiredAttributes: [],
+ requiredInputs: [],
+ requiredContext: [],
+ },
+ {
name: "fail",
logic: async (_input, _config) => {
throw Error("I am a failed KYC program, oh my!");
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-form-bad-measure.ts b/packages/taler-harness/src/integrationtests/test-kyc-form-bad-measure.ts
@@ -0,0 +1,150 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import {
+ codecForKycProcessClientInformation,
+ Configuration,
+ j2s,
+ TransactionIdStr,
+ TransactionMajorState,
+ TransactionMinorState,
+} from "@gnu-taler/taler-util";
+import { readResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ configureCommonKyc,
+ createKycTestkudosEnvironment,
+ withdrawViaBankV3,
+} from "../harness/environments.js";
+import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
+
+function adjustExchangeConfig(config: Configuration) {
+ configureCommonKyc(config);
+
+ config.setString("KYC-RULE-R1", "operation_type", "withdraw");
+ config.setString("KYC-RULE-R1", "enabled", "yes");
+ config.setString("KYC-RULE-R1", "exposed", "yes");
+ config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
+ config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
+ config.setString("KYC-RULE-R1", "timeframe", "1d");
+ config.setString("KYC-RULE-R1", "next_measures", "M1 M2");
+
+ config.setString("KYC-MEASURE-M1", "check_name", "C1");
+ config.setString("KYC-MEASURE-M1", "context", "{}");
+ config.setString("KYC-MEASURE-M1", "program", "P1");
+
+ config.setString("KYC-MEASURE-M2", "check_name", "C2");
+ config.setString("KYC-MEASURE-M2", "context", "{}");
+ config.setString("KYC-MEASURE-M2", "program", "NONE");
+
+ config.setString(
+ "AML-PROGRAM-P1",
+ "command",
+ "taler-harness aml-program run-program --name undefined-new-measures",
+ );
+ config.setString("AML-PROGRAM-P1", "enabled", "true");
+ config.setString(
+ "AML-PROGRAM-P1",
+ "description",
+ "test for full_name and birthdate",
+ );
+ config.setString("AML-PROGRAM-P1", "description_i18n", "{}");
+ config.setString("AML-PROGRAM-P1", "fallback", "FREEZE");
+
+ config.setString("KYC-CHECK-C1", "type", "FORM");
+ config.setString("KYC-CHECK-C1", "form_name", "myform");
+ config.setString("KYC-CHECK-C1", "description", "my check!");
+ config.setString("KYC-CHECK-C1", "description_i18n", "{}");
+ config.setString("KYC-CHECK-C1", "outputs", "full_name birthdate");
+ config.setString("KYC-CHECK-C1", "fallback", "FREEZE");
+
+ config.setString("KYC-CHECK-C2", "type", "INFO");
+ config.setString("KYC-CHECK-C2", "description", "my check info!");
+ config.setString("KYC-CHECK-C2", "description_i18n", "{}");
+ config.setString("KYC-CHECK-C2", "fallback", "FREEZE");
+}
+
+export async function runKycFormBadMeasureTest(t: GlobalTestState) {
+ // Set up test environment
+
+ const { walletClient, bankClient, exchange, amlKeypair } =
+ await createKycTestkudosEnvironment(t, { adjustExchangeConfig });
+
+ // Withdraw digital cash into the wallet.
+
+ const wres = await withdrawViaBankV3(t, {
+ amount: "TESTKUDOS:20",
+ bankClient,
+ exchange,
+ walletClient,
+ });
+
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: wres.transactionId as TransactionIdStr,
+ txState: {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycRequired,
+ },
+ });
+
+ const txDetails = await walletClient.call(
+ WalletApiOperation.GetTransactionById,
+ {
+ transactionId: wres.transactionId,
+ },
+ );
+
+ console.log(j2s(txDetails));
+ const accessToken = txDetails.kycAccessToken;
+ t.assertTrue(!!accessToken);
+
+ const infoResp = await harnessHttpLib.fetch(
+ new URL(`kyc-info/${txDetails.kycAccessToken}`, exchange.baseUrl).href,
+ );
+
+ const clientInfo = await readResponseJsonOrThrow(
+ infoResp,
+ codecForKycProcessClientInformation(),
+ );
+
+ console.log(j2s(clientInfo));
+
+ const kycId = clientInfo.requirements.find((x) => x.id != null)?.id;
+ t.assertTrue(!!kycId);
+
+ const uploadResp = await harnessHttpLib.fetch(
+ new URL(`kyc-upload/${kycId}`, exchange.baseUrl).href,
+ {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: {
+ full_name: "Alice Abc",
+ birthdate: "2000-01-01",
+ },
+ },
+ );
+
+ console.log("resp status", uploadResp.status);
+
+ t.assertDeepEqual(uploadResp.status, 500);
+}
+
+runKycFormBadMeasureTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/test-tops-aml-measures.ts b/packages/taler-harness/src/integrationtests/test-tops-aml-measures.ts
@@ -42,6 +42,44 @@ export async function runTopsAmlMeasuresTest(t: GlobalTestState) {
challengerSms,
} = await setupMeasuresTestEnvironment(t);
+ // VQF customer type TRUST should lead to
+ // investigation.
+ {
+ await decideMeasure("kyx");
+ await expectNoInvestigate();
+ await submitForm("vqf_902_1_customer", {
+ FORM_ID: "vqf_902_1_customer",
+ FORM_VERSION: 1,
+ CUSTOMER_TYPE: "LEGAL_ENTITY",
+ CUSTOMER_TYPE_VQF: "TRUST",
+ FULL_NAME: "Alice A",
+ DOMICILE_ADDRESS: "Castle St. 1\nWondertown",
+ });
+ await expectInvestigate();
+ }
+
+ return;
+
+ await decideReset();
+
+ // VQF customer type OTHER should lead to
+ // investigation.
+ {
+ await decideMeasure("kyx");
+ await expectNoInvestigate();
+ await submitForm("vqf_902_1_customer", {
+ FORM_ID: "vqf_902_1_customer",
+ FORM_VERSION: 1,
+ CUSTOMER_TYPE: "LEGAL_ENTITY",
+ CUSTOMER_TYPE_VQF: "OTHER",
+ FULL_NAME: "Alice A",
+ DOMICILE_ADDRESS: "Castle St. 1\nWondertown",
+ });
+ await expectInvestigate();
+ }
+
+ await decideReset();
+
{
await decideMeasure("kyx");
await expectNoInvestigate();
@@ -63,6 +101,8 @@ export async function runTopsAmlMeasuresTest(t: GlobalTestState) {
await decideReset();
+ await decideReset();
+
// Test with weird customer type
{
await decideMeasure("kyx");
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -64,6 +64,7 @@ import { runKycDepositAggregateTest } from "./test-kyc-deposit-aggregate.js";
import { runKycDepositDepositKyctransferTest } from "./test-kyc-deposit-deposit-kyctransfer.js";
import { runKycDepositDepositTest } from "./test-kyc-deposit-deposit.js";
import { runKycExchangeWalletTest } from "./test-kyc-exchange-wallet.js";
+import { runKycFormBadMeasureTest } from "./test-kyc-form-bad-measure.js";
import { runKycFormWithdrawalTest } from "./test-kyc-form-withdrawal.js";
import { runKycMerchantActivateBankAccountTest } from "./test-kyc-merchant-activate-bank-account.js";
import { runKycMerchantAggregateTest } from "./test-kyc-merchant-aggregate.js";
@@ -332,6 +333,7 @@ const allTests: TestMainFunction[] = [
runTopsAmlMeasuresTest,
runTopsAmlLegiTest,
runTopsChallengerTwiceTest,
+ runKycFormBadMeasureTest,
];
export interface TestRunSpec {
diff --git a/packages/taler-util/src/http-client/exchange-client.ts b/packages/taler-util/src/http-client/exchange-client.ts
@@ -673,7 +673,7 @@ export class TalerExchangeHttpClient2 {
*/
async checkKycInfo(
token: AccessToken,
- known: KycRequirementInformationId[],
+ known: KycRequirementInformationId[] = [],
longpoll: boolean = false,
): Promise<
| OperationOk<KycProcessClientInformation>