Security Pipeline #106
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 |