CodeQL 2.26.3:Actions污点边界变了

CodeQL 2.26.3 这次更新里,我最关注的不是新增了多少查询,而是 GitHub Actions 的“可信输入边界”发生了几处很具体的调整。

官方列出的变化包括:

merge_group 事件中的 github.event.merge_group
现在会被识别为不可信数据来源

以及:

移除 codeql.actions.security.SelfHostedQuery

原因很直接:

Runner Label 不能可靠区分 Self-hosted Runner 和 Managed Runner。

这两条变化都在提醒同一件事:

CI 安全查询最容易犯的错误,就是把“看起来像可信”的元数据当成真正可信边界。

对写 GitHub Actions、自定义 CodeQL Query 和 Agent 自动修改 CI 的团队来说,这个版本值得认真过一遍。

merge_group为什么值得单独处理

很多团队现在开启 Merge Queue。

Workflow 可能监听:

on:
  merge_group:

过去自定义查询如果只重点考虑:

pull_request
pull_request_target
workflow_dispatch

很可能漏掉 merge_group 的输入传播。

CodeQL 2.26.3 现在把:

github.event.merge_group

识别为不可信数据。

这意味着类似:

- run: |
    echo "${{ github.event.merge_group.some_field }}"

如果进一步进入:

Shell
Path
Environment

就应该进入数据流分析。

不可信不是等于“恶意”,而是不能直接相信

这是安全建模里很重要的区别。

merge_group 数据不是说一定来自攻击者。

而是:

它受外部仓库事件影响

所以如果直接进入高权限步骤,就可能形成输入传播风险。

例如:

Untrusted Event Data
↓
env
↓
shell
↓
privileged step

这才是 CodeQL 真正在建模的东西。

SelfHostedQuery被删很值得注意

GitHub 明确说,codeql.actions.security.SelfHostedQuery 被移除,因为:

runner labels
不能可靠区分
self-hosted 和 managed runner

这非常现实。

很多 Query 会写:

if label == "self-hosted"
then high risk

问题是企业内部 Runner Label 往往是:

linux
x64
large
secure
gpu
internal

甚至 Managed Runner 也可能有类似标签。

所以:

字符串标签

不是可靠的安全身份。

如果你有自定义 Query 依赖这个模块,需要尽快改。

Runner身份最好来自确定性配置源

企业内部可以维护:

Runner Group ID
Runner Registration Source
Org Policy
Environment

而不是从 YAML Label 猜。

安全逻辑:

runner_is_trusted

最好来自:

组织配置

而不是:

名字里有没有 self-hosted

CodeQL 2.26.3还修了Actions缓存投毒判断

官方提到多条:

actions/cache-poisoning/code-injection
actions/cache-poisoning/direct-cache
actions/cache-poisoning/poisonable-step

现在会考虑低信任触发器对默认分支 Cache Scope 是否拥有:

read-only

如果低信任事件只能读取缓存,不能写:

不应该继续把它判成可投毒路径

这能减少误报。

安全扫描真正有用的前提不是“报得多”。

而是:

Source
→ Data Flow
→ Sink
→ 权限

都建模正确。

为什么Cache Poisoning特别容易误报

假设:

pull_request

只能读默认分支 Cache。

Query 如果只看到:

低信任事件
+
Cache

就报警:

Possible Poisoning

会产生大量无效告警。

真正投毒还需要:

attacker can write cache

所以权限语义必须进入数据流模型。

envvar injection也更严格了

2.26.3 调整:

actions/envvar-injection/critical

要求:

untrusted source
和
privileged context
来自同一个 trigger event

同时不再把 PR Head Label 当作可注入换行的来源,因为它不能包含换行。

这也是很典型的误报修正。

如果一个 Query 没理解:

字段实际字符约束

只因为它“用户可控”,就判注入,噪声会很大。

写自定义Query时,别只问“是否用户可控”

还要问:

可控到什么程度?
允许哪些字符?
在哪个事件里?
是否同一个信任边界?
最终Sink如何解析?

例如:

branch name
label
title
body
environment

虽然都可能是外部输入,但风险完全不同。

CodeQL还修了output clobbering的性能问题

官方说明:

actions/output-clobbering/high

不再对仍保持 JSON Encode 的简单 jq Path Filter 报警,同时修复了由未转义正则输入引起的性能问题。

这一条对大型仓库很重要。

静态分析如果一个 Query 自己跑得过慢:

扫描超时
CI排队
开发者关闭规则

最后安全收益反而下降。

所以 Query Performance 本身也是工程质量。

我会给自定义Query做性能基线

至少记录:

Repo Size
Query Time
Peak Memory
Result Count

每次 CodeQL 版本升级后重跑。

例如:

codeql-regression:
  max-query-time-regression: 20%
  max-alert-growth: 30%

如果某个 Query:

结果从 20 条变 1200 条

不要直接上线。

先看:

是真发现更多
还是模型变化制造噪声

JavaScript/Vue这次也补了几个真实数据流模型

官方新增:

ref
shallowRef
toRef
reactive
computed

等 Vue Composition API Helper 的 Flow Model。

同时 useRoute() 的:

query
params
path
fullPath
hash

被识别为客户端远程数据来源。

这会直接影响:

XSS
Path Injection
URL相关数据流

的发现能力。

如果你的前端大量使用 Vue 3,这个版本比单纯 Actions 更新更值得升级。

一个Vue Router例子

const route = useRoute()

const next = route.query.next
window.location.href = next

route.query.next 本质上来自 URL。

如果 Query 模型不知道:

useRoute()

是 Source,后面的数据流可能根本连不起来。

2.26.3 把这层模型补上以后,类似路径更容易进入分析。

Sails Action2也被补成Remote Source

声明的:

inputs

现在会被识别为远程数据源。

这可能影响:

js/path-injection

等查询。

如果你有 Sails 老项目,这类框架模型比新增一条泛化 Query 更有价值。

@fastify/rate-limit也被识别了

js/missing-rate-limiting 现在认识:

@fastify/rate-limit

这能降低一种常见误报:

实际上已经有Rate Limit
但分析器不认识框架

框架模型越准确,安全报告才越有用。

升级CodeQL前我会做两组回归

第一组:

Alert Regression

比较:

旧版
新版

每个 Query:

新增多少
消失多少
Severity变化

第二组:

Performance Regression

比较:

扫描时间
CPU
Memory

不要只看:

“新版支持更多规则”

一个升级报告

{
  "from": "2.26.2",
  "to": "2.26.3",
  "new_alerts": 38,
  "resolved_by_model_change": 12,
  "query_time_delta": "+8.4%",
  "critical_new": 2,
  "custom_query_failures": [
    "internal/self-hosted-runner-check"
  ]
}

尤其要找:

custom_query_failures

因为 SelfHostedQuery 这类 Breaking Change 会直接让内部 Query Pack 失败。

自定义Query必须固定CodeQL版本测试

如果你维护:

security/codeql-custom

CI 至少跑:

当前生产版本
+
目标升级版本

避免 GitHub.com 自动升级后,自定义 Query 才突然坏。

GitHub.com 会自动部署新版 CodeQL;GHES 则会在未来版本包含这些功能,旧 GHES 可以手动升级 CodeQL。

环境不同,升级节奏也要分开管理。

Agent自动改Workflow时,更应该先跑CodeQL

Coding Agent 很喜欢修改:

.github/workflows/*.yml

而 Workflow 本身就是高风险代码。

我会给 Agent 的 PR Gate 单独加:

Actions CodeQL

而且:

Workflow Change

一律提高 Review Risk。

if path.startswith(".github/workflows/"):
    risk += 5

不要把 CI YAML 当普通配置。

一个最小CI Gate

security:
  codeql:
    version: "2.26.3"

  required:
    - actions
    - javascript-typescript

  block:
    severity:
      - critical
      - high

如果有自定义 Query Pack,再锁:

pack version

避免环境漂移。


CodeQL 2.26.3 这次最值得关注的,不是“多支持了几个框架”。

而是它连续修正了几种:

信任边界建模

包括:

merge_group
runner identity
cache write capability
event source relation
Vue route source

这类变化决定静态分析到底是在:

理解真实攻击路径

还是只做字符串匹配。

对企业来说,升级 CodeQL 最重要的动作也不是点击“升级”。

而是跑一遍:

Query Compatibility
Alert Delta
Performance Delta

特别是内部自定义 GitHub Actions Query。

因为安全扫描器的版本变化,本身也应该像业务代码一样进入回归测试。


更多企业级 AI 应用、Agent、RAG 与模型工程化内容,我会继续整理在 智元界

https://www.zyentor.com/