安全工程

白盒扫描 Agent 不应该停在候选漏洞

OpenAnt 把仓库级漏洞发现拆成可达性裁剪、LLM 语义检测、对抗式验证和容器动态验证。本文从授权白盒扫描角度拆解一条 verified finding pipeline:候选、证据、反证、复现、报告放行、人工审核和可验证指标。

来源说明

本文基于 2026-06-21 的每日深度技术研究发布流程写成。今天筛选到的强信号来自 OpenAnt:它不是单独提出一个新的静态分析规则,而是把 LLM 放进仓库级白盒扫描的闭环里,用可达性裁剪降低分析面,用 LLM 做语义候选发现,再用对抗式验证和动态验证把候选压缩成更接近可复核的 finding。

核心来源如下:

  • Nahum Korda 与 Gadi Evron: OpenAnt: LLM-Powered Vulnerability Discovery Through Code Decomposition, Adversarial Verification, and Dynamic Testing, arXiv:2606.19149v2, submitted on 2026-06-17, revised on 2026-06-18。论文报告 OpenAnt 在 8 个开源项目、6 种语言上评测,流水线从 64,132 个函数裁剪到 2,281 个 reachable units、586 个 externally exploitable units、376 个 Stage 4 flagged findings、190 个 adversarially confirmed findings 和 144 个 dynamically verified findings;总评测成本为 1,461.25 美元。
  • OpenAnt GitHub: knostic/OpenAnt。仓库说明 OpenAnt 是 Apache-2.0 开源的 LLM-based vulnerability discovery product,支持 Go、Python,JavaScript/TypeScript、C/C++、PHP、Ruby 处于 beta;README 给出本地 CLI 流程 parse -> enhance -> analyze -> verify -> build-output -> report,并明确只能扫描自己拥有或明确授权测试的代码。
  • Knostic Blog: OpenAnt: Open Sourcing Knostic’s LLM-based Vulnerability Discovery Product。该文提供产品发布背景和技术说明入口;本文只把它作为项目方说明来源,不把营销表述当成独立实验结论。
  • 背景对照:GitHub Docs 的 CodeQL CLI SARIF outputSARIF support for code scanning 说明静态分析结果需要保留 rule、location、fingerprint 和 code flow 等机器可读证据;这为本文的 finding artifact 设计提供了工程参照。

事实边界:OpenAnt 的阶段、数据和限制来自论文、仓库与项目方说明;本文的 pipeline、数据模型、发布门、指标和一周实验计划是我的工程建议,不是 OpenAnt 作者声称的生产通用方案。本文只讨论授权白盒扫描、防御建设和安全工程,不讨论对第三方目标的未授权操作流程。

稳定 slug:2026-06-21-openant-verified-white-box-scanning-pipeline

先给结论

LLM 进入白盒扫描后,真正值得追求的不是“发现更多疑似漏洞”,而是把每个疑似漏洞推进到可复核、可回放、可拒绝、可交接的状态。

OpenAnt 给出的重要工程信号是:仓库级安全 Agent 不能只有一个 analyze repo 大 prompt。它需要一条漏斗式流水线:

flowchart LR
  A["授权代码库<br/>commit + scope"] --> B["代码解析<br/>functions / files / deps"]
  B --> C["可达性裁剪<br/>entrypoints -> reachable units"]
  C --> D["暴露面分类<br/>external / internal / control"]
  D --> E["LLM 候选检测<br/>semantic vulnerability reasoning"]
  E --> F["对抗式验证<br/>attacker capability + victim impact"]
  F --> G["动态验证<br/>sandboxed reproduction"]
  G --> H["Finding artifact<br/>evidence + verdict + limits"]
  H --> I["报告放行<br/>human review / triage / fix"]

我的判断是:安全团队应把 LLM 扫描器当成“候选生成器 + 验证编排器”,而不是事实判定者。最终能进入 PR、工单或发布门的对象不应是自然语言报告,而应是带结构化证据的 finding artifact

这和传统 SAST 的差异不是“LLM 更聪明”。差异在于闭环:静态规则倾向于从代码模式推出风险;LLM 候选检测可以读更宽的业务上下文,但更容易幻觉、过度推断或遗漏前置条件。验证层的职责就是把“看起来可疑”变成“在什么边界下成立、在什么边界下不成立”。

技术问题:候选漏洞不是安全结论

白盒扫描长期面对两个相反问题。

第一,静态分析会产生大量候选。很多候选只说明某条数据流、API 使用或代码模式值得看,不说明攻击者能否到达、权限边界是否满足、保护逻辑是否已覆盖,也不说明漏洞是否影响受害者数据。

第二,动态验证成本高。为每个候选搭环境、构造输入、跑验证、清理状态,不可能无差别覆盖整个仓库。传统 fuzzing 和集成测试也常受限于环境、认证、外部依赖和业务前置条件。

LLM 的价值在两者之间:它能帮助理解代码语义、业务路径、配置、接口约定和保护逻辑,也能生成验证假设。但如果没有验证闭环,LLM 只会把“候选更多”这个问题放大。OpenAnt 的论文把这个问题落到了工程数据上:8 个项目里,Stage 4 flagged findings 是 376 个,经过对抗式验证后剩 190 个,动态验证后剩 144 个。也就是说,LLM 检测层之后仍需要强过滤。

因此,我会把安全 Agent 的输出分成四级:

级别含义能否进入发布门需要保留的证据
Candidate模式或语义上可疑代码位置、规则或推理原因
Reachable candidate从授权入口可达通常否入口、调用链、路由、权限上下文
Confirmed finding对抗式验证认为满足攻击者能力和受害者影响可进入人工 triage威胁模型、反证、影响边界
Verified finding在隔离环境中复现或由等价证据证明可进入修复/发布门复现记录、环境、版本、输出、清理结果

这里最关键的是第二列和第四列。没有证据对象,报告再像样也只是叙述;没有状态级别,团队会把 candidate 当漏洞,把 verified 当绝对真理。

机制拆解:OpenAnt 的六段漏斗

OpenAnt 论文把系统拆成多阶段流水线。抽象到安全工程,可以理解为六段。

第一段:代码解析。 系统先把仓库拆成函数、文件、依赖和调用上下文。这个阶段不负责判漏洞,而是建立后续分析的单位。

第二段:分析单元生成与可达性过滤。 论文报告 OpenSSL 例子中,15,232 个函数先被裁剪到 390 个 reachable units,再通过暴露面分类降到 49 个 externally exposed units。这个数字不应被机械外推到所有项目,但方向很重要:仓库级扫描必须优先减少无关上下文,否则 LLM 成本和误报都会失控。

第三段:暴露面分类。 OpenAnt 将单元分成 exploitable、vulnerable-internal、security control 和 neutral 等类别。工程上这一步相当于给候选设置“攻击面身份”:外部输入能不能到达?是否只是内部辅助函数?是否其实是认证、校验、清洗逻辑?

第四段:漏洞候选检测。 LLM 在裁剪后的单元上做语义安全分析。它能覆盖传统规则难以表达的业务漏洞,例如缺失授权、状态机错误、跨对象访问和不完整校验。但它输出的仍是 flagged,不是最终结论。

第五段:对抗式验证。 这是 OpenAnt 最值得借鉴的部分。验证者不只是问“这是不是漏洞”,而是从攻击者能力、受害者影响、保护逻辑和现实前置条件出发,尝试否定或确认候选。工程上应要求它同时产出支持证据和反证。

第六段:动态验证。 论文描述 OpenAnt 会自动生成验证环境,在 sandboxed containers 中执行并丢弃环境。这个阶段的价值不是追求 100% 复现所有漏洞,而是把最重要的一批 finding 推进到可以被人和 CI 复核的状态。

我会把这六段映射成一个可落地的内部对象模型:

type FindingArtifact = {
  id: string;
  repo: string;
  commit: string;
  scope: "authorized-white-box";
  status: "candidate" | "reachable" | "confirmed" | "verified" | "rejected";
  category: string;
  locations: Array<{ path: string; startLine: number; endLine?: number }>;
  entrypoints: Array<{ kind: "route" | "cli" | "job" | "api"; name: string }>;
  evidence: Array<{ kind: "code" | "trace" | "test" | "config"; ref: string; hash?: string }>;
  counterEvidence: Array<{ claim: string; ref: string }>;
  attackerModel: {
    authenticated: boolean;
    requiredRole?: string;
    controlsVictimData: boolean;
    requiredPreconditions: string[];
  };
  verification: {
    method: "static" | "adversarial-review" | "dynamic-sandbox";
    command?: string;
    containerImage?: string;
    resultRef?: string;
    cleanupVerified: boolean;
  };
  reviewerDecision?: "accept" | "needs-more-evidence" | "false-positive" | "risk-accepted";
};

这个模型刻意把 counterEvidence 放进一等字段。安全 Agent 最容易失败的地方不是找不到可疑代码,而是没有把“为什么它可能不是漏洞”写进报告。

工程判断:验证层要有否决权

如果团队只加一个 LLM 扫描步骤,结果通常是更多 backlog。如果团队把 LLM 用在“发现、解释、验证、复现、交接”多个位置,必须让验证层拥有否决权。

我会按下面的发布门设计:

发布门自动条件人工条件失败处理
Candidate gate有代码位置、类别、初步理由不完整则丢弃
Reachability gate至少一条入口路径或明确内部边界路径不清时抽样复核降级为 internal note
Adversarial gate攻击者能力、受害者影响、反证均结构化高危类别必须安全工程师复核标记 needs-more-evidence
Dynamic gate隔离环境、命令、输出、清理记录存在涉及破坏性副作用必须人工批准不复现但保留 confirmed
Report gate叙述与 artifact 状态一致修复建议和影响范围复核阻断发布或降级摘要

这里有两个边界要说清楚。

第一,动态验证不是所有 finding 的硬门槛。权限绕过、业务逻辑、竞态、环境相关问题可能很难自动复现;强行要求复现会漏掉真实风险。更稳妥的做法是把 confirmedverified 分开,报告里明确证据等级。

第二,对抗式验证不是让 Agent “自由攻击”。在授权白盒流程里,它应该运行在受控代码库、受控环境和明确威胁模型内,输出的是可复核假设与反证,不是对第三方系统的操作指南。

适用场景

这条 verified finding pipeline 适合三类场景。

第一,开源维护者或企业内部安全团队希望对自己拥有的仓库做深度扫描,并且愿意为高价值 finding 支付 LLM 和环境成本。

第二,传统 SAST 已经产生大量误报,团队需要用可达性、权限上下文和动态验证给告警排序,而不是把所有命中都推给开发。

第三,研发平台希望把安全检查接入 PR 或 release gate,但不想让 LLM 直接决定阻断。此时可以让 Agent 生成候选和证据包,最终由策略机和人决定是否阻断。

不适合的场景也很明确:

  • 没有授权边界的外部目标扫描。
  • 不能提供隔离验证环境的生产系统。
  • 需要低成本、低延迟、每次 commit 全量深扫的路径。
  • 组织没有安全 triage 人员,只希望工具“自动判定全部漏洞真假”。

失败模式

这类系统的失败模式至少包括八类。

失败模式结果缓解措施
入口识别漏掉真实攻击面漏报入口枚举测试、框架适配、人工抽样
可达性过度裁剪把真实漏洞裁掉保留裁剪日志和被排除样本
LLM 过度推断业务语义误报要求引用代码证据和反证
验证者和发现者同偏差错误被两阶段共同确认分模型、分 prompt、分证据源运行
动态环境不等价复现失败或虚假复现固定镜像、依赖、配置和数据种子
报告层夸大证据等级candidate 被写成 verified报告从 artifact 渲染,不允许自由改 verdict
成本失控无法进入 CI/CD先做入口裁剪、缓存、增量扫描和预算阈值
高危动作无审批验证过程产生副作用sandbox、只读凭据、人工批准和清理检查

这些问题说明,安全 Agent 的难点不只是 prompt engineering,而是安全流水线本身要像软件交付系统一样可观测、可回放、可限制。

一个工程落地方案

我会用三层架构实现第一版。

flowchart TB
  subgraph Source["授权输入层"]
    R["repo@commit"]
    P["policy.yaml<br/>scope + allowed actions"]
    E["entrypoints.yaml"]
  end

  subgraph Pipeline["扫描与验证层"]
    Parse["parser / indexer"]
    Reach["reachability filter"]
    Detect["LLM detector"]
    Verify["adversarial verifier"]
    Dyn["dynamic verifier<br/>sandbox"]
  end

  subgraph Control["控制与交接层"]
    Store["artifact store"]
    Gate["report gate"]
    Human["security review"]
    Ticket["issue / PR / release gate"]
  end

  R --> Parse --> Reach --> Detect --> Verify --> Dyn --> Store
  P --> Reach
  P --> Dyn
  E --> Reach
  Store --> Gate --> Human --> Ticket

目录结构可以这样开始:

security-agent/
  policy.yaml
  entrypoints.yaml
  scans/
    repo-name/
      commit-sha/
        units.jsonl
        reachability.jsonl
        candidates.jsonl
        confirmations.jsonl
        dynamic-runs/
        findings.jsonl
        report.md
  prompts/
    detector.md
    adversarial-verifier.md
    report-renderer.md
  scripts/
    run-incremental-scan.sh
    verify-artifact-schema.ts
    render-report.ts

policy.yaml 至少应包含:

scope:
  mode: authorized-white-box
  repo: example/service
  allowed_paths:
    - src/
    - packages/api/
  denied_paths:
    - secrets/
dynamic_verification:
  allow_network: false
  allow_external_targets: false
  require_container: true
  require_cleanup_check: true
approval:
  require_human_for:
    - destructive_test
    - production_credential
    - external_callback
reporting:
  min_status_for_blocking: verified
  allow_confirmed_as_warning: true

报告生成应从 findings.jsonl 渲染,而不是让 LLM 从聊天历史自由总结。LLM 可以写解释,但结论字段、状态、证据等级、代码位置和复现状态必须来自 artifact。

可验证指标

我会用下面这组指标判断系统是否真的有工程价值。

指标为什么重要目标形态
Analysis surface reduction控制成本和噪声函数数、reachable units、exposed units 分阶段记录
Candidate-to-confirmed ratio衡量检测层噪声每类漏洞单独统计,不能只看总数
Confirmed-to-verified ratio衡量动态验证能力记录失败原因:环境、前置条件、模型、非漏洞
Evidence coverage防止报告空转每个结论至少有 location、entrypoint、reason、counterEvidence
Human reversal rate衡量自动验证可信度人工 triage 推翻比例按阶段回流
Time per verified finding判断 ROILLM 成本、环境成本、人工时间分开记
Regression utility修复后是否能防回归finding 能否转成测试、规则或查询
Safety incidents验证过程是否越界网络、凭据、文件写入、清理失败必须记账

指标里最值得盯的是 human reversal rate。如果人工经常推翻 verified,说明动态验证环境或报告 gate 有问题;如果人工经常推翻 candidate,说明检测层太宽,但未必是坏事,关键看成本能否接受。

我会如何实现和验证

第一周我不会直接做全仓库自动漏洞发现,而会选一个授权内部服务做小范围实验。

Day 1:定义边界。 固定 repo、commit、允许路径、入口类型和禁止动作。先不接生产凭据,不允许外网访问,不允许对第三方目标发请求。

Day 2:建立索引和入口表。 用现有语言工具抽取函数、路由、CLI、job 和调用关系。对 20 个入口做人工抽样,确认没有明显漏枚举。

Day 3:跑候选检测。 只扫描 reachable units。候选必须输出代码位置、漏洞类别、攻击者模型、受害者影响和至少一个反证问题。

Day 4:跑对抗式验证。 用独立 prompt 或独立模型验证候选,要求验证者优先寻找不可利用理由。没有反证字段的候选不能升级。

Day 5:做隔离动态验证。 只选 3 到 5 个高置信候选进入 sandbox。记录镜像、命令、配置、输出和清理结果。

Day 6:人工 triage。 安全工程师只看 artifact 和最小代码上下文,标记 accept、false positive、needs more evidence 或 risk accepted。

Day 7:复盘 ROI。 计算候选数、confirmed 数、verified 数、人工推翻率、总成本、每个 verified finding 的时间和费用。只有当 verified finding 能转化成修复、回归测试或规则时,才扩大范围。

这个实验的目标不是证明“Agent 能取代 AppSec”,而是判断 Agent 是否能把安全工程师从低价值候选里解放出来,同时不损失证据质量。

局限分析

OpenAnt 的数据足够有启发,但不能简单推成“LLM 白盒扫描已解决”。我会保留几个限制。

第一,论文评测集中在 8 个开源项目,虽然覆盖多语言,但不同企业仓库的框架、认证、部署拓扑和数据边界差异很大。可达性裁剪和动态验证都需要项目适配。

第二,动态验证无法覆盖所有真实漏洞。很多业务逻辑漏洞依赖角色、历史数据、组织策略或外部服务状态,自动 sandbox 很难完整复刻。

第三,成本不是小问题。论文报告 8 个项目总成本为 1,461.25 美元,而且不同项目差异明显。生产落地必须做增量扫描、预算阈值、缓存和高风险路径优先级。

第四,LLM 验证仍可能同源偏差。发现者和验证者如果共享相同错误假设,流水线会给错误结论制造“二次确认”的错觉。因此验证 prompt、模型、证据源和人工抽样需要刻意分离。

第五,报告层仍可能污染结论。即使前面有 artifact,最后的自然语言报告如果自由生成,仍可能把 confirmed 写成 verified,或遗漏重要前置条件。报告必须从结构化 verdict 渲染,并做一致性检查。

自审

  • 事实可靠性:核心事实来自 arXiv 论文、GitHub 仓库、Knostic 项目说明和 GitHub 官方文档;论文数据按作者报告表述,没有写成独立复现实验结果。
  • 来源完整性:已给出论文、仓库、项目方博客和 SARIF/CodeQL 背景文档链接。
  • 是否只是复述:不是。本文把 OpenAnt 抽象成 verified finding pipeline,并给出 artifact 模型、发布门、目录结构、策略示例、指标和一周实验计划。
  • 是否标题党:标题对应核心判断,即白盒扫描 Agent 的产物不能停在 candidate。
  • 是否薄内容:包含机制图、表格、数据模型、工程落地方案、验证指标和具体实验计划。
  • 是否把猜测写成事实:OpenAnt 的评测数据和支持范围明确归因给论文/仓库;pipeline 和落地方案明确标为我的工程建议。
  • 站内重复:本站 2026-06-20 讨论 LLM-solver narration gap,本文讨论候选漏洞到 verified finding 的白盒扫描漏斗;主题相邻但不重复。
  • 安全边界:全文限定授权白盒扫描、防御建设、sandbox 和人工审核,没有提供攻击第三方目标的操作流程。