diff --git a/.github/workflows/ace-editor.lock.yml b/.github/workflows/ace-editor.lock.yml index 8b5784506d8..510bc36dc84 100644 --- a/.github/workflows/ace-editor.lock.yml +++ b/.github/workflows/ace-editor.lock.yml @@ -952,7 +952,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 cc0bf4c4f65..68abca6e6c1 100644 --- a/.github/workflows/archie.lock.yml +++ b/.github/workflows/archie.lock.yml @@ -1349,7 +1349,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 75e30fdee4f..a9d25af7946 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1301,7 +1301,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 b7e91975bad..5fc4d82bad4 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -1681,7 +1681,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 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 diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml index 4a36dce8982..a113248b039 100644 --- a/.github/workflows/mergefest.lock.yml +++ b/.github/workflows/mergefest.lock.yml @@ -1311,7 +1311,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 34d40566396..b79b1ddfa63 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1398,7 +1398,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 7f5c790b4a4..ad4beb7732e 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -1312,7 +1312,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 1db14de82e2..ec27039609f 100644 --- a/.github/workflows/pr-nitpick-reviewer.lock.yml +++ b/.github/workflows/pr-nitpick-reviewer.lock.yml @@ -1392,7 +1392,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 de89090a5f0..4092a7e2798 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1561,7 +1561,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 7c6922b26bd..3e3f28a1679 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1579,7 +1579,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 20acfb444fb..f6561e4b9b4 100644 --- a/.github/workflows/security-review.lock.yml +++ b/.github/workflows/security-review.lock.yml @@ -1440,7 +1440,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 cf2fb991929..65ac223d6da 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -1370,7 +1370,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 1dc62d4316f..81e8b582a2c 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1571,7 +1571,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..e8532616e69 100644 --- a/pkg/workflow/compiler_pre_activation_job.go +++ b/pkg/workflow/compiler_pre_activation_job.go @@ -437,6 +437,32 @@ 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. + // + // 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( + &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 +510,82 @@ 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. +// 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:") +} + +// 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 +// continue to work even though bots rarely carry an OWNER/MEMBER/COLLABORATOR association. +// +// 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, +// and preserves existing on.bots allow-list behaviour. +func buildCommentAuthorAssociationCondition(bots []string) 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"), + ) + + 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. + // 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 +} + // 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..292ceb0a027 100644 --- a/pkg/workflow/role_checks_test.go +++ b/pkg/workflow/role_checks_test.go @@ -269,3 +269,250 @@ 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 + wantBotNames []string // bot actor logins expected in the pre_activation if: condition + }{ + { + 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, + }, + { + 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, + 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 { + 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) + + // 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 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 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:. + 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) + } + } + }) + } +} + +// 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) + } +}