Files
web-temp/layers/plugins/dompurify-html.ts
“hyeonggkim” 87b1ca0db1 feat: CtLayout01 커스텀 콘텐츠 기능 개선
- fnCustomVideo 유튜브 팝업 함수 추가
- script/link exclude 속성 지원
- 전역 함수 등록 로직 통합 (registerGlobalFunctions)
- CSS Selector injection 보안 취약점 수정
- 사용되지 않는 변수/props 제거
- DOMPurify exclude, defer 속성 허용

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 19:21:19 +09:00

166 lines
3.1 KiB
TypeScript

// @ts-ignore
import DOMPurify from 'dompurify'
import type { DirectiveBinding } from 'vue'
// DOMPurify 설정 타입
interface DOMPurifyConfig {
ALLOWED_TAGS: string[]
ALLOWED_ATTR: string[]
}
// 공통 허용 태그
const commonTags = [
'br',
'div',
'b',
'strong',
'i',
'em',
'u',
'a',
'p',
'ul',
'li',
's',
'ol',
'button',
'span',
'img',
'pre',
'input',
'dl',
'dt',
'dd',
'table',
'thead',
'tbody',
'tfoot',
'video',
'source',
'tr',
'th',
'td',
'section',
] as const
// 공통 허용 속성
const commonAttrs = [
'style',
'id',
'href',
'src',
'target',
'alt',
'class',
'width',
'height',
'frameborder',
'colspan',
'rowspan',
'scope',
'type',
'autoplay',
'muted',
'loop',
'playsinline',
'controls',
'preload',
'poster',
] as const
// 기본 설정 (일반 사용자용)
const defaultConfig: DOMPurifyConfig = {
ALLOWED_TAGS: [...commonTags],
ALLOWED_ATTR: [...commonAttrs],
}
// 관리자용 설정 (확장된 태그 및 속성)
const adminConfig: DOMPurifyConfig = {
ALLOWED_TAGS: [
...commonTags,
'iframe',
'link',
'style',
'script',
'blockquote',
],
ALLOWED_ATTR: [
...commonAttrs,
'onclick',
'rel',
'media',
'integrity',
'crossorigin',
'referrerpolicy',
'allowfullscreen',
'exclude-custom',
'exclude',
'defer',
],
}
/**
* HTML 문자열을 안전하게 정화하는 헬퍼 함수
* @param value - 정화할 HTML 문자열
* @param config - DOMPurify 설정
* @returns 정화된 HTML 문자열
*/
function sanitizeHtml(value: unknown, config: DOMPurifyConfig): string {
// SSR 환경에서는 빈 문자열 반환
if (!import.meta.client) {
return ''
}
// 값이 없거나 문자열이 아닌 경우 빈 문자열 반환
if (!value || typeof value !== 'string') {
return ''
}
try {
return DOMPurify.sanitize(value, config)
} catch (error) {
// 에러 발생 시 원본 값 반환하지 않고 빈 문자열 반환 (보안상 안전)
if (import.meta.dev) {
// eslint-disable-next-line no-console
console.error('[DOMPurify] Sanitization error:', error)
}
return ''
}
}
/**
* 디렉티브 핸들러 생성 함수
* @param config - DOMPurify 설정
* @returns 디렉티브 핸들러 객체
*/
function createDirectiveHandler(config: DOMPurifyConfig) {
return {
mounted(el: HTMLElement, binding: DirectiveBinding) {
el.innerHTML = sanitizeHtml(binding.value, config)
},
updated(el: HTMLElement, binding: DirectiveBinding) {
el.innerHTML = sanitizeHtml(binding.value, config)
},
}
}
export default defineNuxtPlugin(nuxtApp => {
// SSR 환경에서는 디렉티브 등록하지 않음
// if (!import.meta.client) {
// return
// }
// 기본 디렉티브: v-dompurify-html
nuxtApp.vueApp.directive(
'dompurify-html',
createDirectiveHandler(defaultConfig)
)
// 관리자용 디렉티브: v-dompurify-admin
nuxtApp.vueApp.directive(
'dompurify-admin',
createDirectiveHandler(adminConfig)
)
})