Fix: fixed zalgo text + html formatting inside of the review message

This commit is contained in:
Moyasee
2025-10-03 15:52:40 +03:00
parent 899f68318f
commit a92563509b
4 changed files with 58 additions and 2 deletions

View File

@@ -15,6 +15,7 @@ import { EditGameModal, DeleteReviewModal } from "./modals";
import { ReviewSortOptions } from "./review-sort-options"; import { ReviewSortOptions } from "./review-sort-options";
import { ReviewPromptBanner } from "./review-prompt-banner"; import { ReviewPromptBanner } from "./review-prompt-banner";
import { sanitizeHtml } from "@shared";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { cloudSyncContext, gameDetailsContext } from "@renderer/context"; import { cloudSyncContext, gameDetailsContext } from "@renderer/context";
import { AuthPage } from "@shared"; import { AuthPage } from "@shared";
@@ -123,7 +124,12 @@ export function GameDetailsContent() {
// Tiptap editor for review input // Tiptap editor for review input
const editor = useEditor({ const editor = useEditor({
extensions: [StarterKit], extensions: [
StarterKit.configure({
// Disable link extension to prevent automatic link rendering and XSS
link: false,
}),
],
content: "", content: "",
editorProps: { editorProps: {
attributes: { attributes: {
@@ -739,7 +745,7 @@ export function GameDetailsContent() {
<div <div
className="game-details__review-content" className="game-details__review-content"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: review.reviewHtml, __html: sanitizeHtml(review.reviewHtml),
}} }}
/> />
<div className="game-details__review-actions"> <div className="game-details__review-actions">

View File

@@ -200,6 +200,8 @@ $hero-height: 300px;
border-radius: 6px; border-radius: 6px;
padding: calc(globals.$spacing-unit * 2); padding: calc(globals.$spacing-unit * 2);
margin-bottom: calc(globals.$spacing-unit * 2); margin-bottom: calc(globals.$spacing-unit * 2);
overflow: hidden;
word-wrap: break-word;
} }
&__review-header { &__review-header {
@@ -233,6 +235,7 @@ $hero-height: 300px;
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.9);
font-size: globals.$small-font-size; font-size: globals.$small-font-size;
font-weight: 600; font-weight: 600;
display: inline-flex;
&--clickable { &--clickable {
cursor: pointer; cursor: pointer;
@@ -383,6 +386,11 @@ $hero-height: 300px;
&__review-content { &__review-content {
color: globals.$body-color; color: globals.$body-color;
line-height: 1.5; 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 { &__reviews-loading {

View File

@@ -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;
}

View File

@@ -19,6 +19,7 @@ import { format } from "date-fns";
import { AchievementNotificationInfo } from "@types"; import { AchievementNotificationInfo } from "@types";
export * from "./constants"; export * from "./constants";
export * from "./html-sanitizer";
export class UserNotLoggedInError extends Error { export class UserNotLoggedInError extends Error {
constructor() { constructor() {