ugiordan/code-claim-verifier
GitHub: ugiordan/code-claim-verifier
CodeClaimVerifier 通过确定性的代码检查手段(grep、文件读取、lockfile 解析)对 LLM 关于源代码的事实性声明进行自动化验证,以识别和拦截 LLM 幻觉,提升代码安全分类结果的可信度。
Stars: 0 | Forks: 0
# CodeClaimVerifier
对 LLM 关于源代码的声明进行确定性验证。验证过程无需任何 LLM 调用。
## 问题所在
LLM 在推理代码时会做出事实性断言:“函数 X 没有调用者”、“包版本是 2.4.1”、“此文件是自动生成的”。这些断言驱动着真实的决策:分类裁定、审查批准、迁移计划。但 LLM 会产生幻觉,却没有人去检查它们关于代码的声明是否真的属实。
**如果没有 CCV**,LLM 可能会通过产生“无调用者”的幻觉,来忽略一个在被调用了三处的函数上真实存在的漏洞。或者它可能会声称使用了一个危险的函数(而实际上并没有),从而将一个非问题升级为风险。这两者都会浪费时间并带来风险。
**有了 CCV**,LLM 所做的每一个关于代码的事实性声明都会被提取、分类,并使用 grep、文件读取和 lockfile 解析与实际的代码库进行核对验证。grep 不会产生幻觉。
## 实际作用
```
LLM says: "torch.load() at model.py:42 has no callers.
The vulnerability is dead code."
CCV extracts 3 claims:
FILE_EXISTS(path=model.py) -> VERIFIED (file exists)
FUNCTION_CALLED(name=torch.load) -> VERIFIED (call sites found)
HAS_CALLERS(name=torch.load, false) -> REFUTED (grep found 2 callers)
Result: 67% verified, action=FLAG
"The LLM claimed torch.load has no callers, but grep found 2 call sites.
Re-triage this finding."
```
LLM 在关于调用者的判断上错了。如果没有 CCV,这个漏洞就会被当作死代码而忽略。有了 CCV,它就会被标记出来以供人工审查。
## 应用场景
### 捕捉漏报(遗漏的漏洞)
一个正在对 CVE 进行分类的 LLM 说:“有漏洞的函数 `yaml.load()` 没有在代码库的任何地方被导入。这个 CVE 不影响我们。”
CCV 提取出 `IMPORT_EXISTS(module=yaml)`,对仓库进行 grep 搜索,在 `config/parser.py` 中找到了 `import yaml`。LLM 关于“未被导入”的声明被驳回(REFUTED)。CVE 保持打开状态,而不是被错误地关闭。
### 捕捉误报(错误警报)
一个正在审查代码的 LLM 说:“函数 `process_input()` 在被调用时没有进行过滤清理。这是一个注入漏洞。”
CCV 提取出 `FUNCTION_CALLED(name=process_input, expected=true)`。Grep 发现调用点为零(该函数在上个冲刺中已被移除)。LLM 的声明被驳回(REFUTED)。安全团队就不会浪费时间去调查一个不存在的调用路径。
### 校准置信度
一个 LLM 生成了包含 5 个事实性声明的分类报告。CCV 验证了其中 4 个(80% 的验证率)。操作:提升(BOOST)。该分类结果是可靠的。
另一个 LLM 的分类报告有 3 个声明,1 个被验证,2 个被驳回(33% 的验证率)。操作:覆盖(OVERRIDE)。该 LLM 正在产生幻觉。不要相信这个分类结果,重新运行或升级给人工处理。
| 验证率 | 操作 | 含义 |
|---|---|---|
| 80-100% | 提升 (BOOST) | 声明经核实无误。信任 LLM 的结论。 |
| 50-79% | 标记 (FLAG) | 部分声明核实失败。应由人工审查。 |
| <50% | 覆盖 (OVERRIDE) | 大部分声明有误。不要相信此输出。 |
## 工作原理
```
LLM Reasoning (text)
|
v
[1. Extract Claims] -- 1 LLM call, structured output
| "torch.load has no callers" -> HAS_CALLERS(name=torch.load, expected=false)
v
[2. Build Dependencies] -- 0 LLM calls
| HAS_CALLERS depends on FUNCTION_EXISTS
| synthesize missing prerequisites
v
[3. Verify Each Claim] -- 0 LLM calls
| grep, os.path.exists, lockfile parse
| language-aware patterns (Python, Go, TS, Java, C, Rust)
v
[4. Propagate Failures] -- 0 LLM calls
| if FILE_EXISTS is REFUTED, flag all dependent claims as SUSPECT
v
[5. Calibrate] -- 0 LLM calls
| weighted verification rate -> action (BOOST/FLAG/OVERRIDE)
v
VerificationReport
```
提取过程仅需一次 LLM 调用。验证过程无需任何 LLM 调用。验证步骤使用的工具与开发人员检查时使用的相同:此文件存在吗?此函数是否已定义?此包是否处于此版本?
## 快速开始
```
from code_claim_verifier import CodeClaimVerifier
verifier = CodeClaimVerifier(
llm_function=my_llm_call, # (system_prompt, user_prompt) -> str
repo_path="/path/to/repo",
)
report = verifier.verify(
reasoning="torch.load() at model.py:42 has no callers in the codebase...",
evidence={"call_chain": [], "mitigations_found": ["sanitizer at util.py:15"]},
finding_file="model.py",
)
print(report.verification_rate) # 0.67
print(report.action) # "FLAG"
print(report.hallucination_rate) # 0.33
# 检查单个 claim
for claim in report.per_claim:
print(f" {claim.claim.claim_type}: {claim.verdict} ({claim.evidence[:80]})")
```
## 17 种声明类型
| 类别 | 类型 | 验证方式 |
|---|---|---|
| **文件/路径** | FILE_EXISTS, LINE_CONTENT, FILE_CLASSIFICATION, GENERATED_OR_VENDORED | `os.path.isfile()`、文件读取、路径正则匹配、头部标记 |
| **函数** | FUNCTION_EXISTS, FUNCTION_CALLED, HAS_CALLERS | 针对定义和调用点进行具有语言感知能力的 grep 搜索 |
| **依赖项** | IMPORT_EXISTS, PACKAGE_VERSION, DEPENDENCY_TYPE, CVE_AFFECTS_VERSION | 导入 grep、lockfile 解析、使用 [OSV API](https://osv.dev/) 进行 CVE 检查 |
| **代码** | ABSENCE, MITIGATION_EXISTS, ENTRY_POINT | 限定范围的 grep(取反)、文件读取、框架模式 grep |
| **Auth Chain** | CALL_CHAIN, DEFAULT_VALUE, CONFIG_FLAG | 多跳调用路径 grep、默认值/nil 检查、配置标志 grep |
每种类型都有记录在案的置信度水平(对于缺失声明为 0.60,对于文件存在为 0.99),反映了验证方法的精确度。
## 声明链
LLM 的声明具有隐含的依赖关系。如果文件不存在,“第 42 行包含 `torch.load()`”就毫无意义。
CCV 会自动推断这些依赖关系:
- LINE_CONTENT 依赖于 FILE_EXISTS(同一路径)
- FUNCTION_CALLED 依赖于 FUNCTION_EXISTS(同一名称)
- CALL_CHAIN 依赖于 FUNCTION_EXISTS(链中的每个函数)
- IMPORT_EXISTS 依赖于 FILE_EXISTS(同一文件)
如果文件不存在,所有关于其内容的声明都会被标记为可疑(SUSPECT),并降低置信度。如果函数不存在,关于它被调用的声明就会被标记。
这可以捕捉连锁幻觉:LLM 虚构了一个文件,然后对其内容做出详细的声明。CCV 会驳回文件存在的声明,并标记其下游的所有内容。
## CPG 集成
为了在函数声明上获得更高的准确性,CCV 可以使用来自 [architecture-analyzer](https://github.com/ugiordan/architecture-analyzer) 的代码属性图(CPG)。这将用精确的 AST 级别查询来取代 grep,适用于:
- **FUNCTION_EXISTS**:精确节点查找,取代正则匹配
- **FUNCTION_CALLED / HAS_CALLERS**:调用边遍历,取代 grep 启发式方法
- **CALL_CHAIN**:通过实际调用图进行多跳路径查询
- **ENTRY_POINT**:来自 CPG 的 HTTP endpoint 节点
首先,在你的代码库上运行 architecture-analyzer 以生成 CPG:
```
arch-analyzer scan --repo /path/to/repo --output /path/to/output/
```
然后将其加载到 CCV 中:
```
verifier = CodeClaimVerifier(llm_function=my_llm, repo_path="/path/to/repo")
verifier.load_cpg("/path/to/output/code-graph.json")
# CPG 自动加载。Function claim 使用 AST 查询。
```
## 批量验证
通过共享缓存和自适应批处理验证多项发现:
```
reports = verifier.verify_batch(
items=[
{"reasoning": "...", "evidence": {}, "finding_file": "model.py"},
{"reasoning": "...", "evidence": {}, "finding_file": "util.go"},
],
domain_context="security triage",
)
```
多项批处理共享一个 grep 缓存(相同的模式只需搜索一次),并使用较少的 LLM 调用进行提取。依赖图是针对每个发现的(不会发生交叉污染)。
## 自定义声明类型
为 CCV 开箱即用未覆盖的声明注册特定领域的验证器:
```
from code_claim_verifier.types import TypedClaim, VerifiedClaim
def verify_has_decorator(claim: TypedClaim, repo_path: str, language: str) -> VerifiedClaim:
from code_claim_verifier.grep import grep
pattern = f"@{claim.parameters['decorator']}\\s*\\ndef\\s+{claim.parameters['function']}"
matches = grep(pattern, repo_path)
return VerifiedClaim(
claim=claim,
verdict="VERIFIED" if matches else "REFUTED",
method_confidence=0.85,
evidence=matches[0][:200] if matches else "no match",
method="grep_decorator",
)
verifier.register(
claim_type="HAS_DECORATOR",
verifier_fn=verify_has_decorator,
extraction_hint="HAS_DECORATOR: {function: str, decorator: str} - checks if a function has a specific decorator",
depends_on=[("FILE_EXISTS", "file", "path")],
)
```
自定义类型可获得与内置类型相同的缓存、链接和校准功能。
## CLI
```
# 验证单个 finding
python -m code_claim_verifier verify \
--repo /path/to/repo \
--reasoning "torch.load() is called at model.py:42" \
--llm-provider anthropic
# 从 JSON 进行批量验证
python -m code_claim_verifier verify-batch \
--repo /path/to/repo \
--input findings.json
# 列出所有 claim 类型及其参数 schema
python -m code_claim_verifier list-types
# 针对 fixture repo 运行评估
python -m code_claim_verifier eval \
--dataset eval/dataset.jsonl \
--fixtures eval/fixtures/
```
## 用于 Agent 集成的 Tool Schema
将 CCV 作为工具暴露给任何 LLM Agent 框架:
```
tools = verifier.as_tools() # includes custom types
tools = CodeClaimVerifier.default_tools() # built-in types only
```
以标准的工具使用格式返回 `extract_claims`、`verify_claim`、`verify_all`、`list_claim_types`。
## 评估框架
内置评估功能,用于衡量验证质量:
```
python -m code_claim_verifier eval \
--dataset eval/dataset.jsonl \
--fixtures eval/fixtures/ \
--output report.json
```
三个阶段:
1. **提取质量**:声明提取相对于真实情况的精确率/召回率
2. **验证准确性**:按声明类型划分的正确判定、混淆矩阵、错误驳回/验证率
3. **校准分析**:基于类型的预测置信度与实际准确率对比、ECE 得分
## 安装
```
pip install code-claim-verifier
```
安装用于 CLI 的 LLM provider:
```
pip install code-claim-verifier[anthropic]
pip install code-claim-verifier[openai]
```
从源码安装:
```
git clone https://github.com/ugiordan/code-claim-verifier
cd code-claim-verifier
pip install -e ".[test]"
```
## 核心设计原则
- **验证过程无需任何 LLM 调用。** grep 不会产生幻觉。`os.path.exists()` 不会产生幻觉。
- **语言感知。** 支持 Python、Go、TypeScript、Java、C/C++、Rust 的函数/导入模式。
- **将缺失(Absence)视为一等公民。** “不存在调用者”是安全分类中最重要的声明,而 CCV 会对其进行验证。
- **声明链。** 通过依赖传播捕捉连锁幻觉。
- **线程安全。** 基于 contextvars 的缓存,可安全用于 Agent 框架中的并发使用。
- **零依赖。** 核心库仅使用标准库。CLI 为可选的 provider。
- **领域无关。** 安全分类、代码审查、重构、迁移、文档记录、架构评估。
## 文档
完整文档:[ugiordan.github.io/code-claim-verifier](https://ugiordan.github.io/code-claim-verifier/)
- [快速入门](https://ugiordan.github.io/code-claim-verifier/getting-started/installation/)
- [17 种声明类型](https://ugiordan.github.io/code-claim-verifier/guides/claim-types/)
- [API 参考](https://ugiordan.github.io/code-claim-verifier/reference/api/)
- [架构](https://ugiordan.github.io/code-claim-verifier/architecture/overview/)
## 背景
受 [Claimify](https://arxiv.org/abs/2502.10855) (ACL 2025) 启发,通过确定性验证将其扩展到代码领域:
- Claimify 仅支持提取。CCV 添加了确定性验证。
- Claimify 会过滤掉缺失声明。CCV 将其视为一等公民。
- Claimify 针对文本进行验证。CCV 针对源代码进行验证。
- Claimify 使用 LLM 作为评判标准。CCV 在验证时无需任何 LLM 调用。
## 许可证
Apache 2.0
## 版权
版权所有 (c) Red Hat, Inc.
标签:DLL 劫持, 事实核查, 代码审查, 大语言模型, 安全分诊, 自动化验证, 逆向工具, 错误基检测, 静态代码分析