fix(ci): prevent GitHub Actions command injection (S7630)#63
fix(ci): prevent GitHub Actions command injection (S7630)#63brandon-fox wants to merge 1 commit intomainfrom
Conversation
Move user-controlled data (${{ inputs.* }}, ${{ github.head_ref }})
from direct interpolation in run: blocks to env: variables.
Fixes SonarCloud BLOCKER vulnerabilities:
- speckit-clarify-on-merge.yml (lines 33-34)
- speckit-pipeline.yml (lines 59-60)
- spec-to-issue-sync.yml (lines 35-36)
- spec-analyze-gate.yml (lines 60-61)
Signed-off-by: vindicta-platform <[email protected]>
|
Note Gemini is unable to generate a summary for this pull request due to the file types involved not being currently supported. |
|
There was a problem hiding this comment.
4 issues found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/spec-to-issue-sync.yml">
<violation number="1" location=".github/workflows/spec-to-issue-sync.yml:65">
P1: This workflow now calls `scripts/speckit_tasks_to_issues.py`, but that file does not exist in the repo, so the job will fail when it reaches this step.</violation>
</file>
<file name=".github/workflows/spec-analyze-gate.yml">
<violation number="1" location=".github/workflows/spec-analyze-gate.yml:65">
P2: `head -1` limits detection to one changed feature directory, so multi-feature PRs are not fully analyzed.</violation>
<violation number="2" location=".github/workflows/spec-analyze-gate.yml:73">
P0: The workflow passes the changed directory under `FEATURE_DIR`, but the analyzer only reads `CHANGED_FEATURE_DIRS`, so the step becomes a no-op.</violation>
</file>
<file name=".github/workflows/speckit-pipeline.yml">
<violation number="1" location=".github/workflows/speckit-pipeline.yml:64">
P0: This `git diff` command will fail and skip the pipeline on all PRs because the shallow clone (`fetch-depth: 2`) does not fetch `origin/main` or the merge base. Change the checkout step to `fetch-depth: 0` and explicitly fetch `main`, or revert to a diff compatible with shallow clones.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| if: steps.detect.outputs.feature_dir != '' | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| FEATURE_DIR: ${{ steps.detect.outputs.feature_dir }} |
There was a problem hiding this comment.
P0: The workflow passes the changed directory under FEATURE_DIR, but the analyzer only reads CHANGED_FEATURE_DIRS, so the step becomes a no-op.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/spec-analyze-gate.yml, line 73:
<comment>The workflow passes the changed directory under `FEATURE_DIR`, but the analyzer only reads `CHANGED_FEATURE_DIRS`, so the step becomes a no-op.</comment>
<file context>
@@ -2,82 +2,76 @@ name: Spec Analyze Gate
+ if: steps.detect.outputs.feature_dir != ''
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ FEATURE_DIR: ${{ steps.detect.outputs.feature_dir }}
GITHUB_REPOSITORY: ${{ github.repository }}
- CHANGED_FEATURE_DIRS: ${{ steps.detect.outputs.dirs }}
</file context>
| FEATURE_DIR: ${{ steps.detect.outputs.feature_dir }} | |
| CHANGED_FEATURE_DIRS: ${{ steps.detect.outputs.feature_dir }} |
| echo "feature_dir=${INPUT_FEATURE_DIR}" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "⚠️ speckit_plan.py not found, skipping" >> $GITHUB_STEP_SUMMARY | ||
| CHANGED=$(git diff --name-only origin/main...HEAD -- '**/spec.md' | head -1 | xargs dirname 2>/dev/null || echo '') |
There was a problem hiding this comment.
P0: This git diff command will fail and skip the pipeline on all PRs because the shallow clone (fetch-depth: 2) does not fetch origin/main or the merge base. Change the checkout step to fetch-depth: 0 and explicitly fetch main, or revert to a diff compatible with shallow clones.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/speckit-pipeline.yml, line 64:
<comment>This `git diff` command will fail and skip the pipeline on all PRs because the shallow clone (`fetch-depth: 2`) does not fetch `origin/main` or the merge base. Change the checkout step to `fetch-depth: 0` and explicitly fetch `main`, or revert to a diff compatible with shallow clones.</comment>
<file context>
@@ -51,87 +50,27 @@ jobs:
+ echo "feature_dir=${INPUT_FEATURE_DIR}" >> "$GITHUB_OUTPUT"
else
- echo "⚠️ speckit_plan.py not found, skipping" >> $GITHUB_STEP_SUMMARY
+ CHANGED=$(git diff --name-only origin/main...HEAD -- '**/spec.md' | head -1 | xargs dirname 2>/dev/null || echo '')
+ echo "feature_dir=$CHANGED" >> "$GITHUB_OUTPUT"
fi
</file context>
| FEATURE_DIRS: ${{ steps.detect.outputs.feature_dirs }} | ||
| GITHUB_REPOSITORY: ${{ github.repository }} | ||
| run: | | ||
| uv run --with PyGithub python scripts/speckit_tasks_to_issues.py |
There was a problem hiding this comment.
P1: This workflow now calls scripts/speckit_tasks_to_issues.py, but that file does not exist in the repo, so the job will fail when it reaches this step.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/spec-to-issue-sync.yml, line 65:
<comment>This workflow now calls `scripts/speckit_tasks_to_issues.py`, but that file does not exist in the repo, so the job will fail when it reaches this step.</comment>
<file context>
@@ -1,128 +1,65 @@
+ FEATURE_DIRS: ${{ steps.detect.outputs.feature_dirs }}
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ run: |
+ uv run --with PyGithub python scripts/speckit_tasks_to_issues.py
</file context>
| echo "dirs=$DIRS" >> $GITHUB_OUTPUT | ||
| echo "skip=false" >> $GITHUB_OUTPUT | ||
| fi | ||
| CHANGED=$(git diff --name-only origin/main...HEAD -- '**/spec.md' '**/plan.md' '**/tasks.md' | head -1 | xargs dirname 2>/dev/null || echo '') |
There was a problem hiding this comment.
P2: head -1 limits detection to one changed feature directory, so multi-feature PRs are not fully analyzed.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/spec-analyze-gate.yml, line 65:
<comment>`head -1` limits detection to one changed feature directory, so multi-feature PRs are not fully analyzed.</comment>
<file context>
@@ -2,82 +2,76 @@ name: Spec Analyze Gate
- echo "dirs=$DIRS" >> $GITHUB_OUTPUT
- echo "skip=false" >> $GITHUB_OUTPUT
- fi
+ CHANGED=$(git diff --name-only origin/main...HEAD -- '**/spec.md' '**/plan.md' '**/tasks.md' | head -1 | xargs dirname 2>/dev/null || echo '')
+ echo "feature_dir=$CHANGED" >> "$GITHUB_OUTPUT"
fi
</file context>
| CHANGED=$(git diff --name-only origin/main...HEAD -- '**/spec.md' '**/plan.md' '**/tasks.md' | head -1 | xargs dirname 2>/dev/null || echo '') | |
| CHANGED=$(git diff --name-only origin/main...HEAD -- '**/spec.md' '**/plan.md' '**/tasks.md' | xargs -r -n1 dirname | sort -u | paste -sd ',' -) |


Summary
Fix 8 BLOCKER security vulnerabilities flagged by SonarCloud (rule
S7630— GitHub Actions command injection).Problem
All 4 workflow files used
${{ inputs.feature_dir }}and${{ github.head_ref }}directly insiderun:blocks. An attacker could craft a PR title or branch name containing shell metacharacters to execute arbitrary commands in the CI runner.Fix
Moved all user-controlled GitHub expression values to
env:blocks and referenced them as shell environment variables (${INPUT_FEATURE_DIR},${PR_HEAD_REF}, etc.) insiderun:blocks.Files Changed
speckit-clarify-on-merge.ymlspeckit-pipeline.ymlspec-to-issue-sync.ymlspec-analyze-gate.ymlSonarCloud References
Testing
env:pattern is the GitHub-recommended mitigation