diff --git a/web/src/ar/common/utils.ts b/web/src/ar/common/utils.ts index e170d0fe47..0badecb717 100644 --- a/web/src/ar/common/utils.ts +++ b/web/src/ar/common/utils.ts @@ -47,3 +47,5 @@ export function getPackageTypesForApiQueryParams(packageTypes: RepositoryPackage export const encodeFileName = (fileName: string): string => { return fileName.replace(INVALID_FILENAME_CHARS, '_') } + +export { decodeHtmlEntities } from './utils/decodeHtmlEntities' diff --git a/web/src/ar/common/utils/decodeHtmlEntities.ts b/web/src/ar/common/utils/decodeHtmlEntities.ts new file mode 100644 index 0000000000..3e49b1b4ed --- /dev/null +++ b/web/src/ar/common/utils/decodeHtmlEntities.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2024 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Safely decodes HTML entities in a string without XSS vulnerability. + * Uses DOMParser which is safer than innerHTML as it doesn't execute scripts. + * + * @param text - The text containing HTML entities to decode + * @returns The decoded text with HTML entities converted to their character equivalents + * + * @example + * decodeHtmlEntities('github.com/rs/xid') // returns 'github.com/rs/xid' + * decodeHtmlEntities('<script>alert(1)</script>') // returns '' (safe, no execution) + */ +export const decodeHtmlEntities = (text: string): string => { + if (!text || typeof text !== 'string') { + return text + } + + // Use DOMParser which is safer than innerHTML + // It parses the content as text/html but doesn't execute scripts + const parser = new DOMParser() + const doc = parser.parseFromString(text, 'text/html') + + // Extract the text content which automatically decodes entities + return doc.documentElement.textContent || text +} diff --git a/web/src/ar/components/Form/DeleteModalContent.tsx b/web/src/ar/components/Form/DeleteModalContent.tsx index cb97f6222a..6a1ce22185 100644 --- a/web/src/ar/components/Form/DeleteModalContent.tsx +++ b/web/src/ar/components/Form/DeleteModalContent.tsx @@ -30,6 +30,7 @@ interface DeleteModalContentProps { content: string placeholder: string inputLabel: string + inputLabelValue?: string deleteBtnText?: string } @@ -41,9 +42,14 @@ function DeleteModalContent({ content, placeholder, inputLabel, + inputLabelValue, deleteBtnText }: DeleteModalContentProps) { const { getString } = useStrings() + + // Construct the label with the value appended if provided + const finalLabel = inputLabelValue ? `${inputLabel} (${inputLabelValue})` : inputLabel + return ( {content} - +