Security: fix script injection via untrusted PR contexts in GitHub Actions workflows - #7658
Conversation
…ocks Agent-Logs-Url: https://github.com/oxsecurity/megalinter/sessions/125dbfd2-9493-412c-8f33-3a62e19aba6e Co-authored-by: nvuillam <17500430+nvuillam@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR hardens multiple GitHub Actions workflows against shell-script injection by avoiding direct interpolation of untrusted PR-controlled contexts in run: steps and instead passing them via env:.
Changes:
- Moved
github.*PR contexts (e.g.,github.head_ref, PR head repo, PR title) from inline${{ }}usage in shell scripts intoenv:variables. - Updated branch/repo selection logic in “Run Test Cases” steps to use the new environment variables.
- Updated the “Get PR title or commit message” step to use env-provided values.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| .github/workflows/deploy-RELEASE-linters.yml | Moves PR-related contexts into env: for the test step to avoid inline expression interpolation in bash. |
| .github/workflows/deploy-DEV.yml | Moves PR-related contexts into env: for the test step; currently introduces a duplicate env: key in the same step. |
| .github/workflows/deploy-DEV-linters.yml | Moves PR title/commit message contexts into env: and updates the test step similarly. |
| .github/workflows/deploy-BETA-linters.yml | Moves PR-related contexts into env: for the test step to avoid inline expression interpolation in bash. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
✅
|
|
@copilot apply fixes from the pr review comments |
…icate env blocks Agent-Logs-Url: https://github.com/oxsecurity/megalinter/sessions/7cc1c034-9d55-4202-bb60-82458fc46929 Co-authored-by: nvuillam <17500430+nvuillam@users.noreply.github.com>
Applied in commit
|
There was a problem hiding this comment.
Pull request overview
Hardens several GitHub Actions workflows against shell command injection by preventing direct interpolation of untrusted PR contexts into run: scripts, and includes minor formatting updates in generated descriptor docs.
Changes:
- Move untrusted GitHub contexts (
github.head_ref, PR head repo, PR title, etc.) intoenv:and reference them as shell variables insiderun:blocks. - Quote
docker run -e ...values that may contain spaces/special characters (e.g., branch/repo). - Adjust some descriptor documentation formatting (autolink URLs, list indentation).
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/deploy-DEV-linters.yml |
Uses env: indirection for PR title/head refs and for branch/repo selection in test step. |
.github/workflows/deploy-DEV.yml |
Uses env: indirection for PR head refs and quotes docker run env values. |
.github/workflows/deploy-BETA-linters.yml |
Uses env: indirection for PR head refs and quotes docker run env values. |
.github/workflows/deploy-RELEASE-linters.yml |
Uses env: indirection for PR head refs and quotes docker run env values. |
docs/descriptors/repository_trivy_sbom.md |
Formats disabled-reason URL as an autolink. |
docs/descriptors/repository_trivy.md |
Formats disabled-reason URL as an autolink. |
docs/descriptors/repository_kics.md |
Formats disabled-reason URL as an autolink. |
docs/descriptors/lua_selene.md |
Formats disabled-reason URL as an autolink. |
docs/descriptors/repository_gitleaks.md |
Adjusts list indentation around PR commit-scan env var examples. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| env: | ||
| EVENT_NAME: ${{ github.event_name }} | ||
| PR_TITLE: ${{ github.event.pull_request.title }} | ||
| HEAD_COMMIT_MSG: ${{ github.event.head_commit.message }} | ||
| run: | | ||
| if [ "${{ github.event_name }}" = "pull_request" ]; then | ||
| echo "title=${{ github.event.pull_request.title }}" >> $GITHUB_OUTPUT | ||
| if [ "$EVENT_NAME" = "pull_request" ]; then | ||
| echo "title=${PR_TITLE}" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "title=${{ github.event.head_commit.message }}" >> $GITHUB_OUTPUT | ||
| echo "title=${HEAD_COMMIT_MSG}" >> $GITHUB_OUTPUT | ||
| fi |
| _This linter has been disabled in this version_ | ||
|
|
||
| _Disabled reason: https://github.com/aquasecurity/trivy/discussions/10425_ | ||
| _Disabled reason: <https://github.com/aquasecurity/trivy/discussions/10425>_ | ||
|
|
||
| **Trivy** is a comprehensive security scanner that detects vulnerabilities, misconfigurations, secrets, and license issues in container images, filesystems, and git repositories. It serves as an all-in-one security solution for modern development workflows. |
| _This linter has been disabled in this version_ | ||
|
|
||
| _Disabled reason: Supply chain compromise - https://socket.dev/blog/checkmarx-supply-chain-compromise_ | ||
| _Disabled reason: Supply chain compromise - <https://socket.dev/blog/checkmarx-supply-chain-compromise>_ | ||
|
|
||
| **KICS** (Keeping Infrastructure as Code Secure) is a comprehensive open-source security scanner that identifies security vulnerabilities, compliance issues, and infrastructure misconfigurations in Infrastructure as Code files. It serves as a critical tool for preventing security risks before deployment to production environments. |
| _This linter has been disabled in this version_ | ||
|
|
||
| _Disabled reason: https://github.com/Kampfkarren/selene/issues/662_ | ||
| _Disabled reason: <https://github.com/Kampfkarren/selene/issues/662>_ | ||
|
|
||
| **Selene** is a blazing-fast modern Lua linter written in Rust that provides comprehensive static analysis for Lua code. It offers extensive configurability and can be tailored to specific Lua environments like Roblox, World of Warcraft addons, or standard Lua. |
|  [](https://github.com/aquasecurity/trivy)  [](https://github.com/aquasecurity/trivy/releases) [](https://github.com/aquasecurity/trivy/commits) [](https://github.com/aquasecurity/trivy/graphs/commit-activity/) [](https://github.com/aquasecurity/trivy/graphs/contributors/) | ||
|
|
||
| _This linter has been disabled in this version_ | ||
|
|
||
| _Disabled reason: https://github.com/aquasecurity/trivy/discussions/10425_ | ||
| _Disabled reason: <https://github.com/aquasecurity/trivy/discussions/10425>_ |
| env: | ||
| EVENT_NAME: ${{ github.event_name }} | ||
| PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} | ||
| GH_REPOSITORY: ${{ github.repository }} | ||
| HEAD_REF: ${{ github.head_ref }} | ||
| REF_NAME: ${{ github.ref_name }} | ||
| run: | | ||
| GITHUB_REPOSITORY=$([ "${{ github.event_name }}" == "pull_request" ] && echo "${{ github.event.pull_request.head.repo.full_name }}" || echo "${{ github.repository }}") | ||
| GITHUB_BRANCH=$([ "${{ github.event_name }}" == "pull_request" ] && echo "${{ github.head_ref }}" || echo "${{ github.ref_name }}") | ||
| GITHUB_REPOSITORY=$([ "$EVENT_NAME" == "pull_request" ] && echo "$PR_HEAD_REPO" || echo "$GH_REPOSITORY") | ||
| GITHUB_BRANCH=$([ "$EVENT_NAME" == "pull_request" ] && echo "$HEAD_REF" || echo "$REF_NAME") |
| _This linter has been disabled in this version_ | ||
|
|
||
| _Disabled reason: https://github.com/aquasecurity/trivy/discussions/10425_ | ||
| _Disabled reason: <https://github.com/aquasecurity/trivy/discussions/10425>_ | ||
|
|
||
| **Trivy SBOM** is a specialized component of Trivy that generates comprehensive Software Bill of Materials (SBOM) documents for enhanced supply chain security and compliance. It provides detailed inventory management for software components and dependencies. |
| Example commands: | ||
|
|
||
| - Source commit SHA: | ||
| - Source commit SHA: | ||
| ```bash | ||
| git rev-list -n 1 refs/remotes/origin/<source_branch> | ||
| ``` | ||
|
|
||
| - Target commit SHA: | ||
| - Target commit SHA: | ||
| ```bash | ||
| git rev-parse refs/remotes/origin/<target_branch> | ||
| ``` |
| if [ "$EVENT_NAME" = "pull_request" ]; then | ||
| echo "title=${PR_TITLE}" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "title=${{ github.event.head_commit.message }}" >> $GITHUB_OUTPUT | ||
| echo "title=${HEAD_COMMIT_MSG}" >> $GITHUB_OUTPUT | ||
| fi |

Untrusted user-controlled contexts (
github.event.pull_request.title,github.head_ref,github.event.pull_request.head.repo.full_name) were interpolated directly intorun:shell steps via${{ }}, allowing arbitrary command execution via a crafted branch name or PR title.Fix
Moved all untrusted contexts into
env:blocks so GitHub assigns them as data before the shell runs, preventing injection regardless of content.Before:
After:
Affected locations
deploy-DEV-linters.yml—get-titlestep (github.event.pull_request.title) andRun Test Casesstep (github.head_ref)deploy-DEV.yml—Run Test Casesstep (github.head_ref)deploy-RELEASE-linters.yml—Run Test Casesstep (github.head_ref)deploy-BETA-linters.yml—Run Test Casesstep (github.head_ref)