[ci] Added automated backport workflow#567
Conversation
📝 WalkthroughWalkthroughA new GitHub Actions workflow file is introduced to automate backporting fixes to a stable branch. The workflow triggers on pushes to master/main and on issue comments. It defines two jobs: one that automatically backports on push events using commit SHA, and another that backports on demand when a pull request comment starting with /backport is created by repository members or owners. Both jobs delegate to a centralized reusable backport workflow, passing relevant parameters and bot secrets. Estimated code review effort🎯 2 (Simple) | ⏱️ ~7 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/backport.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: CI Build
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/backport.yml:
- Around line 30-36: Remove the redundant state check and include COLLABORATOR
in the author association whitelist: update the workflow conditional by removing
the clause "github.event.issue.state == 'closed'" (since merged_at != null
already implies closed) and expand the JSON array passed to contains(...) to
include "COLLABORATOR" alongside "MEMBER" and "OWNER" so the expression becomes
contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'),
github.event.comment.author_association); keep the existing merged_at and
startsWith(github.event.comment.body, '/backport') checks unchanged.
- Around line 11-13: The concurrency group currently uses github.workflow and
github.ref which collapses different event types (issue_comment vs push) into
the same slot; update the concurrency.group value so it includes the event type
and a per-PR identifier to avoid conflation and serialization — e.g. change the
group key built from concurrency.group: backport-${{ github.workflow }}-${{
github.ref }} to include ${{ github.event_name }} and a PR/issue identifier
(like ${{ github.event.pull_request.number || github.event.issue.number }}) and
keep a commit identifier when relevant (e.g. ${{ github.sha }}), so jobs
triggered by different events or different PRs no longer produce the same group
key.
- Line 22: Replace the mutable branch reference in the reusable workflow usage
by pinning both occurrences of
"openwisp/openwisp-utils/.github/workflows/reusable-backport.yml@master" to a
full-length commit SHA (use the commit SHA for the target revision) and add a
short human-readable comment next to each pinned SHA explaining why it’s pinned;
update both "uses:" entries so they reference the immutable commit SHA instead
of "@master" and rely on Dependabot/Renovate to keep the SHA current.
| concurrency: | ||
| group: backport-${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: false |
There was a problem hiding this comment.
Concurrency group conflates unrelated events and serializes independent PR backports
github.sha and github.ref always point to the default branch for issue_comment events. This means both jobs produce the identical group key backport-Backport fixes to stable branch-refs/heads/master, causing two problems:
- A
push-triggered backport and any/backportcomment always land in the same concurrency slot, blocking each other even though they are completely independent operations. - Multiple
/backportcomments on different PRs are serialized unnecessarily, since they all resolve to the same group key.
🔧 Proposed fix
concurrency:
- group: backport-${{ github.workflow }}-${{ github.ref }}
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'issue_comment' && github.event.issue.number || github.ref }}
cancel-in-progress: false🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/backport.yml around lines 11 - 13, The concurrency group
currently uses github.workflow and github.ref which collapses different event
types (issue_comment vs push) into the same slot; update the concurrency.group
value so it includes the event type and a per-PR identifier to avoid conflation
and serialization — e.g. change the group key built from concurrency.group:
backport-${{ github.workflow }}-${{ github.ref }} to include ${{
github.event_name }} and a PR/issue identifier (like ${{
github.event.pull_request.number || github.event.issue.number }}) and keep a
commit identifier when relevant (e.g. ${{ github.sha }}), so jobs triggered by
different events or different PRs no longer produce the same group key.
| jobs: | ||
| backport-on-push: | ||
| if: github.event_name == 'push' | ||
| uses: openwisp/openwisp-utils/.github/workflows/reusable-backport.yml@master |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/backport.ymlRepository: openwisp/docker-openwisp
Length of output: 1656
🌐 Web query:
GitHub security best practices reusable workflows pinning commit SHA
💡 Result:
Best practices (GitHub Actions) for reusable workflows + SHA pinning
1) Pin all third‑party actions to a full-length commit SHA
Use immutable refs to reduce supply-chain risk from moved tags/branches. Prefer a SHA that corresponds to a tagged release, and add a comment with the human version for readability (and easier updates). Example:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7[1]
2) Pin reusable workflow calls to a commit SHA (safest)
When calling a reusable workflow (jobs.<job_id>.uses), {ref} can be a SHA, tag, or branch—but GitHub recommends SHA as the safest for stability and security.
jobs:
ci:
uses: org/repo/.github/workflows/ci.yml@<full_commit_sha>[2]
Note: If the reusable workflow is in the same repo and you use ./.github/workflows/<file>, it’s taken from the same commit as the caller workflow (no @ref). [2]
3) Enforce SHA pinning via org/enterprise policy (where available)
Admins can require actions to be pinned to a full-length commit SHA using the Allowed actions and reusable workflows policy controls. This is explicitly supported as an enforcement option, and GitHub has continued improving these controls (including enforcement and blocking). [3] [4]
4) Restrict what can run (allowlist/denylist)
Use the same policy area to allow only:
- enterprise/internal actions and workflows, plus
- selected external orgs/repos (and optionally block specific ones/versions). [4]
5) Don’t treat SHA pinning as sufficient by itself
Even pinned actions can remain risky if they pull mutable dependencies (e.g., downloading binaries at runtime, unpinned container images, nested unpinned actions). Review for “mutable dependencies,” not just the uses: line. [1]
Sources: GitHub Well-Architected Actions security recommendations [1], GitHub Docs on reusable workflows [2], GitHub Changelog on SHA pinning enforcement & blocking [3], GitHub Enterprise Cloud Docs on Actions policies (allowed actions/reusable workflows + “require pinned SHA”) [4].
🌐 Web query:
tj-actions changed-files compromise March 2025
💡 Result:
In March 2025, the popular GitHub Action tj-actions/changed-files was hit by a supply-chain compromise (tracked as CVE-2025-30066) that caused CI secrets to be exposed via workflow logs, especially in public repositories. [1][2]
What happened
- Attackers added/pointed tags to a malicious commit (widely reported as
0e58ed8…) so that many workflows referencing seemingly “safe” version tags would execute malicious code. [2][3] - The payload pulled and executed a script intended to dump credentials/secrets from the GitHub runner process memory and print them into action logs (often double-base64 encoded/obfuscated). [1][3]
When / who was at risk
- CISA’s alert highlights an exposure window commonly cited as 2025-03-12 00:00 UTC to 2025-03-15 12:00 UTC for repos running the malicious code. [1]
- Public repos were at highest risk because logs (and thus leaked secrets) could be publicly readable; private repos still had exposure risk depending on who could access logs. [2][3]
Remediation (what to do)
- Assume secrets are compromised if your workflows ran the action in the window; identify exposed secrets and rotate them immediately (PATs, cloud keys, npm tokens, RSA keys, etc.). [1][2]
- Update: CISA notes it was patched in v46.0.1. [1]
- Consider replacing with a hardened drop-in alternative (e.g.,
step-security/changed-files) and adopt best practices like pinning to commit SHAs and reducing token permissions. [2]
Key references: CISA Alert (CVE-2025-30066) [1], StepSecurity incident write-up/timeline [2], Wiz & Orca technical analyses [3][4].
Sources
[1] CISA alert on tj-actions/changed-files (CVE-2025-30066)
[2] StepSecurity: “Harden-Runner detection: tj-actions/changed-files action is compromised”
[3] Wiz: “GitHub Action tj-actions/changed-files supply chain attack (CVE-2025-30066)”
[4] Orca Security: “GitHub Action Hack: Malicious Commit in tj-actions/changed-files”
Supply-chain risk: reusable workflow pinned to mutable @master branch
Pinning an action to a full-length commit SHA is currently the only way to use an action as an immutable release. Although pinning to a commit SHA is the most secure option, specifying a tag is more convenient and is widely used; however, there is risk to this approach even if you trust the author, because a tag can be moved or deleted if a bad actor gains access to the repository. The same principles described above for using third-party actions also apply to using third-party workflows.
Using @master means any future commit to openwisp/openwisp-utils is picked up immediately with no review gate. This exact scenario occurred in March 2025 when the popular tj-actions/changed-files action was compromised — more than 23,000 Git repositories were affected when attackers pointed tags to a malicious commit that attempted to dump runner secrets.
Pin both uses: references (lines 22 and 37) to a full-length commit SHA and keep a human-readable comment alongside it:
🔒 Proposed fix
- uses: openwisp/openwisp-utils/.github/workflows/reusable-backport.yml@master
+ uses: openwisp/openwisp-utils/.github/workflows/reusable-backport.yml@<full-commit-sha> # `@master`Apply the same change on Line 37. Use Dependabot or Renovate to keep the pinned SHA up to date automatically.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/backport.yml at line 22, Replace the mutable branch
reference in the reusable workflow usage by pinning both occurrences of
"openwisp/openwisp-utils/.github/workflows/reusable-backport.yml@master" to a
full-length commit SHA (use the commit SHA for the target revision) and add a
short human-readable comment next to each pinned SHA explaining why it’s pinned;
update both "uses:" entries so they reference the immutable commit SHA instead
of "@master" and rely on Dependabot/Renovate to keep the SHA current.
| if: > | ||
| github.event_name == 'issue_comment' && | ||
| github.event.issue.pull_request && | ||
| github.event.issue.pull_request.merged_at != null && | ||
| github.event.issue.state == 'closed' && | ||
| contains(fromJSON('["MEMBER", "OWNER"]'), github.event.comment.author_association) && | ||
| startsWith(github.event.comment.body, '/backport') |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Verify COLLABORATOR exclusion and remove the redundant state check
Two independent observations:
- Redundant guard (Line 34):
github.event.issue.pull_request.merged_at != nullalready guarantees the PR was merged (and therefore closed), makinggithub.event.issue.state == 'closed'a no-op. Removing it reduces noise:
♻️ Proposed simplification
if: >
github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
github.event.issue.pull_request.merged_at != null &&
- github.event.issue.state == 'closed' &&
contains(fromJSON('["MEMBER", "OWNER"]'), github.event.comment.author_association) &&
startsWith(github.event.comment.body, '/backport')COLLABORATORexclusion (Line 35): Direct repository collaborators (author associationCOLLABORATOR) cannot trigger backports under the current check. Confirm this is intentional — external collaborators with commit access are commonly expected to have the same backport privileges asMEMBERs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/backport.yml around lines 30 - 36, Remove the redundant
state check and include COLLABORATOR in the author association whitelist: update
the workflow conditional by removing the clause "github.event.issue.state ==
'closed'" (since merged_at != null already implies closed) and expand the JSON
array passed to contains(...) to include "COLLABORATOR" alongside "MEMBER" and
"OWNER" so the expression becomes
contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'),
github.event.comment.author_association); keep the existing merged_at and
startsWith(github.event.comment.body, '/backport') checks unchanged.
Checklist
Reference to Existing Issue
Related to openwisp/openwisp-utils#501