- 静态分析
- SAST
- 应用安全
- 漏洞扫描
- 代码质量
【免费下载链接】codeql
CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security
本文以 CodeQL 仓库中 C# 查询库(csharp/ql/lib)的 5.0.0 版本变更说明 csharp/ql/lib/change-notes/released/5.0.0.md 为主线,系统梳理该版本引入的破坏性 API 变更与 C# 13 新增分析能力:UnboundGenericType.getInstanceType与ControlFlowGraph中Node.getElement的移除、ReadOnlySpan重载的 MaD 数据流模型、OverloadResolutionPriority特性支持,以及部分属性(partial properties)与索引器支持。读完本文,你将了解如何在升级 CodeQL C# 查询库时完成对应代码迁移,并能利用新增的 QL 类与模型文件扩展 C# 13 代码的污点追踪与数据流分析。
一、版本定位:C# 13 / .NET 9 全量支持的衔接版本
在仓库总变更日志 csharp/ql/lib/CHANGELOG.md 中,5.0.0 位于 4.0.x 之后、6.0.0 之前。紧随其后的 6.0.0 版本宣布"Full support for C# 13 / .NET 9. All new language features are now supported by the extractor"(见 csharp/ql/lib/CHANGELOG.md#L340-L346),而 5.0.0 正是为 C# 13 全面落地铺路的衔接版本:它一面清理历史遗留的废弃 API(Breaking Changes),一面为 C# 13 的三项新语言特性补上 QL 库支持与数据流模型。
因此,5.0.0 的变更可以归纳为两条主线:
- 破坏性变更:删除两个已废弃的 QL 谓词,属于 API 清理与语义对齐;
- 分析能力增强:围绕 C# 13 的
ReadOnlySpan重载建模、OverloadResolutionPriority特性与 partial 成员,扩展了数据流模型与 QL 类。
下文分别展开。
二、破坏性变更:两个废弃谓词的移除与迁移
2.1 删除UnboundGenericType.getInstanceType
5.0.0 删除了UnboundGenericType类上的废弃谓词getInstanceType。该类的定义位于 csharp/ql/lib/semmle/code/csharp/Generics.qll#L128-L164,从源码注释可见:
An unbound generic type. This is a generic type with type parameters (for example
List<T>) or elided type parameters (for exampleList`1).
即"未绑定泛型类型"代表List<T>这类带类型参数的泛型声明本身,与之相对的是G<int>这类已构造(constructed)类型。UnboundGenericType当前提供的核心接口包括:
getAConstructedGeneric():获取该未绑定泛型的一个已构造版本,涵盖封闭构造类型(如G<int>)与开放构造类型(如class Other<T> { G<T> g; }中的G<T>);getUnboundDeclaration():获取自身的未绑定声明;getChild(int n)/getTypeParameter(n):遍历类型参数;toStringWithTypes()/getName():类型名输出。
被删除的getInstanceType正是从"未绑定泛型"反查"实例类型"的旧入口。从仓库当前代码看,这一查询意图已由类型体系自身的继承关系(ConstructedType与UnboundGeneric家族)承载,例如Conversion.qll中通过ConstructedType.getUnboundGeneric()在两种类型间来回切换(见 csharp/ql/lib/semmle/code/csharp/Conversion.qll#L110-L118)。
迁移建议:若你的自定义查询中调用了UnboundGenericType.getInstanceType(),需改为通过ConstructedType/ValueOrRefType的类型层次遍历来获取实例类型;典型替代模式是使用getAConstructedGeneric()获得构造类型后再取具体实例。
2.2 删除ControlFlowGraph.qll中Node.getElement,改用getAstNode
第二个破坏性变更影响控制流图(CFG)API:ControlFlowGraph.qll的Node类上废弃的getElement谓词被删除,官方迁移路径是改用getAstNode。
公共模块 csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll 通过private import internal.ControlFlowGraph引入内部实现,而内部实现 csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll#L71-L74 中定义了:
module Ast implements AstSig<Location> { class AstNode = ControlFlowElementOrCallable; ... }可见AstNode是"控制流元素或可调用对象"的别名,getAstNode()返回的正是与 CFG 节点对应的 AST 节点(ControlFlowElement或Callable)。仓库内部的既有代码已经全面迁移到新 API,例如 csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowReachability.qll#L14-L16 中:
node.getAstNode() = result not exists(node.getAstNode()) and result = node.getEnclosingCallable()迁移建议:将自定义查询中的node.getElement()直接替换为node.getAstNode()。两者语义一致(返回 CFG 节点对应的 AST 元素),旧名仅是历史遗留的别名,替换不改变分析结果。
三、C# 13:ReadOnlySpan重载的 MaD 数据流模型
3.1 背景:MaD(Model-as-Data)模型机制
CodeQL 的数据流分析将"哪些库调用是污点源/汇/传递步骤"声明为外部模型(external models),以 YAML 形式存放在csharp/ql/lib/ext/目录下。这些模型通过可扩展谓词summaryModel被加载进分析,其定义见 csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlowExtensions.qll#L42-L45:
extensible predicate summaryModel( string namespace, string type, boolean subtypes, string name, string signature, string ext, string input, string output, string kind, string provenance, QlBuiltins::ExtensionId madId );模型列语义在 csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll#L60-L95 中有权威说明,要点如下:
input/output描述数据流端点:Argument[n](参数,this表示限定符)、ReturnValue(返回值)、Parameter等;- 端点可追加后缀:
.Element(选择集合中的元素)、.Field[f](字段内容)、.Property[p](属性内容); kind标记模型类别:taint(默认附加污点传播步骤)、value(全局值保持步骤);provenance标记模型来源:manual(人工编写)、df-generated(模型生成器产出)等。
5.0.0 的改动正是为 C# 13 中新增的、以System.ReadOnlySpan<T>为参数的 .NET 重载补充summaryModel,使污点流能够穿越这些新签名。
3.2 具体模型示例
变更说明中点名的示例是String.Format(System.String, System.ReadOnlySpan<System.Object>)。虽然该具体条目属于生成模型,但仓库中同类ReadOnlySpan模型遍布 csharp/ql/lib/ext/System.Text.model.yml,例如:
- ["System.Text", "Encoding", True, "GetBytes", "(System.ReadOnlySpan<System.Char>,System.Span<System.Byte>)", "", "Argument[0].Element", "Argument[1]", "taint", "manual"] - ["System.Text", "Encoding", False, "GetString", "(System.ReadOnlySpan<System.Byte>)", "", "Argument[0].Element", "ReturnValue", "taint", "manual"] - ["System.Text", "StringBuilder", False, "AppendFormat", "(System.String,System.ReadOnlySpan<System.Object>)", "", "Argument[1].Element", "Argument[this]", "taint", "manual"]逐列解读StringBuilder.AppendFormat(System.String, System.ReadOnlySpan<System.Object>)这条模型:
| 列 | 值 | 含义 |
|---|---|---|
| namespace | System.Text | 命名空间 |
| type | StringBuilder | 类型 |
| subtypes | False | 仅精确匹配,不覆盖子类型 |
| name | AppendFormat | 方法名 |
| signature | (System.String,System.ReadOnlySpan<System.Object>) | 参数签名 |
| ext | (空) | 扩展限定 |
| input | Argument[1].Element | 污点从第 2 个参数(format 字符串)进入 |
| output | Argument[this] | 流向StringBuilder实例本身 |
| kind | taint | 污点传播步骤 |
| provenance | manual | 人工编写 |
类似的ReadOnlySpan模型还出现在 csharp/ql/lib/ext/System.IO.model.yml(如Path.Combine(System.ReadOnlySpan<System.String>)、Stream.Write(System.ReadOnlySpan<System.Byte>))与 csharp/ql/lib/ext/System.Collections.Immutable.model.yml(如ImmutableArray.ToImmutableArray<T>(System.ReadOnlySpan<T>))中。
分析能力提升:.Element后缀意味着分析器会把"span 中元素"与"span 整体"当作可传播的污点载体,从而让 C# 13 时代惯用的ReadOnlySpan<byte>、ReadOnlySpan<char>等零拷贝 API 之间的污点链不再断裂——这对检测格式字符串注入、路径操纵、编码转换类漏洞至关重要。
四、C# 13:OverloadResolutionPriority特性支持
C# 13 引入[OverloadResolutionPriority(int)]特性,允许开发者显式声明重载的解析优先级。5.0.0 为此新增了专用 QL 类SystemRuntimeCompilerServicesOverloadResolutionPriorityAttribute,定义于 csharp/ql/lib/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll#L87-L98:
/** An attribute of type `System.Runtime.CompilerServices.OverloadResolutionPriority`. */ class SystemRuntimeCompilerServicesOverloadResolutionPriorityAttribute extends Attribute { SystemRuntimeCompilerServicesOverloadResolutionPriorityAttribute() { this.getNamespace() instanceof SystemRuntimeCompilerServicesNamespace and this.getType().hasName("OverloadResolutionPriorityAttribute") } /** * Gets the priority number. */ int getPriority() { result = this.getConstructorArgument(0).getIntValue() } }该类的设计要点:
- 通过命名空间(
System.Runtime.CompilerServices)加类型名双重约束完成识别,避免误匹配其他同名特性; - 核心方法
getPriority()从构造参数[0]解析出整型优先级数值。
查询用例:借助该类,可以编写 QL 查询找出所有标注了OverloadResolutionPriority的方法及其优先级数值,例如:
import csharp from SystemRuntimeCompilerServicesOverloadResolutionPriorityAttribute attr select attr, attr.getPriority(), attr.getAnnotatedElement()由于该特性影响编译器在多个重载间的选择,建模后数据流分析也能更准确地判定"实际被调用的重载"是哪一个,从而提升调用图与污点分析精度。
五、C# 13:partial 属性与索引器支持
C# 13 允许属性和索引器像方法一样使用partial修饰符拆分声明与实现。5.0.0 为该语言特性补齐了 QL 库支持。
从类型系统层面看,Property与Indexer两个类分别定义在 csharp/ql/lib/semmle/code/csharp/Property.qll#L137 与 csharp/ql/lib/semmle/code/csharp/Property.qll#L313,二者均继承DeclarationWithGetSetAccessors(带 get/set 访问器的声明基类)。而partial修饰符的判定能力来自成员基类 csharp/ql/lib/semmle/code/csharp/Member.qll#L145-L146:
/** Holds if this declaration is `partial`. */ predicate isPartial() { this.hasModifier("partial") }因此,新增支持后可以直接在查询中识别 partial 属性/索引器并关联其多个声明片段:
import csharp from Property p where p.isPartial() select p, "partial property declaration"仓库中对 partial 声明的处理已有先例——ExprOrStmtParent.qll中通过e.(Modifiable).isPartial()判断 partial 成员(见 csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll#L84),DataFlowDispatch.qll也注明 partial 方法可能有多个源码位置(见 csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll#L65)。5.0.0 将此能力扩展至属性与索引器,使跨文件拆分的属性声明在 AST 与数据流层面被完整识别。
六、升级与验证建议
升级路径:升级 CodeQL CLI 及 C# 查询包到包含 5.0.0 的版本后,自定义 QL 查询若仍引用两个被删谓词,编译器会直接报错,可按上文迁移建议逐个替换为getAConstructedGeneric()/getAstNode()。
验证方式:
- 用仓库自带的 QL 测试框架对迁移后的查询跑回归测试,确认与旧版本输出一致(CFG 节点的
getAstNode替换属纯重命名,不应改变结果); - 针对 C# 13 代码库(如使用
ReadOnlySpan的String.Format、标注OverloadResolutionPriority的重载、跨文件 partial 属性),运行数据流/污点类查询(如注入、路径操纵)验证新模型与类是否生效; - 查询模型加载情况可通过
ExternalFlow.qll的summaryModel谓词间接确认——若模型未生效,涉及ReadOnlySpan签名的方法将不产生对应的污点传播步骤。
参考资料
- 变更说明原文:csharp/ql/lib/change-notes/released/5.0.0.md
- 总变更日志:csharp/ql/lib/CHANGELOG.md
UnboundGenericType定义:csharp/ql/lib/semmle/code/csharp/Generics.qll- CFG 公共/内部模块:csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll 与 csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll
OverloadResolutionPriorityQL 类:csharp/ql/lib/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll- 模型语法规范:csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll
- 模型文件示例:csharp/ql/lib/ext/System.Text.model.yml、csharp/ql/lib/ext/System.IO.model.yml、csharp/ql/lib/ext/System.Collections.Immutable.model.yml
- 静态分析
- SAST
- 应用安全
- 漏洞扫描
- 代码质量
【免费下载链接】codeql
CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security
相关推荐
CodeQL C++ 查询库 11.0.0 破坏性变更全解与升级迁移指南
CodeQL C++ 查询库 11.0.0 破坏性变更全解与升级迁移指南 本指南围绕 CodeQL 仓库中 C/C++ 查询库( cpp/ql/lib )11.
静态分析SAST应用安全漏洞扫描代码质量3 步把 Ultimate Vocal Remover 部署到云端:Docker 打包到云服务器实操指南
3 步把 Ultimate Vocal Remover 部署到云端:Docker 打包到云服务器实操指南 当你需要批量把歌里的人声抠出来、只留伴奏时,用 Ult
音频处理人工智能CodeQL C++ 库 0.2.0 变更详解:allowImplicitRead 签名迁移与缓冲区分析增强
CodeQL C++ 库 0.2.0 变更详解:allowImplicitRead 签名迁移与缓冲区分析增强 本文以 CodeQL 仓库内 C++ 语言库的 0
静态分析SAST应用安全漏洞扫描代码质量
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考