Skip to content

fix(ci): prevent GitHub Actions command injection (S7630)#63

Open
brandon-fox wants to merge 1 commit intomainfrom
fix/sonarcloud-gh-actions-injection
Open

fix(ci): prevent GitHub Actions command injection (S7630)#63
brandon-fox wants to merge 1 commit intomainfrom
fix/sonarcloud-gh-actions-injection

Conversation

@brandon-fox
Copy link
Copy Markdown
Member

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 inside run: 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.) inside run: blocks.

Files Changed

File Lines Fixed
speckit-clarify-on-merge.yml 33-34
speckit-pipeline.yml 59-60
spec-to-issue-sync.yml 35-36
spec-analyze-gate.yml 60-61

SonarCloud References

  • Rule: S7630
  • Severity: BLOCKER (Security)
  • 8 issues resolved

Testing

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]>
@gemini-code-assist
Copy link
Copy Markdown

Note

Gemini is unable to generate a summary for this pull request due to the file types involved not being currently supported.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Mar 7, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
2 Security Hotspots

See analysis details on SonarQube Cloud

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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 }}
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 7, 2026

Choose a reason for hiding this comment

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

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>
Suggested change
FEATURE_DIR: ${{ steps.detect.outputs.feature_dir }}
CHANGED_FEATURE_DIRS: ${{ steps.detect.outputs.feature_dir }}
Fix with Cubic

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 '')
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 7, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic

FEATURE_DIRS: ${{ steps.detect.outputs.feature_dirs }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
uv run --with PyGithub python scripts/speckit_tasks_to_issues.py
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 7, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic

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 '')
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 7, 2026

Choose a reason for hiding this comment

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

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>
Suggested change
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 ',' -)
Fix with Cubic

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