LingFlow 安全审查报告
审查日期: 2026-03-25 审查范围: /home/ai/LingFlow 代码库 审查方法: 静态代码分析、模式匹配、AST分析
执行摘要
本次安全审查发现了 2个CRITICAL级别、4个HIGH级别、5个MEDIUM级别 和 3个LOW级别 的安全问题。最严重的问题是沙箱实现中存在潜在的逃逸风险,以及测试代码中包含危险的模式示例。
严重程度统计
| 严重程度 | 数量 | 状态 |
|---|---|---|
| CRITICAL | 2 | 需立即修复 |
| HIGH | 4 | 需尽快修复 |
| MEDIUM | 5 | 建议修复 |
| LOW | 3 | 可选修复 |
CRITICAL 级别问题
1. 沙箱 exec() 调用 - 潜在沙箱逃逸
文件: lingflow/common/sandbox.py:269
问题描述:
在 _execute_code_wrapper 方法中直接使用 exec() 执行编译后的代码。虽然已经实施了模块白名单和内置函数限制,但仍存在以下风险:
- AST 污染攻击: 攻击者可以通过构造特殊的 AST 节点绕过沙箱限制
- 属性访问绕过: 虽然限制了
os、sys等模块的导入,但可能通过对象属性链访问 - 描述符协议利用: 可以通过
__class__、__bases__等特殊属性进行逃逸
CWE: CWE-94 (Code Injection), CWE-265 (Privilege Containment)
修复建议:
# 1. 实施更严格的 globals/locals 过滤
def _sanitize_globals(self, globals_dict):
"""清理全局命名空间"""
safe_globals = self._create_safe_globals()
for key, value in globals_dict.items():
if key in safe_globals:
safe_globals[key] = value
return safe_globals
# 2. 在 exec 后验证对象类型
def _validate_result(self, result):
"""验证返回结果不包含危险对象"""
forbidden_types = (types.ModuleType, types.FunctionType, type)
for key, value in result.items():
if isinstance(value, forbidden_types):
raise SandboxError(f" forbidden type in result: {key}")
2. 条件分支技能中的 eval() AST 求值
文件: skills/conditional-branch/implementation.py:130
问题描述:
虽然代码尝试使用 ast.literal_eval 进行安全评估,但存在双重评估风险:
- 双重 eval 调用:
eval(ast.literal_eval(node.id))先进行literal_eval再进行eval - 变量名注入:
ast.literal_eval(node.id)只能处理字面量,对变量名会抛出异常 - 逻辑错误: 第107行存在
lambda a, b: a or or的明显错误
CWE: CWE-94 (Code Injection)
修复建议:
# 完全移除 eval,使用安全的字面量评估
elif isinstance(node, ast.Name):
# 只允许 True, False, None
if node.id == 'True':
return True
elif node.id == 'False':
return False
elif node.id == 'None':
return None
else:
raise Exception(f"不允许使用变量: {node.id}")
# 修复 lambda 错误
ast.Or: lambda a, b: a or b, # 原来是 a or or
HIGH 级别问题
3. 危险内置函数白名单不完整
文件: lingflow/common/sandbox.py:66-93
问题描述:
SAFE_BUILTINS 中的字典嵌套结构实际上不会按预期工作。Python 的 __builtins__ 应该是一个模块或字典,而不是包含键 '__builtins__' 的字典。
CWE: CWE-284 (Improper Access Control)
修复建议:
4. 测试代码包含 SQL 注入示例
文件: lingflow/testing/scenarios/test_security.py:102, lingflow/testing/scenario.py:139
问题描述:
# test_security.py:102
code_content="def get_user(user_id):\n query = 'SELECT * FROM users WHERE id = ' + user_id\n return db.execute(query)"
# scenario.py:139
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
测试文件中包含直接字符串拼接的 SQL 查询示例,虽然是用于测试检测能力,但这些模式可能被误用。
CWE: CWE-89 (SQL Injection)
修复建议:
# 使用注释或标记表明这是故意的不安全代码
UNSAFE_CODE_EXAMPLE = """# UNSAFE: DO NOT USE IN PRODUCTION
def get_user(user_id):
query = 'SELECT * FROM users WHERE id = ' + user_id # SQL INJECTION VULNERABILITY
return db.execute(query)
# SAFE: Use parameterized queries instead
def get_user_safe(user_id):
return db.execute('SELECT * FROM users WHERE id = ?', (user_id,))
"""
5. 危险模式检测不足
文件: lingflow/common/sandbox.py:349-351
问题描述:
dangerous_imports = [
'os.', 'sys.', 'subprocess.', 'eval(', 'exec(',
'compile(', '__import__', 'open(', 'open '
]
这个危险模式列表可以通过多种方式绕过:
- 字符串拼接绕过:
os__ = 'os'; import os__不会被检测到 - 导入别名:
import os as operating_system不会被检测到 - getattr 绕过:
getattr(__import__('os'), 'system')不会被检测到
CWE: CWE-94 (Code Injection)
修复建议:
# 使用 AST 分析而非字符串匹配
# 这已经在 SecurityAnalyzer 中实现,应该在 Sandbox 中使用它
from lingflow.common.security_analyzer import SecurityAnalyzer
def validate_code(self, code: str) -> bool:
analyzer = SecurityAnalyzer(allowed_modules=self.ALLOWED_MODULES)
violations = analyzer.analyze(code)
critical = [v for v in violations if v.severity == 'CRITICAL']
return len(critical) == 0
6. pickle 用于序列化结果
文件: lingflow/common/sandbox.py:276-277
问题描述:
使用 pickle.dumps 来测试可序列化性。虽然这里只是测试而不进行实际的反序列化,但这可能留下安全隐患的印象。
CWE: CWE-502 (Deserialization of Untrusted Data)
修复建议:
# 使用更安全的序列化测试方法
def _is_serializable(self, value):
"""检查值是否可序列化"""
try:
import json
json.dumps(value) # JSON 更安全
return True
except (TypeError, ValueError):
return False
MEDIUM 级别问题
7. subprocess.run 未验证 shell 参数
文件: skills/code-review-js/implementation.py:170-296
问题描述:
result = subprocess.run(
['npx', 'eslint', '--version'],
cwd=self.target_dir,
capture_output=True,
text=True,
timeout=30
)
代码审查技能使用 subprocess.run 调用 npx、npm 和 tsc。虽然没有使用 shell=True,但命令参数未经验证直接传递。
CWE: CWE-78 (OS Command Injection)
修复建议:
# 验证 npx/npm 是否在预期路径
def _validate_command(self, cmd: str) -> bool:
"""验证命令是否在允许列表中"""
allowed_commands = {'npx', 'npm', 'tsc'}
return cmd in allowed_commands
# 或使用绝对路径
NPX_PATH = shutil.which('npx')
if not NPX_PATH:
raise RuntimeError("npx not found")
8. 沙箱超时后的进程处理
文件: lingflow/common/sandbox.py:162-168
问题描述:
if process.is_alive():
process.terminate()
process.join(timeout=1.0)
if process.is_alive():
process.kill()
process.join()
超时后的进程终止使用 terminate() 然后 kill()。但在某些情况下,子进程可能创建自己的子进程,这些不会被终止。
CWE: CWE-409 (Improper Handling of Highly Consistent Operations)
修复建议:
# 使用进程组来确保所有子进程被终止
import os
import signal
def _terminate_process_tree(self, process):
"""终止整个进程树"""
if process.is_alive():
try:
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
process.join(timeout=1.0)
if process.is_alive():
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
except ProcessLookupError:
pass
9. 测试代码中的硬编码 API 密钥示例
文件: lingflow/testing/scenarios/test_security.py:191
问题描述:
测试代码包含看起来像真实 API 密钥的硬编码示例。
CWE: CWE-798 (Use of Hard-coded Credentials)
修复建议:
# 使用明显假的示例密钥
code_content="api_key = 'sk-00000000000000000000000000000000'\npassword = 'FAKE_PASSWORD_FOR_TESTING'"
10. YAML 配置加载缺少类型验证
文件: lingflow/common/config.py:81
问题描述:
虽然使用了 yaml.safe_load,但对加载的配置内容没有进行类型验证。恶意 YAML 文件可能注入非预期类型。
CWE: CWE-20 (Improper Input Validation)
修复建议:
# 添加配置模式验证
def _validate_config(self, config: dict) -> bool:
"""验证配置结构"""
expected_schema = {
'workflow': dict,
'skills': dict,
'agents': dict,
'compression': dict,
'logging': dict
}
for key, expected_type in expected_schema.items():
if key in config and not isinstance(config[key], expected_type):
raise ValueError(f"Invalid config type for {key}")
return True
11. 文件删除操作缺少权限检查
文件: skills/skill-versioning/implementation.py:138, skills/code-review-js/integrate.py:131
问题描述:
这些调用使用 shutil.rmtree 删除目录,但缺少目标路径验证,可能被利用删除重要文件。
CWE: CWE-22 (Path Traversal)
修复建议:
import os
def safe_rmtree(path: str, allowed_base: str):
"""安全地删除目录"""
real_path = os.path.realpath(path)
real_base = os.path.realpath(allowed_base)
if not real_path.startswith(real_base):
raise ValueError(f"Path {path} is outside allowed base {allowed_base}")
shutil.rmtree(real_path)
LOW 级别问题
12. 测试代码中的命令注入示例
文件: lingflow/testing/e2e/test_full_workflow.py:151
问题描述:
测试代码包含 os.system 示例(虽然仅在测试字符串中)。
CWE: CWE-78 (OS Command Injection)
修复建议: 添加注释说明这是不安全示例。
13. 宽泛的异常处理
文件: skills/code-review-js/implementation.py:410
问题描述:
多个位置使用裸 except: 捕获所有异常,可能隐藏安全问题。
CWE: CWE-392 (Misuse of Inner Class)
修复建议:
14. 环境变量引用未经验证
文件: 多个文件
问题描述: 代码中多处引用 os.environ.get() 但对返回值没有验证。
CWE: CWE-20 (Improper Input Validation)
修复建议: 添加环境变量类型和范围验证。
安全最佳实践观察
已实现的安全措施
- yaml.safe_load(): 使用安全的 YAML 加载方法
- 沙箱进程隔离: 使用
multiprocessing.Process进行隔离 - 模块白名单:
ALLOWED_MODULES限制可导入的模块 - 超时限制:
SandboxTimeoutError防止长时间运行 - AST 安全分析:
SecurityAnalyzer提供深度代码检查 - 宪法约束系统:
Constitution类实现安全原则验证
缺失的安全措施
- 速率限制: 无 API 调用速率限制
- 审计日志: 缺少详细的安全审计日志
- 输入验证: 部分用户输入缺少严格验证
- 依赖安全扫描: 没有自动化依赖漏洞扫描
- 密钥管理: 未实现安全的密钥存储方案
修复优先级建议
立即修复 (P0)
- 沙箱 exec() 加强化化 (CRITICAL-1)
- 条件分支 eval() 修复 (CRITICAL-2)
尽快修复 (P1)
- SAFE_BUILTINS 结构修正 (HIGH-3)
- 测试代码标记 (HIGH-4, HIGH-5)
- 使用 AST 替代字符串匹配 (HIGH-5)
计划修复 (P2)
- subprocess 参数验证 (MEDIUM-7)
- 进程树终止 (MEDIUM-8)
- 配置验证 (MEDIUM-10)
可选修复 (P3)
- 异常处理改进
- 环境变量验证
依赖安全审查
当前依赖
建议
- 添加依赖固定: 使用
requirements.lock或poetry.lock - 自动化扫描: 集成
pip-audit或safety到 CI/CD - 定期更新: 建立每月依赖更新流程
结论
LingFlow 项目已实施了多个安全措施,特别是沙箱隔离和 AST 安全分析。然而,沙箱实现中的 exec() 调用和条件分支中的双重 eval() 构成了主要的安全风险。建议优先修复 CRITICAL 级别的问题,然后逐步处理 HIGH 和 MEDIUM 级别的问题。
整体安全评分: C+ (需要改进)
修复所有 CRITICAL 和 HIGH 级别问题后,预计安全评分可提升至 B+。
审查人员: Security Expert Agent 审查工具: Grep模式匹配, AST分析, 手动代码审查 报告版本: 1.0