Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/workflows/pr-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: PR Review

on:
pull_request:
types: [opened, reopened, synchronize]
issue_comment:
types: [created]

jobs:
check:
# NOTE: comment body matching is exact — /review or /pr-reviewer with no trailing spaces, newlines, or mixed case
# This does not fail the workflow; non-matching comments simply do not trigger the job
if: |
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) ||
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fork check only prevents direct PR events from forks, but issue_comment events on PRs from forks are still allowed through (since the comment is on the repo itself). This is actually the correct pattern for fork PRs triggered by trusted collaborators, but it should be explicitly documented so future maintainers don't accidentally weaken the permission check thinking it's redundant.

Suggested change
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) ||
# NOTE: For fork PRs, only issue_comment from admin/maintain collaborators can trigger review.
# The fork check on pull_request events prevents auto-triggering from untrusted forks.
if: |
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) ||
(github.event_name == 'issue_comment' && github.event.issue.pull_request != null &&
(github.event.comment.body == '/review' || github.event.comment.body == '/pr-reviewer'))

(github.event_name == 'issue_comment' && github.event.issue.pull_request != null &&
(github.event.comment.body == '/review' || github.event.comment.body == '/pr-reviewer'))
runs-on: ubuntu-latest
outputs:
allowed: ${{ steps.gate.outputs.allowed }}
pr_number: ${{ steps.gate.outputs.pr_number }}
head_sha: ${{ steps.gate.outputs.head_sha }}
steps:
- name: Gate check
id: gate
run: |
set -euo pipefail
if [ "$EVENT_NAME" = "pull_request" ]; then
echo "allowed=true" >> $GITHUB_OUTPUT
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "head_sha=$HEAD_SHA" >> $GITHUB_OUTPUT
else
# Fall back to "none" if user is not a collaborator (gh api returns 404) so allowed=false is output cleanly
PERM=$(gh api repos/$GITHUB_REPOSITORY/collaborators/$COMMENT_USER_LOGIN/permission --jq '.permission' 2>/dev/null || echo "none")
# Intentionally require admin or maintain; write collaborators are excluded to
# limit who can trigger potentially expensive/sensitive review automation.
if [ "$PERM" = "admin" ] || [ "$PERM" = "maintain" ]; then
DATA=$(gh api repos/$GITHUB_REPOSITORY/pulls/$ISSUE_NUMBER)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gh api call for PR data is vulnerable to command injection if ISSUE_NUMBER is somehow tampered with. While it comes from github.event.issue.number (an integer), it's safer to quote it explicitly and validate it's numeric before use.

Suggested change
DATA=$(gh api repos/$GITHUB_REPOSITORY/pulls/$ISSUE_NUMBER)
ISSUE_NUMBER_SAFE=$(echo "$ISSUE_NUMBER" | grep -E '^[0-9]+$' || { echo 'Invalid issue number'; exit 1; })
DATA=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$ISSUE_NUMBER_SAFE")

echo "allowed=true" >> $GITHUB_OUTPUT
echo "pr_number=$ISSUE_NUMBER" >> $GITHUB_OUTPUT
echo "head_sha=$(echo "$DATA" | jq -r '.head.sha')" >> $GITHUB_OUTPUT
else
echo "allowed=false" >> $GITHUB_OUTPUT
fi
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
EVENT_NAME: ${{ github.event_name }}
PR_NUMBER: ${{ github.event.pull_request.number }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
COMMENT_USER_LOGIN: ${{ github.event.comment.user.login }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
# GITHUB_REPOSITORY is set automatically by GitHub Actions (owner/repo)

review:
needs: check
if: needs.check.outputs.allowed == 'true'
uses: adobe/aio-reusable-workflows/.github/workflows/pr-review.yml@main
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pinning the reusable workflow to @main means any update to the upstream workflow (including breaking changes or security issues) will automatically affect this workflow. Consider pinning to a specific SHA or tag for production stability and supply chain security.

Suggested change
uses: adobe/aio-reusable-workflows/.github/workflows/pr-review.yml@main
uses: adobe/aio-reusable-workflows/.github/workflows/pr-review.yml@<specific-sha-or-tag>

with:
pr_number: ${{ needs.check.outputs.pr_number }}
head_sha: ${{ needs.check.outputs.head_sha }}
secrets:
AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.APP_BUILDER_AWS_BEARER_TOKEN_BEDROCK }}
Loading