From a92563509bebc6e2a29ccc19be6f7fc36711ab3e Mon Sep 17 00:00:00 2001 From: Moyasee Date: Fri, 3 Oct 2025 15:52:40 +0300 Subject: [PATCH] Fix: fixed zalgo text + html formatting inside of the review message --- .../game-details/game-details-content.tsx | 10 ++++- .../src/pages/game-details/game-details.scss | 8 ++++ src/shared/html-sanitizer.ts | 41 +++++++++++++++++++ src/shared/index.ts | 1 + 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/shared/html-sanitizer.ts diff --git a/src/renderer/src/pages/game-details/game-details-content.tsx b/src/renderer/src/pages/game-details/game-details-content.tsx index a66971b6..07ffcd10 100644 --- a/src/renderer/src/pages/game-details/game-details-content.tsx +++ b/src/renderer/src/pages/game-details/game-details-content.tsx @@ -15,6 +15,7 @@ import { EditGameModal, DeleteReviewModal } from "./modals"; import { ReviewSortOptions } from "./review-sort-options"; import { ReviewPromptBanner } from "./review-prompt-banner"; +import { sanitizeHtml } from "@shared"; import { useTranslation } from "react-i18next"; import { cloudSyncContext, gameDetailsContext } from "@renderer/context"; import { AuthPage } from "@shared"; @@ -123,7 +124,12 @@ export function GameDetailsContent() { // Tiptap editor for review input const editor = useEditor({ - extensions: [StarterKit], + extensions: [ + StarterKit.configure({ + // Disable link extension to prevent automatic link rendering and XSS + link: false, + }), + ], content: "", editorProps: { attributes: { @@ -739,7 +745,7 @@ export function GameDetailsContent() {
diff --git a/src/renderer/src/pages/game-details/game-details.scss b/src/renderer/src/pages/game-details/game-details.scss index 13847f55..dd479ab1 100644 --- a/src/renderer/src/pages/game-details/game-details.scss +++ b/src/renderer/src/pages/game-details/game-details.scss @@ -200,6 +200,8 @@ $hero-height: 300px; border-radius: 6px; padding: calc(globals.$spacing-unit * 2); margin-bottom: calc(globals.$spacing-unit * 2); + overflow: hidden; + word-wrap: break-word; } &__review-header { @@ -233,6 +235,7 @@ $hero-height: 300px; color: rgba(255, 255, 255, 0.9); font-size: globals.$small-font-size; font-weight: 600; + display: inline-flex; &--clickable { cursor: pointer; @@ -383,6 +386,11 @@ $hero-height: 300px; &__review-content { color: globals.$body-color; line-height: 1.5; + word-wrap: break-word; + word-break: break-word; + overflow-wrap: break-word; + white-space: pre-wrap; + max-width: 100%; } &__reviews-loading { diff --git a/src/shared/html-sanitizer.ts b/src/shared/html-sanitizer.ts new file mode 100644 index 00000000..55af55aa --- /dev/null +++ b/src/shared/html-sanitizer.ts @@ -0,0 +1,41 @@ +function removeZalgoText(text: string): string { + const zalgoRegex = /[\u0300-\u036F\u1AB0-\u1AFF\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/g; + + return text.replace(zalgoRegex, ''); +} + +export function sanitizeHtml(html: string): string { + if (!html || typeof html !== 'string') { + return ''; + } + + let cleanText = html.replace(/<[^>]*>/g, ''); + + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = cleanText; + cleanText = tempDiv.textContent || tempDiv.innerText || ''; + + cleanText = removeZalgoText(cleanText); + + cleanText = cleanText.replace(/\s+/g, ' ').trim(); + + if (!cleanText || cleanText.length === 0) { + return ''; + } + + return cleanText; +} + +export function stripHtml(html: string): string { + if (!html || typeof html !== 'string') { + return ''; + } + + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = html; + let cleanText = tempDiv.textContent || tempDiv.innerText || ''; + + cleanText = removeZalgoText(cleanText); + + return cleanText; +} \ No newline at end of file diff --git a/src/shared/index.ts b/src/shared/index.ts index 000ffd22..9a4b4516 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -19,6 +19,7 @@ import { format } from "date-fns"; import { AchievementNotificationInfo } from "@types"; export * from "./constants"; +export * from "./html-sanitizer"; export class UserNotLoggedInError extends Error { constructor() {