Esc
输入关键词开始搜索
News

Correct Code, Vulnerable Dependencies: A Large Scale Measurement Study of LLM-Specified Library Versions

Correct Code, Vulnerable Dependencies: A Large Scale Measurement Study of LLM-Specified Library Versions

原文链接:https://arxiv.org/abs/2605.06279 作者:Chengjie Wang、Jingzheng Wu、Xiang Ling、Tianyue Luo、Chen Zhao 机构:Institute of Software, Chinese Academy of Sciences;University of Chinese Academy of Sciences;Key Laboratory of System Software (CAS) 发布日期:2026-05-07

速查卡

项目内容
一句话总结LLM 不只是会 hallucinate 包名,它更隐蔽的风险是:会为真实存在的依赖钉上真实存在、但带 CVE 或根本不兼容的版本号。
大白话版模型帮你写的 Python 代码也许逻辑没问题,但它顺手写上的 requests==...django==...cryptography==... 版本,可能一装就炸,或者装得上却自带已知漏洞。
核心数字• 基于 1000 个 Stack Overflow 任务构建 PinTrace • 10 个 LLM 全测 • 明示版本时 36.70%–55.70% 的任务至少带 1 个已知 CVE • 62.75%–74.51% 的相关漏洞是 Critical/High • 静态兼容率 19.70%–63.20%,动态通过率 6.49%–48.62%
评级A — 这不是边角安全问题,而是 LLM coding workflow 里一个此前被系统性忽视的主风险面
代码数据集与代码已开源:https://github.com/dw763j/PinTrace
关键词dependency versioning, CVE, supply chain security, PinTrace, BigCodeBench, Python, LLM coding assistants

核心 Insight

这篇论文最厉害的地方,在于它把一个之前经常被忽略的问题单独拎了出来:

过去大家谈 LLM 写代码的安全问题,主要盯两件事:

  1. 代码逻辑本身会不会有 CWE 式漏洞;
  2. 模型会不会 hallucinate 一个根本不存在的包名。

这篇论文说:还有第三类,而且在真实开发里可能更阴。

那就是模型给你推荐的包名是对的,代码看起来也对,但它偷偷选择了一个带已知 CVE、或者和代码 API 不兼容的历史版本。

这个问题为什么严重?因为它很容易通过第一层人工 review:

  • import 语句看起来很正常;
  • 版本号也是“真的”;
  • 甚至有时候还能 install 成功。

但风险在于:

  • 你把旧漏洞一起带进来了;
  • 或者代码和版本 API 不匹配,部署时才炸;
  • 更糟的是,不同模型还会收敛到同一批“高频但脆弱”的版本上。

为什么这个想法 work?

因为依赖版本选择本质上不是一个“语言建模小细节”,而是软件供应链决策。

人类开发者在选版本时,会参考:

  • changelog
  • 安全通告
  • 兼容性说明
  • Python / OS / transitive deps 环境

LLM 则是按训练语料中的显著性和共现关系做推断。一个版本在网上出现得越多、教程越多、Stack Overflow 答案越多,它就越可能被模型选中;而这类“高曝光老版本”恰恰最有机会积累 CVE、踩中新环境不兼容坑。

所以这篇论文要证明的核心不是“某个模型粗心”,而是: 模型在做版本决策时,遵循的是语言统计偏好,而不是安全与兼容性目标。

方法详解

整体架构

论文构建了一条非常完整的版本风险评测流水线:

真实 Stack Overflow 任务
  → 让 10 个 LLM 生成带依赖的 Python 代码
  → 抽取第三方库与版本标注
  → 检查版本是否真实存在
  → 对照 OSV 索引看是否带已知 CVE
  → 在隔离环境中安装并做静态兼容检查
  → 用 BigCodeBench 测动态执行
  → 再做 root cause 与 mitigation probe

重点是作者把“安全性”和“兼容性”分开测:

  • 安全:版本有没有已知漏洞;
  • 兼容:这个版本和生成代码能不能一起工作。

数据集:PinTrace

组件 1:任务来源

做什么: 从真实开发问题里构造版本评测任务。

怎么做:

  • 从 Stack Overflow 抽题
  • 只保留有 accepted answer 的问题
  • accepted answer 必须含 Python 代码块
  • 代码块能 parse 成 AST
  • 至少 import 一个第三方库
  • 时间窗口限定在 2020-01-01 到 2026-01-01

结果: 形成 1000 个任务的 PinTrace 数据集。

组件 2:为什么只测 Python

作者只测 Python,不是偷懒,而是为了实验闭环:

  • Python 是 LLM-assisted coding 的高频语言
  • PyPI 版本历史完整
  • OSV / PyPI 元数据足够成熟,适合做大规模版本解析与漏洞索引
  • Python 生态 release 节奏快,最容易暴露版本级兼容问题

组件 3:平衡采样

作者没有直接随机抽 Stack Overflow,因为那会被 pandas、numpy 之类高频库淹没。于是他们做了 TPL-balanced sampling,保证 267 个不同第三方库都有足够覆盖。

数据集统计

指标数值
任务数1000
覆盖第三方库数267
问题 token 长度均值 / 中位数430.6 / 286.5
accepted answer 代码 token 均值 / 中位数202.0 / 123.5
每任务平均 TPL import 数1.6
至少 2 个 TPL import 的任务占比46.4%
至少 3 个 TPL import 的任务占比13.1%

这很关键,因为版本冲突和多依赖交互,必须在有多库组合的任务里才会充分暴露。

被测模型

论文评估 10 个模型,包含闭源和开源:

  • GPT-5.4
  • Claude-Sonnet-4.6
  • Gemini-3.1-Pro
  • DeepSeek-V3.2
  • Kimi-K2.5
  • Qwen3.5-397B
  • Qwen3-235B
  • Qwen3-30B
  • MiniMax-M2.5
  • Llama-4-Scout

这使结论不容易被解释成“某一家模型训练差”。

两种 prompting mode

这是论文设计里非常妙的一点。

作者区分两种场景:

  1. Inline mode

    • 模型在代码里直接对 import 或依赖附上版本信息
    • 这更像“模型主动做版本决策”
  2. Explicit mode

    • 让模型直接创建 manifest / requirements 一类文件
    • 若没写版本,安装时会回退到最新 release

这个区分非常重要,因为它揭示:

  • inline 更容易显式带来漏洞版本;
  • explicit 则可能因为“没钉版本”把兼容性交给环境,形成另一类风险。

漏洞索引构建

作者基于 OSV 构建 (library, version) 到 CVE / CVSS 的映射:

  • 只保留 PyPI 生态记录
  • 解析 explicit version lists 与 range specs
  • 结合完整 PyPI release history 扩展受影响版本
  • 去重 alias 到 canonical CVE

兼容性检查链路

兼容性分两层:

静态兼容

  • 能不能装上(installation verification)
  • 能不能通过 ty static type checking

动态兼容

  • 在 BigCodeBench 上跑测试
  • 看任务级是否 pass / fail / error

这套设计很好,因为很多版本问题根本跑不到业务逻辑层:安装就先炸了。

与现有方法的关键区别

维度之前常见做法本文方法为什么更好
安全焦点代码逻辑漏洞 / hallucinated packages真实依赖的真实版本是否安全抓住更隐蔽也更常见的风险面
数据来源小样本或合成任务1000 个真实 Stack Overflow 任务更接近真实开发场景
依赖分析只看包名是否存在看版本是否存在、是否带 CVE、是否兼容闭环完整
验证方式静态分析常见OSV + 安装 + 类型检查 + BigCodeBench 动测能区分不同失败来源

实验结果

主实验 1:模型到底有多爱“指定版本”

在是否主动钉版本这件事上,prompting mode 的影响比模型能力本身还大。

论文报告:

  • 在一种提示条件下,模型显式指定版本的比例可高达 95.18%
  • 在另一种 manifest 场景下,有些模型只在 6.45%–59.19% 的情况下写出版本

这说明“模型是否在做版本决策”本身就受提示设计强烈影响。

主实验 2:漏洞暴露率高得离谱

指标结果
任务层面至少 1 个已知 CVE 的占比36.70%–55.70%
脆弱版本中 Critical / High 的占比62.75%–74.51%
相关 CVE 在模型知识截断前已公开的比例72.27%–91.37%
跨全部模型聚合的 pre-cutoff CVE-model 对81.8%

解读:

  • 这不是“模型不知道漏洞后来才被披露”。大多数漏洞在训练前就已经公开。
  • 问题不在于模型完全无知,而在于模型无法在推理时把“版本字符串”稳定映射到“漏洞数据库事实”。
  • 所以一句“请避免有 CVE 的版本”基本没用。

主实验 3:静态兼容率并不高

模式 / 指标范围
静态兼容率(所有模型)19.70%–63.20%
动态通过率(所有模型)6.49%–48.62%

细看 BigCodeBench,问题更刺眼:

  • inline mode 下,9/10 模型动态通过率低于 20%
  • error 列非常高,说明大量任务根本没跑到测试逻辑,先死在安装 / 依赖解析阶段

代表性结果表:版本暴露与兼容性

模型任务漏洞暴露率 τU(基线)静态兼容率 τC(基线)BigCodeBench 动态通过率(基线)
GPT-5.446.20%77.35%48.62%
Claude-Sonnet-4.650.50%16.16%7.32%
Gemini-3.1-Pro50.70%35.91%18.23%
DeepSeek-V3.254.00%31.63%15.61%
Kimi-K2.555.70%32.60%13.54%
Qwen3.5-397B50.40%20.03%9.81%
Qwen3-235B51.70%28.73%6.91%
Qwen3-30B36.70%41.57%16.16%
MiniMax-M2.552.10%15.88%6.49%
Llama-4-Scout51.70%23.34%8.15%

解读:

  • GPT-5.4 是明显的 outlier,兼容性最好,但漏洞暴露仍不低。
  • 这说明“代码能力更强”并不自动解决“版本风险”。
  • 大部分模型的主问题不是代码语法,而是依赖解析根本走不通。

主实验 4:模型会跨家族收敛到同一批脆弱版本

这是论文最震撼的发现之一。

作者统计到:

  • 共有 6378 次 vulnerable version assignment
  • 分布在 1289 个唯一的脆弱 (library, version) 对上
  • 其中 top-10 就占了 26.34%

最常见的高频脆弱版本包括:

  • django==6.0.1
  • requests==2.31.0
  • flask==3.1.2
  • flask==2.3.3
  • djangorestframework==3.14.0

其中 django==6.0.1 在全部 10 个模型里累计出现 400 次。

这说明风险不是某家模型的偶发 bug,而是整个训练分布在把模型往同一批“高曝光旧版本”上推。

静态兼容失败的主因:安装阶段就死

论文把不兼容任务的首要诊断标签做了统计。最主要的失败类别是:

  • dependency installation error
  • unresolved attribute reference
  • unresolved import
  • invalid argument type

而安装失败里,最主要的两个子因是:

  1. dependency resolution conflict
  2. missing_distutils

后者很有意思:很多老版本包依赖的构建流程仍假设 Python 自带 distutils,但 Python 3.12 把它从标准库移除了。于是模型偏爱的“旧但常见版本”,在现代环境里反而更容易装不上。

环境敏感性:3.10 和 3.12 是一道分水岭

论文又做了 Python 3.8 / 3.10 / 3.12 / 3.14 的鲁棒性实验。

结论很清楚:

  • inline mode 在 Python 3.10 附近兼容性最好
  • 到 3.12 后明显塌
  • explicit mode 则往往随 Python 版本更新而改善

以 DeepSeek-V3.2 为例:

  • inline 静态兼容率从 Python 3.10 的 90.77%,掉到 Python 3.12 的 34.19%
  • BigCodeBench 动态通过率从 38.25% 掉到 16.73%

这进一步证明:问题核心在模型钉的版本,不在代码逻辑本身。

邻近版本替换实验:能救回来多少

论文做了一个非常有说服力的因果实验:

  • 固定生成代码不变
  • 只在每个失败依赖周围搜索相邻版本(最多 ±3 个 release index 邻居)
  • 看是否能恢复安装与静态兼容

结果:

  • 17.19%–55.00% 的 qualifying tasks 可以仅靠换邻近版本恢复安装
  • 7.81%–46.04% 可以进一步恢复到静态兼容
  • 跨所有模型,被替换后新引入 CVE 的恢复任务总共只有 5 个

这几乎是钉死结论了: 很多失败真不是代码写错,而是版本选错。

Mitigation probe:一句“注意安全”没啥用,外部 version anchor 才有用

作者比较了四种条件:

  • Baseline
  • abl-instruct:加自然语言安全提示,要求避开有 CVE 的版本
  • abl-version:为每个库注入预计算的较安全版本锚点
  • abl-rag:在 version anchor 基础上,再检索 top-20 API signatures 注入 prompt

结果非常直接:

  1. 安全提示几乎没用

    • 对大多数模型,漏洞暴露率只改善 0–2.2 个百分点。
  2. version anchoring 有显著效果

    • 任务漏洞暴露率 τU 压到 10.90%–27.60%
    • 静态兼容率提升到 80.92%–93.64%
    • 动态通过率提升到 36.31%–54.56%
  3. 再加 RAG 收益很有限

    • τC 只比 abl-version 多 0.10–2.05 个点
    • 动态通过率最多多 1.03 个点
    • 有时还会倒退

这说明版本风险的最优解不是“让模型自己想明白”,而是给它外部约束和 grounding。

复现评估

维度评分(1-5)详细说明
数据可得性⭐⭐⭐⭐⭐PinTrace 已公开,Stack Overflow / PyPI / OSV 都可复用。
代码可得性⭐⭐⭐⭐GitHub 已给出,但完整跑通仍要处理环境与 API 调度。
算力需求⭐⭐⭐10 模型 × 1000 任务不算便宜,但比大规模训练论文更可复现实验。
工程复杂度⭐⭐⭐⭐需要版本解析、OSV 索引、隔离环境安装、BigCodeBench 动测。
预期收益⭐⭐⭐⭐⭐对 IDE、Copilot 类产品、企业 DevSecOps、内部 coding platform 都很有现实价值。

复现建议: 最实用的复现实验不是全量抄论文,而是:

  1. 抽你们团队最常见的 100 个依赖型 coding task;
  2. 统计模型会不会显式钉版本;
  3. 把版本过一遍 OSV / pip audit / internal allowlist;
  4. 再测 install success 与 smoke tests。

批判性分析

局限性(论文承认的 + 我们额外看到的)

  1. 只覆盖 Python / PyPI

    • npm、Maven、Cargo 等生态未纳入,跨语言结论还不能直接外推。
  2. 依赖风险还没完全覆盖 transitive chain 的深层行为

    • 论文已经碰到 dependency conflicts,但对多层传递依赖的长期演化影响仍未完全展开。
  3. 真实企业项目的私有镜像、锁文件策略、内部仓库策略没有被模拟

    • 因此它更像“公共互联网开发场景”的强结论。
  4. baseline 指标和 prompting mode 强耦合

    • 某些结果会受到“你怎么让模型输出版本”这个提示设计影响,这既是现实,也是解释时要小心的地方。

改进方向

  1. 跨生态扩展

    • 下一步最该做 npm / Maven / Rust crate 版 PinTrace。
  2. 引入锁文件与企业策略

    • requirements.txtpoetry.lock、内部镜像 allowlist 对风险的实际缓释效果。
  3. 把安全与修复联动起来

    • 不只测“错没错”,还测模型能否在给定 vulnerability context 后自动修到安全版本。

独立观察

  1. 这篇论文实际上给“AI 代码生成安全”换了坐标系。

    • 以后不能只问“代码对不对”,还得问“依赖是不是被模型顺手带歪了”。
  2. 它和最近关于 constraint decay、maintainability 的工作连起来看,会得到一个更完整的判断:

    • coding agent 的问题越来越不是“生成不出来”,而是“生成得太像能用,但供应链和工程纪律在暗处漏水”。
  3. 对产品方最重要的建议不是换个更聪明模型,而是把外部 policy / OSV / allowlist / version anchoring 接进生成链路。

对领域的影响

短期内,这篇论文很可能推动:

  • IDE / agent 平台把 dependency guardrail 变成标配
  • 生成式代码评测从“逻辑漏洞”扩展到“版本漏洞”
  • 企业把 SCA(software composition analysis)前移到 LLM 生成时刻

中期内,更成熟的 coding agent 体系应该长成这样:

  • 模型负责生成意图与代码骨架
  • 外部 resolver / policy engine 负责版本落地
  • 安全与兼容性不再交给模型单独拍脑袋

一句话,这篇论文不是在说“模型写代码不行”,而是在说: 如果你让模型独自决定依赖版本,它会很稳定地把你带进一个又脆又不兼容的统计陷阱里。