From acd10553e5dfcb1f1128af926967d333a9ff6a90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 09:07:09 +0000 Subject: [PATCH 1/9] Initial plan From 8884df9841b140aa1a894fe5c7844e0e7c4a244b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 09:41:21 +0000 Subject: [PATCH 2/9] fix(rgs-004): add author_association guard to pre_activation jobs for comment-triggered workflows Resolves the RGS-004 static-analysis finding by adding an explicit `author_association` check to the `if:` condition of the `pre_activation` job in compiled workflows that: - are triggered by `issue_comment` or `pull_request_review_comment` events, AND - have permission checks enabled (i.e. `roles` is NOT set to `all`) The new condition: (github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment') || contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) prevents the `pre_activation` job from even starting for commenters with no affiliation to the repository, complementing the existing `check_membership` step that provides the authoritative per-user role check inside the job. Workflows with `roles: all` intentionally opt out of permission checks and are left unchanged. All 205 workflow lock files have been recompiled to reflect the change. Agent-Logs-Url: https://github.com/github/gh-aw/sessions/42a49fdd-afdd-4956-846b-7028f3bf4f91 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/ace-editor.lock.yml | 2 +- .github/workflows/archie.lock.yml | 2 +- .github/workflows/brave.lock.yml | 2 +- .github/workflows/cloclo.lock.yml | 2 +- .github/workflows/grumpy-reviewer.lock.yml | 14 +- .github/workflows/mergefest.lock.yml | 2 +- .github/workflows/pdf-summary.lock.yml | 2 +- .github/workflows/plan.lock.yml | 2 +- .../workflows/pr-nitpick-reviewer.lock.yml | 2 +- .github/workflows/q.lock.yml | 2 +- .github/workflows/scout.lock.yml | 2 +- .github/workflows/security-review.lock.yml | 2 +- .github/workflows/tidy.lock.yml | 2 +- .github/workflows/unbloat-docs.lock.yml | 2 +- pkg/workflow/compiler_pre_activation_job.go | 56 ++++++++ pkg/workflow/role_checks_test.go | 128 ++++++++++++++++++ 16 files changed, 204 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ace-editor.lock.yml b/.github/workflows/ace-editor.lock.yml index 6c4c7feadb5..503478abf7f 100644 --- a/.github/workflows/ace-editor.lock.yml +++ b/.github/workflows/ace-editor.lock.yml @@ -951,7 +951,7 @@ jobs: }); pre_activation: - if: "github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/ace ') || startsWith(github.event.comment.body, '/ace\n') || github.event.comment.body == '/ace') && github.event.issue.pull_request != null" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/ace ') || startsWith(github.event.comment.body, '/ace\n') || github.event.comment.body == '/ace') && github.event.issue.pull_request != null)" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml index ba7b4a167f4..317e78169d7 100644 --- a/.github/workflows/archie.lock.yml +++ b/.github/workflows/archie.lock.yml @@ -1348,7 +1348,7 @@ jobs: } pre_activation: - if: "github.event_name == 'issues' && (startsWith(github.event.issue.body, '/archie ') || startsWith(github.event.issue.body, '/archie\n') || github.event.issue.body == '/archie') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/archie ') || startsWith(github.event.comment.body, '/archie\n') || github.event.comment.body == '/archie') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/archie ') || startsWith(github.event.comment.body, '/archie\n') || github.event.comment.body == '/archie') && github.event.issue.pull_request != null || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/archie ') || startsWith(github.event.pull_request.body, '/archie\n') || github.event.pull_request.body == '/archie')" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/archie ') || startsWith(github.event.issue.body, '/archie\n') || github.event.issue.body == '/archie') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/archie ') || startsWith(github.event.comment.body, '/archie\n') || github.event.comment.body == '/archie') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/archie ') || startsWith(github.event.comment.body, '/archie\n') || github.event.comment.body == '/archie') && github.event.issue.pull_request != null || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/archie ') || startsWith(github.event.pull_request.body, '/archie\n') || github.event.pull_request.body == '/archie'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index 5f55b217859..66192e302ad 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1300,7 +1300,7 @@ jobs: } pre_activation: - if: "github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/brave ') || startsWith(github.event.comment.body, '/brave\n') || github.event.comment.body == '/brave') && github.event.issue.pull_request == null" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/brave ') || startsWith(github.event.comment.body, '/brave\n') || github.event.comment.body == '/brave') && github.event.issue.pull_request == null)" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index 276232a92ed..eb72def9492 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -1680,7 +1680,7 @@ jobs: } pre_activation: - if: "github.event_name == 'issues' && (startsWith(github.event.issue.body, '/cloclo ') || startsWith(github.event.issue.body, '/cloclo\n') || github.event.issue.body == '/cloclo') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/cloclo ') || startsWith(github.event.pull_request.body, '/cloclo\n') || github.event.pull_request.body == '/cloclo') || github.event_name == 'discussion' && (startsWith(github.event.discussion.body, '/cloclo ') || startsWith(github.event.discussion.body, '/cloclo\n') || github.event.discussion.body == '/cloclo') || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') || github.event_name == 'issues' && github.event.label.name == 'cloclo' || github.event_name == 'pull_request' && github.event.label.name == 'cloclo' || github.event_name == 'discussion' && github.event.label.name == 'cloclo'" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/cloclo ') || startsWith(github.event.issue.body, '/cloclo\n') || github.event.issue.body == '/cloclo') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/cloclo ') || startsWith(github.event.pull_request.body, '/cloclo\n') || github.event.pull_request.body == '/cloclo') || github.event_name == 'discussion' && (startsWith(github.event.discussion.body, '/cloclo ') || startsWith(github.event.discussion.body, '/cloclo\n') || github.event.discussion.body == '/cloclo') || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') || github.event_name == 'issues' && github.event.label.name == 'cloclo' || github.event_name == 'pull_request' && github.event.label.name == 'cloclo' || github.event_name == 'discussion' && github.event.label.name == 'cloclo')" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index 869cf47a073..bc52d0c6c93 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -1340,18 +1340,18 @@ jobs: DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock 2>/dev/null || echo '0') export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e CODEX_HOME -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.1' - cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_05bb518f02848b63_EOF + cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_c362cab272c469da_EOF [history] persistence = "none" [shell_environment_policy] inherit = "core" include_only = ["CODEX_API_KEY", "HOME", "OPENAI_API_KEY", "PATH"] - GH_AW_MCP_CONFIG_05bb518f02848b63_EOF + GH_AW_MCP_CONFIG_c362cab272c469da_EOF # Generate JSON config for MCP gateway GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_6993abcbbcdf9c65_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_14296061dfd61c42_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { }, @@ -1362,11 +1362,11 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_6993abcbbcdf9c65_EOF + GH_AW_MCP_CONFIG_14296061dfd61c42_EOF # Sync converter output to writable CODEX_HOME for Codex mkdir -p /tmp/gh-aw/mcp-config - cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_2562ee5624f085c8_EOF + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_4ac45934b8af5480_EOF model_provider = "openai-proxy" [model_providers.openai-proxy] name = "OpenAI AWF proxy" @@ -1376,7 +1376,7 @@ jobs: [shell_environment_policy] inherit = "core" include_only = ["CODEX_API_KEY", "HOME", "OPENAI_API_KEY", "PATH"] - GH_AW_CODEX_SHELL_POLICY_2562ee5624f085c8_EOF + GH_AW_CODEX_SHELL_POLICY_4ac45934b8af5480_EOF awk ' BEGIN { skip_openai_proxy = 0 } /^[[:space:]]*model_provider[[:space:]]*=/ { next } @@ -1455,7 +1455,7 @@ jobs: } pre_activation: - if: "((github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy')) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request_review_comment'))) && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id)" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (((github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy')) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request_review_comment'))) && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml index eb3403c5603..be58d0ca3f1 100644 --- a/.github/workflows/mergefest.lock.yml +++ b/.github/workflows/mergefest.lock.yml @@ -1310,7 +1310,7 @@ jobs: } pre_activation: - if: "github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/mergefest ') || startsWith(github.event.comment.body, '/mergefest\n') || github.event.comment.body == '/mergefest') && github.event.issue.pull_request != null" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/mergefest ') || startsWith(github.event.comment.body, '/mergefest\n') || github.event.comment.body == '/mergefest') && github.event.issue.pull_request != null)" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 63e9c9f7377..d7feaaf5f62 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1397,7 +1397,7 @@ jobs: } pre_activation: - if: "(github.event_name == 'issue_comment' || github.event_name == 'issues') && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/summarize ') || startsWith(github.event.issue.body, '/summarize\n') || github.event.issue.body == '/summarize') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/summarize ') || startsWith(github.event.comment.body, '/summarize\n') || github.event.comment.body == '/summarize') && github.event.issue.pull_request == null) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'issues'))" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && ((github.event_name == 'issue_comment' || github.event_name == 'issues') && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/summarize ') || startsWith(github.event.issue.body, '/summarize\n') || github.event.issue.body == '/summarize') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/summarize ') || startsWith(github.event.comment.body, '/summarize\n') || github.event.comment.body == '/summarize') && github.event.issue.pull_request == null) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'issues')))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index 60d7a6f09c5..9c14457d683 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -1311,7 +1311,7 @@ jobs: } pre_activation: - if: "github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/plan ') || startsWith(github.event.comment.body, '/plan\n') || github.event.comment.body == '/plan') && github.event.issue.pull_request == null || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/plan ') || startsWith(github.event.comment.body, '/plan\n') || github.event.comment.body == '/plan')" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/plan ') || startsWith(github.event.comment.body, '/plan\n') || github.event.comment.body == '/plan') && github.event.issue.pull_request == null || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/plan ') || startsWith(github.event.comment.body, '/plan\n') || github.event.comment.body == '/plan'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml index df99858b036..a35a22db495 100644 --- a/.github/workflows/pr-nitpick-reviewer.lock.yml +++ b/.github/workflows/pr-nitpick-reviewer.lock.yml @@ -1391,7 +1391,7 @@ jobs: } pre_activation: - if: "github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/nit ') || startsWith(github.event.comment.body, '/nit\n') || github.event.comment.body == '/nit') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/nit ') || startsWith(github.event.comment.body, '/nit\n') || github.event.comment.body == '/nit')" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/nit ') || startsWith(github.event.comment.body, '/nit\n') || github.event.comment.body == '/nit') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/nit ') || startsWith(github.event.comment.body, '/nit\n') || github.event.comment.body == '/nit'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 170d6a6fef3..63144674d89 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1560,7 +1560,7 @@ jobs: } pre_activation: - if: "(github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment') && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/q ') || startsWith(github.event.issue.body, '/q\n') || github.event.issue.body == '/q') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/q ') || startsWith(github.event.comment.body, '/q\n') || github.event.comment.body == '/q') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/q ') || startsWith(github.event.comment.body, '/q\n') || github.event.comment.body == '/q') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/q ') || startsWith(github.event.comment.body, '/q\n') || github.event.comment.body == '/q') || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/q ') || startsWith(github.event.pull_request.body, '/q\n') || github.event.pull_request.body == '/q') || github.event_name == 'discussion' && (startsWith(github.event.discussion.body, '/q ') || startsWith(github.event.discussion.body, '/q\n') || github.event.discussion.body == '/q') || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/q ') || startsWith(github.event.comment.body, '/q\n') || github.event.comment.body == '/q')) || (!(github.event_name == 'issues')) && (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request')) && (!(github.event_name == 'pull_request_review_comment')) && (!(github.event_name == 'discussion')) && (!(github.event_name == 'discussion_comment'))" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && ((github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment') && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/q ') || startsWith(github.event.issue.body, '/q\n') || github.event.issue.body == '/q') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/q ') || startsWith(github.event.comment.body, '/q\n') || github.event.comment.body == '/q') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/q ') || startsWith(github.event.comment.body, '/q\n') || github.event.comment.body == '/q') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/q ') || startsWith(github.event.comment.body, '/q\n') || github.event.comment.body == '/q') || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/q ') || startsWith(github.event.pull_request.body, '/q\n') || github.event.pull_request.body == '/q') || github.event_name == 'discussion' && (startsWith(github.event.discussion.body, '/q ') || startsWith(github.event.discussion.body, '/q\n') || github.event.discussion.body == '/q') || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/q ') || startsWith(github.event.comment.body, '/q\n') || github.event.comment.body == '/q')) || (!(github.event_name == 'issues')) && (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request')) && (!(github.event_name == 'pull_request_review_comment')) && (!(github.event_name == 'discussion')) && (!(github.event_name == 'discussion_comment')))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index b3f57521d19..c54e05d7d99 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1578,7 +1578,7 @@ jobs: } pre_activation: - if: "(github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment') && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/scout ') || startsWith(github.event.issue.body, '/scout\n') || github.event.issue.body == '/scout') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/scout ') || startsWith(github.event.comment.body, '/scout\n') || github.event.comment.body == '/scout') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/scout ') || startsWith(github.event.comment.body, '/scout\n') || github.event.comment.body == '/scout') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/scout ') || startsWith(github.event.comment.body, '/scout\n') || github.event.comment.body == '/scout') || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/scout ') || startsWith(github.event.pull_request.body, '/scout\n') || github.event.pull_request.body == '/scout') || github.event_name == 'discussion' && (startsWith(github.event.discussion.body, '/scout ') || startsWith(github.event.discussion.body, '/scout\n') || github.event.discussion.body == '/scout') || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/scout ') || startsWith(github.event.comment.body, '/scout\n') || github.event.comment.body == '/scout')) || (!(github.event_name == 'issues')) && (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request')) && (!(github.event_name == 'pull_request_review_comment')) && (!(github.event_name == 'discussion')) && (!(github.event_name == 'discussion_comment'))" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && ((github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment') && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/scout ') || startsWith(github.event.issue.body, '/scout\n') || github.event.issue.body == '/scout') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/scout ') || startsWith(github.event.comment.body, '/scout\n') || github.event.comment.body == '/scout') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/scout ') || startsWith(github.event.comment.body, '/scout\n') || github.event.comment.body == '/scout') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/scout ') || startsWith(github.event.comment.body, '/scout\n') || github.event.comment.body == '/scout') || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/scout ') || startsWith(github.event.pull_request.body, '/scout\n') || github.event.pull_request.body == '/scout') || github.event_name == 'discussion' && (startsWith(github.event.discussion.body, '/scout ') || startsWith(github.event.discussion.body, '/scout\n') || github.event.discussion.body == '/scout') || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/scout ') || startsWith(github.event.comment.body, '/scout\n') || github.event.comment.body == '/scout')) || (!(github.event_name == 'issues')) && (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request')) && (!(github.event_name == 'pull_request_review_comment')) && (!(github.event_name == 'discussion')) && (!(github.event_name == 'discussion_comment')))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/security-review.lock.yml b/.github/workflows/security-review.lock.yml index 9e2a4667417..617f6423b6d 100644 --- a/.github/workflows/security-review.lock.yml +++ b/.github/workflows/security-review.lock.yml @@ -1439,7 +1439,7 @@ jobs: } pre_activation: - if: "github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/security-review ') || startsWith(github.event.comment.body, '/security-review\n') || github.event.comment.body == '/security-review') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/security-review ') || startsWith(github.event.comment.body, '/security-review\n') || github.event.comment.body == '/security-review')" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/security-review ') || startsWith(github.event.comment.body, '/security-review\n') || github.event.comment.body == '/security-review') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/security-review ') || startsWith(github.event.comment.body, '/security-review\n') || github.event.comment.body == '/security-review'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index b83351931a7..bc86a93c52e 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -1369,7 +1369,7 @@ jobs: } pre_activation: - if: "github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/tidy ') || startsWith(github.event.comment.body, '/tidy\n') || github.event.comment.body == '/tidy') && github.event.issue.pull_request != null || !(github.event_name == 'issue_comment')" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/tidy ') || startsWith(github.event.comment.body, '/tidy\n') || github.event.comment.body == '/tidy') && github.event.issue.pull_request != null || !(github.event_name == 'issue_comment'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 595e585b591..291ea137363 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1570,7 +1570,7 @@ jobs: } pre_activation: - if: "github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/unbloat ') || startsWith(github.event.comment.body, '/unbloat\n') || github.event.comment.body == '/unbloat') && github.event.issue.pull_request != null || !(github.event_name == 'issue_comment')" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/unbloat ') || startsWith(github.event.comment.body, '/unbloat\n') || github.event.comment.body == '/unbloat') && github.event.issue.pull_request != null || !(github.event_name == 'issue_comment'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go index 021e310cb95..1c7e13418a0 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -437,6 +437,25 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec } } + // For comment-triggered workflows that require permission checks, add an author_association + // guard to the job-level if: condition. This prevents the job from running at all for + // unauthorized commenters (skipped/gray ⊘ vs running and then denying inside check_membership). + // The guard only applies when: + // - the workflow has permission checks enabled (needsPermissionCheck == true), AND + // - the compiled on: section includes issue_comment or pull_request_review_comment events. + // Workflows with roles:all opt out of needsPermissionCheck and are intentionally unrestricted. + if needsPermissionCheck && hasCommentEventInOn(data.On) { + commentAuthCondition := RenderCondition(buildCommentAuthorAssociationCondition()) + if jobIfCondition != "" { + jobIfCondition = RenderCondition(BuildAnd( + &ExpressionNode{Expression: commentAuthCondition}, + &ExpressionNode{Expression: jobIfCondition}, + )) + } else { + jobIfCondition = commentAuthCondition + } + } + // In script mode, explicitly add a cleanup step (mirrors post.js in dev/release/action mode). if c.actionMode.IsScript() { steps = append(steps, c.generateScriptModeCleanupStep()) @@ -484,6 +503,43 @@ func buildLabelNamesCondition(labelNames []string) string { return result.Render() } +// hasCommentEventInOn reports whether the rendered on: section includes issue_comment or +// pull_request_review_comment events. These are the events flagged by RGS-004 because +// any GitHub user (including unaffiliated outsiders) can post a comment and trigger the workflow. +func hasCommentEventInOn(on string) bool { + return strings.Contains(on, "issue_comment") || strings.Contains(on, "pull_request_review_comment") +} + +// buildCommentAuthorAssociationCondition returns a ConditionNode that passes for non-comment +// events and for comment events whose author is an OWNER, MEMBER, or COLLABORATOR. +// +// The generated expression is: +// +// (github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment') +// || contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) +// +// This satisfies the RGS-004 rule (explicit author_association check for comment-triggered +// workflows) while remaining transparent to non-comment events such as push or schedule. +func buildCommentAuthorAssociationCondition() ConditionNode { + notIssueComment := BuildNotEquals( + BuildPropertyAccess("github.event_name"), + BuildStringLiteral("issue_comment"), + ) + notPRReviewComment := BuildNotEquals( + BuildPropertyAccess("github.event_name"), + BuildStringLiteral("pull_request_review_comment"), + ) + notCommentEvent := BuildAnd(notIssueComment, notPRReviewComment) + + authorizedAssoc := BuildFunctionCall( + "contains", + BuildFunctionCall("fromJSON", BuildStringLiteral(`["OWNER","MEMBER","COLLABORATOR"]`)), + BuildPropertyAccess("github.event.comment.author_association"), + ) + + return BuildOr(notCommentEvent, authorizedAssoc) +} + // generateReportSkipStep generates the "Report skip reason" step for the pre-activation job. // The step runs with if: always() and writes skip reasons to the GitHub Actions job summary // extractPreActivationCustomFields extracts custom steps and outputs from jobs.pre-activation field in frontmatter. diff --git a/pkg/workflow/role_checks_test.go b/pkg/workflow/role_checks_test.go index 44a26ca5679..e2a818fe711 100644 --- a/pkg/workflow/role_checks_test.go +++ b/pkg/workflow/role_checks_test.go @@ -269,3 +269,131 @@ func TestInferEventsFromTriggers(t *testing.T) { }) } } + +// TestCommentAuthorAssociationConditionInPreActivation verifies that the compiler adds +// an explicit author_association guard to the pre_activation job's if: condition when the +// workflow is triggered by issue_comment or pull_request_review_comment events and permission +// checks are enabled (i.e. roles is NOT set to "all"). This addresses the RGS-004 static +// analysis finding. +func TestCommentAuthorAssociationConditionInPreActivation(t *testing.T) { + tests := []struct { + name string + frontmatter string + wantAssocCheck bool + }{ + { + name: "issue_comment trigger with default roles gets author_association check", + frontmatter: `--- +on: + issue_comment: + types: [created] +engine: copilot +--- + +Test workflow +`, + wantAssocCheck: true, + }, + { + name: "slash_command trigger compiles to issue_comment and gets check", + frontmatter: `--- +on: + slash_command: + name: test + events: [issue_comment] +engine: copilot +--- + +Test workflow +`, + wantAssocCheck: true, + }, + { + name: "pull_request_review_comment trigger gets author_association check", + frontmatter: `--- +on: + pull_request_review_comment: + types: [created] +engine: copilot +--- + +Test workflow +`, + wantAssocCheck: true, + }, + { + name: "issue_comment trigger with roles:all does NOT get author_association check", + frontmatter: `--- +on: + roles: all + issue_comment: + types: [created] +engine: copilot +--- + +Test workflow +`, + wantAssocCheck: false, + }, + { + name: "push trigger only does NOT get author_association check", + frontmatter: `--- +on: + push: + branches: [main] +engine: copilot +--- + +Test workflow +`, + wantAssocCheck: false, + }, + { + name: "workflow_dispatch-only trigger does NOT get author_association check", + frontmatter: `--- +on: + workflow_dispatch: + roles: [write] +engine: copilot +--- + +Test workflow +`, + wantAssocCheck: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpDir := testutil.TempDir(t, "comment-auth-test") + compiler := NewCompiler() + + workflowPath := filepath.Join(tmpDir, "test-workflow.md") + err := os.WriteFile(workflowPath, []byte(tt.frontmatter), 0644) + if err != nil { + t.Fatalf("Failed to write workflow file: %v", err) + } + + err = compiler.CompileWorkflow(workflowPath) + if err != nil { + t.Fatalf("Failed to compile workflow: %v", err) + } + + outputPath := filepath.Join(tmpDir, "test-workflow.lock.yml") + compiledContent, err := os.ReadFile(outputPath) + if err != nil { + t.Fatalf("Failed to read compiled workflow: %v", err) + } + + compiledStr := string(compiledContent) + + hasCheck := strings.Contains(compiledStr, "author_association") + if tt.wantAssocCheck && !hasCheck { + t.Errorf("Expected compiled workflow to contain author_association check, but it was absent.\nWorkflow:\n%s", compiledStr) + } + if !tt.wantAssocCheck && hasCheck { + t.Errorf("Expected compiled workflow to NOT contain author_association check, but it was present.\nWorkflow:\n%s", compiledStr) + } + }) + } +} From 83afc087492f00555e3f3e3fd642258dbcd3c5cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 09:45:37 +0000 Subject: [PATCH 3/9] refactor: use colon-suffixed key matching in hasCommentEventInOn for precision Addresses code review feedback by matching 'issue_comment:' and 'pull_request_review_comment:' (with colon) instead of bare substrings, making it unambiguous that we are checking for YAML event keys in the compiled on: section. Agent-Logs-Url: https://github.com/github/gh-aw/sessions/42a49fdd-afdd-4956-846b-7028f3bf4f91 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_pre_activation_job.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go index 1c7e13418a0..d639409426a 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -506,8 +506,10 @@ func buildLabelNamesCondition(labelNames []string) string { // hasCommentEventInOn reports whether the rendered on: section includes issue_comment or // pull_request_review_comment events. These are the events flagged by RGS-004 because // any GitHub user (including unaffiliated outsiders) can post a comment and trigger the workflow. +// data.On is compiled YAML generated by the compiler, so checking for the event name followed by a +// colon (':') reliably identifies a trigger key without false-positives from embedded strings. func hasCommentEventInOn(on string) bool { - return strings.Contains(on, "issue_comment") || strings.Contains(on, "pull_request_review_comment") + return strings.Contains(on, "issue_comment:") || strings.Contains(on, "pull_request_review_comment:") } // buildCommentAuthorAssociationCondition returns a ConditionNode that passes for non-comment From e04e8e25f21a04e991d677fd55fd6925dc0e73b6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 10:00:44 +0000 Subject: [PATCH 4/9] chore: merge main and recompile all workflow lock files Agent-Logs-Url: https://github.com/github/gh-aw/sessions/2f777bd5-8d1d-4b13-8271-db54ae3a2acc Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/grumpy-reviewer.lock.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index ed93b6517d3..a18e3c97d81 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -1340,18 +1340,18 @@ jobs: DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock 2>/dev/null || echo '0') export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e CODEX_HOME -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.3' - cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_397935c42eb71de3_EOF + cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_add0259e46a30c2a_EOF [history] persistence = "none" [shell_environment_policy] inherit = "core" include_only = ["CODEX_API_KEY", "HOME", "OPENAI_API_KEY", "PATH"] - GH_AW_MCP_CONFIG_397935c42eb71de3_EOF + GH_AW_MCP_CONFIG_add0259e46a30c2a_EOF # Generate JSON config for MCP gateway GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_4ed57bfdf517bc45_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_0b715e912204d212_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { }, @@ -1362,11 +1362,11 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_4ed57bfdf517bc45_EOF + GH_AW_MCP_CONFIG_0b715e912204d212_EOF # Sync converter output to writable CODEX_HOME for Codex mkdir -p /tmp/gh-aw/mcp-config - cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_b3b1f8224793788f_EOF + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_f4f9b06591122573_EOF model_provider = "openai-proxy" [model_providers.openai-proxy] name = "OpenAI AWF proxy" @@ -1376,7 +1376,7 @@ jobs: [shell_environment_policy] inherit = "core" include_only = ["CODEX_API_KEY", "HOME", "OPENAI_API_KEY", "PATH"] - GH_AW_CODEX_SHELL_POLICY_b3b1f8224793788f_EOF + GH_AW_CODEX_SHELL_POLICY_f4f9b06591122573_EOF awk ' BEGIN { skip_openai_proxy = 0 } /^[[:space:]]*model_provider[[:space:]]*=/ { next } @@ -1455,7 +1455,7 @@ jobs: } pre_activation: - if: "((github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy')) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request_review_comment'))) && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id)" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (((github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy')) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request_review_comment'))) && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id))" runs-on: ubuntu-slim permissions: contents: read From c6892c9d224e72a093ed8e203a6fa3a979980029 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 10:16:38 +0000 Subject: [PATCH 5/9] fix(rgs-004): add bot actor exemption to author_association guard; tighten test to use pre_activation section Agent-Logs-Url: https://github.com/github/gh-aw/sessions/5b214baf-6bf3-4d89-a7f3-03169377d187 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/update_actions_test.go | 2 +- pkg/workflow/compiler_pre_activation_job.go | 29 ++++++++++--- pkg/workflow/role_checks_test.go | 48 +++++++++++++++++++-- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/pkg/cli/update_actions_test.go b/pkg/cli/update_actions_test.go index d2b8f87e2b5..0dff19cb331 100644 --- a/pkg/cli/update_actions_test.go +++ b/pkg/cli/update_actions_test.go @@ -760,7 +760,7 @@ func TestUpdateActions_NeverDowngrades(t *testing.T) { t.Fatalf("failed to chdir: %v", err) } - if err := UpdateActions(context.Background(), true, false, false); err != nil { + if err := UpdateActions(context.Background(), true, false, false, 0); err != nil { t.Fatalf("UpdateActions() error = %v", err) } diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go index d639409426a..a1ad363e03e 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -445,7 +445,7 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec // - the compiled on: section includes issue_comment or pull_request_review_comment events. // Workflows with roles:all opt out of needsPermissionCheck and are intentionally unrestricted. if needsPermissionCheck && hasCommentEventInOn(data.On) { - commentAuthCondition := RenderCondition(buildCommentAuthorAssociationCondition()) + commentAuthCondition := RenderCondition(buildCommentAuthorAssociationCondition(data.Bots)) if jobIfCondition != "" { jobIfCondition = RenderCondition(BuildAnd( &ExpressionNode{Expression: commentAuthCondition}, @@ -514,15 +514,22 @@ func hasCommentEventInOn(on string) bool { // buildCommentAuthorAssociationCondition returns a ConditionNode that passes for non-comment // events and for comment events whose author is an OWNER, MEMBER, or COLLABORATOR. +// Actors listed in bots (from on.bots) are also exempted so that bot/app-triggered workflows +// continue to work even though bots rarely carry an OWNER/MEMBER/COLLABORATOR association. // -// The generated expression is: +// The generated expression (without bots) is: // // (github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment') // || contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) // +// With one or more bots an additional OR clause is appended for each bot: +// +// || github.actor == 'dependabot[bot]' +// // This satisfies the RGS-004 rule (explicit author_association check for comment-triggered -// workflows) while remaining transparent to non-comment events such as push or schedule. -func buildCommentAuthorAssociationCondition() ConditionNode { +// workflows) while remaining transparent to non-comment events such as push or schedule, +// and preserves existing on.bots allow-list behaviour. +func buildCommentAuthorAssociationCondition(bots []string) ConditionNode { notIssueComment := BuildNotEquals( BuildPropertyAccess("github.event_name"), BuildStringLiteral("issue_comment"), @@ -539,7 +546,19 @@ func buildCommentAuthorAssociationCondition() ConditionNode { BuildPropertyAccess("github.event.comment.author_association"), ) - return BuildOr(notCommentEvent, authorizedAssoc) + result := BuildOr(notCommentEvent, authorizedAssoc) + + // Allow explicitly listed bot/app actors so on.bots behaviour is preserved. + // Bots typically carry no OWNER/MEMBER/COLLABORATOR association, so we exempt + // them by actor login rather than by author_association. + for _, bot := range bots { + result = BuildOr(result, BuildEquals( + BuildPropertyAccess("github.actor"), + BuildStringLiteral(bot), + )) + } + + return result } // generateReportSkipStep generates the "Report skip reason" step for the pre-activation job. diff --git a/pkg/workflow/role_checks_test.go b/pkg/workflow/role_checks_test.go index e2a818fe711..1c647fd7688 100644 --- a/pkg/workflow/role_checks_test.go +++ b/pkg/workflow/role_checks_test.go @@ -361,6 +361,22 @@ Test workflow `, wantAssocCheck: false, }, + { + name: "issue_comment trigger with on.bots allows bot actor in pre_activation if", + frontmatter: `--- +on: + issue_comment: + types: [created] + bots: + - dependabot[bot] + - renovate[bot] +engine: copilot +--- + +Test workflow +`, + wantAssocCheck: true, + }, } for _, tt := range tests { @@ -387,12 +403,38 @@ Test workflow compiledStr := string(compiledContent) - hasCheck := strings.Contains(compiledStr, "author_association") + // Extract the pre_activation job section so we check only the job-level if:, + // not unrelated occurrences in other jobs or comments. + preActivationSection := extractJobSection(compiledStr, "pre_activation") + + // When there is no pre_activation job (e.g. roles:all or workflow_dispatch-only), + // the author_association guard is clearly absent — handle both outcomes here. + if preActivationSection == "" { + if tt.wantAssocCheck { + t.Errorf("Expected pre_activation job section to be present and contain author_association check, but the section was not found") + } + // wantAssocCheck == false and no pre_activation section → test passes. + return + } + + // Look for the author_association guard within the pre_activation job section. + // The job-level if: may be rendered as a block scalar (if: >\n ), + // so we check the whole section rather than a single line. Any occurrence of + // author_association in this section comes from the job-level if: expression. + hasCheck := strings.Contains(preActivationSection, "author_association") if tt.wantAssocCheck && !hasCheck { - t.Errorf("Expected compiled workflow to contain author_association check, but it was absent.\nWorkflow:\n%s", compiledStr) + t.Errorf("Expected pre_activation job if: to contain author_association check, but it was absent.\nFull pre_activation section:\n%s", preActivationSection) } if !tt.wantAssocCheck && hasCheck { - t.Errorf("Expected compiled workflow to NOT contain author_association check, but it was present.\nWorkflow:\n%s", compiledStr) + t.Errorf("Expected pre_activation job if: to NOT contain author_association check, but it was present.\nFull pre_activation section:\n%s", preActivationSection) + } + + // For the bot test case, also verify bot actor exemptions appear in the job if:. + if tt.wantAssocCheck && strings.Contains(tt.frontmatter, "bots:") { + hasBot := strings.Contains(preActivationSection, "dependabot") && strings.Contains(preActivationSection, "renovate") + if !hasBot { + t.Errorf("Expected pre_activation job if: to contain bot actor exemptions, but they were absent.\nFull pre_activation section:\n%s", preActivationSection) + } } }) } From c90e438ae939d8c820802273749d394e1a0e9cea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 10:20:14 +0000 Subject: [PATCH 6/9] refactor(rgs-004): use BuildDisjunction for bot OR terms; make bot test assertions data-driven Agent-Logs-Url: https://github.com/github/gh-aw/sessions/5b214baf-6bf3-4d89-a7f3-03169377d187 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_pre_activation_job.go | 16 +++++++++++----- pkg/workflow/role_checks_test.go | 9 +++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go index a1ad363e03e..1cb6469bf63 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -551,11 +551,17 @@ func buildCommentAuthorAssociationCondition(bots []string) ConditionNode { // Allow explicitly listed bot/app actors so on.bots behaviour is preserved. // Bots typically carry no OWNER/MEMBER/COLLABORATOR association, so we exempt // them by actor login rather than by author_association. - for _, bot := range bots { - result = BuildOr(result, BuildEquals( - BuildPropertyAccess("github.actor"), - BuildStringLiteral(bot), - )) + // Use BuildDisjunction to collect all bot conditions into a flat OR rather than + // building a deeply nested binary tree with repeated BuildOr calls. + if len(bots) > 0 { + botTerms := make([]ConditionNode, len(bots)) + for i, bot := range bots { + botTerms[i] = BuildEquals( + BuildPropertyAccess("github.actor"), + BuildStringLiteral(bot), + ) + } + result = BuildOr(result, BuildDisjunction(false, botTerms...)) } return result diff --git a/pkg/workflow/role_checks_test.go b/pkg/workflow/role_checks_test.go index 1c647fd7688..1a1524b13ad 100644 --- a/pkg/workflow/role_checks_test.go +++ b/pkg/workflow/role_checks_test.go @@ -280,6 +280,7 @@ func TestCommentAuthorAssociationConditionInPreActivation(t *testing.T) { name string frontmatter string wantAssocCheck bool + wantBotNames []string // bot actor logins expected in the pre_activation if: condition }{ { name: "issue_comment trigger with default roles gets author_association check", @@ -376,6 +377,7 @@ engine: copilot Test workflow `, wantAssocCheck: true, + wantBotNames: []string{"dependabot[bot]", "renovate[bot]"}, }, } @@ -430,10 +432,9 @@ Test workflow } // For the bot test case, also verify bot actor exemptions appear in the job if:. - if tt.wantAssocCheck && strings.Contains(tt.frontmatter, "bots:") { - hasBot := strings.Contains(preActivationSection, "dependabot") && strings.Contains(preActivationSection, "renovate") - if !hasBot { - t.Errorf("Expected pre_activation job if: to contain bot actor exemptions, but they were absent.\nFull pre_activation section:\n%s", preActivationSection) + for _, botName := range tt.wantBotNames { + if !strings.Contains(preActivationSection, botName) { + t.Errorf("Expected pre_activation job if: to contain bot actor exemption for %q, but it was absent.\nFull pre_activation section:\n%s", botName, preActivationSection) } } }) From 292dcd068cd2bd8311fd84a75534b69207af2902 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 10:31:04 +0000 Subject: [PATCH 7/9] fix(rgs-004): disable static author_association guard when bots contain expressions Agent-Logs-Url: https://github.com/github/gh-aw/sessions/17eae8a2-66f1-4171-ab82-34f214a90bb8 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_pre_activation_job.go | 18 +++++++++++++++++- pkg/workflow/role_checks_test.go | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go index 1cb6469bf63..35550f0989f 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -444,7 +444,11 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec // - the workflow has permission checks enabled (needsPermissionCheck == true), AND // - the compiled on: section includes issue_comment or pull_request_review_comment events. // Workflows with roles:all opt out of needsPermissionCheck and are intentionally unrestricted. - if needsPermissionCheck && hasCommentEventInOn(data.On) { + // + // Exception: if any bot name in data.Bots is a GitHub Actions expression (contains ${{), + // we cannot evaluate it statically. In that case the guard is skipped entirely so the + // runtime check_membership step always runs and handles bot authorization at runtime. + if needsPermissionCheck && hasCommentEventInOn(data.On) && !botsContainExpression(data.Bots) { commentAuthCondition := RenderCondition(buildCommentAuthorAssociationCondition(data.Bots)) if jobIfCondition != "" { jobIfCondition = RenderCondition(BuildAnd( @@ -512,6 +516,18 @@ func hasCommentEventInOn(on string) bool { return strings.Contains(on, "issue_comment:") || strings.Contains(on, "pull_request_review_comment:") } +// botsContainExpression reports whether any entry in bots is a GitHub Actions expression +// (i.e. contains "${{"). When true, the static author_association guard must be disabled so +// that check_membership always runs and evaluates the bot list at runtime. +func botsContainExpression(bots []string) bool { + for _, bot := range bots { + if strings.Contains(bot, "${{") { + return true + } + } + return false +} + // buildCommentAuthorAssociationCondition returns a ConditionNode that passes for non-comment // events and for comment events whose author is an OWNER, MEMBER, or COLLABORATOR. // Actors listed in bots (from on.bots) are also exempted so that bot/app-triggered workflows diff --git a/pkg/workflow/role_checks_test.go b/pkg/workflow/role_checks_test.go index 1a1524b13ad..cb2b312e07e 100644 --- a/pkg/workflow/role_checks_test.go +++ b/pkg/workflow/role_checks_test.go @@ -379,6 +379,22 @@ Test workflow wantAssocCheck: true, wantBotNames: []string{"dependabot[bot]", "renovate[bot]"}, }, + { + name: "issue_comment trigger with expression bot disables static guard so runtime check always runs", + frontmatter: `--- +on: + issue_comment: + types: [created] + bots: + - ${{ vars.TRUSTED_BOT }} +engine: copilot +--- + +Test workflow +`, + // The static guard must be absent; check_membership handles the bot at runtime. + wantAssocCheck: false, + }, } for _, tt := range tests { From 639a11797665d1372fceb3797661466a64b99edf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 10:46:38 +0000 Subject: [PATCH 8/9] fix(rgs-004): also disable static guard when on: section contains GHA expression; add import test Agent-Logs-Url: https://github.com/github/gh-aw/sessions/9a652c4f-a3e3-4aa3-8765-8eb235c7c3e2 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_pre_activation_job.go | 11 ++-- pkg/workflow/role_checks_test.go | 60 +++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/pkg/workflow/compiler_pre_activation_job.go b/pkg/workflow/compiler_pre_activation_job.go index 35550f0989f..e8532616e69 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -445,10 +445,13 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec // - the compiled on: section includes issue_comment or pull_request_review_comment events. // Workflows with roles:all opt out of needsPermissionCheck and are intentionally unrestricted. // - // Exception: if any bot name in data.Bots is a GitHub Actions expression (contains ${{), - // we cannot evaluate it statically. In that case the guard is skipped entirely so the - // runtime check_membership step always runs and handles bot authorization at runtime. - if needsPermissionCheck && hasCommentEventInOn(data.On) && !botsContainExpression(data.Bots) { + // Exceptions — the static guard is skipped and runtime check_membership always runs: + // 1. Any bot name in data.Bots is a GitHub Actions expression (contains ${{): we cannot + // embed the bot identity into a static if: expression. This also applies to bots that + // originate from imported shared agentic workflows. + // 2. The compiled on: section itself contains a GitHub Actions expression (contains ${{): + // event detection cannot be performed reliably at compile time. + if needsPermissionCheck && hasCommentEventInOn(data.On) && !botsContainExpression(data.Bots) && !strings.Contains(data.On, "${{") { commentAuthCondition := RenderCondition(buildCommentAuthorAssociationCondition(data.Bots)) if jobIfCondition != "" { jobIfCondition = RenderCondition(BuildAnd( diff --git a/pkg/workflow/role_checks_test.go b/pkg/workflow/role_checks_test.go index cb2b312e07e..292ceb0a027 100644 --- a/pkg/workflow/role_checks_test.go +++ b/pkg/workflow/role_checks_test.go @@ -456,3 +456,63 @@ Test workflow }) } } + +// TestCommentAuthorAssociationImportedExpressionBot verifies that when a shared agentic workflow +// contributes an expression-based bot (e.g. "${{ vars.TRUSTED_BOT }}") via imports, the static +// author_association guard is disabled and check_membership is always reached at runtime. +func TestCommentAuthorAssociationImportedExpressionBot(t *testing.T) { + tmpDir := testutil.TempDir(t, "comment-auth-import-test") + compiler := NewCompiler() + + // Shared agentic workflow: no on: field, but defines a bot with a GHA expression. + sharedContent := `--- +bots: + - "${{ vars.TRUSTED_BOT }}" +--- +` + sharedPath := filepath.Join(tmpDir, "shared-bots.md") + err := os.WriteFile(sharedPath, []byte(sharedContent), 0644) + if err != nil { + t.Fatalf("Failed to write shared workflow file: %v", err) + } + + // Main workflow imports the shared workflow; its own on: has issue_comment. + mainContent := `--- +on: + issue_comment: + types: [created] +engine: copilot +imports: + - shared-bots.md +--- + +Test workflow +` + mainPath := filepath.Join(tmpDir, "main-workflow.md") + err = os.WriteFile(mainPath, []byte(mainContent), 0644) + if err != nil { + t.Fatalf("Failed to write main workflow file: %v", err) + } + + err = compiler.CompileWorkflow(mainPath) + if err != nil { + t.Fatalf("Failed to compile workflow: %v", err) + } + + lockPath := filepath.Join(tmpDir, "main-workflow.lock.yml") + compiled, err := os.ReadFile(lockPath) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + + preActivationSection := extractJobSection(string(compiled), "pre_activation") + if preActivationSection == "" { + t.Fatal("Expected pre_activation job section to be present") + } + + // The static guard must be absent: the expression bot cannot be evaluated at compile time, + // so check_membership must always run to handle authorization at runtime. + if strings.Contains(preActivationSection, "author_association") { + t.Errorf("Expected pre_activation job if: to NOT contain author_association check (expression bot from import), but it was present.\nFull pre_activation section:\n%s", preActivationSection) + } +} From 8055c24d0917ef7343a10bf0c400245f78b1aedc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 1 May 2026 10:56:50 +0000 Subject: [PATCH 9/9] chore: merge main and recompile all workflows Agent-Logs-Url: https://github.com/github/gh-aw/sessions/f36ae35e-ecee-4354-9948-d13bb8f1212d Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/grumpy-reviewer.lock.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index 1cdf9103ee9..bcc583eaca7 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -1341,18 +1341,18 @@ jobs: DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock 2>/dev/null || echo '0') export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e CODEX_HOME -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.3' - cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_fd7b8c535f7b48f2_EOF + cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_c8afa9c962eee9ad_EOF [history] persistence = "none" [shell_environment_policy] inherit = "core" include_only = ["CODEX_API_KEY", "HOME", "OPENAI_API_KEY", "PATH"] - GH_AW_MCP_CONFIG_fd7b8c535f7b48f2_EOF + GH_AW_MCP_CONFIG_c8afa9c962eee9ad_EOF # Generate JSON config for MCP gateway GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_5d0ee4f187050744_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_0c545e1357fbd02a_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { }, @@ -1363,11 +1363,11 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_5d0ee4f187050744_EOF + GH_AW_MCP_CONFIG_0c545e1357fbd02a_EOF # Sync converter output to writable CODEX_HOME for Codex mkdir -p /tmp/gh-aw/mcp-config - cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_8a2e6842095e3d6b_EOF + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_acb7ee44fc6df1cf_EOF model_provider = "openai-proxy" [model_providers.openai-proxy] name = "OpenAI AWF proxy" @@ -1377,7 +1377,7 @@ jobs: [shell_environment_policy] inherit = "core" include_only = ["CODEX_API_KEY", "HOME", "OPENAI_API_KEY", "PATH"] - GH_AW_CODEX_SHELL_POLICY_8a2e6842095e3d6b_EOF + GH_AW_CODEX_SHELL_POLICY_acb7ee44fc6df1cf_EOF awk ' BEGIN { skip_openai_proxy = 0 } /^[[:space:]]*model_provider[[:space:]]*=/ { next } @@ -1456,7 +1456,7 @@ jobs: } pre_activation: - if: "((github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy')) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request_review_comment'))) && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id)" + if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (((github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy')) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request_review_comment'))) && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id))" runs-on: ubuntu-slim permissions: contents: read