From b352a4c6995a5c884491aff413aec51cf84a2b15 Mon Sep 17 00:00:00 2001 From: chanwoo7 Date: Thu, 3 Sep 2026 08:33:51 +0900 Subject: [PATCH] =?UTF-8?q?fix(conversation):=20=EB=AF=B8=EB=A6=AC?= =?UTF-8?q?=EB=B3=B4=EA=B8=B0=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EB=B3=B5?= =?UTF-8?q?=EC=9B=90=20=EC=88=9C=EC=84=9C=20=EC=88=98=EC=A0=95=20(CodeQL?= =?UTF-8?q?=20js/double-escaping)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeQL 알럿 #7 해소: stripHtmlToPreview가 &를 </>보다 먼저 복원해 "&lt;" 같은 이중 이스케이프 입력이 두 번 풀려 "<"가 되던 문제. & 복원을 마지막으로 이동(단일 복원 보장) + 회귀 spec 추가. 미리보기는 plain text 표기라 실행 위험은 없었으나 표기 왜곡 해소. --- ...conversation-center-mappers.helper.spec.ts | 4 ++++ .../conversation-center-mappers.helper.ts | 20 +++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/features/conversation/services/conversation-center-mappers.helper.spec.ts b/src/features/conversation/services/conversation-center-mappers.helper.spec.ts index ea83c53..ee48c4a 100644 --- a/src/features/conversation/services/conversation-center-mappers.helper.spec.ts +++ b/src/features/conversation/services/conversation-center-mappers.helper.spec.ts @@ -18,6 +18,10 @@ describe('conversation-center-mappers.helper', () => { 'A & B <3>', ); }); + + it('이중 이스케이프는 한 번만 복원한다(&lt; → <)', () => { + expect(stripHtmlToPreview('&lt;b&gt;')).toBe('<b>'); + }); }); describe('toLastMessagePreview', () => { diff --git a/src/features/conversation/services/conversation-center-mappers.helper.ts b/src/features/conversation/services/conversation-center-mappers.helper.ts index 6c2ea16..fc40ddc 100644 --- a/src/features/conversation/services/conversation-center-mappers.helper.ts +++ b/src/features/conversation/services/conversation-center-mappers.helper.ts @@ -6,14 +6,18 @@ * 공백 정리로 충분하다(저장 원문은 그대로 유지). */ export function stripHtmlToPreview(html: string): string { - return html - .replace(/<[^>]*>/g, ' ') - .replace(/ /g, ' ') - .replace(/&/g, '&') - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/\s+/g, ' ') - .trim(); + return ( + html + .replace(/<[^>]*>/g, ' ') + .replace(/ /g, ' ') + .replace(/</g, '<') + .replace(/>/g, '>') + // & 복원은 반드시 마지막 — 먼저 풀면 "&lt;" 같은 이중 + // 이스케이프가 두 번 풀려 "<"가 된다 (CodeQL js/double-escaping) + .replace(/&/g, '&') + .replace(/\s+/g, ' ') + .trim() + ); } /** 마지막 메시지 row → 미리보기 텍스트. TEXT는 원문, HTML은 태그 제거. */