Spaces:
Sleeping
Sleeping
| """ | |
| utils/security.py — LatentLearn 多维安全防护与敏感拦截层 | |
| 功能: | |
| 1. HTML 标签剥离(防止 XSS 脚本跨站攻击和注入污染) | |
| 2. 递归清洗上传文档(防止注入文本对大模型及前端组件造成危害) | |
| 3. 用户提问长度限制(防御 DoS 与 Token 爆满) | |
| 4. 提示词注入拦截(秒级拦截绕过指令) | |
| 5. 核心有毒/违规内容拦截(自残自杀、毒品枪支、网络诈骗、洗钱等) | |
| """ | |
| from __future__ import annotations | |
| import re | |
| # 1. 提示词注入攻击正则检测 (Prompt Injection Detection Patterns) | |
| _INJECTION_PATTERNS = [ | |
| r"ignore\s+(?:the\s+)?(?:previous|system|prior)\s+(?:instruction|prompt|rule|message|setting|directive)", | |
| r"forget\s+(?:the\s+)?(?:previous|system|prior)\s+(?:instruction|prompt|rule|message|setting|directive)", | |
| r"you\s+must\s+now\s+act\s+as", | |
| r"you\s+are\s+now\s+a\s+", | |
| r"bypass\s+(?:the\s+)?(?:system|security|filter|guardrail)", | |
| r"忽略(?:之前|先前的?)(?:指令|提示|规则|设定|说明|约束)", | |
| r"忘记(?:之前|先前的?)(?:指令|提示|规则|设定|说明|约束)", | |
| r"你现在(是|扮演|开始充当)\s*", | |
| r"解除(?:系统|安全)?限制", | |
| r"绕过(?:安全|系统|过滤)?", | |
| r"print\s+(?:your\s+)?system\s+(?:prompt|message|instruction)", | |
| r"输出(?:你?的?)?系统(?:提示词|指令|初始设置)" | |
| ] | |
| # 2. 政治/黄暴/自残敏感词本地正则过滤 (Mild local keyword blacklist for toxic/illegal/self-harm queries) | |
| _TOXIC_PATTERNS = [ | |
| r"(?:自杀|自残|割腕|服毒|跳楼|不想活了|suicide|self-harm|kill\s+myself|end\s+my\s+life)", | |
| r"(?:吸毒|冰毒|海洛因|贩毒|大麻|meth|heroin|marijuana|drug\s+deal)", | |
| r"(?:制作炸弹|枪支制造|制造枪支|bomb\s+making|make\s+a\s+bomb|weapons\s+manufacturing)", | |
| r"(?:洗钱|诈骗|传销|money\s+laundering|scam|pyramid\s+scheme)" | |
| ] | |
| # 3. HTML 标签剥离正则 (XSS Sanitizer) | |
| _HTML_TAG_PATTERN = re.compile(r"<[^>]+>") | |
| def sanitize_text(text: str | None) -> str | None: | |
| """过滤 HTML 标签以防止 XSS 攻击,保证文本纯净""" | |
| if not text: | |
| return text | |
| # 剥离所有 HTML 标签,只保留纯文本 | |
| return _HTML_TAG_PATTERN.sub("", text).strip() | |
| def sanitize_document(document: dict | None) -> dict | None: | |
| """ | |
| 递归清洗上传的学习文档: | |
| - 清洗标题 (title) | |
| - 清洗大章节正文 (content) | |
| - 递归清洗大纲小节结构 (structure) | |
| 返回清洗后 100% 纯净、无 script/HTML 注入的安全文档字典。 | |
| """ | |
| if not document: | |
| return document | |
| sanitized = dict(document) | |
| if "title" in sanitized and isinstance(sanitized["title"], str): | |
| sanitized["title"] = sanitize_text(sanitized["title"]) | |
| if "content" in sanitized and isinstance(sanitized["content"], str): | |
| sanitized["content"] = sanitize_text(sanitized["content"]) | |
| if "structure" in sanitized and isinstance(sanitized["structure"], list): | |
| new_struct = [] | |
| for sec in sanitized["structure"]: | |
| if isinstance(sec, dict): | |
| new_sec = dict(sec) | |
| if "title" in new_sec and isinstance(new_sec["title"], str): | |
| new_sec["title"] = sanitize_text(new_sec["title"]) | |
| if "content" in new_sec and isinstance(new_sec["content"], str): | |
| new_sec["content"] = sanitize_text(new_sec["content"]) | |
| new_struct.append(new_sec) | |
| sanitized["structure"] = new_struct | |
| return sanitized | |
| def check_query_safety(query: str | None) -> tuple[bool, str | None]: | |
| """ | |
| 检查用户输入的安全健康状况。 | |
| 返回: (is_safe, error_hint) | |
| """ | |
| if not query: | |
| return True, None | |
| # A. 限制输入长度,防止拒绝服务(DoS)或 Token 爆满 | |
| if len(query) > 1000: | |
| return False, "Input query is too long (maximum 1000 characters). / 输入提问过长(最多支持 1000 字)。" | |
| # B. 过滤 HTML 注入 / XSS 并校验清洗后字符 | |
| sanitized = sanitize_text(query) | |
| if not sanitized or not sanitized.strip(): | |
| return False, "Input query is empty after sanitization. / 输入文本清洗后为空,请不要注入无效 HTML 代码。" | |
| # C. 提示词注入检测 | |
| for pattern in _INJECTION_PATTERNS: | |
| if re.search(pattern, sanitized, re.IGNORECASE): | |
| return False, "Security Alert: Prompt injection attempt detected. Please stay on study focus. / 安全警告:检测到提示词指令注入,请专注于学术和文档阅读辅导。" | |
| # D. 敏感/有毒内容拦截 | |
| for pattern in _TOXIC_PATTERNS: | |
| if re.search(pattern, sanitized, re.IGNORECASE): | |
| return False, "Safety Alert: Content violates safety guidelines. / 安全提示:提问涉及高风险或有害主题,已被系统自动拦截。" | |
| return True, None | |