mirror of https://github.com/Mai-with-u/MaiBot.git
112 lines
2.7 KiB
TypeScript
112 lines
2.7 KiB
TypeScript
/**
|
||
* 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))
|
||
}
|