Address review feedback on the build-failure-analysis pre-gate - #17240
Open
YuliiaKovalova wants to merge 1 commit into
Open
Address review feedback on the build-failure-analysis pre-gate#17240YuliiaKovalova wants to merge 1 commit into
YuliiaKovalova wants to merge 1 commit into
Conversation
Follow-up to dotnet#17228. Resolves the five review threads left on that PR, plus one bug found while validating the change. Security fix (thread on line 133): the pre-gate matched the union of `role_name` and `permission` over `admin|maintain|maintainer|write`. That was a widening, not a safety net. The REST docs for `GET /repos/{owner}/{repo}/collaborators/{username}/permission` state that `permission` returns the legacy base roles `admin|write|read|none`, "where the maintain role is mapped to write and the triage role is mapped to read", while `role_name` gives "the name of the assigned role, including custom roles". So `.permission in {admin, write}` is already exactly "has push access or better" - the set `roles: [admin, maintainer, write]` describes, maintainers included - and consulting `role_name` only added the risk that a custom organization role merely *named* like a privileged one (a custom `maintainer` inheriting read) would clear the gate with no push access. The gate now tests `.permission` only. Also fixed while validating that change: `gh api ... --jq '.permission'` does not fail closed cleanly. On a non-2xx response `gh` prints the error document to stdout and `--jq` does not filter it, so `perm` becomes a raw JSON blob that is then echoed into the workflow log. Reading the response first and extracting with `jq` yields an empty string for any error shape. Documentation of the hand-written gates (threads on lines 90, 93, 110): added KEEP IN SYNC notes to both places that restate `roles:` by hand, a note that PR-only scoping comes from `github.event.issue.pull_request` in this expression rather than from the compiler, and a note that `contains()` is a substring match unlike the authoritative `check_command_position`, so a mention or an edited comment can still cost a runner before `pre_activation` discards the result. Input validation (thread on line 125): `COMMENTER` is interpolated into an API path and into log output, so it now gets the same shape check `PR_NUMBER` and `BUILD_ID` already had. Bot logins such as `github-actions[bot]` and empty values are rejected before the API call. `shell: bash` added to every hand-written `run:` step in both workflows. Separate concern, same files: the two workflows both resolved their concurrency group to `build-failure-analysis-<pr>`. Concurrency groups are repository-global and `cancel-in-progress` is true, so an automatic analysis and an on-demand `/analyze-build-failure` for the same PR cancelled each other. Observed live in a fork. The command workflow now uses `build-failure-analysis-cmd-<pr>`; each still collapses its own repeats. Verification: `gh aw compile --strict` with v0.77.5 (matching the repo's locks), 0 errors / 0 warnings; `bash -n` clean on every generated run block; the new gate exercised against the live GitHub API for admin (allow), read (deny), bot login (rejected before the call), empty login (rejected), path-traversal login (rejected) and a nonexistent user (deny). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e5db9021-5257-4cf5-83ba-486a3e398391
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the Build Failure Analysis GitHub Actions workflows (and their gh-aw generated lock files) to incorporate prior review feedback, tighten authorization logic for the pre-download “cost gate”, and prevent cross-workflow cancellation via concurrency group collisions.
Changes:
- Tighten the command workflow’s pre-gate authorization to rely on the GitHub REST
permissionfield only (avoiding a potential security widening viarole_name) and validateCOMMENTERbefore using it in API paths/logs. - Add explicit
shell: bashto hand-writtenrun:steps to ensure consistent shell behavior. - Split concurrency groups between automatic vs slash-command workflows to avoid them canceling each other for the same PR.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| .github/workflows/build-failure-analysis.md | Adds explicit shell: bash to hand-written steps for consistent execution. |
| .github/workflows/build-failure-analysis.lock.yml | Regenerates lock output reflecting updated shell settings and metadata. |
| .github/workflows/build-failure-analysis-command.md | Fixes pre-gate permission logic, validates commenter login, documents intentional over-approximation, and separates concurrency group. |
| .github/workflows/build-failure-analysis-command.lock.yml | Regenerates lock output reflecting updated concurrency, shell settings, and permission gate logic. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This was referenced Aug 3, 2026
YuliiaKovalova
enabled auto-merge (squash)
August 3, 2026 15:43
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #17228. Resolves the five review threads @Evangelink left on that PR, plus a bug found while validating the change and one separate concurrency issue in the same files.
E1 — the
role_nameunion was a security widening (thread on line 133)You were right, and my code comment was self-contradictory: it claimed
role_name"reports the precise role (somaintainandtriagestay distinct)" and thatpermissionalone would miss maintainers. Both cannot be true.The REST docs for this endpoint say it plainly:
So
.permission in {admin, write}is exactly "has push access or better" — precisely the setroles: [admin, maintainer, write]describes, maintainers included. My stated rationale for addingrole_namewas factually wrong, and consulting it only added the risk you identified: a custom organization role merely named like a privileged one (a custommaintainerinheriting read) would clear the gate with no push access. Reverted to.permissiononly.One nuance worth recording, since it cuts the other way from "exactly equivalent": gh-aw's own check (v0.83.1,
actions/setup/js/check_permissions_utils.cjs) prefersrole_name, normalizesmaintainer→maintain, and falls back toinherited_rolefor custom roles. So.permission-only is a slightly stricter early-out than the authoritativepre_activationcheck that follows it. That is the safe direction for a pre-gate — it can cost a legitimate user a skipped download, never grant one access — so I've gone with your version.Bug found while validating this. Your suggested snippet used
gh api ... --jq '.permission'. Live-testing it showed it does not fail closed cleanly: on a non-2xx responseghprints the error document to stdout, and--jqdoes not filter it, sopermbecame a multi-line raw JSON blob that then got echoed into the workflow log. Kept the two-step shape instead, which yields""for any error shape:Exercised against the live GitHub API: admin → allow;
read→ deny;github-actions[bot]→ rejected before the call; empty login → rejected;foo/../../bar→ rejected; nonexistent user → deny ('none').E2 —
contains()is a substring match (thread on line 93)Documented, as you suggested. The comment now states that
contains()matches anywhere in the body while the authoritativecheck_command_positionrequires a valid position, so a write-access user merely mentioning the command — or editing an old comment that quotes it, sincetypes:includesedited— still starts the job and pays for the download beforepre_activationdiscards the result. It stays a deliberate over-approximation:if:expressions have no regex, andstartsWithwould reject the leading whitespace/newlines gh-aw accepts. It can cost a runner; it can never grant access.E3 — PR-only scoping is not compiler-enforced (thread on line 90)
Documented.
github.event.issue.pull_requestis what keeps plain issue comments out; gh-aw emits no such filter of its own despiteevents: [pull_request_comment](verified in the generated lock). Noted that it degrades safely without it —repos/.../pulls/<issue#>404s and no binlog is emitted — but it would pay for a runner first.E4 — authorization restated in three places (thread on line 110)
Added KEEP IN SYNC with
roles:notes to both hand-written gates, spelling out that editingroles:does not update them because onlypre_activationis generated from the frontmatter. Agreed the underlying footgun is upstream — customneeds:jobs are scheduled ahead ofpre_activation, so the only way to gate on cost is to restate the policy by hand.E5 —
COMMENTERunvalidated; missingshell:(thread on line 125)COMMENTERnow gets the same shape checkPR_NUMBERandBUILD_IDalready had (grep -qE '^[A-Za-z0-9-]+$'), rejecting bot logins likegithub-actions[bot]and empty values before the API call.shell: bashadded to every hand-writtenrun:step in both workflows.Separate concern: colliding concurrency groups
Not from the review, but in the same files. Both workflows resolved their group to
build-failure-analysis-<pr>. Groups are repository-global andcancel-in-progressis true, so an automatic analysis and an on-demand/analyze-build-failurefor the same PR cancelled each other. Reproduced live in a fork: a command run hadpre_activation/activation/agent/safe_outputscancelled when an automatic run for the same PR number started. The command workflow now usesbuild-failure-analysis-cmd-<pr>; each still collapses its own repeat invocations.Verification
gh aw compile --strictwith v0.77.5 (matching this repo's existing locks): 0 errors, 0 warningsbash -nclean on every generatedrun:block.gitattributes/actions-lock.jsoncompile side-effects revertedThe same two changes are going into microsoft/testfx#10401 and dotnet/sdk#55539, which carry ports of this workflow.