streamline some github actions (#7430)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2026-02-23 08:54:18 -05:00
committed by GitHub
parent b58144632b
commit ef3f5fa6c2
5 changed files with 147 additions and 20 deletions
+8 -4
View File
@@ -181,9 +181,11 @@ jobs:
run: |
echo "number=$(jq -r '.number' /tmp/issue.json)" >> $GITHUB_OUTPUT
echo "title<<TITLE_EOF" >> $GITHUB_OUTPUT
# SECURITY: Use random delimiter to prevent injection if title contains our delimiter
DELIMITER="EOF_$(openssl rand -hex 8)"
echo "title<<$DELIMITER" >> $GITHUB_OUTPUT
jq -r '.title' /tmp/issue.json >> $GITHUB_OUTPUT
echo "TITLE_EOF" >> $GITHUB_OUTPUT
echo "$DELIMITER" >> $GITHUB_OUTPUT
- name: Run goose
id: goose
@@ -202,9 +204,11 @@ jobs:
if [ -n "$(git status --porcelain)" ] && [ -f /tmp/issue_summary.txt ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "summary<<SUMMARY_EOF" >> $GITHUB_OUTPUT
# SECURITY: Use random delimiter to prevent injection if summary contains our delimiter
SUMMARY_DELIMITER="EOF_$(openssl rand -hex 8)"
echo "summary<<$SUMMARY_DELIMITER" >> $GITHUB_OUTPUT
cat /tmp/issue_summary.txt >> $GITHUB_OUTPUT
echo "SUMMARY_EOF" >> $GITHUB_OUTPUT
echo "$SUMMARY_DELIMITER" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
+9 -5
View File
@@ -274,9 +274,11 @@ jobs:
INSTRUCTIONS="No specific instructions - perform a general code review."
fi
echo "instructions<<INSTRUCTIONS_EOF" >> $GITHUB_OUTPUT
# SECURITY: Use random delimiter to prevent injection if comment contains our delimiter
DELIMITER="EOF_$(openssl rand -hex 8)"
echo "instructions<<$DELIMITER" >> $GITHUB_OUTPUT
echo "$INSTRUCTIONS" >> $GITHUB_OUTPUT
echo "INSTRUCTIONS_EOF" >> $GITHUB_OUTPUT
echo "$DELIMITER" >> $GITHUB_OUTPUT
- name: Run goose review
id: goose
@@ -285,14 +287,16 @@ jobs:
PR_TITLE: ${{ github.event.issue.title }}
PR_BODY: ${{ github.event.issue.body }}
REVIEW_INSTRUCTIONS: ${{ steps.instructions.outputs.instructions }}
# SECURITY: Pass issue JSON via environment variable to avoid heredoc injection
# (GHSA-mm8p-57gq-3xj6) - user-controlled content could terminate heredoc early
ISSUE_JSON: ${{ toJson(github.event.issue) }}
run: |
mkdir -p $HOME/.local/share/goose/sessions
mkdir -p $HOME/.config/goose
git config --global --add safe.directory "$GITHUB_WORKSPACE"
cat > /tmp/pr.json << 'PRJSON'
${{ toJson(github.event.issue) }}
PRJSON
# SECURITY: Use printf with env var instead of heredoc to prevent injection
printf '%s' "$ISSUE_JSON" > /tmp/pr.json
echo "$GOOSE_RECIPE" | envsubst '$PR_NUMBER $PR_TITLE $PR_BODY $REVIEW_INSTRUCTIONS' > /tmp/recipe.yaml
+53 -2
View File
@@ -1,4 +1,8 @@
# This workflow is triggered by a comment on PR with the text ".build-cli"
#
# SECURITY: This workflow checks out and builds code from PRs. To prevent
# malicious code execution (GHSA-4h72-4h3w-4587, GHSA-mqm8-hhf6-wvjq),
# we verify the commenter has write access before proceeding.
on:
issue_comment:
types: [created]
@@ -28,11 +32,56 @@ jobs:
name: Trigger on ".build-cli" PR comment
runs-on: ubuntu-latest
outputs:
continue: 'true'
continue: ${{ steps.security_check.outputs.authorized }}
pr_number: ${{ steps.command.outputs.issue_number || github.event.inputs.pr_number }}
head_sha: ${{ steps.set_head_sha.outputs.head_sha || github.sha }}
steps:
# SECURITY: Verify commenter has write access BEFORE any checkout
# This prevents attackers from triggering builds on their own malicious PRs
- name: Verify commenter permissions
id: security_check
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
// workflow_dispatch requires repo write access, so it's inherently safe
if (context.eventName === 'workflow_dispatch') {
core.setOutput('authorized', 'true');
console.log('✅ workflow_dispatch - authorized');
return;
}
const commenter = context.payload.comment.user.login;
console.log(`Checking permissions for: ${commenter}`);
try {
const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username: commenter
});
const allowed = ['admin', 'maintain', 'write'].includes(permission.permission);
console.log(`Permission level: ${permission.permission}, Authorized: ${allowed}`);
if (!allowed) {
// Post a comment explaining the rejection
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
body: `⚠️ @${commenter} Only repository collaborators with write access can trigger builds.`
});
core.setOutput('authorized', 'false');
} else {
core.setOutput('authorized', 'true');
}
} catch (error) {
console.log(`Permission check failed: ${error.message}`);
core.setOutput('authorized', 'false');
}
- name: Run command action
if: steps.security_check.outputs.authorized == 'true'
uses: github/command@v2.0.3
id: command
with:
@@ -42,10 +91,12 @@ jobs:
allowed_contexts: pull_request
- name: Checkout code
if: steps.security_check.outputs.authorized == 'true'
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Get PR head SHA with gh
id: set_head_sha
if: steps.security_check.outputs.authorized == 'true'
run: |
echo "Get PR head SHA with gh"
HEAD_SHA=$(gh pr view "$ISSUE_NUMBER" --json headRefOid -q .headRefOid)
@@ -92,4 +143,4 @@ jobs:
- [📦 Windows (x86_64)](https://nightly.link/${{ github.repository }}/actions/runs/${{ github.run_id }}/goose-x86_64-pc-windows-gnu.zip)
These links are provided by nightly.link and will work even if you're not logged into GitHub.
+55 -4
View File
@@ -1,7 +1,12 @@
# This workflow is triggered by a comment on PR with the text ".bundle"
# It bundles the ARM64 Desktop App, then creates a PR comment with a link to download the app.
#
# SECURITY: This workflow checks out and builds code from PRs. To prevent
# malicious code execution (GHSA-4h72-4h3w-4587), we verify the commenter
# has write access before proceeding.
on:
issue_comment:
types: [created]
workflow_dispatch:
inputs:
pr_number:
@@ -28,11 +33,56 @@ jobs:
name: Trigger on ".bundle" PR comment
runs-on: ubuntu-latest
outputs:
continue: 'true'
continue: ${{ steps.security_check.outputs.authorized }}
pr_number: ${{ steps.command.outputs.issue_number || github.event.inputs.pr_number }}
pr_sha: ${{ steps.get_pr_info.outputs.sha }}
steps:
# SECURITY: Verify commenter has write access BEFORE any checkout
# This prevents attackers from triggering builds on their own malicious PRs
- name: Verify commenter permissions
id: security_check
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
// workflow_dispatch requires repo write access, so it's inherently safe
if (context.eventName === 'workflow_dispatch') {
core.setOutput('authorized', 'true');
console.log('✅ workflow_dispatch - authorized');
return;
}
const commenter = context.payload.comment.user.login;
console.log(`Checking permissions for: ${commenter}`);
try {
const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username: commenter
});
const allowed = ['admin', 'maintain', 'write'].includes(permission.permission);
console.log(`Permission level: ${permission.permission}, Authorized: ${allowed}`);
if (!allowed) {
// Post a comment explaining the rejection
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
body: `⚠️ @${commenter} Only repository collaborators with write access can trigger builds.`
});
core.setOutput('authorized', 'false');
} else {
core.setOutput('authorized', 'true');
}
} catch (error) {
console.log(`Permission check failed: ${error.message}`);
core.setOutput('authorized', 'false');
}
- name: Debug workflow trigger
if: steps.security_check.outputs.authorized == 'true'
env:
WORKFLOW_NAME: ${{ github.workflow }}
WORKFLOW_REF: ${{ github.ref }}
@@ -50,6 +100,7 @@ jobs:
echo "Repository: ${REPOSITORY}"
- name: Run command action
if: steps.security_check.outputs.authorized == 'true'
uses: github/command@3442f3fa1efe01bdb024b157083c337902d17372 # v2.0.3
id: command
with:
@@ -61,7 +112,7 @@ jobs:
# Get the PR's SHA
- name: Get PR info
id: get_pr_info
if: ${{ steps.command.outputs.continue == 'true' || github.event_name == 'workflow_dispatch' }}
if: steps.security_check.outputs.authorized == 'true'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
@@ -152,4 +203,4 @@ jobs:
* optionally run `codesign --force --deep --sign - --entitlements ui/desktop/entitlements.plist '/path/to/Goose.app'`
* start the app
The signing step is only needed if you do something that uses mac entitlements like speech to text
The signing step is only needed if you do something that uses mac entitlements like speech to text
+22 -5
View File
@@ -25,14 +25,29 @@ jobs:
with:
egress-policy: audit
- name: Checkout PR
# SECURITY FIX (GHSA-7qhh-cph9-6ppm): Checkout base branch for trusted Dockerfile
# The PR could contain a malicious Dockerfile that exfiltrates secrets.
# We checkout the base branch (trusted) for building the scanner image,
# and only fetch recipe files from the PR for scanning.
- name: Checkout base branch (trusted code)
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: ${{ github.event.pull_request.base.sha }}
fetch-depth: 0
path: trusted
- name: Fetch PR recipe files only
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
path: pr-content
sparse-checkout: |
documentation/src/pages/recipes/data/recipes/
- name: Check if recipe files changed in this push
id: recipe_changes
working-directory: pr-content
run: |
set -e
echo "🔍 Checking if recipe files were modified in this push..."
@@ -68,6 +83,7 @@ jobs:
- name: Find recipe files in PR (new or modified)
id: find_recipes
if: steps.recipe_changes.outputs.recipe_files_changed == 'true'
working-directory: pr-content
run: |
set -e
echo "Looking for recipe files in PR (new or modified)..."
@@ -97,8 +113,8 @@ jobs:
echo "has_recipes=true" >> "$GITHUB_OUTPUT"
echo "recipe_count=$RECIPE_COUNT" >> "$GITHUB_OUTPUT"
# Save recipe file paths for later steps
echo "$RECIPE_FILES" > "$RUNNER_TEMP/recipe_files.txt"
# Save recipe file paths for later steps (with pr-content prefix for mounting)
echo "$RECIPE_FILES" | sed 's|^|pr-content/|' > "$RUNNER_TEMP/recipe_files.txt"
fi
- name: Set up Docker Buildx
@@ -117,14 +133,15 @@ jobs:
DOCKER_BUILDKIT: 1
IMAGE_TAG: ${{ github.sha }}
run: |
# SECURITY: Build from trusted/ directory (base branch) to prevent malicious Dockerfile
docker buildx build \
--pull \
--no-cache \
--load \
--platform linux/amd64 \
-t "recipe-scanner:${IMAGE_TAG}" \
-f recipe-scanner/Dockerfile \
recipe-scanner/
-f trusted/recipe-scanner/Dockerfile \
trusted/recipe-scanner/
- name: Scan all recipe files
if: steps.find_recipes.outputs.has_recipes == 'true' && steps.recipe_changes.outputs.recipe_files_changed == 'true'