Skip to content

Commit 2392a4f

Browse files
Sora4431claude
andcommitted
fix(web): sanitize-html に明示的 allowlist を追加 + メモ化でパフォーマンス改善
- 許可タグ・属性・URLスキームを明示的に指定し、ライブラリのデフォルト 変更によるセキュリティ後退を防止 - 入力文字列ベースのキャッシュ(最大200件)で再レンダリング時の sanitize コストを削減 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ef32a0b commit 2392a4f

1 file changed

Lines changed: 34 additions & 1 deletion

File tree

  • apps/web/src/lib/themes/standard-seasons/shared/utils

apps/web/src/lib/themes/standard-seasons/shared/utils/markdown.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,42 @@ marked.setOptions({
77
gfm: true,
88
});
99

10+
const sanitizeOptions: sanitizeHtml.IOptions = {
11+
allowedTags: [
12+
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
13+
'p', 'br', 'hr',
14+
'ul', 'ol', 'li',
15+
'strong', 'em', 'del', 'code', 'pre',
16+
'blockquote',
17+
'a',
18+
'table', 'thead', 'tbody', 'tr', 'th', 'td',
19+
'input',
20+
],
21+
allowedAttributes: {
22+
a: ['href', 'target', 'rel'],
23+
input: ['type', 'checked', 'disabled'],
24+
},
25+
allowedSchemes: ['http', 'https', 'mailto'],
26+
};
27+
28+
const cache = new Map<string, string>();
29+
1030
export function renderMarkdown(memoOrNotes: string | null | undefined): string {
1131
const text = getMemoText(memoOrNotes);
1232
if (!text) return '';
33+
34+
const cached = cache.get(text);
35+
if (cached) return cached;
36+
1337
const raw = marked.parse(text, { async: false }) as string;
14-
return sanitizeHtml(raw);
38+
const result = sanitizeHtml(raw, sanitizeOptions);
39+
40+
// キャッシュサイズを制限(LRU 的に古いものから削除)
41+
if (cache.size > 200) {
42+
const firstKey = cache.keys().next().value!;
43+
cache.delete(firstKey);
44+
}
45+
cache.set(text, result);
46+
47+
return result;
1548
}

0 commit comments

Comments
 (0)