Skip to content

Vanna AI vanna.ask 执行 Plotly 代码

一句话

据 NVD,Vanna AI vanna.ask 的可视化流程曾把 LLM 生成的 Plotly 代码交给 exec,属于 ToLO-Exec

公开事实与待核验

本页采用的读法
公开来源明确支持CVE-2024-5826、Vanna AI、vanna.ask / visualization path、LLM 生成 Plotly 代码、exec
ToLO 解释本 CVE 主体按 S_LLM^{framework}ToLO-Exec 读;同一产品还可能存在 text-to-SQL 路径,但那不是本页主结论。
版本状态公开来源没有给出本站可闭合的 affected/fixed 版本范围。
为什么 pendingrelease v0.6.2 可作为相关公开来源,但本页未核验到明确安全修复声明或移除 exec 的补丁链。

怎么读这个案例

这个案例说明 ToLO 不只出现在 agent 或数学链里数据分析应用也可能让 LLM 生成代码:

  1. 先生成 SQL 查数据(ToLO-SQL 候选)
  2. 拿到 dataframe
  3. 再生成 Plotly 代码画图(ToLO-Exec 候选)
  4. exec 执行 plotly 代码

用户看到的是”自动可视化”,程序内部却可能执行模型生成的 Python 代码

这个案例揭示了 ToLO 的一个普遍现象:一个产品功能可能涉及多个 ToLO 子类,审计时必须按”每条 source-to-sink 路径”为单位,而不是”每个功能”。

影响组件

影响点是 src/vanna/base/base.pyvanna.askvisualize 路径

公开来源确认:

  • CVE 编号 CVE-2024-5826
  • 分配 CWE-94(Code Injection)
  • 涉及 execgenerate_plotly_code

未给出可闭合的受影响版本范围;GitHub v0.6.2 release 也未声明安全修复。所以 affected_versionsfixed_in 都保持 unknown

ToLO 路径

整个 vanna.ask 路径上其实有两个 ToLO 候选:

路径 1:ToLO-SQL(text-to-SQL 部分)

内容
SourceS_LLM^{framework} —— generate_sql 返回的 SQL
Transform框架直接拿 SQL 去查数据库
Sinkconnection.execute(sql) 或类似
Guard 缺失取决于数据库账号权限是否受限

(详细 ToLO-SQLGraphCypherQAChain 案例。)

路径 2:ToLO-Exec(可视化部分,本 CVE 主体)

内容
SourceS_LLM^{framework} —— generate_plotly_code 从 LLM 返回 Python/Plotly 代码
Transformask 在生成 SQL、查询并得到 df 后,把代码传给绘图 helper
Sinkget_plotly_figure 中的 exec(plotly_code, ...)(ToLO-Exec / CWE-94)
Guard 缺失缺少与代码执行匹配的 sandbox、C_SAFE^{allowlist}C_SAFE^{capability}
Impact公开来源按 code injection / code execution 描述;本页不扩展具体输入
Repair pattern待核验。理想方向是移除任意代码 sink,改成结构化 chart spec 与列/图表 allowlist

这条路径和数学链案例相似,但业务语义不同: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

风险点不是某条具体输入,而是 plotly_code 的语义空间仍是通用 Python。只要它来自 LLM 输出且进入 exec,图表功能就变成执行边界问题。

修复方式

公开 release 与 issue 只显示 hardening guide 线索,未核验到明确的 fixed version 或移除 exec 的补丁

因此本站暂不写入修复结论,fixed_in 保持 unknown

在版本和补丁未核验前,本站只记录公开来源确认的路径与风险,不推断上游已经彻底修复。后续若找到明确 commit,应补充修复属于:

  • 移除 sink(完全不让 LLM 生成代码,改用图表类型 schema)
  • 沙箱执行(exec 包在 docker / RestrictedPython 里)
  • 能力门控(allow_dangerous_code=False 默认)
  • 配置提示(只警告,不强制)中的哪一种。

为什么仍保持 pending

  • NVD 与 huntr 说明了风险路径,但本页尚未核验到一个明确的 fixed version。
  • v0.6.2 release 被列为公开线索,但 release note 是否声明并完全覆盖该 CVE 需要继续核对。
  • 同一 ask 工作流可能同时包含 SQL 与 Plotly 两条 ToLO 路径;本页只把 CVE-2024-5826 的主体写成 ToLO-Exec,避免把 text-to-SQL 的额外事实写成已核验结论。

一个理想化的修复方向

把 LLM 的输出从”任意 plotly 代码”收窄到结构化”图表规格”:

from pydantic import BaseModel
from 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 / 数据分析产品如果支持 LLM 生成 SQL、Python 或图表代码,就应该按 source-to-sink 路径做静态扫描和人工复核。

学习者要带走什么

  1. “生成图表代码”也是代码执行,不因为业务场景是可视化就安全。
  2. 如果修复来源不清楚,案例页应保留 pending,不要推断 fixed version。这是研究诚信。
  3. 数据分析工具常同时涉及 SQL sink 和 code execution sink,应按路径分别分析,不要混在一起。
  4. 理想修复是把 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)
  • 限制可访问变量、禁用文件 / 网络访问
  • 记录人工确认(对高敏感图表)

至少前三条是工程上能做的最小集。

公开来源

下一步阅读