commit e85915661698925977c6622d11b6f242fea84d11
Author: Vaclav Honzik <vaclavhonzik98@gmail.com>
Date:   Sun May 1 23:27:39 2022 +0200

    DomPurify + Escaping Impl
    
    re #9626

diff --git a/frontend/package.json b/frontend/package.json
index 9d62c32..e1d1c3a 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -11,12 +11,14 @@
     "@mui/material": "^5.5.2",
     "@reduxjs/toolkit": "^1.8.1",
     "axios": "^0.26.0",
+    "dompurify": "^2.3.6",
     "dotenv": "^16.0.0",
     "formik": "^2.2.9",
     "jwt-decode": "^3.1.2",
     "leaflet": "^1.8.0",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
+    "react-html-parser": "^2.0.2",
     "react-leaflet": "3.2.5",
     "react-leaflet-textpath": "^2.1.1",
     "react-redux": "^7.2.6",
@@ -60,6 +62,7 @@
     "@testing-library/jest-dom": "^5.14.1",
     "@testing-library/react": "^12.0.0",
     "@testing-library/user-event": "^13.2.1",
+    "@types/dompurify": "^2.3.3",
     "@types/jest": "^27.0.1",
     "@types/leaflet": "^1.7.9",
     "@types/node": "^16.7.13",
diff --git a/frontend/src/features/Catalog/CatalogItemDetail.tsx b/frontend/src/features/Catalog/CatalogItemDetail.tsx
index cf7effa..41645b2 100644
--- a/frontend/src/features/Catalog/CatalogItemDetail.tsx
+++ b/frontend/src/features/Catalog/CatalogItemDetail.tsx
@@ -1,10 +1,4 @@
-import {
-    Button,
-    Divider,
-    Grid,
-    Paper,
-    Typography,
-} from '@mui/material'
+import { Button, Divider, Grid, Paper, Typography } from '@mui/material'
 import { Fragment, useEffect, useState } from 'react'
 import { useParams } from 'react-router-dom'
 import axiosInstance from '../../api/api'
@@ -14,6 +8,8 @@ import ContentLoading from '../Reusables/ContentLoading'
 import CatalogItemMap from './CatalogItemMap'
 import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'
 import { Link as RouterLink } from 'react-router-dom'
+import DOMPurify from 'dompurify'
+import { formatHtmlStringToReactDom } from '../../utils/formatting/HtmlUtils'
 
 const apiError =
     'Error while fetching data from the server, please try again later.'
@@ -117,7 +113,6 @@ const CatalogItemDetail = () => {
             <ShowErrorIfPresent err={err} />
 
             <Paper style={{ minHeight: '100vh' }} variant="outlined">
-
                 {isItemLoading && !err ? <ContentLoading /> : null}
                 {!isItemLoading && item ? (
                     <Grid container justifyContent="space-around">
diff --git a/frontend/src/features/Catalog/CatalogTable.tsx b/frontend/src/features/Catalog/CatalogTable.tsx
index b647157..898bfbe 100644
--- a/frontend/src/features/Catalog/CatalogTable.tsx
+++ b/frontend/src/features/Catalog/CatalogTable.tsx
@@ -23,6 +23,7 @@ import {
     ShowAllItemsOption,
 } from './catalogSlice'
 import { fetchItems } from './catalogThunks'
+import { formatHtmlStringToReactDom } from '../../utils/formatting/HtmlUtils'
 
 // Catalog table component
 const CatalogTable = () => {
@@ -94,7 +95,7 @@ const CatalogTable = () => {
                     ...textStyle,
                 }}
             >
-                {value || 'N/A'}
+                {formatHtmlStringToReactDom(value as string)}
             </Typography>
         </TableCell>
     )
diff --git a/frontend/src/utils/formatting/HtmlUtils.tsx b/frontend/src/utils/formatting/HtmlUtils.tsx
new file mode 100644
index 0000000..449c4c5
--- /dev/null
+++ b/frontend/src/utils/formatting/HtmlUtils.tsx
@@ -0,0 +1,15 @@
+import DOMPurify from 'dompurify'
+import { ReactNode } from 'react'
+
+/**
+ * Formats HTML string to safe virtual DOM nodes.
+ * @param htmlString string to format
+ */
+export const formatHtmlStringToReactDom = (htmlString: string, defaultValue: string = '-'): ReactNode => {
+    if (!htmlString || htmlString.length === 0) {
+        return defaultValue
+    }
+    return (
+        <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(htmlString) }} />
+    )
+}
diff --git a/frontend/src/utils/statePersistence.ts b/frontend/src/utils/statePersistence.ts
deleted file mode 100644
index 69cbe48..0000000
--- a/frontend/src/utils/statePersistence.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-// TODO this could be replaced with redux library
-const persist = (name: string, value: any) => {
-    localStorage.setItem(name, JSON.stringify(value))
-    return value
-}
-
-const load = (name: string) => {
-    const item = localStorage.getItem(name)
-    return item ? JSON.parse(item) : undefined
-}
-
-export { persist, load }
diff --git a/frontend/src/utils/thunkResponse.ts b/frontend/src/utils/thunkResponse.ts
deleted file mode 100644
index 400726e..0000000
--- a/frontend/src/utils/thunkResponse.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-
-export default interface ThunkResponse<T> {
-    success: boolean
-    data?: T
-}
\ No newline at end of file
