From fabbca64e94ab30bbbd06be5caae47fe20d0935a Mon Sep 17 00:00:00 2001 From: hyungi Date: Mon, 15 Jun 2026 15:06:58 +0900 Subject: [PATCH] =?UTF-8?q?feat(markdown):=20=EC=99=B8=EB=B6=80=20?= =?UTF-8?q?=EB=A7=81=ED=81=AC=20=EC=83=88=20=ED=83=AD=20+=20rel=3Dnoopener?= =?UTF-8?q?=20noreferrer=20(P0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docMarked link 렌더러: http/https 링크에 target=_blank rel=noopener noreferrer (탭내빙 차단, 코퍼스 521건). 내부/'#'프래그먼트/상대/mailto 는 무손 — outline gfmHeadingId 경로 유지(클릭 인터셉터 없음=충돌 0). marked15 토큰객체 시그니처. SANITIZE_OPTS ADD_ATTR 에 target/rel. load-bearing 게이트: 상대 .md=코퍼스 0건·doc_key 부재 → path→id prop/document_links 미구현(dead). [[..]]=13건 대부분 인용 노이즈([[3\]]) → resolution/스트립 미구현. 외부 링크 하드닝만 정당화됨. Co-Authored-By: Claude Opus 4.8 (1M context) --- frontend/src/lib/utils/docMarkdown.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/frontend/src/lib/utils/docMarkdown.ts b/frontend/src/lib/utils/docMarkdown.ts index 9af6d30..cbf7173 100644 --- a/frontend/src/lib/utils/docMarkdown.ts +++ b/frontend/src/lib/utils/docMarkdown.ts @@ -65,6 +65,19 @@ docMarked.use({ `` ); }, + // 외부 링크(http/https) → 새 탭 + rel=noopener noreferrer (탭내빙 차단). 521건 실재. + // 내부/프래그먼트/상대 링크는 손대지 않음 — `#` anchor 는 gfmHeadingId/outline 경로 유지 + // (클릭 인터셉터 없음 → 충돌 0), 상대 .md(코퍼스 0건)는 기본 동작(inert). marked 15 토큰객체 시그니처. + link(token: any): string { + const href = (token?.href ?? '') as string; + const text = this.parser.parseInline(token?.tokens ?? []); + const titleAttr = token?.title ? ` title="${escAttr(token.title as string)}"` : ''; + const safeHref = escAttr(href); + if (/^https?:\/\//i.test(href)) { + return `${text}`; + } + return `${text}`; + }, }, }); @@ -82,6 +95,8 @@ const SANITIZE_OPTS = { 'data-md-image-internal', 'data-md-image-alt', 'loading', + 'target', + 'rel', ], ADD_TAGS: ['figure', 'figcaption'], FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'link', 'meta'],