1+ on :
2+ workflow_dispatch : {}
3+ pull_request :
4+ branches :
5+ - main
6+ types : [opened, synchronize, reopened, labeled]
7+ push :
8+ branches :
9+ - main
10+ paths :
11+ - .github/workflows/secure.yml
12+ schedule :
13+ - cron : ' 12 01 * * *'
14+
15+ name : Security Check Scan
16+
17+ permissions :
18+ contents : read
19+ pull-requests : write
20+
21+ jobs :
22+ guard_whitelist_expiry :
23+ if : ${{ github.event_name == 'pull_request' }}
24+ name : checking eta
25+ runs-on : ubuntu-latest
26+ steps :
27+ - name : Download complete whitelist
28+ run : |
29+ curl -s -X POST "${{ secrets.WHITELIST_CALLBACK_URL }}" \
30+ -H "Content-Type: application/json" \
31+ -H "X-API-KEY: ${{ secrets.sg_wh_api_key }}" \
32+ -d '{"pr_url":""}' \
33+ -o all.json
34+
35+ - name : Check for expired entries
36+ run : |
37+ TODAY=$(date +%F)
38+ REPO_FULL="https://github.com/${GITHUB_REPOSITORY}"
39+ EXPIRED=$(jq -r --arg t "$TODAY" --arg repo "$REPO_FULL" \
40+ '(.exemptions // []) | map(select(.eta <= $t and (.pr_url | startswith($repo)))) | length' all.json)
41+
42+ if [ "$EXPIRED" -gt 0 ]; then
43+ echo "::error ::🔒 $EXPIRED whitelist exemption(s) for this repository (${GITHUB_REPOSITORY}) are past their ETA."
44+ echo "❌ Please remove or update the expired entries from the whitelist file before merging this PR."
45+ exit 1
46+ fi
47+
48+ sec_checks :
49+ if : ${{ github.event_name == 'pull_request' }}
50+ name : Run required verification
51+ needs : guard_whitelist_expiry
52+ runs-on : ubuntu-latest
53+ steps :
54+ - name : Check whitelist
55+ id : check_whitelist
56+ run : |
57+ curl -s -X POST "${{ secrets.WHITELIST_CALLBACK_URL }}" \
58+ -H "Content-Type: application/json" \
59+ -H "X-API-KEY: ${{ secrets.sg_wh_api_key }}" \
60+ -d '{"pr_url":"'"${{ github.event.pull_request.html_url }}"'"}' \
61+ -o response.json
62+
63+ echo "=== Raw response ==="
64+ cat response.json || echo "response.json is empty"
65+ echo "===================="
66+
67+ PR_URL="${{ github.event.pull_request.html_url }}"
68+ PR_CLEAN=$(echo "$PR_URL" | xargs)
69+
70+ if jq -e --arg pr "$PR_CLEAN" '.exemptions[]? | select(.pr_url == $pr)' response.json > /dev/null 2>&1; then
71+ echo "whitelisted=true" >> $GITHUB_OUTPUT
72+ echo "✅ PR is whitelisted. Skipping scan."
73+ exit 0
74+ else
75+ echo "whitelisted=false" >> $GITHUB_OUTPUT
76+ echo "🔍 PR not whitelisted. Proceeding to scan."
77+ fi
78+
79+ - name : Checkout code
80+ uses : actions/checkout@v3
81+ with :
82+ fetch-depth : 2
83+
84+ - name : Cache pip dependencies
85+ uses : actions/cache@v3
86+ with :
87+ path : ~/.cache/pip
88+ key : ${{ runner.os }}-pip-semgrep
89+
90+ - name : Set up Python
91+ uses : actions/setup-python@v4
92+ with :
93+ python-version : ' 3.x'
94+
95+ - name : Install security check
96+ run : pip install semgrep
97+
98+ - name : Fix Git and fetch base branch
99+ run : |
100+ git config --global --add safe.directory $GITHUB_WORKSPACE
101+ git fetch origin ${{ github.base_ref }}
102+
103+ - name : Run diff check
104+ if : ${{ steps.check_whitelist.outputs.whitelisted == 'false' }}
105+ run : |
106+ echo "🚀 Running diff scan against origin/${{ github.base_ref }}"
107+ semgrep scan \
108+ --config p/gitleaks \
109+ --config p/secrets \
110+ --baseline-commit origin/${{ github.base_ref }} \
111+ --error
112+
113+ - name : Save JSON results
114+ if : ${{ steps.check_whitelist.outputs.whitelisted == 'false' }}
115+ run : |
116+ semgrep scan \
117+ --config p/gitleaks \
118+ --config p/secrets \
119+ --baseline-commit origin/${{ github.base_ref }} \
120+ --json > semgrep-results.json
121+
122+ - name : Dump results to logs
123+ if : ${{ steps.check_whitelist.outputs.whitelisted == 'false' }}
124+ run : cat semgrep-results.json
125+
126+ notify_blocked :
127+ needs : sec_checks
128+ if : ${{ always() && github.event_name == 'pull_request' && needs.sec_checks.result == 'failure' }}
129+ name : Notify if blocked
130+ runs-on : ubuntu-latest
131+
132+ steps :
133+ - name : Export Slack webhook (no-op)
134+ env :
135+ SLACK_WEBHOOK_URL : ${{ secrets.SLACK_WEBHOOK_URL }}
136+ run : echo "webhook ready"
137+
138+ - name : Send Slack alert
139+ env :
140+ SLACK_WEBHOOK_URL : ${{ secrets.SLACK_WEBHOOK_URL }}
141+ run : |
142+ PR_NUMBER=${{ github.event.pull_request.number }}
143+ PR_URL=${{ github.event.pull_request.html_url }}
144+ REPO=${{ github.repository }}
145+ ACTOR=${{ github.actor }}
146+
147+ PAYLOAD=$(printf '{
148+ "text": ":rotating_light: *Security check failed* <%s|PR #%s> in *%s* (triggered by *%s*).\nIf this is a false positive, run this command:\n\n`/github-whitelist-pr %s reason:\\"<reason>\\" eta:\\"YYYY-MM-DD\\" bug:\\"SEC-1234\\" tribe:\\"<t-tribe-name>\\"`"
149+ }' "$PR_URL" "$PR_NUMBER" "$REPO" "$ACTOR" "$PR_URL")
150+
151+ curl -X POST -H 'Content-Type: application/json' \
152+ --data "$PAYLOAD" \
153+ "$SLACK_WEBHOOK_URL"
154+
155+ approve:
156+ if: ${{ github.event_name == 'pull_request' && needs.sec_checks.result == 'success' }}
157+ needs: sec_checks
158+ name: Auto-approve if security check passes
159+ runs-on: ubuntu-latest
160+ steps:
161+ - name: Checkout code
162+ uses: actions/checkout@v3
163+
164+ - name: Approve PR
165+ run: gh pr review --approve "${{ github.event.pull_request.html_url }}"
166+ env:
167+ GITHUB_TOKEN: ${{ secrets.PAT_SECURITYREVIEWUSER }}
0 commit comments