Feature/detect unsupported sf fields (#2495)

* detect unrecognised data-cse attributes and don't create a SF for them

* Added unit test for new functionality

* added changeset
This commit is contained in:
sponglord
2023-12-20 12:51:12 +01:00
committed by GitHub
parent 952ae27819
commit a62f32ae06
6 changed files with 44 additions and 9 deletions

View File

@@ -0,0 +1,5 @@
---
'@adyen/adyen-web': patch
---
Detect when the value of a data-cse attribute is not supported, and don't create a SF for it

View File

@@ -47,7 +47,7 @@ describe("Testing setupSecuredField's createCardSecuredFields functionality", ()
SecuredFieldMock.mockClear();
});
test("setupSecuredField's createCardSecuredFields function should call the onBrand callback", async () => {
test("setupSecuredField's createCardSecuredFields function, as a single-branded card, should call the onBrand callback", async () => {
myCSF.state.type = 'mc';
myCSF.isSingleBrandedCard = true;

View File

@@ -1,4 +1,4 @@
import { setupSecuredField } from './createSecuredFields';
import { createSecuredFields, setupSecuredField } from './createSecuredFields';
import { DATA_ENCRYPTED_FIELD_ATTR, ENCRYPTED_CARD_NUMBER, ENCRYPTED_EXPIRY_DATE, SF_CONFIG_TIMEOUT } from '../../configuration/constants';
import { SecuredFields } from '../../types';
import Language from '../../../../../../language';
@@ -18,12 +18,14 @@ let MySecuredField;
const myCSF = {
state: { type: 'card', hasSeparateDateFields: null, securedFields: {} as SecuredFields, iframeCount: 0, originalNumIframes: 2, numIframes: 2 },
config: { shouldDisableIOSArrowKeys: null },
props: { i18n: new Language('en-US', {}) },
props: { rootNode: null, i18n: new Language('en-US', {}) },
callbacks: {
onLoad: jest.fn(() => {}),
onTouchstartIOS: jest.fn(() => {})
},
createSecuredFields,
setupSecuredField,
createNonCardSecuredFields: jest.fn(() => {}),
encryptedAttrName: DATA_ENCRYPTED_FIELD_ATTR,
destroySecuredFields: jest.fn(() => {
console.log('### createSecuredFields.test::calling destroySecuredFields:: ');
@@ -52,6 +54,7 @@ const dummyObj = { foo: 'bar' };
describe('Testing CSFs setupSecuredField functionality', () => {
beforeEach(() => {
console.log = jest.fn(() => {});
console.warn = jest.fn(() => {});
MySecuredField = {
fieldType: ENCRYPTED_CARD_NUMBER,
@@ -108,6 +111,23 @@ describe('Testing CSFs setupSecuredField functionality', () => {
SecuredFieldMock.mockClear();
});
test('Calling setupSecuredField with an unsupported value for the data-cse attribute should see that a SF is not created and that a warning is given', () => {
const rootNode = document.createElement('div');
const unsupportedEl = makeDiv('encryptedCustomField');
rootNode.appendChild(unsupportedEl);
myCSF.props.rootNode = rootNode;
const numIframes: number = myCSF.createSecuredFields();
expect(numIframes).toEqual(0);
expect(console.warn).toBeCalledWith(
`WARNING: 'encryptedCustomField' is not a valid type for the '${DATA_ENCRYPTED_FIELD_ATTR}' attribute. A SecuredField will not be created for this element.`
);
expect(myCSF.createNonCardSecuredFields).toBeCalledWith([]);
});
test('Calling setupSecuredField to see that an "encryptedCardNumber" SF is created and stored in state', () => {
myCSF.setupSecuredField(makeDiv(ENCRYPTED_CARD_NUMBER));

View File

@@ -6,7 +6,8 @@ import {
DATA_ENCRYPTED_FIELD_ATTR,
DATA_INFO,
DATA_UID,
SF_CONFIG_TIMEOUT
SF_CONFIG_TIMEOUT,
ALL_SECURED_FIELDS
} from '../../configuration/constants';
import { existy } from '../../utilities/commonUtils';
import cardType from '../utils/cardType';
@@ -22,8 +23,17 @@ import AdyenCheckoutError from '../../../../../../core/Errors/AdyenCheckoutError
export function createSecuredFields(): number {
this.encryptedAttrName = DATA_ENCRYPTED_FIELD_ATTR;
// Detect DOM elements that qualify as securedField holders
const securedFields: HTMLElement[] = select(this.props.rootNode, `[${this.encryptedAttrName}]`);
// Detect DOM elements that qualify as securedField holders & filter them for valid types
const securedFields: HTMLElement[] = select(this.props.rootNode, `[${this.encryptedAttrName}]`).filter(field => {
const fieldType: string = getAttribute(field, this.encryptedAttrName);
const isValidType = ALL_SECURED_FIELDS.includes(fieldType);
if (!isValidType) {
console.warn(
`WARNING: '${fieldType}' is not a valid type for the '${this.encryptedAttrName}' attribute. A SecuredField will not be created for this element.`
);
}
return isValidType;
});
/**
* cvcPolicy - 'required' | 'optional' | 'hidden'

View File

@@ -8,7 +8,7 @@ import '../../style.scss';
import { MockReactApp } from './MockReactApp';
import { searchFunctionExample } from '../../utils';
const onlyShowCard = true;
const onlyShowCard = false;
const showComps = {
clickToPay: true,
@@ -53,7 +53,7 @@ getPaymentMethods({ amount, shopperLocale }).then(async paymentMethodsResponse =
// Stored Card
if (!onlyShowCard && showComps.storedCard) {
if (checkout.paymentMethodsResponse.storedPaymentMethods && checkout.paymentMethodsResponse.storedPaymentMethods.length > 0) {
const storedCardData = checkout.paymentMethodsResponse.storedPaymentMethods[2];
const storedCardData = checkout.paymentMethodsResponse.storedPaymentMethods[0];
window.storedCard = checkout
.create('card', {
...storedCardData,

View File

@@ -227,7 +227,7 @@ function handlePaymentResult(result, component) {
function startPayment(component) {
if (!component.isValid) return component.showValidation();
const allow3DS2 = paymentsConfig.additionalData.allow3DS2 || false;
const allow3DS2 = paymentsConfig.additionalData?.allow3DS2 || false;
const riskdata = checkout.modules.risk.data;