Security Scan #4
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
| name: Security Scan | |
| on: | |
| schedule: | |
| # Run security scans daily at 2 AM UTC | |
| - cron: '0 2 * * *' | |
| workflow_dispatch: | |
| push: | |
| branches: [ main ] | |
| paths: | |
| - '**/requirements.txt' | |
| - '**/package.json' | |
| - '**/package-lock.json' | |
| - '**/Containerfile' | |
| jobs: | |
| dependency-scan: | |
| name: Dependency Security Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.9' | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v3 | |
| with: | |
| node-version: '18' | |
| - name: Install Python dependencies | |
| working-directory: ./backend | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install safety bandit | |
| - name: Run Python security scan with Safety | |
| working-directory: ./backend | |
| run: | | |
| safety check --json --output safety-report.json || true | |
| cat safety-report.json | |
| - name: Run Python security scan with Bandit | |
| working-directory: ./backend | |
| run: | | |
| bandit -r . -f json -o bandit-report.json || true | |
| cat bandit-report.json | |
| - name: Install Node.js dependencies | |
| working-directory: ./frontend | |
| run: npm ci | |
| - name: Run npm audit | |
| working-directory: ./frontend | |
| run: | | |
| npm audit --audit-level=moderate --json > npm-audit-report.json || true | |
| cat npm-audit-report.json | |
| - name: Upload security reports | |
| uses: actions/upload-artifact@v3 | |
| with: | |
| name: security-reports | |
| path: | | |
| backend/safety-report.json | |
| backend/bandit-report.json | |
| frontend/npm-audit-report.json | |
| container-scan: | |
| name: Container Security Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Podman | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y podman | |
| - name: Build backend image | |
| working-directory: ./backend | |
| run: | | |
| podman build -t redhat-learning-paths-backend:scan -f Containerfile . | |
| - name: Build frontend image | |
| working-directory: ./frontend | |
| run: | | |
| podman build -t redhat-learning-paths-frontend:scan -f Containerfile . | |
| - name: Run Trivy scan on backend image | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: 'redhat-learning-paths-backend:scan' | |
| format: 'sarif' | |
| output: 'backend-trivy-results.sarif' | |
| - name: Run Trivy scan on frontend image | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: 'redhat-learning-paths-frontend:scan' | |
| format: 'sarif' | |
| output: 'frontend-trivy-results.sarif' | |
| - name: Upload Trivy scan results to GitHub Security tab | |
| uses: github/codeql-action/upload-sarif@v2 | |
| if: always() | |
| with: | |
| sarif_file: 'backend-trivy-results.sarif' | |
| - name: Upload Trivy scan results to GitHub Security tab | |
| uses: github/codeql-action/upload-sarif@v2 | |
| if: always() | |
| with: | |
| sarif_file: 'frontend-trivy-results.sarif' | |
| codeql-analysis: | |
| name: CodeQL Security Analysis | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| contents: read | |
| security-events: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| language: [ 'python', 'javascript' ] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@v2 | |
| with: | |
| languages: ${{ matrix.language }} | |
| queries: security-extended,security-and-quality | |
| - name: Autobuild | |
| uses: github/codeql-action/autobuild@v2 | |
| - name: Perform CodeQL Analysis | |
| uses: github/codeql-action/analyze@v2 | |
| with: | |
| category: "/language:${{matrix.language}}" | |
| secrets-scan: | |
| name: Secrets Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Run TruffleHog OSS | |
| uses: trufflesecurity/trufflehog@main | |
| with: | |
| path: ./ | |
| base: main | |
| head: HEAD | |
| extra_args: --debug --only-verified | |
| sbom-generation: | |
| name: Generate SBOM | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Podman | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y podman | |
| - name: Build images | |
| run: | | |
| cd backend && podman build -t redhat-learning-paths-backend:sbom -f Containerfile . | |
| cd ../frontend && podman build -t redhat-learning-paths-frontend:sbom -f Containerfile . | |
| - name: Generate SBOM for backend | |
| uses: anchore/sbom-action@v0 | |
| with: | |
| image: redhat-learning-paths-backend:sbom | |
| format: spdx-json | |
| output-file: backend-sbom.spdx.json | |
| - name: Generate SBOM for frontend | |
| uses: anchore/sbom-action@v0 | |
| with: | |
| image: redhat-learning-paths-frontend:sbom | |
| format: spdx-json | |
| output-file: frontend-sbom.spdx.json | |
| - name: Upload SBOM artifacts | |
| uses: actions/upload-artifact@v3 | |
| with: | |
| name: sbom-reports | |
| path: | | |
| backend-sbom.spdx.json | |
| frontend-sbom.spdx.json | |
| compliance-check: | |
| name: Compliance Check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Check for required security files | |
| run: | | |
| echo "Checking for security-related files..." | |
| # Check for security policy | |
| if [ ! -f "SECURITY.md" ]; then | |
| echo "❌ SECURITY.md file not found" | |
| exit 1 | |
| else | |
| echo "✅ SECURITY.md found" | |
| fi | |
| # Check for license | |
| if [ ! -f "LICENSE" ] && [ ! -f "LICENSE.md" ] && [ ! -f "LICENSE.txt" ]; then | |
| echo "❌ LICENSE file not found" | |
| exit 1 | |
| else | |
| echo "✅ LICENSE found" | |
| fi | |
| # Check for contributing guidelines | |
| if [ ! -f "CONTRIBUTING.md" ]; then | |
| echo "⚠️ CONTRIBUTING.md not found (recommended)" | |
| else | |
| echo "✅ CONTRIBUTING.md found" | |
| fi | |
| - name: Check container security best practices | |
| run: | | |
| echo "Checking Containerfile security practices..." | |
| # Check for non-root user in Containerfiles | |
| if ! grep -q "USER.*[^0]" backend/Containerfile; then | |
| echo "❌ Backend Containerfile should run as non-root user" | |
| exit 1 | |
| fi | |
| if ! grep -q "USER.*[^0]" frontend/Containerfile; then | |
| echo "❌ Frontend Containerfile should run as non-root user" | |
| exit 1 | |
| fi | |
| echo "✅ Containerfiles use non-root users" | |
| security-summary: | |
| name: Security Summary | |
| runs-on: ubuntu-latest | |
| needs: [dependency-scan, container-scan, codeql-analysis, secrets-scan, sbom-generation, compliance-check] | |
| if: always() | |
| steps: | |
| - name: Generate security summary | |
| run: | | |
| echo "# Security Scan Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ needs.dependency-scan.result }}" == "success" ]; then | |
| echo "| Dependency Scan | ✅ Passed |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| Dependency Scan | ❌ Failed |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.container-scan.result }}" == "success" ]; then | |
| echo "| Container Scan | ✅ Passed |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| Container Scan | ❌ Failed |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.codeql-analysis.result }}" == "success" ]; then | |
| echo "| CodeQL Analysis | ✅ Passed |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| CodeQL Analysis | ❌ Failed |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.secrets-scan.result }}" == "success" ]; then | |
| echo "| Secrets Scan | ✅ Passed |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| Secrets Scan | ❌ Failed |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.sbom-generation.result }}" == "success" ]; then | |
| echo "| SBOM Generation | ✅ Passed |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| SBOM Generation | ❌ Failed |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.compliance-check.result }}" == "success" ]; then | |
| echo "| Compliance Check | ✅ Passed |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| Compliance Check | ❌ Failed |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "For detailed results, check the individual job logs and security tab." >> $GITHUB_STEP_SUMMARY |