Skip to content

Commit 18a5f88

Browse files
authored
Merge pull request Expensify#68208 from callstack-internal/fix/67875-validation-loophole
fix: Validation loophole in global reimbursements
2 parents 38fa9c2 + a0e44a9 commit 18a5f88

2 files changed

Lines changed: 91 additions & 19 deletions

File tree

src/pages/ReimbursementAccount/NonUSD/utils/getInitialSubStepForBankInfoStep.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,50 @@
11
import CONST from '@src/CONST';
22
import type {ReimbursementAccountForm} from '@src/types/form';
3-
import type {CorpayFields} from '@src/types/onyx';
3+
import type {CorpayFields, CorpayFormField} from '@src/types/onyx';
44
import type {SubStepValues} from './getBankInfoStepValues';
55

66
/**
77
* Returns the initial subStep for the Bank info step based on already existing data
88
*/
99
function getInitialSubStepForBusinessInfoStep(data: SubStepValues<keyof ReimbursementAccountForm>, corpayFields: CorpayFields | undefined): number {
10-
const bankAccountDetailsFields = corpayFields?.formFields?.filter((field) => !field.id.includes(CONST.NON_USD_BANK_ACCOUNT.BANK_INFO_STEP_ACCOUNT_HOLDER_KEY_PREFIX));
11-
const accountHolderDetailsFields = corpayFields?.formFields?.filter((field) => field.id.includes(CONST.NON_USD_BANK_ACCOUNT.BANK_INFO_STEP_ACCOUNT_HOLDER_KEY_PREFIX));
12-
const hasAnyMissingBankAccountDetails = bankAccountDetailsFields?.some((field) => data?.[field.id as keyof ReimbursementAccountForm] === '');
13-
const hasAnyMissingAccountHolderDetails = accountHolderDetailsFields?.some((field) => data?.[field.id as keyof ReimbursementAccountForm] === '');
10+
if (!corpayFields?.formFields) {
11+
return 0;
12+
}
13+
14+
const isFieldInvalidOrMissing = (field: CorpayFormField): boolean => {
15+
const fieldID = field.id as keyof ReimbursementAccountForm;
16+
const value = data?.[fieldID];
17+
18+
if (value === '' || value === null || value === undefined) {
19+
return true;
20+
}
21+
22+
if (field.validationRules && field.validationRules.length > 0) {
23+
const strValue = String(value);
24+
return field.validationRules.some((rule) => {
25+
if (!rule.regEx) {
26+
return false;
27+
}
28+
29+
const regex = new RegExp(rule.regEx);
30+
return !regex.test(strValue);
31+
});
32+
}
33+
34+
return false;
35+
};
36+
37+
const bankAccountDetailsFields = corpayFields.formFields.filter((field) => !field.id.includes(CONST.NON_USD_BANK_ACCOUNT.BANK_INFO_STEP_ACCOUNT_HOLDER_KEY_PREFIX));
38+
const accountHolderDetailsFields = corpayFields.formFields.filter((field) => field.id.includes(CONST.NON_USD_BANK_ACCOUNT.BANK_INFO_STEP_ACCOUNT_HOLDER_KEY_PREFIX));
39+
40+
const hasInvalidBankAccountDetails = bankAccountDetailsFields.some(isFieldInvalidOrMissing);
41+
const hasInvalidAccountHolderDetails = accountHolderDetailsFields.some(isFieldInvalidOrMissing);
1442

15-
if (corpayFields === undefined || hasAnyMissingBankAccountDetails) {
43+
if (hasInvalidBankAccountDetails) {
1644
return 0;
1745
}
1846

19-
if (hasAnyMissingAccountHolderDetails) {
47+
if (hasInvalidAccountHolderDetails) {
2048
return 1;
2149
}
2250

src/pages/ReimbursementAccount/NonUSD/utils/getInitialSubStepForBusinessInfoStep.ts

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,85 @@
1+
import {Str} from 'expensify-common';
2+
import {
3+
isValidAddress,
4+
isValidCompanyName,
5+
isValidEmail,
6+
isValidPhoneInternational,
7+
isValidRegistrationNumber,
8+
isValidTaxIDEINNumber,
9+
isValidWebsite,
10+
isValidZipCodeInternational,
11+
} from '@libs/ValidationUtils';
112
import CONST from '@src/CONST';
213
import INPUT_IDS from '@src/types/form/ReimbursementAccountForm';
314

415
const BUSINESS_INFO_STEP_KEYS = INPUT_IDS.ADDITIONAL_DATA.CORPAY;
516

17+
function isMissingValidCompanyName(companyName: string) {
18+
return companyName === '' || !isValidCompanyName(companyName);
19+
}
20+
21+
function isMissingValidWebsite(website: string) {
22+
return website === '' || !isValidWebsite(Str.sanitizeURL(website, CONST.COMPANY_WEBSITE_DEFAULT_SCHEME));
23+
}
24+
25+
function isMissingValidAddress(street: string, city: string, postalCode: string, state: string, country: string) {
26+
return (
27+
street === '' ||
28+
city === '' ||
29+
postalCode === '' ||
30+
country === '' ||
31+
((country === CONST.COUNTRY.US || country === CONST.COUNTRY.CA) && state === '') ||
32+
(country === '' && state === '') ||
33+
!isValidAddress(street) ||
34+
(country === CONST.COUNTRY.US && !isValidZipCodeInternational(postalCode))
35+
);
36+
}
37+
38+
function isMissingValidBusinessContactInformation(contactNumber: string, contactEmail: string) {
39+
return contactNumber === '' || contactEmail === '' || !isValidPhoneInternational(contactNumber) || !isValidEmail(contactEmail);
40+
}
41+
42+
function isMissingValidRegistrationNumber(registrationNumber: string, country: string) {
43+
return registrationNumber === '' || !isValidRegistrationNumber(registrationNumber, country as keyof typeof CONST.COUNTRY);
44+
}
45+
46+
function isMissingValidTaxIDEINNumber(taxIDEINNumber: string, country: string) {
47+
return taxIDEINNumber === '' || !isValidTaxIDEINNumber(taxIDEINNumber, country as keyof typeof CONST.COUNTRY);
48+
}
49+
650
/**
751
* Returns the initial subStep for the Business info step based on already existing data
852
*/
953
function getInitialSubStepForBusinessInfoStep(data: Record<string, string>): number {
10-
if (data[BUSINESS_INFO_STEP_KEYS.COMPANY_NAME] === '') {
54+
if (isMissingValidCompanyName(data[BUSINESS_INFO_STEP_KEYS.COMPANY_NAME])) {
1155
return 0;
1256
}
1357

14-
if (data[BUSINESS_INFO_STEP_KEYS.COMPANY_WEBSITE] === '') {
58+
if (isMissingValidWebsite(data[BUSINESS_INFO_STEP_KEYS.COMPANY_WEBSITE])) {
1559
return 1;
1660
}
1761

1862
if (
19-
data[BUSINESS_INFO_STEP_KEYS.COMPANY_STREET] === '' ||
20-
data[BUSINESS_INFO_STEP_KEYS.COMPANY_CITY] === '' ||
21-
data[BUSINESS_INFO_STEP_KEYS.COMPANY_POSTAL_CODE] === '' ||
22-
data[BUSINESS_INFO_STEP_KEYS.COMPANY_COUNTRY_CODE] === '' ||
23-
((data[BUSINESS_INFO_STEP_KEYS.COMPANY_COUNTRY_CODE] === CONST.COUNTRY.US || data[BUSINESS_INFO_STEP_KEYS.COMPANY_COUNTRY_CODE] === CONST.COUNTRY.CA) &&
24-
data[BUSINESS_INFO_STEP_KEYS.COMPANY_STATE] === '') ||
25-
(data[BUSINESS_INFO_STEP_KEYS.COMPANY_COUNTRY_CODE] === '' && data[BUSINESS_INFO_STEP_KEYS.COMPANY_STATE] === '')
63+
isMissingValidAddress(
64+
data[BUSINESS_INFO_STEP_KEYS.COMPANY_STREET],
65+
data[BUSINESS_INFO_STEP_KEYS.COMPANY_CITY],
66+
data[BUSINESS_INFO_STEP_KEYS.COMPANY_POSTAL_CODE],
67+
data[BUSINESS_INFO_STEP_KEYS.COMPANY_STATE],
68+
data[BUSINESS_INFO_STEP_KEYS.COMPANY_COUNTRY_CODE],
69+
)
2670
) {
2771
return 2;
2872
}
2973

30-
if (data[BUSINESS_INFO_STEP_KEYS.BUSINESS_CONTACT_NUMBER] === '' || data[BUSINESS_INFO_STEP_KEYS.BUSINESS_CONFIRMATION_EMAIL] === '') {
74+
if (isMissingValidBusinessContactInformation(data[BUSINESS_INFO_STEP_KEYS.BUSINESS_CONTACT_NUMBER], data[BUSINESS_INFO_STEP_KEYS.BUSINESS_CONFIRMATION_EMAIL])) {
3175
return 3;
3276
}
3377

34-
if (data[BUSINESS_INFO_STEP_KEYS.BUSINESS_REGISTRATION_INCORPORATION_NUMBER] === '') {
78+
if (isMissingValidRegistrationNumber(data[BUSINESS_INFO_STEP_KEYS.BUSINESS_REGISTRATION_INCORPORATION_NUMBER], data[BUSINESS_INFO_STEP_KEYS.COMPANY_COUNTRY_CODE])) {
3579
return 4;
3680
}
3781

38-
if (data[BUSINESS_INFO_STEP_KEYS.TAX_ID_EIN_NUMBER] === '') {
82+
if (isMissingValidTaxIDEINNumber(data[BUSINESS_INFO_STEP_KEYS.TAX_ID_EIN_NUMBER], data[BUSINESS_INFO_STEP_KEYS.COMPANY_COUNTRY_CODE])) {
3983
return 5;
4084
}
4185

0 commit comments

Comments
 (0)