MaiBot/dashboard/src/lib/theme/sanitizer.ts

112 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* CSS 安全过滤器 - 用于过滤用户自定义 CSS 中的危险内容
* 防范外部资源加载和 XSS 注入
*/
interface SanitizeResult {
css: string
warnings: string[]
}
/**
* 过滤规则:基于正则表达式的危险模式检测
* 与匹配的危险模式相关的警告消息
*/
interface FilterRule {
pattern: RegExp
message: string
}
/**
* 定义所有过滤规则
*/
const filterRules: FilterRule[] = [
{
pattern: /@import\s+(?:url\()?['"]?(?:https?:|\/\/)?[^)'"]+['"]?\)?[;]?/gi,
message: '移除 @import 语句(禁止加载外部资源)',
},
{
pattern: /url\s*\(\s*(?:https?:|\/\/|data:|javascript:)[^)]*\)/gi,
message: '移除 url() 调用(禁止外部请求)',
},
{
pattern: /javascript:/gi,
message: '移除 javascript: 协议XSS 防护)',
},
{
pattern: /expression\s*\(\s*[^)]*\)/gi,
message: '移除 expression() 函数IE 遗留 XSS 向量)',
},
{
pattern: /-moz-binding\s*:\s*[^;]+/gi,
message: '移除 -moz-binding 属性Firefox XSS 向量)',
},
{
pattern: /behavior\s*:\s*[^;]+/gi,
message: '移除 behavior: 属性IE HTC',
},
]
/**
* 将原始 CSS 按行分割并跟踪行号
*/
function splitCSSByLines(css: string): string[] {
return css.split(/\r?\n/)
}
/**
* 在 CSS 中查找模式匹配的行号
*/
function findMatchingLineNumbers(css: string, pattern: RegExp): number[] {
const lines = splitCSSByLines(css)
const matchingLines: number[] = []
lines.forEach((line, index) => {
if (pattern.test(line)) {
matchingLines.push(index + 1) // 行号从 1 开始
}
})
return matchingLines
}
/**
* 过滤 CSS 中的危险内容
* @param rawCSS 原始 CSS 字符串
* @returns 包含过滤后的 CSS 和警告列表的对象
*/
export function sanitizeCSS(rawCSS: string): SanitizeResult {
let sanitizedCSS = rawCSS
const warnings: string[] = []
// 应用所有过滤规则
filterRules.forEach((rule) => {
const lineNumbers = findMatchingLineNumbers(sanitizedCSS, rule.pattern)
// 对每个匹配的行生成警告
lineNumbers.forEach((lineNum) => {
warnings.push(`Line ${lineNum}: ${rule.message}`)
})
// 从 CSS 中移除匹配内容
sanitizedCSS = sanitizedCSS.replace(rule.pattern, '')
})
// 清理多余的空白行
sanitizedCSS = sanitizedCSS.replace(/\n\s*\n/g, '\n').trim()
return {
css: sanitizedCSS,
warnings,
}
}
/**
* 快速检查 CSS 是否包含危险模式
* @param css CSS 字符串
* @returns 如果包含危险模式返回 true否则返回 false
*/
export function isCSSSafe(css: string): boolean {
return !filterRules.some((rule) => rule.pattern.test(css))
}