Skip to content

Security Pipeline

Security Pipeline #106

Workflow file for this run

# Copyright (c) 2025 Foia Stream
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# FOIA Stream CI/CD Security Pipeline
#
# Implements comprehensive security scanning in CI/CD
# Addresses: GAP-003 (Security Scanning in CI/CD Pipeline)
# Controls: SA-11, A.8.25, A.8.31, 3.14.1
#
# Note: Optimized for private repos (no SARIF uploads - requires GitHub Advanced Security)
name: Security Pipeline
on:
push:
branches: [master, develop]
pull_request:
branches: [master]
schedule:
- cron: '0 2 * * *'
workflow_dispatch:
env:
BUN_VERSION: '1.3.2'
jobs:
sast:
name: Static Analysis
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Install dependencies
run: bun install --frozen-lockfile
- name: TypeScript check
run: |
echo "## TypeScript Check" >> $GITHUB_STEP_SUMMARY
if bun run typecheck 2>&1 | tee typescript-output.txt; then
echo "✅ TypeScript check passed" >> $GITHUB_STEP_SUMMARY
else
echo "❌ TypeScript errors found" >> $GITHUB_STEP_SUMMARY
fi
continue-on-error: true
- name: Biome lint check
run: |
echo "## Biome Lint Check" >> $GITHUB_STEP_SUMMARY
if bun run lint 2>&1 | tee lint-output.txt; then
echo "✅ Linting passed" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ Linting issues found" >> $GITHUB_STEP_SUMMARY
fi
continue-on-error: true
- name: Semgrep SAST scan
run: |
echo "## Semgrep Security Scan" >> $GITHUB_STEP_SUMMARY
pip install semgrep
semgrep scan --config=p/security-audit --config=p/secrets --json > semgrep-output.json 2>&1 || true
FINDINGS=$(cat semgrep-output.json | jq '.results | length' 2>/dev/null || echo "0")
echo "Found $FINDINGS potential issues" >> $GITHUB_STEP_SUMMARY
continue-on-error: true
dependency-scan:
name: Dependency Scanning
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Audit dependencies
run: |
echo "## Dependency Audit Results" >> $GITHUB_STEP_SUMMARY
npm audit --json > audit-results.json 2>&1 || true
if [ -f audit-results.json ]; then
CRITICAL=$(cat audit-results.json | jq '.metadata.vulnerabilities.critical // 0' 2>/dev/null || echo "0")
HIGH=$(cat audit-results.json | jq '.metadata.vulnerabilities.high // 0' 2>/dev/null || echo "0")
echo "| Severity | Count |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Critical | $CRITICAL |" >> $GITHUB_STEP_SUMMARY
echo "| High | $HIGH |" >> $GITHUB_STEP_SUMMARY
if [ "$CRITICAL" != "0" ] || [ "$HIGH" != "0" ]; then
echo "⚠️ Critical or High vulnerabilities found" >> $GITHUB_STEP_SUMMARY
else
echo "✅ No critical or high vulnerabilities" >> $GITHUB_STEP_SUMMARY
fi
fi
continue-on-error: true
secret-scan:
name: Secret Detection
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Gitleaks scan
run: |
echo "## Secret Detection" >> $GITHUB_STEP_SUMMARY
curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.18.4/gitleaks_8.18.4_linux_x64.tar.gz | tar -xz
if ./gitleaks detect --source . --report-format json --report-path gitleaks-report.json 2>&1; then
echo "✅ No secrets detected" >> $GITHUB_STEP_SUMMARY
else
LEAKS=$(cat gitleaks-report.json | jq 'length' 2>/dev/null || echo "unknown")
echo "⚠️ $LEAKS potential secrets detected" >> $GITHUB_STEP_SUMMARY
fi
continue-on-error: true
license-check:
name: License Compliance
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Check licenses
run: |
echo "## License Compliance" >> $GITHUB_STEP_SUMMARY
bun add -d license-checker
bunx license-checker --summary --out licenses.txt
cat licenses.txt >> $GITHUB_STEP_SUMMARY
if bunx license-checker --failOn 'GPL;AGPL' 2>/dev/null; then
echo "✅ No problematic licenses found" >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ Potentially problematic licenses detected" >> $GITHUB_STEP_SUMMARY
fi
continue-on-error: true
security-tests:
name: Security Tests
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Run tests
run: |
echo "## Test Results" >> $GITHUB_STEP_SUMMARY
cd apps/api
if bun run test 2>&1 | tee test-output.txt; then
echo "✅ All tests passed" >> $GITHUB_STEP_SUMMARY
else
echo "❌ Some tests failed" >> $GITHUB_STEP_SUMMARY
exit 1
fi
env:
DATABASE_URL: ':memory:'
JWT_SECRET: 'test-jwt-secret-for-ci-security-tests-only'
NODE_ENV: test
security-report:
name: Security Report
runs-on: ubuntu-latest
needs: [sast, dependency-scan, secret-scan, security-tests, license-check]
if: always()
steps:
- name: Generate security summary
run: |
echo "# 🔒 Security Scan Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Scan | Status |" >> $GITHUB_STEP_SUMMARY
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| SAST | ${{ needs.sast.result == 'success' && '✅ Passed' || '⚠️ Issues Found' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Dependency Scan | ${{ needs.dependency-scan.result == 'success' && '✅ Passed' || '⚠️ Issues Found' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Secret Detection | ${{ needs.secret-scan.result == 'success' && '✅ Passed' || '⚠️ Issues Found' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Security Tests | ${{ needs.security-tests.result == 'success' && '✅ Passed' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY
echo "| License Check | ${{ needs.license-check.result == 'success' && '✅ Passed' || '⚠️ Issues Found' }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Run Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY
- name: Fail if tests failed
if: needs.security-tests.result == 'failure'
run: exit 1