Skip to content

Commit 52137c9

Browse files
committed
Security: Move permissions to job level and prevent script injection
- Move permissions from global to job level for both workflows - Replace direct GitHub context usage with environment variables - Prevent script injection attacks in GitHub Actions scripts - Follow GitHub security best practices for workflow design REF: GitHub security recommendations for workflow permissions and injection prevention
1 parent 577f2df commit 52137c9

File tree

2 files changed

+43
-23
lines changed

2 files changed

+43
-23
lines changed

.github/workflows/ai-code-analysis.yml

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,27 @@ on:
2424
description: 'Personal access token for user verification'
2525
required: true
2626

27-
permissions:
28-
contents: read
29-
pull-requests: write
30-
issues: write
31-
3227
jobs:
3328
analyze-ai-code:
3429
runs-on: ubuntu-latest
30+
permissions:
31+
contents: read
32+
pull-requests: write
33+
issues: write
3534
env:
36-
# Security: Move GitHub context to environment variables to prevent injection
3735
PR_USER_LOGIN: ${{ github.event.pull_request.user.login }}
3836
PR_HEAD_SHA: ${{ inputs.target_ref || github.event.pull_request.head.sha }}
3937
PR_BASE_REF: ${{ inputs.base_ref || github.event.pull_request.base.ref }}
4038
GITHUB_TOKEN_INPUT: ${{ inputs.github_token || github.token }}
4139
PAT_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
40+
GITHUB_REPOSITORY: ${{ github.repository }}
41+
GITHUB_EVENT_NAME: ${{ github.event_name }}
42+
PR_MERGED_AT: ${{ github.event.pull_request.merged_at }}
43+
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
44+
PR_USER_TYPE: ${{ github.event.pull_request.user.type }}
45+
ISSUE_NUMBER: ${{ github.event.number }}
46+
REPO_OWNER: ${{ github.repository_owner }}
47+
REPO_NAME: ${{ github.event.repository.name }}
4248

4349
steps:
4450
- name: Verify user
@@ -231,6 +237,14 @@ jobs:
231237
script: |
232238
const fs = require('fs');
233239
240+
241+
const repository = process.env.GITHUB_REPOSITORY;
242+
const eventName = process.env.GITHUB_EVENT_NAME;
243+
const prMergedAt = process.env.PR_MERGED_AT;
244+
const prHeadRef = process.env.PR_HEAD_REF;
245+
const prUserLogin = process.env.PR_USER_LOGIN;
246+
const issueNumber = process.env.ISSUE_NUMBER;
247+
234248
// Read analysis results
235249
const results = JSON.parse(fs.readFileSync('ai_analysis_results.json', 'utf8'));
236250

@@ -364,10 +378,10 @@ jobs:
364378
comment += `<details>\n<summary>📊 Raw Data (for dashboard)</summary>\n\n\`\`\`json\n`;
365379
comment += JSON.stringify({
366380
timestamp: new Date().toISOString(),
367-
repository: context.repo.full_name,
368-
pullRequest: context.issue.number,
369-
branch: context.payload.pull_request.head.ref,
370-
author: context.payload.pull_request.user.login,
381+
repository: repository,
382+
pullRequest: issueNumber,
383+
branch: prHeadRef,
384+
author: prUserLogin,
371385
...results
372386
}, null, 2);
373387
comment += `\n\`\`\`\n\n</details>\n\n`;

.github/workflows/ai-dashboard.yml

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@ on:
2424
description: 'ShiftAI token for auto-merge operations'
2525
required: false
2626

27-
permissions:
28-
contents: write
29-
pull-requests: write
30-
3127
jobs:
3228
ai-dashboard-update:
3329
# Only run if PR was actually merged (or manual/schedule trigger)
3430
if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || github.event_name == 'workflow_call'
3531
runs-on: ubuntu-latest
32+
permissions:
33+
contents: write
34+
pull-requests: write
3635
env:
36+
# Security: Move GitHub context to environment variables to prevent injection
3737
PR_TITLE: ${{ github.event.pull_request.title || inputs.pr_title || 'Manual/Scheduled Run' }}
3838
PR_HEAD_REF: ${{ github.event.pull_request.head.ref || 'manual' }}
3939
PR_AUTHOR: ${{ github.event.pull_request.user.login || 'system' }}
@@ -42,6 +42,9 @@ jobs:
4242
TARGET_BRANCH: ${{ inputs.target_branch || github.event.repository.default_branch || 'master' }}
4343
GITHUB_TOKEN_INPUT: ${{ github.token }}
4444
SHIFTAI_TOKEN_INPUT: ${{ secrets.SHIFTAI_TOKEN }}
45+
GITHUB_REPOSITORY: ${{ github.repository }}
46+
GITHUB_EVENT_NAME: ${{ github.event_name }}
47+
PR_MERGED_AT: ${{ github.event.pull_request.merged_at }}
4548

4649
steps:
4750
- name: Check if this is a dashboard update PR (prevent loops)
@@ -469,17 +472,17 @@ jobs:
469472
# Add the preserved changes
470473
git add .github/data/ai-analysis-history.json AI-DASHBOARD.md
471474
472-
# Commit changes
475+
# Commit changes - using environment variables to prevent injection
473476
if [[ "$PR_NUMBER" == "manual" ]]; then
474477
COMMIT_MSG="📊 Manual Dashboard Update
475478
476479
- Updated: $(date -u +"%Y-%m-%d %H:%M UTC")
477-
- Trigger: ${{ github.event_name }}
480+
- Trigger: $GITHUB_EVENT_NAME
478481
- Manual dashboard refresh"
479482
else
480483
COMMIT_MSG="📊 Track merged PR #$PR_NUMBER: $PR_TITLE
481484
482-
- Merged: ${{ github.event.pull_request.merged_at }}
485+
- Merged: $PR_MERGED_AT
483486
- Author: $PR_AUTHOR
484487
- Updated dashboard with historical data"
485488
fi
@@ -503,6 +506,7 @@ jobs:
503506
const prTitle = process.env.PR_TITLE;
504507
const prAuthor = process.env.PR_AUTHOR;
505508
const targetBranch = process.env.TARGET_BRANCH;
509+
const eventName = process.env.GITHUB_EVENT_NAME;
506510
const { owner, repo } = context.repo;
507511
508512
console.log('Environment check:');
@@ -545,7 +549,7 @@ jobs:
545549
'- Historical data file (if any new data available)',
546550
'',
547551
'### 🔄 Update Details:',
548-
'- **Trigger**: ' + context.eventName,
552+
'- **Trigger**: ' + eventName,
549553
'- **Updated**: ' + new Date().toISOString(),
550554
'',
551555
'---',
@@ -616,11 +620,14 @@ jobs:
616620
- name: Auto-merge Dashboard PR
617621
if: steps.check-skip.outputs.should-skip == 'false' && steps.create-pr.outputs.dashboard-pr-number && (github.event_name == 'pull_request_target' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || github.event_name == 'workflow_call')
618622
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
623+
env:
624+
DASHBOARD_PR_NUMBER: ${{ steps.create-pr.outputs.dashboard-pr-number }}
619625
with:
620626
github-token: ${{ env.SHIFTAI_TOKEN_INPUT || env.GITHUB_TOKEN_INPUT }}
621627
script: |
622-
const dashboardPrNumber = '${{ steps.create-pr.outputs.dashboard-pr-number }}';
628+
const dashboardPrNumber = process.env.DASHBOARD_PR_NUMBER;
623629
const shiftaiToken = process.env.SHIFTAI_TOKEN_INPUT;
630+
const targetBranch = process.env.TARGET_BRANCH;
624631
const { owner, repo } = context.repo;
625632
626633
if (!shiftaiToken) {
@@ -698,7 +705,6 @@ jobs:
698705
}
699706
700707
// SECURITY CHECK 5: Validate base branch
701-
const targetBranch = process.env.TARGET_BRANCH || 'master';
702708
if (prData.base.ref !== targetBranch) {
703709
console.log('❌ SECURITY: PR base is not target branch - aborting force merge');
704710
console.log(' Base branch:', prData.base.ref);
@@ -733,7 +739,7 @@ jobs:
733739
console.log('🔒 Confirmed: This is a legitimate dashboard PR created by GitHub Actions');
734740
735741
// SECURITY CHECK 7: Validate workflow trigger event
736-
const currentEvent = context.eventName;
742+
const currentEvent = process.env.GITHUB_EVENT_NAME;
737743
const allowedTriggers = ['pull_request_target', 'workflow_dispatch', 'schedule', 'workflow_call'];
738744
739745
if (!allowedTriggers.includes(currentEvent)) {
@@ -783,7 +789,7 @@ jobs:
783789
if [[ "$PR_NUMBER" != "manual" ]]; then
784790
echo "✅ Tracked PR #$PR_NUMBER: $PR_TITLE"
785791
echo "👤 Author: $PR_AUTHOR"
786-
echo "🕐 Merged: ${{ github.event.pull_request.merged_at }}"
792+
echo "🕐 Merged: $PR_MERGED_AT"
787793
else
788794
echo "🔄 Manual/scheduled dashboard refresh"
789795
fi
@@ -798,7 +804,7 @@ jobs:
798804
echo " • ⏳ Waiting for manual review and merge"
799805
fi
800806
echo ""
801-
echo "🔗 View dashboard: https://github.com/${{ github.repository }}/blob/$TARGET_BRANCH/AI-DASHBOARD.md"
807+
echo "🔗 View dashboard: https://github.com/$GITHUB_REPOSITORY/blob/$TARGET_BRANCH/AI-DASHBOARD.md"
802808
803809
- name: Skip notification
804810
if: steps.check-skip.outputs.should-skip == 'true'

0 commit comments

Comments
 (0)