Skip to content

chore(ci-cd): refactor the branch enforcement workflow#163

Merged
orama254 merged 1 commit intoprodfrom
trigger-the-checker
Mar 13, 2026
Merged

chore(ci-cd): refactor the branch enforcement workflow#163
orama254 merged 1 commit intoprodfrom
trigger-the-checker

Conversation

@orama254
Copy link
Member

Fixes Issue

Changes proposed

Check List (Check all the applicable boxes)

  • My code follows the code style of this project.
  • My change requires changes to the documentation.
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • This PR does not contain plagiarized content.
  • The title of my pull request is a short description of the requested changes.

Screenshots

Note to reviewers

@vercel
Copy link

vercel bot commented Mar 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
reactdevske-website-vuvs Ready Ready Preview Mar 13, 2026 2:32pm

@orama254 orama254 merged commit 15b60e5 into prod Mar 13, 2026
4 of 6 checks passed
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR refactors the branch-check.yml CI workflow to enforce that only the develop branch may be merged into prod (previously main), adding comprehensive PR event triggers and improved diagnostic output. The change fits the project's branching model described in CONTRIBUTING.md.

Key changes:

  • Target branch updated from main to prod to match the actual production branch name
  • Added opened, synchronize, reopened, and edited PR event types for broader coverage
  • Job renamed to check-source-branch with a more descriptive name
  • Conditional check now explicitly validates both source and target branches with clearer ::error:: annotations

Issues found:

  • Script injection risk: ${{ github.head_ref }} and ${{ github.base_ref }} are interpolated directly into the shell script. Since github.head_ref is user-controlled (a contributor can name a branch anything), this is a known GitHub Actions injection vector. These values should be passed as env: variables instead.
  • Redundant condition: The [ "$BASE_BRANCH" = "prod" ] guard is unnecessary because the workflow trigger already filters to only PRs targeting prod.
  • Missing newline at end of file: The file still lacks a trailing newline, carried over from the previous version.

Confidence Score: 2/5

  • Not safe to merge as-is — the direct interpolation of user-controlled github.head_ref into a shell script introduces a script injection vulnerability.
  • The functional improvement (targeting prod and adding event types) is sound, but the security issue with direct context interpolation into shell is a known GitHub Actions vulnerability that should be resolved before merging. A fork contributor could craft a branch name to inject arbitrary shell commands into the runner.
  • .github/workflows/branch-check.yml — specifically lines 18–19 where github.head_ref and github.base_ref are interpolated directly into the shell script.

Important Files Changed

Filename Overview
.github/workflows/branch-check.yml Refactors branch enforcement workflow from main to prod target and adds PR event types; introduces a script injection vulnerability by directly interpolating github.head_ref and github.base_ref into the shell script instead of using environment variables.

Sequence Diagram

sequenceDiagram
    participant Dev as Developer
    participant GH as GitHub
    participant WF as branch-check.yml
    participant Runner as ubuntu-latest Runner

    Dev->>GH: Opens/updates PR (head: any branch → base: prod)
    GH->>WF: Triggers on pull_request (opened/sync/reopened/edited)
    WF->>Runner: Start check-source-branch job
    Runner->>Runner: Interpolate ${{ github.head_ref }} → HEAD_BRANCH
    Runner->>Runner: Interpolate ${{ github.base_ref }} → BASE_BRANCH
    alt BASE_BRANCH = "prod" AND HEAD_BRANCH ≠ "develop"
        Runner-->>GH: exit 1 (::error:: messages)
        GH-->>Dev: ❌ Check fails — branch not allowed
    else HEAD_BRANCH = "develop"
        Runner-->>GH: exit 0 (success message)
        GH-->>Dev: ✅ Check passes — merge allowed
    end
Loading

Last reviewed commit: e797bbd

Comment on lines +18 to +19
HEAD_BRANCH="${{ github.head_ref }}"
BASE_BRANCH="${{ github.base_ref }}"
Copy link
Contributor

Choose a reason for hiding this comment

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

Script injection vulnerability via direct context interpolation

${{ github.head_ref }} and ${{ github.base_ref }} are GitHub Actions template expressions that are resolved before the shell script executes. This means a branch name like develop"; malicious_command; echo " would be injected verbatim into the shell, potentially allowing a contributor (with the ability to open a PR from a fork) to execute arbitrary commands in the runner.

GitHub Security Lab documents this as a known injection vector, and github.head_ref is explicitly listed as an untrusted input.

The fix is to assign the context values to environment variables and reference those in the script instead:

      - name: Check source branch
        env:
          HEAD_BRANCH: ${{ github.head_ref }}
          BASE_BRANCH: ${{ github.base_ref }}
        run: |
          echo "PR: ${HEAD_BRANCH} → ${BASE_BRANCH}"

          if [ "$BASE_BRANCH" = "prod" ] && [ "$HEAD_BRANCH" != "develop" ]; then
            echo "::error::Merging to 'prod' is only allowed from the 'develop' branch."
            echo "::error::Source branch '${HEAD_BRANCH}' is not permitted."
            exit 1
          fi

          echo "✅ Source branch '${HEAD_BRANCH}' is allowed to merge into '${BASE_BRANCH}'."

This ensures the branch name is treated as data, not as executable shell syntax.


echo "PR: ${HEAD_BRANCH} → ${BASE_BRANCH}"

if [ "$BASE_BRANCH" = "prod" ] && [ "$HEAD_BRANCH" != "develop" ]; then
Copy link
Contributor

Choose a reason for hiding this comment

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

Redundant BASE_BRANCH condition

Since the workflow trigger already filters to only run on PRs targeting prod (via branches: [prod] in the on.pull_request config), $BASE_BRANCH will always equal "prod" at runtime. The [ "$BASE_BRANCH" = "prod" ] guard is therefore a no-op in practice.

While this is harmless and could be seen as defensive coding, removing it would simplify the logic and make it easier for future maintainers to understand:

Suggested change
if [ "$BASE_BRANCH" = "prod" ] && [ "$HEAD_BRANCH" != "develop" ]; then
if [ "$HEAD_BRANCH" != "develop" ]; then

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

fi No newline at end of file
fi

echo "✅ Source branch '${HEAD_BRANCH}' is allowed to merge into '${BASE_BRANCH}'." No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing newline at end of file

The file is missing a trailing newline (indicated by \ No newline at end of file in the diff). POSIX standard and most linters expect text files to end with a newline character. This was also missing in the previous version and should be addressed.

Suggested change
echo "✅ Source branch '${HEAD_BRANCH}' is allowed to merge into '${BASE_BRANCH}'."
echo "✅ Source branch '${HEAD_BRANCH}' is allowed to merge into '${BASE_BRANCH}'."

(Add a newline after this final line.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant