Skip to content

Commit c3cc5d8

Browse files
authored
Add security scanning workflow
This workflow defines a comprehensive security scanning pipeline for pull requests and pushes to the main branch, including dependency audits, static analysis, and secret scanning.
1 parent 42f7121 commit c3cc5d8

File tree

1 file changed

+200
-0
lines changed

1 file changed

+200
-0
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
name: Security scans
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
push:
7+
branches: [ main ]
8+
9+
env:
10+
PHP_VERSION: '8.0' # Adjust if you prefer 8.2/8.3
11+
12+
jobs:
13+
prepare:
14+
name: Prepare repo & PHP
15+
runs-on: ubuntu-latest
16+
outputs:
17+
has-composer: ${{ steps.check.outputs.has_composer }}
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
22+
- name: Set up PHP
23+
uses: shivammathur/setup-php@v4
24+
with:
25+
php-version: ${{ env.PHP_VERSION }}
26+
extensions: mbstring, intl, pdo, pdo_mysql, ftp
27+
coverage: none
28+
29+
- name: Check for composer.json
30+
id: check
31+
run: |
32+
if [ -f composer.json ]; then
33+
echo "has_composer=true" >> $GITHUB_OUTPUT
34+
else
35+
echo "has_composer=false" >> $GITHUB_OUTPUT
36+
fi
37+
38+
- name: Install composer deps (if composer.json)
39+
if: steps.check.outputs.has_composer == 'true'
40+
run: |
41+
composer install --no-interaction --prefer-dist || true
42+
timeout-minutes: 20
43+
44+
dependency-audit:
45+
name: Composer / Dependency checks
46+
runs-on: ubuntu-latest
47+
needs: prepare
48+
if: needs.prepare.outputs.has-composer == 'true'
49+
steps:
50+
- uses: actions/checkout@v4
51+
52+
- name: Setup PHP for audit
53+
uses: shivammathur/setup-php@v4
54+
with:
55+
php-version: ${{ env.PHP_VERSION }}
56+
57+
- name: Composer - show version
58+
run: composer --version || true
59+
60+
- name: Composer audit (Composer >= 2.4)
61+
id: compaudit
62+
run: |
63+
if composer --version | grep -q "Composer"; then
64+
composer audit --format=json > composer-audit.json || true
65+
echo "composer-audit=true" >> $GITHUB_OUTPUT || true
66+
else
67+
echo "composer-audit=false" >> $GITHUB_OUTPUT || true
68+
fi
69+
70+
- name: Upload composer audit
71+
if: always() && (exists('composer-audit.json') || true)
72+
uses: actions/upload-artifact@v4
73+
with:
74+
name: composer-audit
75+
path: composer-audit.json
76+
77+
- name: Add Roave advisory (optional)
78+
run: composer require --dev roave/security-advisories:^1 || true
79+
80+
semgrep:
81+
name: Semgrep SAST
82+
runs-on: ubuntu-latest
83+
needs: prepare
84+
steps:
85+
- uses: actions/checkout@v4
86+
87+
- name: Install semgrep
88+
run: |
89+
python3 -m pip install --user semgrep
90+
export PATH="$HOME/.local/bin:$PATH"
91+
semgrep --version
92+
93+
- name: Run semgrep scan
94+
env:
95+
HOMEDIR: ${{ runner.temp }}
96+
run: |
97+
export PATH="$HOME/.local/bin:$PATH"
98+
semgrep --config p/php --json --output semgrep-report.json || true
99+
100+
- name: Upload semgrep report
101+
if: always()
102+
uses: actions/upload-artifact@v4
103+
with:
104+
name: semgrep-report
105+
path: semgrep-report.json
106+
107+
sast-php:
108+
name: Optional PHP SAST (PHPStan / Psalm)
109+
runs-on: ubuntu-latest
110+
needs: prepare
111+
if: needs.prepare.outputs.has-composer == 'true'
112+
steps:
113+
- uses: actions/checkout@v4
114+
115+
- name: Setup PHP
116+
uses: shivammathur/setup-php@v4
117+
with:
118+
php-version: ${{ env.PHP_VERSION }}
119+
120+
- name: Run PHPStan if present
121+
run: |
122+
if [ -x vendor/bin/phpstan ]; then
123+
vendor/bin/phpstan analyse -l max src || true
124+
elif command -v phpstan >/dev/null 2>&1; then
125+
phpstan analyse -l max src || true
126+
else
127+
echo "phpstan not found, skipping"
128+
fi
129+
130+
- name: Run Psalm (taint) if present
131+
run: |
132+
if [ -x vendor/bin/psalm ]; then
133+
vendor/bin/psalm --show-info=false --taint-analysis --report=psalm-security-report.xml || true
134+
ls -la psalm-security-report.xml || true
135+
else
136+
echo "psalm not found, skipping"
137+
fi
138+
139+
- name: Upload Psalm report (if exists)
140+
if: always()
141+
uses: actions/upload-artifact@v4
142+
with:
143+
name: psalm-security-report
144+
path: psalm-security-report.xml
145+
146+
secret-scan:
147+
name: Secret scanning (Gitleaks)
148+
runs-on: ubuntu-latest
149+
steps:
150+
- uses: actions/checkout@v4
151+
- name: Run gitleaks
152+
uses: zricethezav/gitleaks-action@v2
153+
with:
154+
args: detect --source . --report-format json --report-path gitleaks-report.json || true
155+
- name: Upload gitleaks report
156+
if: always()
157+
uses: actions/upload-artifact@v4
158+
with:
159+
name: gitleaks-report
160+
path: gitleaks-report.json
161+
162+
dast-zap:
163+
name: DAST - OWASP ZAP baseline (staging only)
164+
runs-on: ubuntu-latest
165+
needs: prepare
166+
if: ${{ secrets.STAGING_URL != '' }}
167+
env:
168+
TARGET_URL: ${{ secrets.STAGING_URL }}
169+
steps:
170+
- name: Run ZAP baseline scan
171+
uses: zaproxy/action-baseline@v1
172+
with:
173+
target: ${{ env.TARGET_URL }}
174+
rules_file_name: zap-rules.md
175+
format: 'github'
176+
- name: Upload ZAP artifacts
177+
if: always()
178+
uses: actions/upload-artifact@v4
179+
with:
180+
name: zap-output
181+
path: .
182+
183+
dependency-review:
184+
name: Dependency review (GitHub)
185+
runs-on: ubuntu-latest
186+
steps:
187+
- uses: actions/checkout@v4
188+
- name: Dependency review
189+
uses: github/dependency-review-action@v2
190+
with:
191+
token: ${{ secrets.GITHUB_TOKEN }}
192+
193+
summary:
194+
name: Summary (non-blocking)
195+
runs-on: ubuntu-latest
196+
needs: [dependency-audit, semgrep, sast-php, secret-scan, dast-zap, dependency-review]
197+
steps:
198+
- name: Print summary message
199+
run: |
200+
echo "Security scan pipeline finished. Check artifacts (semgrep/psalm/composer/gitleaks/zap) and PR annotations for findings."

0 commit comments

Comments
 (0)