- 静态分析
- SAST
- 应用安全
- 漏洞扫描
- 代码质量
【免费下载链接】codeql
CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security
导读
本文基于 CodeQL 仓库 change-notes/1.21/analysis-csharp.md 的变更记录,系统梳理 1.21 版本中 C# 分析器的全部能力升级:C# 8 语言特性的提取与分析支持、两条新增安全/并发查询、三条既有查询的误报率改进,以及 QL 库中针对属性、类型参数约束、模式匹配与 switch 表达式的新增建模类。读完本文,你将掌握这些新特性对应的 QL 类、谓词与测试用例的位置,能够直接在仓库中定位实现细节,并在自己的 CodeQL 查询中正确使用这些新 API。
1. 版本总览:C# 8 全量支持
1.21 版本的 C# 分析器对 C# 8 特性实现了从提取器(extractor)到 QL 库的全链路支持:既有 5 项语法特性进入代码提取流程,同时 QL 库中新增 10 余个建模类与多个新谓词,还在此基础上发布了 2 条新查询并优化了 3 条既有查询。这意味着此前无法被分析器识别的 C# 8 语法,在本版本中已可以被正确解析、建模,并参与数据流与污点分析。
2. 新增查询
1.21 版本新增了一条面向并发与安全的告警查询:
| 查询 | 标签 | 目的 |
|---|---|---|
线程不安全地捕获 ICryptoTransform 对象(cs/thread-unsafe-icryptotransform-captured-in-lambda) | concurrency、security、external/cwe/cwe-362 | 标记一类实例:其字段类型为System.Security.Cryptography.ICryptoTransform,该实例被 lambda 捕获,且出现在疑似线程初始化方法中。默认情况下该查询结果不会在 LGTM 上展示 |
这条查询对应仓库中的 ThreadUnsafeICryptoTransformLambda.ql。从源码可以看到它的实现思路:
import semmle.code.csharp.security.dataflow.flowsinks.ParallelSink import ICryptoTransform module NotThreadSafeCryptoUsageIntoParallelInvokeConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source instanceof LambdaCapturingICryptoTransformSource } predicate isSink(DataFlow::Node sink) { sink instanceof ParallelSink } ... }它基于数据流配置(DataFlow::ConfigSig)实现:以"捕获了ICryptoTransform类型对象或包含该类字段的 lambda"为源(source),以"并行执行入口"(ParallelSink)为汇(sink),从而识别出"对象被 lambda 捕获并在疑似线程初始化方法中使用"的危险模式。其告警消息明确指出:在并发线程中使用ICryptoTransform实例不仅可能产生错误,某些情况下还会导致计算结果不正确,这正是 CWE-362(TOCTOU 竞态)类问题的典型场景。
3. 既有查询的改进
1.21 版本对三条既有查询做了针对性优化,核心目标是降低误报率:
| 查询 | 预期影响 | 变更内容 |
|---|---|---|
常量条件(cs/constant-condition) | 更少的误报 | 现在忽略null值出现在空合并表达式左侧条件表达式中的代码。例如(a ? b : null) ?? c中,null不再被视为常量条件 |
线程不安全地使用静态 ICryptoTransform 字段(cs/thread-unsafe-icryptotransform-field-in-class) | 更少的误报 | 结果判定标准更新:新增嵌套属性、嵌套字段与集合的判定;告警消息格式调整为突出静态字段;查询名称同步更新 |
无用的向上转换(cs/useless-upcast) | 更少的误报 | 现在忽略用于消歧构造函数调用目标的向上转换 |
3.1 常量条件:空合并语境下的 null 豁免
查询实现位于 ConstantCondition.ql。其nullCheck谓词通过NullCoalescingOperation追踪空合并运算:
predicate nullCheck(Expr e, boolean direct) { exists(QualifiableExpr qe | qe.isConditional() and direct = true | ...) or exists(NullCoalescingOperation nce | nce.getLeftOperand() = e and direct = true) or exists(ConditionalExpr ce | ce.getThen() = e or ce.getElse() = e | nullCheck(ce, _) and direct = false ) }当null位于(a ? b : null) ?? c这类条件表达式中时,它只是"被合并"的候选值而非真正的常量条件,因此被豁免,从而消除此类误报。
3.2 静态 ICryptoTransform 字段:判定标准扩展
实现位于 ThreadUnsafeICryptoTransform.ql。新版本中UsesICryptoTransform类递归地判定"直接或间接使用ICryptoTransform"的类型:
class UsesICryptoTransform extends ValueOrRefType { UsesICryptoTransform() { this instanceof ICryptoTransform or this.getAField().getType() instanceof UsesICryptoTransform or this.getAProperty().getType() instanceof UsesICryptoTransform or getAnEnumeratedType(this) instanceof UsesICryptoTransform } }其中getAnEnumeratedType会遍历类型实现的IEnumerable<T>泛型接口并取出类型实参,实现对集合的判定;getAField/getAProperty的递归组合则覆盖了嵌套字段与嵌套属性。同时,UnsafeField要求字段为静态(isStatic())且未标记ThreadStaticAttribute,告警消息改为高亮静态字段本身。
3.3 无用的向上转换:构造函数消歧豁免
实现位于 UselessUpcast.ql。源码中定义了isDisambiguatingConstructorCall谓词(UselessUpcast.ql),专门识别"向上转换被用作构造函数调用实参、以帮助重载决议选择正确构造函数"的情形:
private predicate isDisambiguatingConstructorCall(Constructor other, int args) { exists(ConstructorCall cc, Constructor target, ValueOrRefType t | this.isArgument(cc, target) | ... ) }这类向上转换虽然形式上"多余",但实际上承担了消歧职责,因此在新版本中被排除在告警之外。
4. 代码提取的变更
1.21 版本的提取器(csharp/extractor)新增了对以下 C# 8 特性的提取支持:
- 范围表达式(Range expressions),如
1..^1 - 递归模式(Recursive patterns)
- 使用声明语句(Using declaration statements)
- 局部函数上的
static修饰符 - 空合并赋值表达式(Null-coalescing assignment expressions)
unmanaged类型参数约束
提取器的实现可从 Factory.cs 看到:SyntaxKind.RangeExpression与SyntaxKind.IndexExpression分别被分派到RangeExpression.Create与IndexExpression的处理逻辑;范围表达式的切片语义则在 ElementAccess.cs 中通过RangeExpressionSyntax识别并调用对应的切片方法(如string.AsSpan)完成建模。
测试覆盖同样完备:仓库的 csharp/ql/test/library-tests/csharp8 目录下提供了ranges.cs、patterns.cs、UsingDeclarations.cs、NullCoalescingAssignment.cs、StaticLocalFunctions.cs、UnmanagedGenericStructs.cs等测试文件及对应.expected结果文件。例如在 PrintAst.expected 中可以看到提取结果的实证:
# 8| 0: [AssignCoalesceExpr] ... ??= ... # 8| 0: [UsingDeclStmt] using ... ...; # 13| 1: [RecursivePatternExpr] { ... }5. QL 库的变更
5.1 属性类新增两个谓词
Attribute.qll 中的Attribute类新增:
getConstructorArgument(int i):返回特性构造函数调用的实参。例如在MyAttribute[true, Foo = 0]中,只有true是构造函数实参;getNamedArgument(string name):返回用于初始化字段/属性的命名实参。例如上例中Foo = 0的0。
这两个谓词在库内已有实际使用,例如 CodeAnalysis.qll 通过getConstructorArgument(0).getValue()读取CallerMemberName等特性的构造实参。
5.2 类型参数约束新增unmanaged判定
Generics.qll 中TypeParameterConstraints类新增谓词hasUnmanagedTypeConstraint(),用于判断类型参数是否带有unmanaged约束(与已有的hasValueTypeConstraint、hasRefTypeConstraint等谓词并列,内部通过general_type_parameter_constraints(this, 4)实现)。测试用例见 UnmanagedGenericStructs.cs。
5.3 C# 8 特性建模类一览
QL 库针对 C# 8 新增了以下建模类(主要位于 Expr.qll 与 Stmt.qll):
| QL 类 | 建模的 C# 8 语法 | 源码位置 |
|---|---|---|
AssignCoalesceExpr | 空合并赋值x ??= y | Assignment.qll |
IndexExpr | 末尾索引表达式^1 | Expr.qll |
RangeExpr | 范围表达式1..^1 | Expr.qll |
PatternExpr及其子类 | 模式表达式(详见 5.4 节) | Expr.qll |
PatternMatch及子类Case、IsExpr | 模式匹配 | Expr.qll |
SwitchExpr | switch 表达式 | Expr.qll |
SwitchCaseExpr | switch 表达式的分支(arm),如(false, false) => true | Expr.qll |
Switch | 同时建模SwitchExpr与SwitchStmt | Expr.qll |
Case | 同时建模CaseStmt与SwitchCaseExpr | Expr.qll |
UsingStmt | 同时建模UsingBlockStmt与UsingDeclStmt | Stmt.qll |
这里有几个值得注意的设计点:
AssignCoalesceExpr同时继承AssignOperation与NullCoalescingOperation(Assignment.qll),因此它既能被赋值相关逻辑识别,也能被空合并相关逻辑(如 3.1 节的NullCoalescingOperation)识别;IndexExpr.getExpr()返回^之后的子表达式,toString()为^...(Expr.qll);RangeExpr提供getStart()/getEnd()/hasStart()/hasEnd()谓词,覆盖1..3、1..^1、3..、..、..5、..^1等全部六种书写形式(Expr.qll);SwitchExpr的getCase(int n)/getACase()返回SwitchCaseExpr,SwitchCaseExpr则通过getPattern()、getCondition()(when子句)、getBody()(=>右侧结果)分别暴露分支的三个组成部分(Expr.qll)。
5.4 模式表达式类族详解
PatternExpr是"出现在模式中的表达式"的基类(Expr.qll),通过getPatternMatch()可回溯其所属的模式匹配。1.21 版本为它引入了六个新子类:
| 子类 | 建模的语法 | 示例 |
|---|---|---|
DiscardPatternExpr | 弃元模式 | x is (_, false)中的_ |
LabeledPatternExpr | 属性模式中的带标签模式 | { Length: 5 }中的Length: 5 |
RecursivePatternExpr | 递归模式 | x is string { Length: 5 } s |
TypeAccessPatternExpr | 类型访问模式 | x is string中的string |
TypePatternExpr | 类型模式 | x is string s |
VariablePatternExpr | 变量声明模式 | x is string s中的s |
同时新增两个模式匹配建模类:
PatternMatch:模式被匹配的抽象,子类为Case与IsExpr;IsExpr:is表达式,如x is string(Expr.qll)。
此外,PositionalPatternExpr(位置模式(int x, int y),Expr.qll)与PropertyPatternExpr(属性模式Length: int len,Expr.qll)也一并加入。其中RecursivePatternExpr提供了组合访问能力:getPositionalPatterns()、getPropertyPatterns()、getTypeAccess()、getVariableDeclExpr()分别对应递归模式中的位置模式、属性模式、类型访问与绑定变量(Expr.qll),可直接用于编写针对复杂模式匹配的分析查询。
5.5 废弃与合并
与新增类同步,库中还完成了一组 API 整合:
IsConstantExpr、IsTypeExpr、IsPatternExpr三个旧类被废弃,统一由IsExpr取代;Switch同时建模SwitchExpr与SwitchStmt,避免对两种 switch 形式分别建模;Case同时建模CaseStmt与SwitchCaseExpr;UsingStmt同时建模UsingBlockStmt与UsingDeclStmt(后者是 C# 8 的 using 声明语句,见 Stmt.qll)。
这种"向上合并"的设计意味着:凡是只关心 switch 语义、case 语义或 using 语义的查询,现在只需针对Switch/Case/UsingStmt编写一次,即可同时覆盖语句与表达式两种形态。
6. 测试与验证
仓库为本次更新提供了完整的测试支撑,主要集中在两个位置:
- csharp/ql/test/library-tests/csharp8:覆盖 C# 8 特性的库测试。其中
ranges.ql/ranges.expected验证范围表达式,patterns.ql/patterns.expected验证各类模式表达式,NullCoalescingAssignment.ql验证空合并赋值,UsingDeclarations.ql与UsingControlFlow.ql验证 using 声明语句及控制流,StaticLocalFunctions.ql与UnmanagedGenericStructs.ql验证静态局部函数与unmanaged约束,switchCaseExprTypes.ql、switchexprcontrolflow.ql验证 switch 表达式及控制流; - csharp/ql/test/library-tests/controlflow/graph/Dominance.expected 等控制流测试中也包含了新语法节点参与支配分析的结果。
读者可直接运行codeql test run csharp/ql/test/library-tests/csharp8复现这些测试,或通过.expected文件对照检查自己的查询输出格式。
7. 小结
1.21 版本是 CodeQL C# 分析器对 C# 8 的完整拥抱:提取器覆盖了范围表达式、递归模式、using 声明、静态局部函数、空合并赋值与unmanaged约束六项语法能力;QL 库以PatternExpr、SwitchExpr、RangeExpr、AssignCoalesceExpr、UsingStmt等新类完成语义建模,并同步废弃合并了旧 API;查询层面新增了针对 ICryptoTransform 并发误用的安全查询,同时降低了常量条件、静态 ICryptoTransform 字段与无用向上转换三条查询的误报率。对于需要分析现代 C# 代码库的安全研究人员,这意味着:可以信任 CodeQL 对 C# 8 语法的 AST 与数据流分析结果,并直接借助getConstructorArgument、hasUnmanagedTypeConstraint、PatternExpr子类等新 API 编写更精准的自定义查询。
- 静态分析
- SAST
- 应用安全
- 漏洞扫描
- 代码质量
【免费下载链接】codeql
CodeQL: the libraries and queries that power security researchers around the world, as well as code scanning in GitHub Advanced Security
相关推荐
CodeQL 1.21 C/C++ 分析改进全解:新增安全查询、既有查询强化与 QL 库 API 更新
CodeQL 1.21 C/C++ 分析改进全解:新增安全查询、既有查询强化与 QL 库 API 更新 本文以 CodeQL 仓库 change notes/1
静态分析SAST应用安全漏洞扫描代码质量CodeQL 1.20 的 C/C++ 分析改进详解:新查询、查询更新与 QL 库增强
CodeQL 1.20 的 C/C++ 分析改进详解:新查询、查询更新与 QL 库增强 本指南基于 CodeQL 仓库中 change notes/1.20/a
静态分析SAST应用安全漏洞扫描代码质量CodeQL C/C++ 1.18 分析能力升级:新查询、查询改进与 QL 库变更深度解析
CodeQL C/C++ 1.18 分析能力升级:新查询、查询改进与 QL 库变更深度解析 本文对应 CodeQL 仓库 change notes/1.18/a
静态分析SAST应用安全漏洞扫描代码质量
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考