세션·로그인 제한, ZIP/업로드 스트리밍, CSP 최소 강제와 Report-Only, Playwright 격리 검증을 추가하고 미디어 목록을 reconcile 후 database 모드로 고정한다. Co-authored-by: Cursor <cursoragent@cursor.com>
42 lines
1.5 KiB
JavaScript
42 lines
1.5 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
import { sanitizeHtmlBlock } from '../lib/markdown-html-block.js'
|
|
|
|
/**
|
|
* 실행성 HTML 우회 입력을 제거하는지 검증한다.
|
|
* @returns {void}
|
|
*/
|
|
const testDangerousHtmlRemoval = () => {
|
|
const attacks = [
|
|
'<img src="x" onerror="alert(1)">',
|
|
'<a href="javascript:alert(1)">링크</a>',
|
|
'<svg><a xlink:href="javascript:alert(1)">위험</a></svg>',
|
|
'<script><img src=x onerror=alert(1)></script>',
|
|
'<div style="background:url(javascript:alert(1))" onclick=alert(1)>본문</div>',
|
|
'<iframe srcdoc="<script>alert(1)</script>"></iframe>'
|
|
]
|
|
|
|
for (const attack of attacks) {
|
|
const sanitized = sanitizeHtmlBlock(attack)
|
|
assert.doesNotMatch(sanitized, /onerror|onclick|javascript:|<script|<iframe|<svg|style=/i)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 허용된 본문 HTML과 새 창 링크 보정을 검증한다.
|
|
* @returns {void}
|
|
*/
|
|
const testSafeHtmlPreservation = () => {
|
|
const sanitized = sanitizeHtmlBlock(
|
|
'<table><tr><th scope="col">제목</th><td colspan="2">본문</td></tr></table>'
|
|
+ '<a href="https://example.com" target="_blank">외부 링크</a>'
|
|
)
|
|
|
|
assert.match(sanitized, /<table>/)
|
|
assert.match(sanitized, /scope="col"/)
|
|
assert.match(sanitized, /rel="noopener noreferrer"/)
|
|
}
|
|
|
|
test('HTML 블록의 실행성 태그·속성·URL을 제거한다', testDangerousHtmlRemoval)
|
|
test('HTML 블록의 안전한 표와 링크를 보존한다', testSafeHtmlPreservation)
|