taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

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:
Mpackages/taler-harness/src/harness/tops.ts | 23++++++++++-------------
Mpackages/taler-harness/src/index.ts | 18++++++++++++++++++
Apackages/taler-harness/src/integrationtests/test-kyc-form-bad-measure.ts | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-harness/src/integrationtests/test-tops-aml-measures.ts | 40++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-harness/src/integrationtests/testrunner.ts | 2++
Mpackages/taler-util/src/http-client/exchange-client.ts | 2+-
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>