diff --git a/.github/workflows/preview-deploy.yml b/.github/workflows/preview-deploy.yml
index 0cfdb6882..3074503de 100644
--- a/.github/workflows/preview-deploy.yml
+++ b/.github/workflows/preview-deploy.yml
@@ -21,6 +21,14 @@ jobs:
contents: read
pull-requests: write
deployments: write
+ env:
+ # Defense-in-depth: pull `github.event.*` expressions out of shell
+ # and JS contexts so a future copy/paste of one of these step
+ # bodies onto a user-controlled string (branch name, PR title,
+ # commit message) doesn't open a command-injection path into a
+ # workflow that carries CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID.
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
steps:
- name: Wait for PR CI to pass
@@ -80,8 +88,8 @@ jobs:
- name: Configure preview files for this PR
working-directory: workers/preview
run: |
- sed -i "s/\${PR_NUMBER}/${{ github.event.pull_request.number }}/g" wrangler.toml
- sed -i "s/\${PR_NUMBER}/${{ github.event.pull_request.number }}/g" src/index.ts
+ sed -i "s/\${PR_NUMBER}/${PR_NUMBER}/g" wrangler.toml
+ sed -i "s/\${PR_NUMBER}/${PR_NUMBER}/g" src/index.ts
cat wrangler.toml
- name: Delete existing preview container app before redeploy
@@ -91,7 +99,7 @@ jobs:
working-directory: workers/preview
run: |
set -euo pipefail
- CONTAINER_NAME="sure-preview-${{ github.event.pull_request.number }}-railscontainer"
+ CONTAINER_NAME="sure-preview-${PR_NUMBER}-railscontainer"
echo "Looking for stale preview container app: $CONTAINER_NAME"
CONTAINER_ID=$(npx wrangler containers list --json | jq -r --arg NAME "$CONTAINER_NAME" '
@@ -113,7 +121,7 @@ jobs:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
working-directory: workers/preview
run: |
- WORKER_NAME="sure-preview-${{ github.event.pull_request.number }}"
+ WORKER_NAME="sure-preview-${PR_NUMBER}"
echo "Ensuring fresh preview deployment for $WORKER_NAME"
npx wrangler delete --name "$WORKER_NAME" --force || echo "Existing preview not found; continuing"
@@ -126,7 +134,7 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.payload.pull_request.head.sha,
- environment: `preview-pr-${{ github.event.pull_request.number }}`,
+ environment: `preview-pr-${process.env.PR_NUMBER}`,
auto_merge: false,
required_contexts: [],
description: 'PR Preview Deployment'
@@ -140,11 +148,12 @@ jobs:
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
+ CLOUDFLARE_WORKERS_SUBDOMAIN: ${{ secrets.CLOUDFLARE_WORKERS_SUBDOMAIN }}
run: |
- npx wrangler deploy --var "PR_NUMBER:${{ github.event.pull_request.number }}"
+ npx wrangler deploy --var "PR_NUMBER:${PR_NUMBER}"
# Get the deployment URL
- PREVIEW_URL="https://sure-preview-${{ github.event.pull_request.number }}.${{ secrets.CLOUDFLARE_WORKERS_SUBDOMAIN }}.workers.dev"
+ PREVIEW_URL="https://sure-preview-${PR_NUMBER}.${CLOUDFLARE_WORKERS_SUBDOMAIN}.workers.dev"
echo "preview_url=${PREVIEW_URL}" >> "$GITHUB_OUTPUT"
- name: Warm preview container
@@ -172,9 +181,13 @@ jobs:
- name: Comment on PR
if: success()
uses: actions/github-script@v7
+ env:
+ PREVIEW_URL: ${{ steps.deploy.outputs.preview_url }}
with:
script: |
- const previewUrl = '${{ steps.deploy.outputs.preview_url }}';
+ const previewUrl = process.env.PREVIEW_URL;
+ const issueNumber = Number(process.env.PR_NUMBER);
+ const headSha = process.env.HEAD_SHA;
const commentBody = `## 🚀 Preview Deployment Ready
Your preview environment has been deployed to Cloudflare Containers with the PR's Docker image.
@@ -185,13 +198,13 @@ jobs:
> 💤 The container will sleep after 30 minutes of inactivity and wake on the next request.
---
- Deployed from commit ${{ github.event.pull_request.head.sha }}`;
+ Deployed from commit ${headSha}`;
// Find existing comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
- issue_number: ${{ github.event.pull_request.number }}
+ issue_number: issueNumber
});
const botComment = comments.find(comment =>
@@ -210,7 +223,7 @@ jobs:
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
- issue_number: ${{ github.event.pull_request.number }},
+ issue_number: issueNumber,
body: commentBody
});
}
@@ -218,7 +231,7 @@ jobs:
if: success()
uses: actions/upload-artifact@v6
with:
- name: preview-cleanup-pr-${{ github.event.pull_request.number }}
+ name: preview-cleanup-pr-${{ env.PR_NUMBER }}
path: |
workers/preview/wrangler.toml
retention-days: 2