From 5e9866cde9147702022ced0c3db90d8df8ac6448 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Fri, 17 Jan 2025 18:05:50 +0100
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20data=20sanitation=20to=20the?=
=?UTF-8?q?=20Redux=20store?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/data/styling/hooks.js | 74 +++++++++++++++----
.../resources/js/data/styling/reducer.js | 29 +++++++-
2 files changed, 87 insertions(+), 16 deletions(-)
diff --git a/modules/ppcp-settings/resources/js/data/styling/hooks.js b/modules/ppcp-settings/resources/js/data/styling/hooks.js
index e50896ea3..ffcde584f 100644
--- a/modules/ppcp-settings/resources/js/data/styling/hooks.js
+++ b/modules/ppcp-settings/resources/js/data/styling/hooks.js
@@ -83,52 +83,86 @@ export const useLocationProps = ( location ) => {
const { getLocationProp, setLocationProp } = useHooks();
const details = STYLING_LOCATIONS[ location ] ?? {};
+ const sanitize = ( value ) => ( undefined === value ? true : !! value );
+
return {
choices: Object.values( STYLING_LOCATIONS ),
details,
- isActive: getLocationProp( location, 'enabled' ),
- setActive: ( state ) => setLocationProp( location, 'enabled', state ),
+ isActive: sanitize( getLocationProp( location, 'enabled' ) ),
+ setActive: ( state ) =>
+ setLocationProp( location, 'enabled', sanitize( state ) ),
};
};
export const usePaymentMethodProps = ( location ) => {
const { getLocationProp, setLocationProp } = useHooks();
+ const sanitize = ( value ) => {
+ if ( Array.isArray( value ) ) {
+ return value;
+ }
+ return value ? [ value ] : [];
+ };
+
return {
choices: Object.values( STYLING_PAYMENT_METHODS ),
- paymentMethods: getLocationProp( location, 'methods' ),
+ paymentMethods: sanitize( getLocationProp( location, 'methods' ) ),
setPaymentMethods: ( methods ) =>
- setLocationProp( location, 'methods', methods ),
+ setLocationProp( location, 'methods', sanitize( methods ) ),
};
};
export const useColorProps = ( location ) => {
const { getLocationProp, setLocationProp } = useHooks();
+ const sanitize = ( value ) => {
+ const isValidColor = Object.values( STYLING_COLORS ).some(
+ ( color ) => color.value === value
+ );
+ return isValidColor ? value : STYLING_COLORS.gold.value;
+ };
+
return {
choices: Object.values( STYLING_COLORS ),
- color: getLocationProp( location, 'color' ),
- setColor: ( color ) => setLocationProp( location, 'color', color ),
+ color: sanitize( getLocationProp( location, 'color' ) ),
+ setColor: ( color ) =>
+ setLocationProp( location, 'color', sanitize( color ) ),
};
};
export const useShapeProps = ( location ) => {
const { getLocationProp, setLocationProp } = useHooks();
+ const sanitize = ( value ) => {
+ const isValidColor = Object.values( STYLING_SHAPES ).some(
+ ( color ) => color.value === value
+ );
+ return isValidColor ? value : STYLING_SHAPES.rect.value;
+ };
+
return {
choices: Object.values( STYLING_SHAPES ),
- shape: getLocationProp( location, 'shape' ),
- setShape: ( shape ) => setLocationProp( location, 'shape', shape ),
+ shape: sanitize( getLocationProp( location, 'shape' ) ),
+ setShape: ( shape ) =>
+ setLocationProp( location, 'shape', sanitize( shape ) ),
};
};
export const useLabelProps = ( location ) => {
const { getLocationProp, setLocationProp } = useHooks();
+ const sanitize = ( value ) => {
+ const isValidColor = Object.values( STYLING_LABELS ).some(
+ ( color ) => color.value === value
+ );
+ return isValidColor ? value : STYLING_LABELS.paypal.value;
+ };
+
return {
choices: Object.values( STYLING_LABELS ),
- label: getLocationProp( location, 'label' ),
- setLabel: ( label ) => setLocationProp( location, 'label', label ),
+ label: sanitize( getLocationProp( location, 'label' ) ),
+ setLabel: ( label ) =>
+ setLocationProp( location, 'label', sanitize( label ) ),
};
};
@@ -137,11 +171,19 @@ export const useLayoutProps = ( location ) => {
const { details } = useLocationProps( location );
const isAvailable = false !== details.props.layout;
+ const sanitize = ( value ) => {
+ const isValidColor = Object.values( STYLING_LAYOUTS ).some(
+ ( color ) => color.value === value
+ );
+ return isValidColor ? value : STYLING_LAYOUTS.vertical.value;
+ };
+
return {
choices: Object.values( STYLING_LAYOUTS ),
isAvailable,
- layout: getLocationProp( location, 'layout' ),
- setLayout: ( layout ) => setLocationProp( location, 'layout', layout ),
+ layout: sanitize( getLocationProp( location, 'layout' ) ),
+ setLayout: ( layout ) =>
+ setLocationProp( location, 'layout', sanitize( layout ) ),
};
};
@@ -155,10 +197,14 @@ export const useTaglineProps = ( location ) => {
STYLING_LAYOUTS.horizontal.value ===
getLocationProp( location, 'layout' );
+ const sanitize = ( value ) => !! value;
+
return {
isAvailable,
- tagline: isAvailable ? getLocationProp( location, 'tagline' ) : false,
+ tagline: isAvailable
+ ? sanitize( getLocationProp( location, 'tagline' ) )
+ : false,
setTagline: ( tagline ) =>
- setLocationProp( location, 'tagline', tagline ),
+ setLocationProp( location, 'tagline', sanitize( tagline ) ),
};
};
diff --git a/modules/ppcp-settings/resources/js/data/styling/reducer.js b/modules/ppcp-settings/resources/js/data/styling/reducer.js
index 5ce6062a9..db92d646b 100644
--- a/modules/ppcp-settings/resources/js/data/styling/reducer.js
+++ b/modules/ppcp-settings/resources/js/data/styling/reducer.js
@@ -70,6 +70,19 @@ const defaultPersistent = Object.freeze( {
} ),
} );
+const sanitizeLocation = ( oldDetails, newDetails ) => {
+ // Skip if provided details are not a plain object.
+ if (
+ ! newDetails ||
+ 'object' !== typeof newDetails ||
+ Array.isArray( newDetails )
+ ) {
+ return oldDetails;
+ }
+
+ return { ...oldDetails, ...newDetails };
+};
+
// Reducer logic.
const [ setTransient, setPersistent ] = createSetters(
@@ -96,8 +109,20 @@ const reducer = createReducer( defaultTransient, defaultPersistent, {
return cleanState;
},
- [ ACTION_TYPES.HYDRATE ]: ( state, payload ) =>
- setPersistent( state, payload.data ),
+ [ ACTION_TYPES.HYDRATE ]: ( state, payload ) => {
+ const validData = Object.keys( defaultPersistent ).reduce(
+ ( data, location ) => {
+ data[ location ] = sanitizeLocation(
+ state.data[ location ],
+ payload.data[ location ]
+ );
+ return data;
+ },
+ {}
+ );
+
+ return setPersistent( state, validData );
+ },
} );
export default reducer;