Copilot commented on code in PR #4453:
URL: https://github.com/apache/solr/pull/4453#discussion_r3272036331


##########
.github/workflows/renovate-changelog-push.yml:
##########
@@ -0,0 +1,155 @@
+name: Generate Renovate Changelog (Stage 2 - Push)
+
+# Stage 2: runs after Stage 1 completes, in the base-repo context with access 
to
+# SOLRBOT_GITHUB_TOKEN. Downloads the pre-generated artifact (no fork code 
executed here),
+# validates metadata, and pushes the changelog file to the fork branch.
+on:
+  workflow_run:
+    workflows:
+      - "Generate Renovate Changelog (Stage 1 - Prepare)"
+    types:
+      - completed
+
+# Each Stage 2 run corresponds to a unique Stage 1 run (unique 
workflow_run.id).
+# No cancel-in-progress: Stage 1's concurrency already serializes per-PR.
+concurrency:
+  group: renovate-changelog-push-${{ github.event.workflow_run.id }}
+
+permissions:
+  actions: read  # Required to download artifacts from another workflow run
+  contents: read # Minimal; actual write access to fork uses 
SOLRBOT_GITHUB_TOKEN PAT
+
+jobs:
+  push-changelog:
+    # Only proceed if Stage 1 succeeded for the expected fork.
+    # Checking head_repository here avoids a spurious artifact-not-found 
failure
+    # when Stage 1 ran but skipped its generate job (e.g. non-solrbot PR).
+    if: |
+      github.event.workflow_run.conclusion == 'success' &&
+      github.event.workflow_run.head_repository.full_name == 
'solrbot/apache-_-solr'
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Download artifact from Stage 1
+        uses: 
actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
+        with:
+          name: renovate-changelog-artifact
+          run-id: ${{ github.event.workflow_run.id }}
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          path: downloaded-artifact/
+
+      - name: Read and validate PR metadata
+        id: meta
+        run: |
+          set -euo pipefail
+          META_FILE="downloaded-artifact/pr-metadata.env"
+          if [ ! -f "$META_FILE" ]; then
+            echo "::error::Metadata file missing from artifact"; exit 1
+          fi
+
+          # Parse with grep/cut rather than source to avoid executing file 
content as shell
+          PR_NUMBER=$(grep '^PR_NUMBER=' "$META_FILE" | cut -d= -f2-)
+          HEAD_REF=$(grep  '^HEAD_REF='  "$META_FILE" | cut -d= -f2-)
+          HEAD_REPO=$(grep '^HEAD_REPO=' "$META_FILE" | cut -d= -f2-)
+
+          if [ -z "$PR_NUMBER" ] || [ -z "$HEAD_REF" ] || [ -z "$HEAD_REPO" ]; 
then
+            echo "::error::Missing required metadata fields (PR_NUMBER, 
HEAD_REF, or HEAD_REPO)"; exit 1
+          fi

Review Comment:
   With `set -euo pipefail`, if any of these keys are missing from the metadata 
file then `grep` exits non-zero and the step fails immediately, so the explicit 
`::error::Missing required metadata fields ...` message below never runs. 
Consider making these assignments non-fatal (e.g., `... || true`) and then 
relying on the subsequent emptiness check for a clear error message.



##########
.github/workflows/renovate-changelog-push.yml:
##########
@@ -0,0 +1,155 @@
+name: Generate Renovate Changelog (Stage 2 - Push)
+
+# Stage 2: runs after Stage 1 completes, in the base-repo context with access 
to
+# SOLRBOT_GITHUB_TOKEN. Downloads the pre-generated artifact (no fork code 
executed here),
+# validates metadata, and pushes the changelog file to the fork branch.
+on:
+  workflow_run:
+    workflows:
+      - "Generate Renovate Changelog (Stage 1 - Prepare)"
+    types:
+      - completed
+
+# Each Stage 2 run corresponds to a unique Stage 1 run (unique 
workflow_run.id).
+# No cancel-in-progress: Stage 1's concurrency already serializes per-PR.
+concurrency:
+  group: renovate-changelog-push-${{ github.event.workflow_run.id }}
+
+permissions:
+  actions: read  # Required to download artifacts from another workflow run
+  contents: read # Minimal; actual write access to fork uses 
SOLRBOT_GITHUB_TOKEN PAT
+
+jobs:
+  push-changelog:
+    # Only proceed if Stage 1 succeeded for the expected fork.
+    # Checking head_repository here avoids a spurious artifact-not-found 
failure
+    # when Stage 1 ran but skipped its generate job (e.g. non-solrbot PR).
+    if: |
+      github.event.workflow_run.conclusion == 'success' &&
+      github.event.workflow_run.head_repository.full_name == 
'solrbot/apache-_-solr'
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Download artifact from Stage 1
+        uses: 
actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
+        with:
+          name: renovate-changelog-artifact
+          run-id: ${{ github.event.workflow_run.id }}
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          path: downloaded-artifact/
+
+      - name: Read and validate PR metadata
+        id: meta
+        run: |
+          set -euo pipefail
+          META_FILE="downloaded-artifact/pr-metadata.env"
+          if [ ! -f "$META_FILE" ]; then
+            echo "::error::Metadata file missing from artifact"; exit 1
+          fi
+
+          # Parse with grep/cut rather than source to avoid executing file 
content as shell
+          PR_NUMBER=$(grep '^PR_NUMBER=' "$META_FILE" | cut -d= -f2-)
+          HEAD_REF=$(grep  '^HEAD_REF='  "$META_FILE" | cut -d= -f2-)
+          HEAD_REPO=$(grep '^HEAD_REPO=' "$META_FILE" | cut -d= -f2-)
+
+          if [ -z "$PR_NUMBER" ] || [ -z "$HEAD_REF" ] || [ -z "$HEAD_REPO" ]; 
then
+            echo "::error::Missing required metadata fields (PR_NUMBER, 
HEAD_REF, or HEAD_REPO)"; exit 1
+          fi
+
+          # Security: verify this is the expected fork before using 
SOLRBOT_GITHUB_TOKEN
+          if [ "$HEAD_REPO" != "solrbot/apache-_-solr" ]; then
+            echo "::error::Unexpected HEAD_REPO: '$HEAD_REPO'. Expected 
'solrbot/apache-_-solr'. Aborting."; exit 1
+          fi
+
+          # Validate PR_NUMBER is a plain positive integer (prevents injection)
+          if ! [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]]; then
+            echo "::error::PR_NUMBER is not a valid positive integer: 
'$PR_NUMBER'"; exit 1
+          fi
+
+          # Validate HEAD_REF looks like a safe branch name (prevents path 
traversal).
+          # Includes @ because Renovate uses it in branch names (e.g. 
renovate/node@lts).
+          if ! [[ "$HEAD_REF" =~ ^[a-zA-Z0-9._/@:-]+$ ]]; then
+            echo "::error::HEAD_REF contains unexpected characters: 
'$HEAD_REF'"; exit 1
+          fi
+
+          echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
+          echo "head_ref=$HEAD_REF"   >> "$GITHUB_OUTPUT"
+          echo "head_repo=$HEAD_REPO" >> "$GITHUB_OUTPUT"
+          echo "Validated: PR#${PR_NUMBER} on ${HEAD_REPO}@${HEAD_REF}"
+
+      - name: Clone fork branch
+        env:
+          SOLRBOT_TOKEN: ${{ secrets.SOLRBOT_GITHUB_TOKEN }}
+          HEAD_REPO: ${{ steps.meta.outputs.head_repo }}
+          HEAD_REF: ${{ steps.meta.outputs.head_ref }}
+        run: |
+          set -euo pipefail
+          # Store credentials so the token never appears in the command line 
or process list
+          git config --global credential.helper store
+          printf 'https://x-access-token:%[email protected]\n' "$SOLRBOT_TOKEN" 
> ~/.git-credentials

Review Comment:
   After writing `~/.git-credentials`, it should be restricted and ideally 
cleaned up (e.g., `chmod 600 ~/.git-credentials` and remove it after the push). 
This reduces the chance of accidental exposure in later steps or 
troubleshooting output within the same job.
   



##########
.github/workflows/renovate-changelog-push.yml:
##########
@@ -0,0 +1,155 @@
+name: Generate Renovate Changelog (Stage 2 - Push)
+
+# Stage 2: runs after Stage 1 completes, in the base-repo context with access 
to
+# SOLRBOT_GITHUB_TOKEN. Downloads the pre-generated artifact (no fork code 
executed here),
+# validates metadata, and pushes the changelog file to the fork branch.
+on:
+  workflow_run:
+    workflows:
+      - "Generate Renovate Changelog (Stage 1 - Prepare)"
+    types:
+      - completed
+
+# Each Stage 2 run corresponds to a unique Stage 1 run (unique 
workflow_run.id).
+# No cancel-in-progress: Stage 1's concurrency already serializes per-PR.
+concurrency:
+  group: renovate-changelog-push-${{ github.event.workflow_run.id }}
+
+permissions:
+  actions: read  # Required to download artifacts from another workflow run
+  contents: read # Minimal; actual write access to fork uses 
SOLRBOT_GITHUB_TOKEN PAT
+
+jobs:
+  push-changelog:
+    # Only proceed if Stage 1 succeeded for the expected fork.
+    # Checking head_repository here avoids a spurious artifact-not-found 
failure
+    # when Stage 1 ran but skipped its generate job (e.g. non-solrbot PR).
+    if: |
+      github.event.workflow_run.conclusion == 'success' &&
+      github.event.workflow_run.head_repository.full_name == 
'solrbot/apache-_-solr'
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Download artifact from Stage 1
+        uses: 
actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
+        with:
+          name: renovate-changelog-artifact
+          run-id: ${{ github.event.workflow_run.id }}
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          path: downloaded-artifact/
+
+      - name: Read and validate PR metadata
+        id: meta
+        run: |
+          set -euo pipefail
+          META_FILE="downloaded-artifact/pr-metadata.env"
+          if [ ! -f "$META_FILE" ]; then
+            echo "::error::Metadata file missing from artifact"; exit 1
+          fi
+
+          # Parse with grep/cut rather than source to avoid executing file 
content as shell
+          PR_NUMBER=$(grep '^PR_NUMBER=' "$META_FILE" | cut -d= -f2-)
+          HEAD_REF=$(grep  '^HEAD_REF='  "$META_FILE" | cut -d= -f2-)
+          HEAD_REPO=$(grep '^HEAD_REPO=' "$META_FILE" | cut -d= -f2-)
+
+          if [ -z "$PR_NUMBER" ] || [ -z "$HEAD_REF" ] || [ -z "$HEAD_REPO" ]; 
then
+            echo "::error::Missing required metadata fields (PR_NUMBER, 
HEAD_REF, or HEAD_REPO)"; exit 1
+          fi
+
+          # Security: verify this is the expected fork before using 
SOLRBOT_GITHUB_TOKEN
+          if [ "$HEAD_REPO" != "solrbot/apache-_-solr" ]; then
+            echo "::error::Unexpected HEAD_REPO: '$HEAD_REPO'. Expected 
'solrbot/apache-_-solr'. Aborting."; exit 1
+          fi
+
+          # Validate PR_NUMBER is a plain positive integer (prevents injection)
+          if ! [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]]; then
+            echo "::error::PR_NUMBER is not a valid positive integer: 
'$PR_NUMBER'"; exit 1
+          fi
+
+          # Validate HEAD_REF looks like a safe branch name (prevents path 
traversal).
+          # Includes @ because Renovate uses it in branch names (e.g. 
renovate/node@lts).
+          if ! [[ "$HEAD_REF" =~ ^[a-zA-Z0-9._/@:-]+$ ]]; then
+            echo "::error::HEAD_REF contains unexpected characters: 
'$HEAD_REF'"; exit 1

Review Comment:
   The HEAD_REF allowlist includes ":", which is meaningful in `git push` 
refspecs and could lead to unexpected behavior if it ever appears in the value. 
Consider validating `HEAD_REF` using `git check-ref-format --branch` (or 
explicitly rejecting refname edge cases like `:`, `@{`, `..`, trailing `.lock`, 
etc.) before using it in `git clone --branch` and `git push ... 
refs/heads/${HEAD_REF}`.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to