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>
This commit is contained in:
312
.github/workflows/security.yml
vendored
Normal file
312
.github/workflows/security.yml
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
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
|
||||
Reference in New Issue
Block a user