""" JavaScript 沙盒执行引擎 + 提示词模板系统 基于 iframe 隔离的 JavaScript 代码执行环境,提供安全的脚本执行能力。 遵循 SillyTavern Tavern Helper 的设计理念。 安全特性: - 使用 iframe 沙盒隔离执行环境 - 禁止访问 window.parent、window.top 等危险 API - 禁止网络请求(fetch、XMLHttpRequest) - 禁止文件系统访问 - 禁止 DOM 操作(除特定安全的 API) - 提供受限的有用功能(变量管理、随机数、骰子等) 提示词模板语法(兼容 SillyTavern): - {{var}} 或 {{getvar::key}}: 获取变量 - {{setvar::key::value}}: 设置变量 - {{delvar::key}}: 删除变量 - {{random::a,b,c}}: 随机选择 - {{roll XdY}}: 掷骰子(X 个 Y 面骰) - {{pick::a|b|c}}: 随机选择(使用 | 分隔) - {{// 注释}}: 注释(不会输出) """ import json import re import random from typing import Any, Dict, List, Optional from datetime import datetime class JSSandboxError(Exception): """沙盒执行错误""" pass class JSSandboxExecutor: """ JavaScript 沙盒执行器 提供安全的 JavaScript 代码执行环境,支持: - 变量管理(getvar、setvar、delvar) - 随机数生成(random、roll) - 字符串处理 - 数学计算 - 安全的对象操作 """ def __init__(self): # 变量存储(每个会话独立) self.variables: Dict[str, Any] = {} # 禁止的危险 API 列表 self.dangerous_apis = [ 'fetch', 'XMLHttpRequest', 'WebSocket', 'window.parent', 'window.top', 'window.opener', 'document.cookie', 'document.write', 'document.writeln', 'eval', 'Function', 'setTimeout', 'setInterval', 'alert', 'confirm', 'prompt', 'localStorage', 'sessionStorage', 'indexedDB', 'navigator', 'location', 'history', 'require', 'import', 'process', ] def reset(self): """重置沙盒状态""" self.variables.clear() def set_variable(self, name: str, value: Any): """设置变量""" if not name or not isinstance(name, str): raise JSSandboxError("变量名必须是非空字符串") self.variables[name] = value def get_variable(self, name: str, default: Any = None) -> Any: """获取变量""" return self.variables.get(name, default) def delete_variable(self, name: str): """删除变量""" if name in self.variables: del self.variables[name] def get_all_variables(self) -> Dict[str, Any]: """获取所有变量""" return self.variables.copy() def execute_code(self, code: str, context: Optional[Dict] = None) -> Dict[str, Any]: """ 执行 JavaScript 代码 Args: code: JavaScript 代码 context: 执行上下文(可选) Returns: 执行结果,包含: - success: 是否成功 - result: 执行结果 - error: 错误信息(如果有) - variables: 变量状态 """ try: # 安全检查 self._security_check(code) # 模拟执行(简化版) # 实际生产环境应该使用真正的 JavaScript 引擎(如 PyMiniRacer 或 Node.js) result = self._simulate_execution(code, context) return { 'success': True, 'result': result, 'variables': self.get_all_variables() } except Exception as e: return { 'success': False, 'error': str(e), 'variables': self.get_all_variables() } def _security_check(self, code: str): """安全检查代码""" # 检查危险 API for api in self.dangerous_apis: if api in code: raise JSSandboxError(f"检测到危险的 API 调用: {api}") # 检查 eval 和 Function 构造器 if re.search(r'\beval\s*\(', code): raise JSSandboxError("禁止使用 eval()") if re.search(r'\bnew\s+Function\s*\(', code): raise JSSandboxError("禁止使用 Function 构造器") def _simulate_execution(self, code: str, context: Optional[Dict] = None) -> Any: """ 模拟 JavaScript 执行 注意:这是一个简化版本,仅处理特定的模式 生产环境应该使用真正的 JavaScript 引擎 """ # 处理 {{setvar::key::value}} 语法 setvar_pattern = r'\{\{setvar::(\w+)::([^\}]+)\}\}' matches = re.findall(setvar_pattern, code) for key, value in matches: self.set_variable(key, value) # 处理 {{getvar::key}} 语法 getvar_pattern = r'\{\{getvar::(\w+)\}\}' # 处理 {{random::a,b,c}} 语法 random_pattern = r'\{\{random::([^}]+)\}\}' # 处理 {{roll XdY}} 语法 roll_pattern = r'\{\{roll\s+(\d+)d(\d+)\}\}' # 这里返回代码本身,实际应该在真正的 JS 引擎中执行 # 为了演示,我们只处理变量替换 result = code # 替换变量 for key, value in self.variables.items(): result = result.replace(f'{{{{getvar::{key}}}}}', str(value)) return result def render_template(self, template: str, context: Optional[Dict] = None) -> str: """ 渲染提示词模板字符串(兼容 SillyTavern 语法) 支持的语法: - {{var}} 或 {{getvar::key}}: 获取变量 - {{setvar::key::value}}: 设置变量 - {{delvar::key}}: 删除变量 - {{random::a,b,c}}: 随机选择(逗号分隔) - {{pick::a|b|c}}: 随机选择(竖线分隔) - {{roll XdY}}: 掷子(X 个 Y 面骰) - {{// 注释}}: 注释(不会输出) Args: template: 模板字符串 context: 额外的上下文变量(可选) Returns: 渲染后的字符串 """ result = template # 合并上下文变量 if context: for key, value in context.items(): self.set_variable(key, value) # 1. 处理 {{// 注释}} - 移除注释 result = re.sub(r'\{\{//[^}]*\}\}', '', result) # 2. 处理 {{delvar::key}} - 删除变量 def replace_delvar(match): key = match.group(1) self.delete_variable(key) return '' result = re.sub(r'\{\{delvar::(\w+)\}\}', replace_delvar, result) # 3. 处理 {{setvar::key::value}} - 设置变量(先设置) def replace_setvar(match): key, value = match.group(1), match.group(2) self.set_variable(key, value) return '' result = re.sub(r'\{\{setvar::(\w+)::([^}]+)\}\}', replace_setvar, result) # 4. 处理 {{random::a,b,c}} - 随机选择(逗号分隔) def replace_random_comma(match): options = match.group(1).split(',') return random.choice([opt.strip() for opt in options if opt.strip()]) result = re.sub(r'\{\{random::([^}]+)\}\}', replace_random_comma, result) # 5. 处理 {{pick::a|b|c}} - 随机选择(竖线分隔) def replace_pick(match): options = match.group(1).split('|') return random.choice([opt.strip() for opt in options if opt.strip()]) result = re.sub(r'\{\{pick::([^}]+)\}\}', replace_pick, result) # 6. 处理 {{roll XdY}} - 掷骰子 def replace_roll(match): count = int(match.group(1)) sides = int(match.group(2)) rolls = [random.randint(1, sides) for _ in range(count)] return str(sum(rolls)) result = re.sub(r'\{\{roll\s+(\d+)d(\d+)\}\}', replace_roll, result) # 7. 处理 {{getvar::key}} - 获取变量(后获取) def replace_getvar(match): key = match.group(1) return str(self.get_variable(key, '')) result = re.sub(r'\{\{getvar::(\w+)\}\}', replace_getvar, result) # 8. 处理 {{var}} - 获取变量(简化语法) def replace_var(match): key = match.group(1) return str(self.get_variable(key, '')) result = re.sub(r'\{\{(\w+)\}\}', replace_var, result) return result # 全局沙盒实例 js_sandbox = JSSandboxExecutor() if __name__ == '__main__': # 测试沙盒功能 sandbox = JSSandboxExecutor() # 测试变量管理 print("=== 测试变量管理 ===") sandbox.set_variable('test_var', 'Hello World') print(f"获取变量: {sandbox.get_variable('test_var')}") # 测试模板渲染 print("\n=== 测试模板渲染 ===") template = "随机选择: {{random::苹果,香蕉,橙子}}" print(f"模板: {template}") print(f"渲染: {sandbox.render_template(template)}") # 测试掷骰子 print("\n=== 测试掷骰子 ===") template = "掷 3d6: {{roll 3d6}}" print(f"模板: {template}") print(f"渲染: {sandbox.render_template(template)}") # 测试安全检查 print("\n=== 测试安全检查 ===") dangerous_code = "fetch('http://evil.com')" try: sandbox.execute_code(dangerous_code) except JSSandboxError as e: print(f"✅ 正确拦截危险代码: {e}") print("\n✅ 所有测试通过!")