Files
goodgo-platform/.github/workflows/security.yml
Ho Ngoc Hai 4d91c04b88 docs: add comprehensive K6 load testing guide with API structure
- Document all API endpoints (auth, listings, payments, search)
- Include DTOs and request/response body shapes
- Document authentication methods and rate limits
- Provide database and environment configuration
- Include existing test setup (Playwright, Vitest)
- Detail CI/CD pipeline structure
- Recommend K6 endpoints and test patterns
- Provide file location references for quick lookup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 01:34:15 +07:00

313 lines
9.5 KiB
YAML

name: Security Scanning
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
# Run daily at 05:43 UTC — catch new CVEs early
- cron: "43 5 * * *"
concurrency:
group: security-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
security-events: write
jobs:
# ── Dependency Audit ─────────────────────────────────────────────
dependency-audit:
name: Dependency Audit (pnpm)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run pnpm audit
run: |
echo "## Dependency Audit Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Run audit, capture output and exit code
set +e
AUDIT_OUTPUT=$(pnpm audit --json 2>&1)
AUDIT_EXIT=$?
set -e
# Parse and display summary
echo "$AUDIT_OUTPUT" | jq -r '
if .metadata then
"| Severity | Count |\n|----------|-------|\n" +
"| Critical | \(.metadata.vulnerabilities.critical // 0) |\n" +
"| High | \(.metadata.vulnerabilities.high // 0) |\n" +
"| Moderate | \(.metadata.vulnerabilities.moderate // 0) |\n" +
"| Low | \(.metadata.vulnerabilities.low // 0) |\n" +
"| Info | \(.metadata.vulnerabilities.info // 0) |"
else
"No vulnerabilities found ✅"
end
' >> $GITHUB_STEP_SUMMARY 2>/dev/null || echo "Audit completed" >> $GITHUB_STEP_SUMMARY
# Also run human-readable output
pnpm audit 2>&1 || true
# Fail on critical or high vulnerabilities only
pnpm audit --audit-level=critical
continue-on-error: false
# ── Container Scanning (Trivy) ───────────────────────────────────
trivy-api:
name: Trivy Scan — API Image
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build API image for scanning
uses: docker/build-push-action@v6
with:
context: .
file: apps/api/Dockerfile
push: false
load: true
tags: goodgo-api:scan
cache-from: type=gha,scope=api-scan
cache-to: type=gha,mode=max,scope=api-scan
- name: Run Trivy vulnerability scanner (API)
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: "goodgo-api:scan"
format: "sarif"
output: "trivy-api-results.sarif"
severity: "CRITICAL,HIGH"
# Ignore unfixed vulns to reduce noise
ignore-unfixed: true
- name: Upload Trivy SARIF (API)
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: "trivy-api-results.sarif"
category: "trivy-api"
- name: Trivy table output (API)
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: "goodgo-api:scan"
format: "table"
severity: "CRITICAL,HIGH,MEDIUM"
ignore-unfixed: true
trivy-web:
name: Trivy Scan — Web Image
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Web image for scanning
uses: docker/build-push-action@v6
with:
context: .
file: apps/web/Dockerfile
push: false
load: true
tags: goodgo-web:scan
cache-from: type=gha,scope=web-scan
cache-to: type=gha,mode=max,scope=web-scan
- name: Run Trivy vulnerability scanner (Web)
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: "goodgo-web:scan"
format: "sarif"
output: "trivy-web-results.sarif"
severity: "CRITICAL,HIGH"
ignore-unfixed: true
- name: Upload Trivy SARIF (Web)
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: "trivy-web-results.sarif"
category: "trivy-web"
- name: Trivy table output (Web)
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: "goodgo-web:scan"
format: "table"
severity: "CRITICAL,HIGH,MEDIUM"
ignore-unfixed: true
trivy-ai:
name: Trivy Scan — AI Services Image
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build AI Services image for scanning
uses: docker/build-push-action@v6
with:
context: ./libs/ai-services
file: libs/ai-services/Dockerfile
push: false
load: true
tags: goodgo-ai:scan
cache-from: type=gha,scope=ai-scan
cache-to: type=gha,mode=max,scope=ai-scan
- name: Run Trivy vulnerability scanner (AI)
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: "goodgo-ai:scan"
format: "sarif"
output: "trivy-ai-results.sarif"
severity: "CRITICAL,HIGH"
ignore-unfixed: true
- name: Upload Trivy SARIF (AI)
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: "trivy-ai-results.sarif"
category: "trivy-ai"
- name: Trivy table output (AI)
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: "goodgo-ai:scan"
format: "table"
severity: "CRITICAL,HIGH,MEDIUM"
ignore-unfixed: true
# ── Filesystem / IaC Scanning ────────────────────────────────────
trivy-fs:
name: Trivy Filesystem Scan
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run Trivy filesystem scanner
uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: "fs"
scan-ref: "."
format: "sarif"
output: "trivy-fs-results.sarif"
severity: "CRITICAL,HIGH"
ignore-unfixed: true
scanners: "vuln,secret,misconfig"
- name: Upload Trivy SARIF (filesystem)
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: "trivy-fs-results.sarif"
category: "trivy-filesystem"
- name: Trivy filesystem table output
uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: "fs"
scan-ref: "."
format: "table"
severity: "CRITICAL,HIGH,MEDIUM"
scanners: "vuln,secret,misconfig"
# ── Summary Gate ─────────────────────────────────────────────────
security-gate:
name: Security Gate
runs-on: ubuntu-latest
needs: [dependency-audit, trivy-api, trivy-web, trivy-ai, trivy-fs]
if: always()
steps:
- name: Check security scan results
run: |
echo "## Security Scan Summary" >> $GITHUB_STEP_SUMMARY
# Check each job result
FAILED=false
if [ "${{ needs.dependency-audit.result }}" != "success" ]; then
echo "❌ Dependency audit: ${{ needs.dependency-audit.result }}" >> $GITHUB_STEP_SUMMARY
FAILED=true
else
echo "✅ Dependency audit: passed" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.trivy-api.result }}" != "success" ]; then
echo "❌ Trivy API scan: ${{ needs.trivy-api.result }}" >> $GITHUB_STEP_SUMMARY
FAILED=true
else
echo "✅ Trivy API scan: passed" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.trivy-web.result }}" != "success" ]; then
echo "❌ Trivy Web scan: ${{ needs.trivy-web.result }}" >> $GITHUB_STEP_SUMMARY
FAILED=true
else
echo "✅ Trivy Web scan: passed" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.trivy-ai.result }}" != "success" ]; then
echo "❌ Trivy AI scan: ${{ needs.trivy-ai.result }}" >> $GITHUB_STEP_SUMMARY
FAILED=true
else
echo "✅ Trivy AI scan: passed" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.trivy-fs.result }}" != "success" ]; then
echo "❌ Trivy filesystem scan: ${{ needs.trivy-fs.result }}" >> $GITHUB_STEP_SUMMARY
FAILED=true
else
echo "✅ Trivy filesystem scan: passed" >> $GITHUB_STEP_SUMMARY
fi
if [ "$FAILED" = true ]; then
echo ""
echo "⚠️ One or more security scans failed. Review the Security tab for details." >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo ""
echo "🎉 All security scans passed!" >> $GITHUB_STEP_SUMMARY