第118题:为什么选该Embedding?通用语义相似是否等于漏洞推理相关?

1. 核心结论
通用语义相似:
Similarity(q,d) Similarity(q,d) Similarity(q,d)
不能直接等价于漏洞推理相关:
Relevancesecurity(q,d) Relevance_{\text{security}}(q,d) Relevancesecurity(q,d)
因为两个代码片段可以在:
- API;
- 函数名;
- 自然语言描述;
- CWE主题;
上高度相似,但它们对当前漏洞结论的作用完全不同。
所以我不会用:
“这个 Embedding 在通用榜单上效果好”
作为选择依据。
我会先定义:
什么文档才算对当前漏洞判断真正相关。
然后在相同数据、相同切分和相同计算预算下比较多个 Embedding,在:
- Recall@K;
- MRR;
- nDCG;
- 跨项目泛化;
- 延迟;
- 索引大小;
- 最终漏洞检测效果;
上选择最合适的模型。
如果当前材料没有这些实验,我会把:
“为什么选这个 Embedding”
明确列为待补实验,而不会声称它已经被证明是最优选择。
2. 为什么“语义相似”和“漏洞相关”不是同一个概念
通用 Embedding 通常希望满足:
$$
SemanticSimilarity(q,d_1)
SemanticSimilarity(q,d_2)
$$
意味着:
d1d_1d1 在自然语言或代码语义上与 qqq 更接近。
但漏洞分析真正需要的问题是:
这个候选证据能不能帮助判断当前代码为什么存在或不存在漏洞?
可以定义:
R(q,d)=1 R(q,d)=1 R(q,d)=1
表示文档 ddd 对查询 qqq 的安全结论提供有效证据。
这里的“有效证据”可能是:
- 相同漏洞机理;
- 相同危险 API 的正确/错误用法;
- 对应 Source;
- 对应 Sink;
- Sanitizer;
- 数据流传播关系;
- 调用链;
- 补丁模式;
- 攻击前置条件;
- 反例代码。
因此:
SemanticSimilarity≠SecurityReasoningRelevance SemanticSimilarity \neq SecurityReasoningRelevance SemanticSimilarity=SecurityReasoningRelevance
这是本题最重要的结论。
3. 一个最简单的反例
假设待分析代码是:
char buf[8];
strcpy(buf, input);
查询目标是判断:
是否存在 Buffer Overflow?
检索系统可能找到两个候选。
候选 A:
char dst[8];
strcpy(dst, src);
与查询代码非常相似。
候选 B:
if (strlen(input) >= sizeof(buf)) {
return ERROR;
}
memcpy(buf, input, strlen(input));
从表面 Token 来看,候选 A 可能更相似。
但漏洞推理真正需要比较的是:
是否检查输入长度
是否可能越界
目标buffer大小
危险函数调用条件
因此一个“表面不像”的安全反例可能比一个“代码很像”的样本更有判别价值。
4. 同API也不等于同漏洞
例如下面两段代码都调用:
memcpy()
第一段:
memcpy(dst, src, user_len);
第二段:
if (user_len <= sizeof(dst)) {
memcpy(dst, src, user_len);
}
它们具有:
相同API
相似Token
相似局部结构
但漏洞标签可能相反。
因此,如果 Embedding 主要学习:
SameAPI→HighSimilarity SameAPI \rightarrow HighSimilarity SameAPI→HighSimilarity
可能会把:
安全实现
和:
漏洞实现
都聚在一起。
对普通代码搜索这可能合理。
对漏洞判别却未必足够。
5. 同CWE也不一定是最相关文档
假设两个样本都属于:
CWE-78 OS Command Injection
一个漏洞来自:
shell=True
另一个来自:
system(user_input)
从分类层面:
CWEA=CWEB CWE_A=CWE_B CWEA=CWEB
但具体:
- Source;
- Propagation;
- Sanitization;
- Sink;
- Exploit Condition;
可能完全不同。
所以“同 CWE”可以作为弱相关信号,但不能自动作为最终 Ground Truth。
真正的 Retrieval Label 必须根据任务定义。
6. 第一步应该先定义Retrieval Relevance
我会先把任务定义成一句可以判断真假的话。
例如:
给定待分析函数 qqq,检索出的代码或文档 ddd 是否包含能够支持当前漏洞存在性判断的证据?
然后定义:
R(q,d)∈{0,1} R(q,d)\in\{0,1\} R(q,d)∈{0,1}
或者使用多级相关性:
R(q,d)∈{0,1,2,3} R(q,d)\in\{0,1,2,3\} R(q,d)∈{0,1,2,3}
例如:
| 等级 | 含义 |
|---|---|
| 0 | 无关 |
| 1 | 同主题/同API,但不能支持判断 |
| 2 | 同漏洞机理,具有参考价值 |
| 3 | 直接包含当前判断所需证据 |
这样训练和评测的 Embedding 才是在学习:
Task Relevance
而不只是:
Semantic Similarity
7. “相关文档”的标注单位也必须明确
不同任务下,相关文档可能完全不同。
7.1 CWE定义
适合回答:
这个漏洞类型的标准定义是什么?
7.2 历史漏洞代码
适合回答:
是否存在相似的漏洞实现模式?
7.3 修复Commit
适合回答:
类似问题历史上是如何修复的?
7.4 Source-Sink路径
适合回答:
不可信数据能否真正到达危险操作?
7.5 安全代码反例
适合回答:
什么情况下同一个API的使用是安全的?
所以不能把:
CWE
Patch
Code
Commit
Attack Path
全部混成一种“相关文档”。
它们承担不同推理角色。
8. 为什么通用Embedding只能作为候选基线
例如 Sentence-BERT 的目标是产生可以通过 Cosine Similarity 比较的语义向量。
CodeBERT 则学习 Programming Language 和 Natural Language 的通用代码表示,并在 Code Search 等任务上验证。
这些结果能够说明模型具备一定:
Semantic Representation
或:
Code Retrieval
能力。
但不能直接推出:
对漏洞推理任务也是最优Embedding。
因为训练目标不同。
可以形式化成:
Ppretrain(q,d)≠Psecurity(q,d) P_{\text{pretrain}}(q,d) \neq P_{\text{security}}(q,d) Ppretrain(q,d)=Psecurity(q,d)
存在 Domain Shift 和 Task Shift。
所以必须重新做安全任务内评测。
9. Code Search能力也不能直接等同于Security Retrieval能力
普通 Code Search 常见查询可能是:
find function that parses JSON
目标是找到功能语义相近代码。
而安全检索查询可能是:
这个user-controlled value是否未经净化进入SQL sink?
后一问题可能需要理解:
Data Flow
Control Flow
Call Relation
Sanitization
因此相关性函数不同。
可以表示成:
Rcode-search≠Rvulnerability R_{\text{code-search}} \neq R_{\text{vulnerability}} Rcode-search=Rvulnerability
即使使用同一个 Embedding,也需要针对新的相关性定义重新验证。
10. 程序结构信息可能比文本相似更重要
代码中的真实关系包括:
Definition
Use
Call
Import
Data Flow
Control Flow
Source
Sink
因此一个候选代码片段可能:
Token相似度很低
但通过 Call Graph 与当前函数直接关联。
例如:
controller.py
↓
service.py
↓
validator.py
↓
database.py
真正的安全证据可能位于:
validator.py
或者:
database.py
而不是文本最像的文件。
DraCo 在 Repository-Level Code Completion 中也观察到,单纯基于文本相似度或简单 import 关系得到的跨文件上下文可能不够相关,因此引入 Data-Flow-Guided Retrieval。
这不能直接证明漏洞检索必须使用 Data Flow,但说明:
代码任务相关性可能由程序关系决定,而非只由文本语义决定。
11. 为什么通常使用Bi-encoder做第一阶段召回
大规模检索库可能包含:
N=105∼108 N=10^5\sim10^8 N=105∼108
个代码 Chunk。
如果使用 Bi-encoder,可以分别计算:
eq=f(q) e_q=f(q) eq=f(q)
ed=g(d) e_d=g(d) ed=g(d)
文档向量:
ed e_d ed
可以提前离线计算并建立向量索引。
查询时只需要计算:
Similarity(eq,ed) Similarity(e_q,e_d) Similarity(eq,ed)
例如:
cos(eq,ed) \cos(e_q,e_d) cos(eq,ed)
或:
eq⊤ed e_q^\top e_d eq⊤ed
所以适合:
Large-Scale Candidate Retrieval。
12. 为什么可以再加Cross-encoder Reranker
Bi-encoder 的问题是:
Query
和:
Document
在编码阶段基本独立。
它很难像 Cross-Encoder 那样进行细粒度 Token-Level Interaction。
因此可以采用两阶段结构:
Bi-encoder
↓
Top-100
↓
Cross-encoder
↓
Top-10
第一阶段负责:
Recall Recall Recall
第二阶段负责:
Precision Precision Precision
即:
Retrieve
→
Rerank
对于漏洞任务,Cross-Encoder 可以进一步判断:
- API 是否真的起相同作用;
- Sanitizer 是否存在;
- Source-Sink关系是否一致;
- 候选是否只是表面相似。
13. Embedding应该比较哪些候选
至少可以加入几类模型。
13.1 Sparse Baseline
例如:
BM25
它对:
- API名称;
- 函数名;
- CVE;
- CWE;
- Identifier;
等精确词匹配可能很强。
因此必须保留。
13.2 通用Embedding
测试通用语义能力是否足够。
13.3 Code-specific Embedding
例如从代码语料训练的模型。
测试:
Programming Language Representation 是否改善检索?
13.4 Security-adapted Embedding
如果有足够漏洞相关性标签,可以进行任务微调。
比较:
Egeneral E_{\text{general}} Egeneral
Ecode E_{\text{code}} Ecode
Esecurity E_{\text{security}} Esecurity
才能回答:
为什么最终选择某一个Embedding?
14. Embedding选择首先看Recall@K
对于第一阶段召回,我最关注:
Recall@K Recall@K Recall@K
定义为:
$$
Recall@K
\frac{
\text{Top-K中命中的相关证据}
}{
\text{全部应召回证据}
}
$$
因为如果真正证据没有进入候选集:
Retriever Miss
后面的:
Reranker
LLM
Agent
都无法恢复。
所以第一阶段目标通常是:
先保证 Evidence Coverage。
15. 还要看MRR和nDCG
如果每个 Query 有一个关键证据,可以用:
$$
MRR
\frac{1}{|Q|}
\sum_q
\frac{1}{rank_q}
$$
它衡量关键证据出现得有多靠前。
如果一个 Query 有多个相关程度不同的证据,可以使用:
nDCG@K nDCG@K nDCG@K
因为它能够考虑:
不同相关等级
+
排序位置
例如:
直接Source-Sink Path
应该排在:
只是同CWE的历史代码
之前。
16. 不能只测离线Retrieval指标
最终目标是漏洞分析。
所以还需要检查:
Retriever→Generator Retriever \rightarrow Generator Retriever→Generator
之后到底有没有真实收益。
可以比较:
No Retrieval
Random Retrieval
BM25
Embedding Retrieval
Embedding + Reranker
Oracle Retrieval
然后记录:
F1vuln F1_{\text{vuln}} F1vuln
LocalizationAccuracy LocalizationAccuracy LocalizationAccuracy
EvidenceAccuracy EvidenceAccuracy EvidenceAccuracy
如果:
Recall@10↑ Recall@10\uparrow Recall@10↑
但最终:
F1vuln F1_{\text{vuln}} F1vuln
完全不变,那么可能说明:
检索指标改善没有转化成实际任务价值。
17. Oracle Retrieval是非常重要的上界实验
人工给出正确证据:
Doracle D_{\text{oracle}} Doracle
然后让同一个 Generator 推理。
得到:
Qoracle Q_{\text{oracle}} Qoracle
当前检索器得到:
Qretrieval Q_{\text{retrieval}} Qretrieval
如果:
Qoracle≫Qretrieval Q_{\text{oracle}} \gg Q_{\text{retrieval}} Qoracle≫Qretrieval
说明:
Retriever仍然是主要瓶颈。
如果:
Qoracle≈Qretrieval Q_{\text{oracle}} \approx Q_{\text{retrieval}} Qoracle≈Qretrieval
但最终效果仍不高,则问题更可能出现在:
Generator / Reasoning
而非 Embedding。
18. Exact Search可以区分Embedding和ANN问题
向量索引通常使用:
Approximate Nearest Neighbor
即 ANN。
但检索失败可能有两种原因。
第一种:
Embedding空间本身排错了
第二种:
Embedding排序正确
但ANN近似搜索没有找到真正近邻
因此可以比较:
Recall@KExact Recall@K_{\text{Exact}} Recall@KExact
和:
Recall@KANN Recall@K_{\text{ANN}} Recall@KANN
如果:
RecallExact≫RecallANN Recall_{\text{Exact}} \gg Recall_{\text{ANN}} RecallExact≫RecallANN
说明主要问题在索引参数或 ANN。
如果两者都低,则更可能是:
RepresentationProblem RepresentationProblem RepresentationProblem
这可以避免把向量索引问题误判成 Embedding 模型问题。
19. Hard Negative为什么重要
假设正样本:
unsafe strcpy usage
负样本只是随机找:
matrix multiplication
这种负样本太容易。
模型只需要学会:
字符串处理
vs
数学计算
就可以区分。
它没有学会真正的漏洞边界。
因此 Hard Negative 应该满足:
Similaritysurface↑ Similarity_{\text{surface}}\uparrow Similaritysurface↑
但:
Relevancesecurity=0 Relevance_{\text{security}}=0 Relevancesecurity=0
也就是:
看起来很像,但安全结论不同。
稠密检索研究如 ANCE 也指出,过于简单、无信息量的负样本会限制表示学习,Hard Negative 能够为 Retriever 提供更有效的判别信号。
20. 漏洞任务中的Hard Negative可以怎么构造
例如 Query 是:
某个SQL Injection漏洞代码
Hard Negative 可以是:
20.1 同API但安全
execute(parameterized_query)
20.2 同项目但安全
避免模型只学习:
repository identity
20.3 同CWE主题但机制不同
避免模型只看到:
CWE关键词
20.4 修复后的代码
例如:
Codevulnerable Code_{\text{vulnerable}} Codevulnerable
和:
Codepatched Code_{\text{patched}} Codepatched
局部结构高度接近,但标签相反。
这种样本特别有利于学习真正的安全决策边界。
21. Hard Negative也有False Negative风险
一个被当成负样本的代码:
unlabeled
不一定真的:
benign
如果它实际存在漏洞:
y=1 y=1 y=1
但训练标签写成:
y~=0 \tilde y=0 y~=0
那么 Contrastive Learning 会强迫模型:
sim(q,d)↓ sim(q,d)\downarrow sim(q,d)↓
这实际上是错误监督。
所以困难负样本需要做:
- 标签来源记录;
- 人工抽检;
- 补丁验证;
- 静态/动态证据复核。
越 Hard 的 Negative,越应该关注 False Negative。
22. 还需要做跨项目切分
如果同一个 Repository 同时出现在 Train 和 Test:
函数名
API组合
编码风格
目录结构
都可能形成 Shortcut。
因此安全检索应重点报告:
Recall@Kcross-repo Recall@K_{\text{cross-repo}} Recall@Kcross-repo
而不只看:
Recall@Krandom-split Recall@K_{\text{random-split}} Recall@Krandom-split
更严格时还可以做:
- Cross-project;
- Cross-time;
- Cross-CWE;
- Cross-language;
评测。
如果一个 Embedding 只在同项目随机切分有效,泛化证据较弱。
23. 为什么不能只根据Embedding维度选择
例如:
Model A: 384 dimensions
Model B: 768 dimensions
Model C: 1024 dimensions
更高维:
d↑ d\uparrow d↑
不代表:
RetrievalQuality↑ RetrievalQuality\uparrow RetrievalQuality↑
同时索引存储近似与:
N×d N\times d N×d
相关。
维度越大通常意味着更高:
- Index Memory;
- Network Cost;
- Similarity Computation Cost。
因此模型选择需要比较:
Quality Quality Quality
和:
Efficiency Efficiency Efficiency
两部分。
24. 一个合理的Embedding选择表
最终可以得到:
| 模型 | Recall@10 | MRR | Cross-Repo Recall | P95延迟 | Index Size | End-to-End |
|---|---|---|---|---|---|---|
| BM25 | … | … | … | … | … | … |
| General Embedding | … | … | … | … | … | … |
| Code Embedding | … | … | … | … | … | … |
| Security-tuned Embedding | … | … | … | … | … | … |
| Hybrid + Reranker | … | … | … | … | … | … |
然后选择:
$$
E^*
\arg\max_E
Utility(E)
$$
其中:
$$
Utility
f(
Recall,
Ranking,
Generalization,
Latency,
Memory,
EndToEnd
)
$$
这才是“为什么选该 Embedding”的实验依据。
25. 可以进一步加入Hybrid Retrieval
代码安全任务既需要:
Semantic Similarity
又可能依赖:
Exact Identifier Match
例如:
- API 名称;
- CVE;
- CWE;
- 函数名;
- 类名;
- 配置项。
因此可以同时使用:
BM25
+
Dense Embedding
生成候选。
再进行:
Fusion
↓
Rerank
这样可以避免 Dense Retriever 丢失重要的稀有标识符。
是否需要 Hybrid Retrieval 仍然必须通过消融实验确定。
26. 如何判断Embedding真正学到了漏洞相关性
我会设计成对反事实。
给定 Anchor:
危险API + 未校验输入
Positive:
不同变量名、不同项目
但具有相同漏洞机理
Hard Negative:
相同API、相似结构
但进行了正确校验
理想 Embedding 应满足:
$$
sim(A,P)
sim(A,N)
$$
即使:
$$
LexicalSimilarity(A,N)
LexicalSimilarity(A,P)
$$
如果模型能够稳定做到这一点,才更能支持:
Embedding 捕获的是漏洞机理,而不只是表面代码相似度。
27. 最关键的消融实验
至少比较:
BM25
General Semantic Embedding
Code Embedding
Task-tuned Embedding
Task-tuned Embedding + Hard Negatives
Task-tuned Embedding + Reranker
Oracle Retrieval
然后分别观察:
27.1 Retrieval
Recall@K, MRR, nDCG Recall@K,\ MRR,\ nDCG Recall@K, MRR, nDCG
27.2 End-to-End
Precision, Recall, F1 Precision,\ Recall,\ F1 Precision, Recall, F1
27.3 Efficiency
Latency, IndexSize, Cost Latency,\ IndexSize,\ Cost Latency, IndexSize, Cost
如果任务微调只改善:
Recall@K Recall@K Recall@K
但不改善最终任务,则不能宣称它提高了漏洞检测能力。
28. 什么结果会推翻“这个Embedding更合适”的主张
需要提前定义 Falsification Condition。
例如:
如果在相同:
- Retrieval Corpus;
- Chunk;
- Query;
- Split;
- ANN配置;
- Top-K;
- Generator;
条件下:
Recall@Kchosen≤Recall@Kbaseline Recall@K_{chosen} \le Recall@K_{baseline} Recall@Kchosen≤Recall@Kbaseline
且:
EndToEndchosen≤EndToEndbaseline EndToEnd_{chosen} \le EndToEnd_{baseline} EndToEndchosen≤EndToEndbaseline
同时:
Costchosen≥Costbaseline Cost_{chosen} \ge Cost_{baseline} Costchosen≥Costbaseline
那么:
“该 Embedding 更适合漏洞检索”
没有被实验支持。
此时应该换模型,或者降低主张。
29. 如果所有Embedding效果都差怎么办
这可能说明问题本身不适合单纯单向量检索。
例如安全相关性主要依赖:
Call Graph
Data Flow
Source-Sink
那么可以将:
SemanticRetriever SemanticRetriever SemanticRetriever
与:
ProgramStructureRetriever ProgramStructureRetriever ProgramStructureRetriever
结合。
例如:
Dense Retrieval
+
Call Graph
+
Data Flow
这时研究问题从:
哪个Embedding最好?
变成:
单纯语义表示是否足以建模安全相关性?
这通常是更有价值的问题。
30. 面试时可以压缩成下面这段
我不会因为某个 Embedding 在通用语义检索或 Code Search 榜单上表现好,就直接认为它适合漏洞检索。首先要定义“相关”:是同主题、同 API、同 CWE,还是能够真正支持当前漏洞结论。我更倾向最后一种,因为漏洞推理可能依赖 Source、Sink、Sanitizer、调用链和数据流,而这些信息不一定具有最高的文本语义相似度。
选择 Embedding 时,我会固定同一个 Retrieval Corpus 和数据切分,对 BM25、通用 Embedding、代码 Embedding 和任务微调 Embedding 比较 Recall@K、MRR、nDCG、跨项目泛化、P95延迟和索引大小。Bi-encoder负责大规模召回,必要时再用 Cross-Encoder 精排。
训练时 Hard Negative 应该尽量是表面相似但漏洞结论不同的样本,例如同 API 的安全代码,而不是随机无关代码,同时要复核 False Negative。
实验上我还会加入 Exact Search、ANN Search 和 Oracle Retrieval。这样可以区分问题到底来自 Embedding 表示、ANN索引还是 Generator。如果某个 Embedding 只提高离线 Recall@K,却没有改善最终漏洞检测结果,我不会把它称为有效的端到端贡献。
所以我的结论是:
通用语义相似不等于漏洞推理相关;Embedding 的选择必须由安全任务定义和任务内实验决定。
31. 当前资料的事实边界
原面经没有提供:
- 实际使用的 Embedding 名称;
- Embedding 维度;
- 是否经过安全领域微调;
- Retrieval Corpus;
- 相关性标注规则;
- Hard Negative 构造方法;
- Recall@K / MRR / nDCG;
- Cross-Repository结果;
- ANN参数;
- Reranker;
- End-to-End消融结果。
因此目前无法可靠回答:
为什么候选人的某个具体Embedding一定是最佳选择
当前能够可靠给出的结论是:
必须先定义漏洞推理相关性,再以任务内检索指标、跨项目泛化、系统成本和端到端效果比较候选Embedding。若尚未完成这些实验,应明确列为待补工作。
32. 来源
- Reimers & Gurevych, Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks, EMNLP-IJCNLP 2019:说明Bi-encoder式Sentence Embedding可以通过Cosine Similarity进行高效语义相似检索。
- Feng et al., CodeBERT: A Pre-Trained Model for Programming and Natural Languages, Findings of EMNLP 2020:提出面向Programming Language和Natural Language的通用预训练表示,并在Code Search等任务上评测。
- Thakur et al., BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models, NeurIPS Datasets and Benchmarks 2021:说明检索模型在不同任务与领域中的Zero-shot泛化差异明显,因此通用检索性能不能直接代表目标领域性能。
- Xiong et al., Approximate Nearest Neighbor Negative Contrastive Learning for Dense Text Retrieval, ICLR 2021:说明无信息量负样本会限制Dense Retrieval训练,并提出基于全局ANN检索的Hard Negative训练。
- Liu et al., RepoBench: Benchmarking Repository-Level Code Auto-Completion Systems, ICLR 2024:将Repository-Level系统拆分为Retrieval、Completion和完整Pipeline评测,说明检索质量和最终任务质量需要分别度量。
- Cheng et al., Dataflow-Guided Retrieval Augmentation for Repository-Level Code Completion, ACL 2024:指出基于文本相似度或简单Import关系得到的跨文件上下文可能与真实代码任务不够相关,并通过Data-Flow-Guided Retrieval引入程序结构相关性。
- Khattab & Zaharia, ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT, SIGIR 2020:展示Late Interaction在检索效率和细粒度Query-Document交互之间的另一种架构取舍。

515

被折叠的 条评论
为什么被折叠?



