Vanna AI vanna.ask 执行 Plotly 代码
一句话
据 NVD,Vanna AI vanna.ask 的可视化流程曾把 LLM 生成的 Plotly 代码交给 exec,属于 ToLO-Exec。
怎么读这个案例
这个案例说明 ToLO 不只出现在 agent 或数学链里。数据分析应用也可能让 LLM 生成代码:
- 先生成 SQL 查数据(
ToLO-SQL候选) - 拿到 dataframe
- 再生成 Plotly 代码画图(
ToLO-Exec候选) exec执行 plotly 代码
用户看到的是”自动可视化”,程序内部却可能执行模型生成的 Python 代码。
这个案例揭示了 ToLO 的一个普遍现象:一个产品功能可能涉及多个 ToLO 子类,审计时必须按”每条 source-to-sink 路径”为单位,而不是”每个功能”。
影响组件
影响点是 src/vanna/base/base.py 中 vanna.ask 的 visualize 路径。
公开来源确认:
- CVE 编号 CVE-2024-5826
- 分配 CWE-94(Code Injection)
- 涉及
exec与generate_plotly_code
但未给出可闭合的受影响版本范围;GitHub v0.6.2 release 也未声明安全修复。所以 affected_versions 和 fixed_in 都保持 unknown。
ToLO 路径
整个 vanna.ask 路径上其实有两个 ToLO 候选:
路径 1: ToLO-SQL(text-to-SQL 部分)
| 列 | 内容 |
|---|---|
| Source | S_LLM^framework — generate_sql 返回的 SQL |
| Transform | 框架直接拿 SQL 去查数据库 |
| Sink | connection.execute(sql) 或类似 |
| Guard 缺失 | 取决于数据库账号权限是否受限 |
(详细 ToLO-SQL 见 GraphCypherQAChain 案例。)
路径 2: ToLO-Exec(可视化部分,本 CVE 主体)
| 列 | 内容 |
|---|---|
| Source | S_LLM^framework — generate_plotly_code 从 LLM 返回 Python/Plotly 代码 |
| Transform | ask 在生成 SQL、查询并得到 df 后,把代码传给绘图 helper |
| Sink | get_plotly_figure 中的 exec(plotly_code, ...)(ToLO-Exec / CWE-94) |
| Guard 缺失 | 缺少与代码执行匹配的 sandbox、C_SAFE^allowlist 或 C_SAFE^capability |
这条路径和数学链案例相似,但业务语义不同:LLM 不是生成数学表达式,而是生成可视化代码。ToLO 视角下,两者都属于”模型输出被提升为可执行程序”。
教学骨架
# Vanna AI ask 简化骨架(修复前)def ask(question: str): # Step 1: 生成 SQL sql = generate_sql(question) # ← S_LLM (路径 1) df = connection.execute(sql).fetchall()
# Step 2: 拿 df 给 LLM 生成 plotly 代码 plotly_code = generate_plotly_code(df, question) # ← S_LLM (路径 2)
# Step 3: 执行 plotly 代码 fig = get_plotly_figure(plotly_code, df) # 内部: # local_vars = {"df": df, "px": plotly.express, "go": plotly.graph_objects} # exec(plotly_code, local_vars) # ← ToLO-Exec sink # return local_vars["fig"]
return fig如果 prompt 被攻击者诱导,plotly_code 可能包含 __import__('os').system(...) 等任意 Python。
修复方式
公开 release 与 issue 只显示 hardening guide 线索,未核验到明确的 fixed version 或移除 exec 的补丁。
因此本站暂不写入修复结论,fixed_in 保持 unknown。
在版本和补丁未核验前,本站只记录公开来源确认的路径与风险,不推断上游已经彻底修复。后续若找到明确 commit,应补充修复属于:
- 移除 sink(完全不让 LLM 生成代码,改用图表类型 schema)
- 沙箱执行(
exec包在 docker / RestrictedPython 里) - 能力门控(
allow_dangerous_code=False默认) - 配置提示(只警告,不强制)中的哪一种。
一个理想化的修复方向
把 LLM 的输出从”任意 plotly 代码”收窄到结构化”图表规格”:
from pydantic import BaseModelfrom typing import Literal
class ChartSpec(BaseModel): chart_type: Literal["line", "bar", "scatter", "pie"] x_column: str y_column: str title: str
def generate_chart_spec(df, question) -> ChartSpec: # 让 LLM 输出 schema 化的 spec return llm.with_structured_output(ChartSpec).invoke(...)
def render(df, spec: ChartSpec): # 应用代码自己根据 spec 构造图表 if spec.x_column not in df.columns or spec.y_column not in df.columns: raise ValueError("invalid column") if spec.chart_type == "line": return px.line(df, x=spec.x_column, y=spec.y_column, title=spec.title) ...这样没有任何 exec,LLM 只决定”画哪种图、用哪列”,而怎么画由开发者代码硬编码。这就是 C_SAFE^schema + C_SAFE^allowlist 组合的典型应用。
对 ToLO 分类的启发
该案例支持 ToLO-Exec taxonomy:危险点不是传统 HTTP 参数直接进入 exec,而是应用把 LLM 生成的”内部可视化代码”当成可信程序片段执行。无需新增 sink 子类。
它还说明 ToLO 不局限于”agent 会调用 shell”这类显眼功能。数据分析、BI、text-to-SQL、自动制图等看似高层的生产力功能,也可能在内部把 LLM 输出送进执行器。
推论:任何 BI / 数据分析产品都应该被静态扫描。流行项目如 PandasAI、Vanna、DataLine、Code Interpreter 类自动可视化都属于高风险面。
学习者要带走什么
- “生成图表代码”也是代码执行,不因为业务场景是可视化就安全。
- 如果修复来源不清楚,案例页应保留
pending,不要推断 fixed version。这是研究诚信。 - 数据分析工具常同时涉及 SQL sink 和 code execution sink,应按路径分别分析,不要混在一起。
- 理想修复是把 LLM 从”写代码”降级到”填 spec”,这是
C_SAFE^schema + allowlist的经典组合。
读完检查
如果一个 BI 工具让 LLM 生成 Plotly / Python 代码,你会建议哪些防御?至少包括:
- 禁用任意
exec,改用结构化 chart spec(C_SAFE^schema) - 列名、表名 allowlist(
C_SAFE^allowlist) - 如果一定要执行代码,用受限图表 DSL(自定义 mini 求值器)而非 Python
- 隔离执行环境(
C_SAFE^capability:容器、subprocess、RestrictedPython) - 限制可访问变量、禁用文件 / 网络访问
- 记录人工确认(对高敏感图表)
至少前三条是工程上能做的最小集。
公开来源
- https://nvd.nist.gov/vuln/detail/CVE-2024-5826
- https://huntr.com/bounties/90620087-44ac-4e43-b659-3c5d30889369
- https://github.com/vanna-ai/vanna/issues/596
- https://github.com/vanna-ai/vanna/releases/tag/v0.6.2
下一步阅读
- CVE-2023-36258 PALChain:同 sink (Exec),展示 PAL 设计本身要求执行 LLM 代码时的修复。
- CVE-2024-8309 GraphCypherQAChain:text-to-query 的 SQL 子类对应案例。
- Defensive Patterns §schema → allowlist:本案的理想修复方向。