From d0d077b9a8b3ee4ba1302699922f18f9cced5c38 Mon Sep 17 00:00:00 2001 From: Talha12Shiekh Date: Thu, 30 Jul 2026 23:21:21 +0500 Subject: [PATCH 1/2] fix(security): validate URL search parameter schema before store injection (#2319) --- .../web/src/components/basic/ReactiveBase.js | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/web/src/components/basic/ReactiveBase.js b/packages/web/src/components/basic/ReactiveBase.js index bcf50f7a3..32d6711fb 100644 --- a/packages/web/src/components/basic/ReactiveBase.js +++ b/packages/web/src/components/basic/ReactiveBase.js @@ -137,16 +137,55 @@ class ReactiveBase extends Component { let selectedValues = {}; let urlValues = {}; + const isValidURLValue = (value) => { + if (value === null || value === undefined) return false; + const type = typeof value; + if (type === 'string' || type === 'number' || type === 'boolean') return true; + if (Array.isArray(value)) { + return value.every((item) => { + const itemType = typeof item; + return itemType === 'string' || itemType === 'number' || itemType === 'boolean'; + }); + } + if (type === 'object' && value.constructor === Object) { + return Object.values(value).every((item) => { + const itemType = typeof item; + return itemType === 'string' || itemType === 'number' || itemType === 'boolean' || item === null; + }); + } + return false; + }; + + const isValidParsedParam = (parsedParams) => { + if (parsedParams === null || parsedParams === undefined) return false; + const type = typeof parsedParams; + if (type === 'string' || type === 'number' || type === 'boolean' || Array.isArray(parsedParams)) { + return true; + } + if (type === 'object' && parsedParams.constructor === Object) { + if (Object.prototype.hasOwnProperty.call(parsedParams, 'value')) { + return isValidURLValue(parsedParams.value); + } + return isValidURLValue(parsedParams); + } + return false; + }; + Array.from(params.keys()).forEach((key) => { try { const parsedParams = JSON.parse(params.get(key)); + if (!isValidParsedParam(parsedParams)) { + return; + } const selectedValue = {}; - if (parsedParams.value) { + if (parsedParams && typeof parsedParams === 'object' && Object.prototype.hasOwnProperty.call(parsedParams, 'value')) { selectedValue.value = parsedParams.value; } else { selectedValue.value = parsedParams; } - if (parsedParams.category) selectedValue.category = parsedParams.category; + if (parsedParams && parsedParams.category && typeof parsedParams.category === 'string') { + selectedValue.category = parsedParams.category; + } selectedValue.reference = 'URL'; selectedValues = { ...selectedValues, From 0d6c6aed6dc54c6c46b19742c0aa9d7e74a30a90 Mon Sep 17 00:00:00 2001 From: Talha12Shiekh Date: Thu, 30 Jul 2026 23:35:31 +0500 Subject: [PATCH 2/2] feat(web): add showItemCount and renderTitle props to MultiList and SingleList components --- .../web/src/components/list/MultiList.d.ts | 2 + packages/web/src/components/list/MultiList.js | 37 ++++++++++++++++--- .../web/src/components/list/SingleList.d.ts | 2 + .../web/src/components/list/SingleList.js | 37 ++++++++++++++++--- 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/packages/web/src/components/list/MultiList.d.ts b/packages/web/src/components/list/MultiList.d.ts index 8f2008678..f440616a0 100644 --- a/packages/web/src/components/list/MultiList.d.ts +++ b/packages/web/src/components/list/MultiList.d.ts @@ -33,6 +33,8 @@ export interface MultiList extends CommonProps { sortBy?: types.sortByWithCount; themePreset?: types.themePreset; title?: types.title; + renderTitle?: (...args: any[]) => any; + showItemCount?: boolean; showMissing?: boolean; missingLabel?: string; showLoadMore?: boolean; diff --git a/packages/web/src/components/list/MultiList.js b/packages/web/src/components/list/MultiList.js index 9a933ddbb..79ebf408a 100644 --- a/packages/web/src/components/list/MultiList.js +++ b/packages/web/src/components/list/MultiList.js @@ -459,6 +459,34 @@ class MultiList extends Component { return getComponent(data, this.props); } + renderTitle() { + const { title, renderTitle, showItemCount, innerClass } = this.props; + const itemsCount = this.listItems ? this.listItems.length : 0; + + if (renderTitle) { + return isFunction(renderTitle) + ? renderTitle(itemsCount, this.listItems) + : renderTitle; + } + + if (title) { + return ( + + <span>{title}</span> + {showItemCount && ( + <span + className={getClassName(innerClass, 'item-count') || null} + style={{ marginLeft: '8px' }} + > + {itemsCount} + </span> + )} + + ); + } + return null; + } + render() { const { selectAllLabel, @@ -490,11 +518,7 @@ class MultiList extends Component { const isAllChecked = selectAllLabel ? !!this.state.currentValue[selectAllLabel] : false; return ( - {this.props.title && ( - - {this.props.title} - - )} + {this.renderTitle()} {this.renderSearch()} {this.hasCustomRenderer ? ( this.getComponent() @@ -654,6 +678,8 @@ MultiList.propTypes = { style: types.style, themePreset: types.themePreset, title: types.title, + renderTitle: types.title, + showItemCount: types.bool, URLParams: types.bool, showMissing: types.bool, missingLabel: types.string, @@ -670,6 +696,7 @@ MultiList.defaultProps = { showCheckbox: true, showCount: true, showSearch: true, + showItemCount: false, size: 100, sortBy: 'count', style: {}, diff --git a/packages/web/src/components/list/SingleList.d.ts b/packages/web/src/components/list/SingleList.d.ts index cc443dea3..ef5173d9e 100644 --- a/packages/web/src/components/list/SingleList.d.ts +++ b/packages/web/src/components/list/SingleList.d.ts @@ -32,6 +32,8 @@ export interface SingleList extends CommonProps { sortBy?: types.sortByWithCount; themePreset?: types.themePreset; title?: types.title; + renderTitle?: (...args: any[]) => any; + showItemCount?: boolean; showMissing?: boolean; missingLabel?: string; showLoadMore?: boolean; diff --git a/packages/web/src/components/list/SingleList.js b/packages/web/src/components/list/SingleList.js index 6ad88fd8c..45bc4289d 100644 --- a/packages/web/src/components/list/SingleList.js +++ b/packages/web/src/components/list/SingleList.js @@ -354,6 +354,34 @@ class SingleList extends Component { return getComponent(data, this.props); } + renderTitle() { + const { title, renderTitle, showItemCount, innerClass } = this.props; + const itemsCount = this.listItems ? this.listItems.length : 0; + + if (renderTitle) { + return isFunction(renderTitle) + ? renderTitle(itemsCount, this.listItems) + : renderTitle; + } + + if (title) { + return ( + + <span>{title}</span> + {showItemCount && ( + <span + className={getClassName(innerClass, 'item-count') || null} + style={{ marginLeft: '8px' }} + > + {itemsCount} + </span> + )} + + ); + } + return null; + } + render() { const { selectAllLabel, @@ -387,11 +415,7 @@ class SingleList extends Component { return ( - {this.props.title && ( - - {this.props.title} - - )} + {this.renderTitle()} {this.renderSearch()} {this.hasCustomRenderer ? ( this.getComponent() @@ -552,6 +576,8 @@ SingleList.propTypes = { index: types.string, enableStrictSelection: types.bool, endpoint: types.endpoint, + renderTitle: types.title, + showItemCount: types.bool, }; SingleList.defaultProps = { @@ -561,6 +587,7 @@ SingleList.defaultProps = { showFilter: true, showRadio: true, showSearch: true, + showItemCount: false, size: 100, sortBy: 'count', style: {},