From a45fc8e983b72ff5d0d99427fb564ad0ef2f7ce1 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Fri, 29 May 2026 12:15:36 +0300 Subject: ci(ai-validation): allow Pass 2 review on external-contributor PRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upstream `anthropics/claude-code-action` performs a write-permission check on `github.actor` before our skip logic runs. On `pull_request_target` the actor is the PR author; external contributors resolve to `read` and the action exits 1 with `Actor does not have write permissions to the repository`. Net effect: AI validation has been failing on every external-contributor PR (LiudmylaNad, teslazonda, scottlaird in the last 4 weeks) while succeeding on maintainer PRs. Failure reproduced on run 26541079685 (PR #2061). Fix: set `allowed_non_write_users: '*'` on the Pass 2 step. The action bypasses the actor check when this input is set and `github_token` is provided (already the case). The action also auto-scrubs Anthropic / cloud / GHA secrets from subprocess envs when this input is set. Safe in THIS workflow because the existing defense-in-depth bounds what Pass 2 can do with untrusted PR content: - `allowedTools` restricted to inline-comment + read-only surfaces - `github_token` is the PR-scoped default (not the broader VYOS_APP_ID) - prompt marks PR content as untrusted via `` - workspace-wipe removes `CLAUDE.md` / `.claude/` before Pass 2 - prepare bundles MD via `git show HEAD:` (blob, not `cp`) Full rationale inlined as a comment block above the new input. 🤖 Generated by [robots](https://vyos.io) (cherry picked from commit 1fa39bd7ac64898f7a21922bc0f195d115d3cdeb) --- .github/workflows/ai-validation.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/.github/workflows/ai-validation.yml b/.github/workflows/ai-validation.yml index 58cd6c38..ed0d3e5e 100644 --- a/.github/workflows/ai-validation.yml +++ b/.github/workflows/ai-validation.yml @@ -566,6 +566,41 @@ jobs: # source / vyos-1x checkouts above, and has no write access # on vyos/vyos-documentation PRs. github_token: ${{ github.token }} + # Bypass the upstream action's actor write-permission check. The + # check (src/github/validation/permissions.ts in claude-code-action) + # calls getCollaboratorPermissionLevel on github.actor and fails + # with `Actor does not have write permissions to the repository` + # when an external contributor opens a PR, because the actor on + # pull_request_target is the PR author and forks resolve to + # `read`. That kills validation on every fork PR before any of + # our own skip logic runs — defeating the workflow's entire + # purpose (catching CLI/default-value drift in contributions + # from non-maintainers). Failure mode reproduced on run + # 26541079685 (PR #2061 from LiudmylaNad); same pattern hit + # every external contributor PR in the prior 4 weeks. + # + # Setting '*' is safe in THIS workflow specifically because the + # surrounding defense-in-depth bounds what Pass 2 can do with + # untrusted PR content: + # - allowedTools is restricted to inline-comment + read-only + # surfaces (no Bash arbitrary, no Write, no Edit — see the + # claude_args block below) + # - github_token is the workflow's PR-scoped default token + # (pull-requests:write + issues:write + contents:read) — + # NOT the broader VYOS_APP_ID token, which is only used for + # the vyos-1x / reviewer-source checkouts + # - the prompt explicitly marks PR content as untrusted with + # markers and tells Claude to treat + # it as data, not instructions + # - the workspace-wipe step removes CLAUDE.md / .claude/ + # before this step runs, so fork-controlled session + # instructions can't be auto-loaded + # - prepare bundles MD files via `git show HEAD:` (blob + # extraction, not `cp`) — symlinks resolve to their target + # paths, not their content + # - the action auto-scrubs Anthropic / cloud / GHA secrets + # from subprocess envs when this input is set + allowed_non_write_users: '*' # claude-code-action@v1 removed the top-level `model` input; CLI # flags including --model now travel via `claude_args` (see the # claude_args: block at the bottom of this step). -- cgit v1.2.3