Skip to content

Commit 3d2c10c

Browse files
authored
fix(ci): validate static workflow identity (#2015)
1 parent 03de81c commit 3d2c10c

2 files changed

Lines changed: 33 additions & 28 deletions

File tree

.github/workflows/semantic-review.yml

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,16 @@ jobs:
2525
with:
2626
script: |
2727
const run = context.payload.workflow_run;
28-
if (run.name !== "CI") throw new Error(`unexpected workflow name: ${run.name}`);
29-
let workflowPath = run.path || "";
30-
if (!workflowPath) {
31-
const workflowId = Number(run.workflow_id || 0);
32-
if (!Number.isInteger(workflowId) || workflowId <= 0) throw new Error("missing workflow id");
33-
const { data: workflow } = await github.rest.actions.getWorkflow({
34-
owner: context.repo.owner,
35-
repo: context.repo.repo,
36-
workflow_id: workflowId,
37-
});
38-
workflowPath = workflow.path || "";
39-
}
40-
if (workflowPath !== ".github/workflows/ci.yml") throw new Error(`unexpected workflow path: ${workflowPath}`);
28+
const workflowId = Number(run.workflow_id || 0);
29+
if (!Number.isInteger(workflowId) || workflowId <= 0) throw new Error("missing workflow id");
30+
const { data: workflow } = await github.rest.actions.getWorkflow({
31+
owner: context.repo.owner,
32+
repo: context.repo.repo,
33+
workflow_id: workflowId,
34+
});
35+
if (workflow.name !== "CI") throw new Error(`unexpected workflow name: ${workflow.name}`);
36+
if (workflow.path !== ".github/workflows/ci.yml") throw new Error(`unexpected workflow path: ${workflow.path}`);
37+
if (run.path && run.path !== workflow.path) throw new Error(`workflow path mismatch: ${run.path}`);
4138
if (run.event !== "pull_request") throw new Error(`unexpected event: ${run.event}`);
4239
if (run.repository.id !== context.payload.repository.id) throw new Error("repository id mismatch");
4340
if (run.repository.full_name !== context.payload.repository.full_name) throw new Error("repository name mismatch");
@@ -253,19 +250,16 @@ jobs:
253250
with:
254251
script: |
255252
const run = context.payload.workflow_run;
256-
if (run.name !== "CI") throw new Error(`unexpected workflow name: ${run.name}`);
257-
let workflowPath = run.path || "";
258-
if (!workflowPath) {
259-
const workflowId = Number(run.workflow_id || 0);
260-
if (!Number.isInteger(workflowId) || workflowId <= 0) throw new Error("missing workflow id");
261-
const { data: workflow } = await github.rest.actions.getWorkflow({
262-
owner: context.repo.owner,
263-
repo: context.repo.repo,
264-
workflow_id: workflowId,
265-
});
266-
workflowPath = workflow.path || "";
267-
}
268-
if (workflowPath !== ".github/workflows/ci.yml") throw new Error(`unexpected workflow path: ${workflowPath}`);
253+
const workflowId = Number(run.workflow_id || 0);
254+
if (!Number.isInteger(workflowId) || workflowId <= 0) throw new Error("missing workflow id");
255+
const { data: workflow } = await github.rest.actions.getWorkflow({
256+
owner: context.repo.owner,
257+
repo: context.repo.repo,
258+
workflow_id: workflowId,
259+
});
260+
if (workflow.name !== "CI") throw new Error(`unexpected workflow name: ${workflow.name}`);
261+
if (workflow.path !== ".github/workflows/ci.yml") throw new Error(`unexpected workflow path: ${workflow.path}`);
262+
if (run.path && run.path !== workflow.path) throw new Error(`workflow path mismatch: ${run.path}`);
269263
if (run.event !== "pull_request") throw new Error(`unexpected event: ${run.event}`);
270264
if (run.conclusion !== "success") throw new Error(`unexpected conclusion: ${run.conclusion}`);
271265
if (run.repository.id !== context.payload.repository.id) throw new Error("repository id mismatch");

scripts/semantic-review-workflow.test.sh

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,15 @@ if ! grep -Fq "if: always() && github.event.workflow_run.conclusion == 'success'
176176
exit 1
177177
fi
178178

179-
require_in_step "$summary_verify_step" 'workflowPath !== ".github/workflows/ci.yml"' "PR quality summary must verify the triggering workflow path"
179+
if grep -Fq 'run.name !== "CI"' "$workflow"; then
180+
echo "semantic-review must not use the dynamic workflow run name as workflow identity" >&2
181+
exit 1
182+
fi
183+
184+
require_in_step "$summary_verify_step" 'github.rest.actions.getWorkflow' "PR quality summary must resolve static workflow metadata"
185+
require_in_step "$summary_verify_step" 'workflow.name !== "CI"' "PR quality summary must verify the static workflow name"
186+
require_in_step "$summary_verify_step" 'workflow.path !== ".github/workflows/ci.yml"' "PR quality summary must verify the static workflow path"
187+
require_in_step "$summary_verify_step" 'run.path && run.path !== workflow.path' "PR quality summary must reject workflow path metadata mismatches"
180188
require_in_step "$summary_verify_step" 'run.event !== "pull_request"' "PR quality summary must only handle pull_request workflow_run events"
181189
require_in_step "$summary_verify_step" 'run.repository.id !== context.payload.repository.id' "PR quality summary must verify workflow_run repository id"
182190
require_in_step "$summary_verify_step" 'const targetHeadSha = run.head_sha' "PR quality summary must use the CI run head SHA as the verified PR head"
@@ -201,7 +209,10 @@ require_in_step "$summary_publish_step" 'CI_QUALITY_SUMMARY_BASE_SHA' "PR qualit
201209
require_in_step "$summary_publish_step" 'CI_QUALITY_SUMMARY_RUN_ID' "PR quality summary publisher must receive verified workflow run id"
202210
require_in_step "$summary_publish_step" 'require("./scripts/ci-quality-summary-publish.js")' "PR quality summary publisher must use the shared CI publisher script"
203211

204-
require_in_step "$verify_step" 'workflowPath !== ".github/workflows/ci.yml"' "semantic-review must verify the triggering workflow path"
212+
require_in_step "$verify_step" 'github.rest.actions.getWorkflow' "semantic-review must resolve static workflow metadata"
213+
require_in_step "$verify_step" 'workflow.name !== "CI"' "semantic-review must verify the static workflow name"
214+
require_in_step "$verify_step" 'workflow.path !== ".github/workflows/ci.yml"' "semantic-review must verify the static workflow path"
215+
require_in_step "$verify_step" 'run.path && run.path !== workflow.path' "semantic-review must reject workflow path metadata mismatches"
205216
require_in_step "$verify_step" 'run.repository.id !== context.payload.repository.id' "semantic-review must verify workflow_run repository id"
206217
require_in_step "$verify_step" 'run.event !== "pull_request"' "semantic-review must only handle pull_request workflow_run events"
207218
require_in_step "$verify_step" 'run.conclusion !== "success"' "semantic-review must only consume successful CI runs"

0 commit comments

Comments
 (0)