name: Security Scan on: push: branches: [ main, develop ] pull_request: branches: [ main ] schedule: # Run daily at 2 AM UTC - cron: '0 2 * * *' workflow_dispatch: permissions: contents: read security-events: write jobs: dependency-scan: runs-on: ubuntu-latest strategy: matrix: component: - path: 'services/sync-coordinator' type: 'npm' - path: 'apps/daemon' type: 'cargo' - path: 'services/storage-hal' type: 'cargo' - path: 'services/compression-engine' type: 'cargo' - path: 'services/ml-optimizer' type: 'pip' - path: '.' type: 'gradle' steps: - name: Checkout uses: actions/checkout@v4 - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: scan-type: 'fs' scan-ref: '${{ matrix.component.path }}' format: 'sarif' output: 'trivy-results-${{ matrix.component.type }}.sarif' severity: 'CRITICAL,HIGH,MEDIUM' - name: Upload Trivy scan results uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: 'trivy-results-${{ matrix.component.type }}.sarif' category: 'trivy-${{ matrix.component.type }}' secret-scan: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Run GitLeaks secret scanner uses: gitleaks/gitleaks-action@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} code-security-scan: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: java, javascript, python, cpp queries: security-and-quality - name: Set up Java uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - name: Build with Gradle run: | chmod +x ./gradlew ./gradlew build -x test --no-daemon -x :apps:android:shared:build - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: category: "/language:multi" semgrep-scan: runs-on: ubuntu-latest name: Semgrep Security Scan steps: - name: Checkout uses: actions/checkout@v4 - name: Run Semgrep run: | pip install semgrep semgrep scan --sarif --config=p/security-audit --config=p/kotlin --config=p/java --config=p/typescript --config=p/python --config=p/javascript --config=p/rust --output=semgrep.sarif || true - name: Upload SARIF file uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: semgrep.sarif license-scan: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: FOSSA Scan if: ${{ secrets.FOSSA_API_KEY != '' }} uses: fossas/fossa-action@main with: api-key: ${{ secrets.FOSSA_API_KEY }} run-tests: true continue-on-error: true - name: Skip FOSSA Scan if: ${{ secrets.FOSSA_API_KEY == '' }} run: | echo "FOSSA_API_KEY secret not found, skipping FOSSA scan" echo "To enable FOSSA scanning, add FOSSA_API_KEY to repository secrets" container-scan: runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' strategy: matrix: service: [backup-engine, storage-hal, ml-optimizer, sync-coordinator] steps: - name: Checkout uses: actions/checkout@v4 - name: Check for Dockerfile id: dockerfile-check run: | cd services/${{ matrix.service }} if [ -f "Dockerfile" ]; then echo "dockerfile_exists=true" >> $GITHUB_OUTPUT echo "Dockerfile found for ${{ matrix.service }}" else echo "dockerfile_exists=false" >> $GITHUB_OUTPUT echo "No Dockerfile found for ${{ matrix.service }}, skipping container scan" fi - name: Build Docker image for scanning if: steps.dockerfile-check.outputs.dockerfile_exists == 'true' run: | cd services/${{ matrix.service }} docker build -t scan-image:${{ matrix.service }} . - name: Run Trivy container scan if: steps.dockerfile-check.outputs.dockerfile_exists == 'true' uses: aquasecurity/trivy-action@master with: image-ref: 'scan-image:${{ matrix.service }}' format: 'sarif' output: 'container-scan-${{ matrix.service }}.sarif' severity: 'CRITICAL,HIGH' continue-on-error: true - name: Upload container scan results if: steps.dockerfile-check.outputs.dockerfile_exists == 'true' && always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: 'container-scan-${{ matrix.service }}.sarif' category: 'container-${{ matrix.service }}' continue-on-error: true infrastructure-scan: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Check infrastructure directory id: infra-check run: | if [ -d "infrastructure" ]; then echo "infra_exists=true" >> $GITHUB_OUTPUT echo "Infrastructure directory found" else echo "infra_exists=false" >> $GITHUB_OUTPUT echo "Infrastructure directory not found, skipping IaC scan" fi - name: Run Checkov IaC scan if: steps.infra-check.outputs.infra_exists == 'true' uses: bridgecrewio/checkov-action@master with: directory: infrastructure/ framework: terraform,kubernetes,dockerfile output_format: sarif output_file_path: checkov-results.sarif log_level: WARNING continue-on-error: true - name: Upload Checkov scan results if: steps.infra-check.outputs.infra_exists == 'true' && always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: checkov-results.sarif category: 'infrastructure' continue-on-error: true security-report: needs: [dependency-scan, secret-scan, code-security-scan, semgrep-scan, license-scan, container-scan, infrastructure-scan] runs-on: ubuntu-latest if: always() steps: - name: Security Scan Summary run: | echo "## Security Scan Results" >> $GITHUB_STEP_SUMMARY echo "- Dependency Scan: ${{ needs.dependency-scan.result }}" >> $GITHUB_STEP_SUMMARY echo "- Secret Scan: ${{ needs.secret-scan.result }}" >> $GITHUB_STEP_SUMMARY echo "- Code Security Scan: ${{ needs.code-security-scan.result }}" >> $GITHUB_STEP_SUMMARY echo "- Semgrep Scan: ${{ needs.semgrep-scan.result }}" >> $GITHUB_STEP_SUMMARY echo "- License Scan: ${{ needs.license-scan.result }}" >> $GITHUB_STEP_SUMMARY echo "- Container Scan: ${{ needs.container-scan.result }}" >> $GITHUB_STEP_SUMMARY echo "- Infrastructure Scan: ${{ needs.infrastructure-scan.result }}" >> $GITHUB_STEP_SUMMARY