Push events for feat/ branches were not matching the feature/ glob, causing CI to skip on all feat/* branches. Co-Authored-By: claude-flow <ruv@ruv.net>
449 lines
13 KiB
YAML
449 lines
13 KiB
YAML
name: Security Scanning
|
||
|
||
on:
|
||
push:
|
||
branches: [ main, develop, 'feat/*' ]
|
||
pull_request:
|
||
branches: [ main, develop ]
|
||
schedule:
|
||
# Run security scans daily at 2 AM UTC
|
||
- cron: '0 2 * * *'
|
||
workflow_dispatch:
|
||
|
||
env:
|
||
PYTHON_VERSION: '3.11'
|
||
|
||
jobs:
|
||
# Static Application Security Testing (SAST)
|
||
sast:
|
||
name: Static Application Security Testing
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
security-events: write
|
||
actions: read
|
||
contents: read
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
|
||
- name: Set up Python
|
||
uses: actions/setup-python@v5
|
||
with:
|
||
python-version: ${{ env.PYTHON_VERSION }}
|
||
cache: 'pip'
|
||
|
||
- name: Install dependencies
|
||
run: |
|
||
python -m pip install --upgrade pip
|
||
pip install -r requirements.txt
|
||
pip install bandit semgrep safety
|
||
|
||
- name: Run Bandit security scan
|
||
run: |
|
||
bandit -r src/ -f sarif -o bandit-results.sarif
|
||
continue-on-error: true
|
||
|
||
- name: Upload Bandit results to GitHub Security
|
||
uses: github/codeql-action/upload-sarif@v3
|
||
if: always()
|
||
with:
|
||
sarif_file: bandit-results.sarif
|
||
category: bandit
|
||
|
||
- name: Run Semgrep security scan
|
||
uses: returntocorp/semgrep-action@v1
|
||
with:
|
||
config: >-
|
||
p/security-audit
|
||
p/secrets
|
||
p/python
|
||
p/docker
|
||
p/kubernetes
|
||
env:
|
||
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
|
||
|
||
- name: Generate Semgrep SARIF
|
||
run: |
|
||
semgrep --config=p/security-audit --config=p/secrets --config=p/python --sarif --output=semgrep.sarif src/
|
||
continue-on-error: true
|
||
|
||
- name: Upload Semgrep results to GitHub Security
|
||
uses: github/codeql-action/upload-sarif@v3
|
||
if: always()
|
||
with:
|
||
sarif_file: semgrep.sarif
|
||
category: semgrep
|
||
|
||
# Dependency vulnerability scanning
|
||
dependency-scan:
|
||
name: Dependency Vulnerability Scan
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
security-events: write
|
||
actions: read
|
||
contents: read
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Set up Python
|
||
uses: actions/setup-python@v5
|
||
with:
|
||
python-version: ${{ env.PYTHON_VERSION }}
|
||
cache: 'pip'
|
||
|
||
- name: Install dependencies
|
||
run: |
|
||
python -m pip install --upgrade pip
|
||
pip install -r requirements.txt
|
||
pip install safety pip-audit
|
||
|
||
- name: Run Safety check
|
||
run: |
|
||
safety check --json --output safety-report.json
|
||
continue-on-error: true
|
||
|
||
- name: Run pip-audit
|
||
run: |
|
||
pip-audit --format=json --output=pip-audit-report.json
|
||
continue-on-error: true
|
||
|
||
- name: Run Snyk vulnerability scan
|
||
uses: snyk/actions/python@master
|
||
env:
|
||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||
with:
|
||
args: --sarif-file-output=snyk-results.sarif
|
||
continue-on-error: true
|
||
|
||
- name: Upload Snyk results to GitHub Security
|
||
uses: github/codeql-action/upload-sarif@v3
|
||
if: always()
|
||
with:
|
||
sarif_file: snyk-results.sarif
|
||
category: snyk
|
||
|
||
- name: Upload vulnerability reports
|
||
uses: actions/upload-artifact@v4
|
||
if: always()
|
||
with:
|
||
name: vulnerability-reports
|
||
path: |
|
||
safety-report.json
|
||
pip-audit-report.json
|
||
snyk-results.sarif
|
||
|
||
# Container security scanning
|
||
container-scan:
|
||
name: Container Security Scan
|
||
runs-on: ubuntu-latest
|
||
needs: []
|
||
if: github.event_name == 'push' || github.event_name == 'schedule'
|
||
permissions:
|
||
security-events: write
|
||
actions: read
|
||
contents: read
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Set up Docker Buildx
|
||
uses: docker/setup-buildx-action@v3
|
||
|
||
- name: Build Docker image for scanning
|
||
uses: docker/build-push-action@v5
|
||
with:
|
||
context: .
|
||
target: production
|
||
load: true
|
||
tags: wifi-densepose:scan
|
||
cache-from: type=gha
|
||
cache-to: type=gha,mode=max
|
||
|
||
- name: Run Trivy vulnerability scanner
|
||
uses: aquasecurity/trivy-action@master
|
||
with:
|
||
image-ref: 'wifi-densepose:scan'
|
||
format: 'sarif'
|
||
output: 'trivy-results.sarif'
|
||
|
||
- name: Upload Trivy results to GitHub Security
|
||
uses: github/codeql-action/upload-sarif@v3
|
||
if: always()
|
||
with:
|
||
sarif_file: 'trivy-results.sarif'
|
||
category: trivy
|
||
|
||
- name: Run Grype vulnerability scanner
|
||
uses: anchore/scan-action@v3
|
||
id: grype-scan
|
||
with:
|
||
image: 'wifi-densepose:scan'
|
||
fail-build: false
|
||
severity-cutoff: high
|
||
output-format: sarif
|
||
|
||
- name: Upload Grype results to GitHub Security
|
||
uses: github/codeql-action/upload-sarif@v3
|
||
if: always()
|
||
with:
|
||
sarif_file: ${{ steps.grype-scan.outputs.sarif }}
|
||
category: grype
|
||
|
||
- name: Run Docker Scout
|
||
uses: docker/scout-action@v1
|
||
if: always()
|
||
with:
|
||
command: cves
|
||
image: wifi-densepose:scan
|
||
sarif-file: scout-results.sarif
|
||
summary: true
|
||
|
||
- name: Upload Docker Scout results
|
||
uses: github/codeql-action/upload-sarif@v3
|
||
if: always()
|
||
with:
|
||
sarif_file: scout-results.sarif
|
||
category: docker-scout
|
||
|
||
# Infrastructure as Code security scanning
|
||
iac-scan:
|
||
name: Infrastructure Security Scan
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
security-events: write
|
||
actions: read
|
||
contents: read
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Run Checkov IaC scan
|
||
uses: bridgecrewio/checkov-action@master
|
||
with:
|
||
directory: .
|
||
framework: kubernetes,dockerfile,terraform,ansible
|
||
output_format: sarif
|
||
output_file_path: checkov-results.sarif
|
||
quiet: true
|
||
soft_fail: true
|
||
|
||
- name: Upload Checkov results to GitHub Security
|
||
uses: github/codeql-action/upload-sarif@v3
|
||
if: always()
|
||
with:
|
||
sarif_file: checkov-results.sarif
|
||
category: checkov
|
||
|
||
- name: Run Terrascan IaC scan
|
||
uses: tenable/terrascan-action@main
|
||
with:
|
||
iac_type: 'k8s'
|
||
iac_version: 'v1'
|
||
policy_type: 'k8s'
|
||
only_warn: true
|
||
sarif_upload: true
|
||
|
||
- name: Run KICS IaC scan
|
||
uses: checkmarx/kics-github-action@master
|
||
with:
|
||
path: '.'
|
||
output_path: kics-results
|
||
output_formats: 'sarif'
|
||
exclude_paths: '.git,node_modules'
|
||
exclude_queries: 'a7ef1e8c-fbf8-4ac1-b8c7-2c3b0e6c6c6c'
|
||
|
||
- name: Upload KICS results to GitHub Security
|
||
uses: github/codeql-action/upload-sarif@v3
|
||
if: always()
|
||
with:
|
||
sarif_file: kics-results/results.sarif
|
||
category: kics
|
||
|
||
# Secret scanning
|
||
secret-scan:
|
||
name: Secret Scanning
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
security-events: write
|
||
actions: read
|
||
contents: read
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
|
||
- name: Run TruffleHog secret scan
|
||
uses: trufflesecurity/trufflehog@main
|
||
with:
|
||
path: ./
|
||
base: main
|
||
head: HEAD
|
||
extra_args: --debug --only-verified
|
||
|
||
- name: Run GitLeaks secret scan
|
||
uses: gitleaks/gitleaks-action@v2
|
||
env:
|
||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
|
||
|
||
- name: Run detect-secrets
|
||
run: |
|
||
pip install detect-secrets
|
||
detect-secrets scan --all-files --baseline .secrets.baseline
|
||
detect-secrets audit .secrets.baseline
|
||
continue-on-error: true
|
||
|
||
# License compliance scanning
|
||
license-scan:
|
||
name: License Compliance Scan
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Set up Python
|
||
uses: actions/setup-python@v5
|
||
with:
|
||
python-version: ${{ env.PYTHON_VERSION }}
|
||
cache: 'pip'
|
||
|
||
- name: Install dependencies
|
||
run: |
|
||
python -m pip install --upgrade pip
|
||
pip install -r requirements.txt
|
||
pip install pip-licenses licensecheck
|
||
|
||
- name: Run license check
|
||
run: |
|
||
pip-licenses --format=json --output-file=licenses.json
|
||
licensecheck --zero
|
||
|
||
- name: Upload license report
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: license-report
|
||
path: licenses.json
|
||
|
||
# Security policy compliance
|
||
compliance-check:
|
||
name: Security Policy Compliance
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Check security policy files
|
||
run: |
|
||
# Check for required security files
|
||
files=("SECURITY.md" ".github/SECURITY.md" "docs/SECURITY.md")
|
||
found=false
|
||
for file in "${files[@]}"; do
|
||
if [[ -f "$file" ]]; then
|
||
echo "✅ Found security policy: $file"
|
||
found=true
|
||
break
|
||
fi
|
||
done
|
||
if [[ "$found" == false ]]; then
|
||
echo "❌ No security policy found. Please create SECURITY.md"
|
||
exit 1
|
||
fi
|
||
|
||
- name: Check for security headers in code
|
||
run: |
|
||
# Check for security-related configurations
|
||
grep -r "X-Frame-Options\|X-Content-Type-Options\|X-XSS-Protection\|Content-Security-Policy" src/ || echo "⚠️ Consider adding security headers"
|
||
|
||
- name: Validate Kubernetes security contexts
|
||
run: |
|
||
# Check for security contexts in Kubernetes manifests
|
||
if [[ -d "k8s" ]]; then
|
||
if find k8s/ -name "*.yaml" -exec grep -l "securityContext" {} \; | wc -l | grep -q "^0$"; then
|
||
echo "⚠️ No security contexts found in Kubernetes manifests"
|
||
else
|
||
echo "✅ Security contexts found in Kubernetes manifests"
|
||
fi
|
||
else
|
||
echo "ℹ️ No k8s/ directory found — skipping Kubernetes security context check"
|
||
fi
|
||
|
||
# Notification and reporting
|
||
security-report:
|
||
name: Security Report
|
||
runs-on: ubuntu-latest
|
||
needs: [sast, dependency-scan, container-scan, iac-scan, secret-scan, license-scan, compliance-check]
|
||
if: always()
|
||
steps:
|
||
- name: Download all artifacts
|
||
uses: actions/download-artifact@v4
|
||
|
||
- name: Generate security summary
|
||
run: |
|
||
echo "# Security Scan Summary" > security-summary.md
|
||
echo "" >> security-summary.md
|
||
echo "## Scan Results" >> security-summary.md
|
||
echo "- SAST: ${{ needs.sast.result }}" >> security-summary.md
|
||
echo "- Dependency Scan: ${{ needs.dependency-scan.result }}" >> security-summary.md
|
||
echo "- Container Scan: ${{ needs.container-scan.result }}" >> security-summary.md
|
||
echo "- IaC Scan: ${{ needs.iac-scan.result }}" >> security-summary.md
|
||
echo "- Secret Scan: ${{ needs.secret-scan.result }}" >> security-summary.md
|
||
echo "- License Scan: ${{ needs.license-scan.result }}" >> security-summary.md
|
||
echo "- Compliance Check: ${{ needs.compliance-check.result }}" >> security-summary.md
|
||
echo "" >> security-summary.md
|
||
echo "Generated on: $(date)" >> security-summary.md
|
||
|
||
- name: Upload security summary
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: security-summary
|
||
path: security-summary.md
|
||
|
||
- name: Notify security team on critical findings
|
||
if: ${{ secrets.SECURITY_SLACK_WEBHOOK_URL != '' && (needs.sast.result == 'failure' || needs.dependency-scan.result == 'failure' || needs.container-scan.result == 'failure') }}
|
||
uses: 8398a7/action-slack@v3
|
||
with:
|
||
status: failure
|
||
channel: '#security'
|
||
text: |
|
||
🚨 Critical security findings detected!
|
||
Repository: ${{ github.repository }}
|
||
Branch: ${{ github.ref }}
|
||
Workflow: ${{ github.workflow }}
|
||
Please review the security scan results immediately.
|
||
env:
|
||
SLACK_WEBHOOK_URL: ${{ secrets.SECURITY_SLACK_WEBHOOK_URL }}
|
||
|
||
- name: Create security issue on critical findings
|
||
if: needs.sast.result == 'failure' || needs.dependency-scan.result == 'failure'
|
||
uses: actions/github-script@v6
|
||
with:
|
||
script: |
|
||
github.rest.issues.create({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
title: `Security Scan Failures - ${new Date().toISOString()}`,
|
||
body: `
|
||
## Security Scan Failures Detected
|
||
|
||
**Workflow Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||
**Branch:** ${{ github.ref }}
|
||
|
||
**Failed Scans:**
|
||
- SAST: ${{ needs.sast.result }}
|
||
- Dependency Scan: ${{ needs.dependency-scan.result }}
|
||
- Container Scan: ${{ needs.container-scan.result }}
|
||
|
||
**Action Required:**
|
||
- [ ] Review security scan results
|
||
- [ ] Address critical vulnerabilities
|
||
- [ ] Update dependencies if needed
|
||
- [ ] Re-run security scans
|
||
|
||
**Security Dashboard:** Check the Security tab for detailed findings.
|
||
`,
|
||
labels: ['security', 'vulnerability', 'urgent']
|
||
}) |