跳转至

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

问题描述:

exec(compiled_code, globals_dict, locals_dict)

_execute_code_wrapper 方法中直接使用 exec() 执行编译后的代码。虽然已经实施了模块白名单和内置函数限制,但仍存在以下风险:

  1. AST 污染攻击: 攻击者可以通过构造特殊的 AST 节点绕过沙箱限制
  2. 属性访问绕过: 虽然限制了 ossys 等模块的导入,但可能通过对象属性链访问
  3. 描述符协议利用: 可以通过 __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

问题描述:

return eval(ast.literal_eval(node.id))

虽然代码尝试使用 ast.literal_eval 进行安全评估,但存在双重评估风险:

  1. 双重 eval 调用: eval(ast.literal_eval(node.id)) 先进行 literal_eval 再进行 eval
  2. 变量名注入: ast.literal_eval(node.id) 只能处理字面量,对变量名会抛出异常
  3. 逻辑错误: 第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 = {
    '__builtins__': {
        'abs': abs, 'all': all, ...
    }
}

SAFE_BUILTINS 中的字典嵌套结构实际上不会按预期工作。Python 的 __builtins__ 应该是一个模块或字典,而不是包含键 '__builtins__' 的字典。

CWE: CWE-284 (Improper Access Control)

修复建议:

# 正确的方式
SAFE_BUILTINS = {
    'abs': abs,
    'all': all,
    # ... 其他安全函数
    # 不使用嵌套的 '__builtins__' 键
}


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  '
]

这个危险模式列表可以通过多种方式绕过:

  1. 字符串拼接绕过: os__ = 'os'; import os__ 不会被检测到
  2. 导入别名: import os as operating_system 不会被检测到
  3. 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

问题描述:

import pickle
pickle.dumps(value)

使用 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 调用 npxnpmtsc。虽然没有使用 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

问题描述:

code_content="api_key = 'sk-1234567890abcdef'\npassword = '123456'"

测试代码包含看起来像真实 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

问题描述:

file_config = yaml.safe_load(f)
if file_config:
    self._merge_config(config, file_config)

虽然使用了 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(current_dir)
shutil.rmtree(test_dir)

这些调用使用 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("process " + input)

测试代码包含 os.system 示例(虽然仅在测试字符串中)。

CWE: CWE-78 (OS Command Injection)

修复建议: 添加注释说明这是不安全示例。


13. 宽泛的异常处理

文件: skills/code-review-js/implementation.py:410

问题描述:

except:
    pass

多个位置使用裸 except: 捕获所有异常,可能隐藏安全问题。

CWE: CWE-392 (Misuse of Inner Class)

修复建议:

except (IOError, OSError, UnicodeDecodeError) as e:
    logger.debug(f"Failed to read file: {e}")


14. 环境变量引用未经验证

文件: 多个文件

问题描述: 代码中多处引用 os.environ.get() 但对返回值没有验证。

CWE: CWE-20 (Improper Input Validation)

修复建议: 添加环境变量类型和范围验证。


安全最佳实践观察

已实现的安全措施

  1. yaml.safe_load(): 使用安全的 YAML 加载方法
  2. 沙箱进程隔离: 使用 multiprocessing.Process 进行隔离
  3. 模块白名单: ALLOWED_MODULES 限制可导入的模块
  4. 超时限制: SandboxTimeoutError 防止长时间运行
  5. AST 安全分析: SecurityAnalyzer 提供深度代码检查
  6. 宪法约束系统: Constitution 类实现安全原则验证

缺失的安全措施

  1. 速率限制: 无 API 调用速率限制
  2. 审计日志: 缺少详细的安全审计日志
  3. 输入验证: 部分用户输入缺少严格验证
  4. 依赖安全扫描: 没有自动化依赖漏洞扫描
  5. 密钥管理: 未实现安全的密钥存储方案

修复优先级建议

立即修复 (P0)

  1. 沙箱 exec() 加强化化 (CRITICAL-1)
  2. 条件分支 eval() 修复 (CRITICAL-2)

尽快修复 (P1)

  1. SAFE_BUILTINS 结构修正 (HIGH-3)
  2. 测试代码标记 (HIGH-4, HIGH-5)
  3. 使用 AST 替代字符串匹配 (HIGH-5)

计划修复 (P2)

  1. subprocess 参数验证 (MEDIUM-7)
  2. 进程树终止 (MEDIUM-8)
  3. 配置验证 (MEDIUM-10)

可选修复 (P3)

  1. 异常处理改进
  2. 环境变量验证

依赖安全审查

当前依赖

pyyaml>=6.0
click>=8.0
flask>=2.0  # 可选
pytest>=7.0  # 开发用

建议

  1. 添加依赖固定: 使用 requirements.lockpoetry.lock
  2. 自动化扫描: 集成 pip-auditsafety 到 CI/CD
  3. 定期更新: 建立每月依赖更新流程

结论

LingFlow 项目已实施了多个安全措施,特别是沙箱隔离和 AST 安全分析。然而,沙箱实现中的 exec() 调用和条件分支中的双重 eval() 构成了主要的安全风险。建议优先修复 CRITICAL 级别的问题,然后逐步处理 HIGH 和 MEDIUM 级别的问题。

整体安全评分: C+ (需要改进)

修复所有 CRITICAL 和 HIGH 级别问题后,预计安全评分可提升至 B+。


审查人员: Security Expert Agent 审查工具: Grep模式匹配, AST分析, 手动代码审查 报告版本: 1.0