fix(ci): sweep stale gate descriptions left by the ECS routing move

The verify job's receipt comment still called the authorize job hosted; the design docs described both gates as calling one read API, named only should_review, cited an unresolvable internal review id, and omitted publish-verify from the residual hosted legs. Update the comment (with the size baseline) and align both language versions with the actual two-gate shape.

Co-authored-by: Qwen-Coder <[email protected]>
This commit is contained in:
wenshaoandQwen-Coder committed 2026-11-07 20:55:24 +08:00
1 parent a139ff3d56
commit ebbeca2992
4 files changed
+41 -31

No files matched your search

+1 -1
View File
@@ -47,7 +47,7 @@
5760 qwen-pr-safety-precheck.yml
3243 qwen-review-runner-schedule.yml
27648 qwen-triage-finalize.yml
407440 qwen-triage.yml
407496 qwen-triage.yml
15726 release-chrome-extension.yml
26801 release-managed-agent-reference.yml
13464 release-managed-runtime.yml
+3 -2
View File
@@ -2891,8 +2891,9 @@ jobs:
run: |-
set -euo pipefail
echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
# The 👀 acknowledgement lives in the hosted authorize job, so the
# requester gets a receipt even when this self-hosted job is queued.
# The 👀 acknowledgement lives in the authorize gate job, so the
# requester gets a receipt ahead of this sandbox lane (for comment
# triggers the gate itself queues with the pool).
# Same mergeability settling loop as the tmux job: refs/pull/N/merge
# is only current once GitHub has computed mergeability.
for attempt in 1 2 3 4 5; do
+20 -13
View File
@@ -10,9 +10,10 @@ Every bot command — `/review` and `/resolve` in
`.github/workflows/qwen-code-pr-review.yml`, `/triage`, `/tmux` and `/verify` in
`.github/workflows/qwen-triage.yml` — first passes through an `authorize` job.
That job is the write-permission gate: it reads the triggering principal from
the event metadata and calls one read API
(`repos/{owner}/{repo}/collaborators/{principal}/permission`). It runs no agent,
checks out nothing, and processes no pull-request content.
the event metadata and calls the permission API
(`repos/{owner}/{repo}/collaborators/{principal}/permission`); the triage gate
also reads the PR author/head and posts receipt reactions/comments. It runs no
agent, checks out nothing, and processes no pull-request content.
Despite being a four-second API-only job, `authorize` was observed waiting
**3h10m** in the GitHub-hosted queue before it could start (PR #15286, run
@@ -118,7 +119,7 @@ PAT-bearing API call cannot be rerouted through the pool's shared, writable
threat that keeps fork-triggered work off persistent runners is a job
_executing_ attacker-authored branch code and leaving a survivor that attacks
later jobs on the same host. `authorize` executes nothing from the PR: it
reads event metadata (a username) and calls one read API. The comment body is
reads event metadata (a username) and calls read/receipt APIs. The comment body is
matched only in GitHub-side `if:` expressions, never passed to the shell.
Routing it to ECS therefore does not widen the fork attack surface.
@@ -145,11 +146,13 @@ PAT-bearing API call cannot be rerouted through the pool's shared, writable
## Risks
- **The command triggers are open to any GitHub user.** The `issue_comment`
disjunct makes the gate fireable by anyone commenting `@qwen-code /review` or
`@qwen-code /resolve` on a fork PR; `workflow_dispatch` still requires write
disjunct makes the gate fireable by anyone commenting a bot command
(`@qwen-code /review`, `/resolve`, `/triage`, `/tmux`, `/verify`) on a fork
PR; `workflow_dispatch` still requires write
access. The gate runs none of that PR's content, so this adds no execution
path — but it does put `CI_BOT_PAT` and this job's `should_review` decision on
the shared pool for a run an outsider scheduled, and `should_review` is the
path — but it does put `CI_BOT_PAT` and this job's authorization decision
(`should_review` in the review workflow, `should_run` in triage) on
the shared pool for a run an outsider scheduled, and that output is the
authorization fact every downstream job consumes without re-deriving it. Both
are the same shared-host class tracked in
[autofix-gate-runner-isolation.md](autofix-gate-runner-isolation.md); this
@@ -159,22 +162,25 @@ PAT-bearing API call cannot be rerouted through the pool's shared, writable
persistent pool a co-resident survivor from an earlier job could in principle
target it — in particular through the pool's shared, writable `~/.config/gh`:
a planted `http_unix_socket` there would receive the PAT and could forge the
permission response that downstream jobs consume as `should_review`. The
permission response that downstream jobs consume as the gate's authorization
output. The
step therefore opens with the same gh pins as `qwen-autofix.yml`'s
`route`/`review-scan` — `export GH_HOST=github.com` plus a private, guarded
`GH_CONFIG_DIR` (a failed `mktemp` exits 1; the gate fails closed) — and a
meta-test pins them. **With those pins** the exposure is the one already
accepted for `review-scan` (which loads `CI_DEV_BOT_PAT` on the same pool;
`route` loads only the read-only `github.token`), and `authorize` is strictly
lighter (one read API, no checkout). Without them the parity would not hold.
lighter (a handful of read/receipt APIs, no checkout). Without them the parity
would not hold.
The structural close for PAT-bearing steps on the shared pool is tracked
separately in
[autofix-gate-runner-isolation.md](autofix-gate-runner-isolation.md); this
change does not regress it.
- **Residual hosted queue downstream.** `resolve-pr` and `publish-resolution`
still run on hosted, so a saturated hosted pool can still delay the agent and
the push. This change removes the gate wait only; it is not a claim that
`/resolve` becomes queue-free.
the push, and `/verify`'s `publish-verify` job stays hosted by design (it
carries OSS credentials). This change removes the gate wait only; it is not a
claim that `/resolve` — or any command — becomes queue-free end to end.
## Extension to the triage command gate
@@ -186,7 +192,8 @@ permission check, the verify acknowledgement, and the disabled-lane notice —
and each now opens with the same self-contained `GH_HOST` + guarded
`GH_CONFIG_DIR` preamble, per the repo's per-step convention. Moving the gate
to the pool without hardening every one of those steps would reproduce the
R2-1 reroute, so the hardening is not optional here. A meta-test in
gh-config reroute (a planted shared config receiving the PAT and forging the
permission response), so the hardening is not optional here. A meta-test in
`.github/scripts/qwen-triage-workflow.test.mjs` pins the disjuncts, the
checkout-free / no-splice invariants, the no-repository-code invariant on
every step (not just the PAT-bearing ones — a credential-free step that runs
@@ -8,9 +8,10 @@
每一条 bot 命令——`qwen-code-pr-review.yml` 里的 `/review`、`/resolve`,以及
`qwen-triage.yml` 里的 `/triage`、`/tmux`、`/verify`——都先经过一个 `authorize`
job。该 job 是写权限门:从事件元数据读出触发主体,并调用一个只读 API
(`repos/{owner}/{repo}/collaborators/{principal}/permission`)。它不跑 agent、不
checkout 任何代码、也不处理任何 pull-request 内容。
job。该 job 是写权限门:从事件元数据读出触发主体,并调用权限 API
(`repos/{owner}/{repo}/collaborators/{principal}/permission`);triage 门还会
读取 PR 作者/head 并发送回执反应/评论。它不跑 agent、不 checkout 任何代码、也不
处理任何 pull-request 内容。
尽管这是一个只需四秒的纯 API job,实测 `authorize` 曾在 GitHub 托管队列里等待
**3 小时 10 分**才得以启动(PR #15286,run 42201221971,2026-11-06:创建于
@@ -106,7 +107,7 @@ PR 载荷的事件(`pull_request_target`、`pull_request_review`、
1. **因为没有任何 fork 代码进入该 job,所以在持久池上是安全的。** 让 fork 触发的
工作远离持久 runner 的威胁,是 job *执行*攻击者撰写的分支代码、并留下幸存进程攻
击同一主机上后续 job。`authorize` 不执行任何来自 PR 的东西:它只读事件元数据(一
个用户名)并调用一个只读 API。评论正文只在 GitHub 侧的 `if:` 表达式里被匹配,绝
个用户名)并调用只读/回执 API。评论正文只在 GitHub 侧的 `if:` 表达式里被匹配,绝
不传入 shell。因此把它路由到 ECS 并不扩大 fork 攻击面。
2. **与仓库既有模式一致。** `qwen-autofix.yml` 的 `route` 与 `review-scan` job 早
@@ -129,27 +130,28 @@ PR 载荷的事件(`pull_request_target`、`pull_request_review`、
## 风险
- **命令触发对任何 GitHub 用户开放。** `issue_comment` 析取项使该门可以被任何人在
fork PR 上评论 `@qwen-code /review` 或 `@qwen-code /resolve` 触发;
`workflow_dispatch` 仍要求写权限。该门不运行该 PR 的任何内容,故未新增执行路径
——但它确实把 `CI_BOT_PAT` 与该 job 的 `should_review` 决定,放到了由外部人员排程
的一次运行所在的共享池上,而 `should_review` 正是每个下游 job 不经重新推导即消费
的授权事实。两者都属于
fork PR 上评论 bot 命令(`@qwen-code /review`、`/resolve`、`/triage`、`/tmux`、
`/verify`)触发;`workflow_dispatch` 仍要求写权限。该门不运行该 PR 的任何内容,
故未新增执行路径——但它确实把 `CI_BOT_PAT` 与该 job 的授权决定(review workflow
的 `should_review`、triage 的 `should_run`),放到了由外部人员排程的一次运行所在
的共享池上,而该输出正是每个下游 job 不经重新推导即消费的授权事实。两者都属于
[autofix-gate-runner-isolation.md](autofix-gate-runner-isolation.md) 跟踪的同一
共享主机类别;本条记录这次触发面扩大,以免被误认为触发集未变。
- **共享主机上的 PAT 暴露。** `authorize` 加载 `CI_BOT_PAT`。在持久池上,先前 job
的同驻幸存进程原则上可能以它为目标——尤其是经由池上共享且可写的 `~/.config/gh`:
在那里植入的 `http_unix_socket` 会收到该 PAT,并能伪造下游 job 当作 `should_review`
在那里植入的 `http_unix_socket` 会收到该 PAT,并能伪造下游 job 当作门的授权输出
消费的权限响应。因此该步骤以与 `qwen-autofix.yml` 的 `route`/`review-scan` 相同的
gh pin 开场——`export GH_HOST=github.com` 加一个私有、带守卫的 `GH_CONFIG_DIR`
(`mktemp` 失败即 exit 1;该门 fail closed)——并由一条 meta-test pin 住它们。
**带着这些 pin**,其暴露才与 `review-scan` 已被接受的相同(它在同一池上加载
`CI_DEV_BOT_PAT`;`route` 只加载只读的 `github.token`),且 `authorize` 严格更轻
(一个只读 API、无 checkout)。没有这些 pin,该对等就不成立。共享池上携带 PAT 步骤
的结构性收口在 [autofix-gate-runner-isolation.md](autofix-gate-runner-isolation.md)
(少量只读/回执 API、无 checkout)。没有这些 pin,该对等就不成立。共享池上携带 PAT
步骤的结构性收口在 [autofix-gate-runner-isolation.md](autofix-gate-runner-isolation.md)
中单独跟踪;本改动不使其倒退。
- **下游仍有托管队列。** `resolve-pr` 与 `publish-resolution` 仍在托管上运行,因此
饱和的托管池仍可能延迟 agent 与推送。本改动只消除门口等待,并不宣称 `/resolve` 变
得无队列。
饱和的托管池仍可能延迟 agent 与推送;`/verify` 的 `publish-verify` job 也出于设计
留在托管(它携带 OSS 凭据)。本改动只消除门口等待,并不宣称 `/resolve`——或任何
命令——端到端变得无队列。
## 扩展到 triage 命令门
@@ -158,7 +160,7 @@ PR 载荷的事件(`pull_request_target`、`pull_request_review`、
与 review 门的单步骤不同,该 job 有**三个**步骤用 `CI_BOT_PAT` 调 `gh`——权限检查、
verify 确认、以及停用通告——每一步现在都以相同的自包含 `GH_HOST` + 带守卫
`GH_CONFIG_DIR` 前导开场,遵循仓库的每步约定。把该门搬上池却不加固其中任何一步,都会
复现 R2-1 改道,因此这里的加固不是可选项。`.github/scripts/qwen-triage-workflow.test.mjs`
复现 gh 配置改道(植入的共享配置收到 PAT 并伪造权限响应),因此这里的加固不是可选项。`.github/scripts/qwen-triage-workflow.test.mjs`
中的一条 meta-test pin 住析取项、无 checkout/无拼接不变量、每个步骤(不限于携带 PAT
的步骤——不携带凭据的步骤若执行植入文件,同样会在持久主机上留下幸存进程)不得执行
仓库代码的不变量、以及每个调 gh 步骤的 gh 配置加固。该 workflow 的记录体积基线