#!/bin/bash # BreakPilot DevSecOps Security Scanning Script # # Usage: ./scripts/security-scan.sh [options] # --all Run all scans # --secrets Run secrets detection (Gitleaks) # --sast Run static analysis (Semgrep, Bandit) # --sca Run dependency scanning (Trivy, Grype) # --sbom Generate SBOM (Syft) # --image Scan Docker images (Trivy) # --ci CI mode (exit on critical findings) set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Default values RUN_ALL=false RUN_SECRETS=false RUN_SAST=false RUN_SCA=false RUN_SBOM=false RUN_IMAGE=false CI_MODE=false # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --all) RUN_ALL=true; shift ;; --secrets) RUN_SECRETS=true; shift ;; --sast) RUN_SAST=true; shift ;; --sca) RUN_SCA=true; shift ;; --sbom) RUN_SBOM=true; shift ;; --image) RUN_IMAGE=true; shift ;; --ci) CI_MODE=true; shift ;; *) echo "Unknown option: $1"; exit 1 ;; esac done # If no specific option, run all if ! $RUN_SECRETS && ! $RUN_SAST && ! $RUN_SCA && ! $RUN_SBOM && ! $RUN_IMAGE; then RUN_ALL=true fi echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}" echo -e "${BLUE}║ BreakPilot DevSecOps Security Scanner ║${NC}" echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" cd "$PROJECT_DIR" SCAN_RESULTS_DIR="$PROJECT_DIR/security-reports" mkdir -p "$SCAN_RESULTS_DIR" TIMESTAMP=$(date +%Y%m%d_%H%M%S) CRITICAL_FOUND=false # ============================================= # 1. Secrets Detection (Gitleaks) # ============================================= run_secrets_scan() { echo -e "\n${YELLOW}[1/5] Secrets Detection (Gitleaks)${NC}" if command -v gitleaks &> /dev/null; then echo "Running Gitleaks..." if gitleaks detect --source . --config .gitleaks.toml --report-path "$SCAN_RESULTS_DIR/gitleaks-${TIMESTAMP}.json" --report-format json 2>&1; then echo -e "${GREEN}✓ No secrets found${NC}" else echo -e "${RED}✗ Secrets detected! Check $SCAN_RESULTS_DIR/gitleaks-${TIMESTAMP}.json${NC}" CRITICAL_FOUND=true fi else echo -e "${YELLOW}⚠ Gitleaks not installed. Install: brew install gitleaks${NC}" fi } # ============================================= # 2. Static Analysis (Semgrep + Bandit) # ============================================= run_sast_scan() { echo -e "\n${YELLOW}[2/5] Static Analysis (SAST)${NC}" # Semgrep if command -v semgrep &> /dev/null; then echo "Running Semgrep..." semgrep scan --config auto --config .semgrep.yml \ --json --output "$SCAN_RESULTS_DIR/semgrep-${TIMESTAMP}.json" \ --severity ERROR || true echo -e "${GREEN}✓ Semgrep scan complete${NC}" else echo -e "${YELLOW}⚠ Semgrep not installed. Install: pip install semgrep${NC}" fi # Bandit (Python) if command -v bandit &> /dev/null; then echo "Running Bandit..." bandit -r backend/ -ll -x backend/tests/* \ -f json -o "$SCAN_RESULTS_DIR/bandit-${TIMESTAMP}.json" 2>/dev/null || true echo -e "${GREEN}✓ Bandit scan complete${NC}" else echo -e "${YELLOW}⚠ Bandit not installed. Install: pip install bandit${NC}" fi } # ============================================= # 3. Dependency Scanning (Trivy + Grype) # ============================================= run_sca_scan() { echo -e "\n${YELLOW}[3/5] Dependency Scanning (SCA)${NC}" # Trivy filesystem scan if command -v trivy &> /dev/null; then echo "Running Trivy filesystem scan..." trivy fs . --config .trivy.yaml \ --format json --output "$SCAN_RESULTS_DIR/trivy-fs-${TIMESTAMP}.json" \ --severity HIGH,CRITICAL || true echo -e "${GREEN}✓ Trivy filesystem scan complete${NC}" else echo -e "${YELLOW}⚠ Trivy not installed. Install: brew install trivy${NC}" fi # Grype if command -v grype &> /dev/null; then echo "Running Grype..." grype dir:. -o json > "$SCAN_RESULTS_DIR/grype-${TIMESTAMP}.json" 2>/dev/null || true echo -e "${GREEN}✓ Grype scan complete${NC}" else echo -e "${YELLOW}⚠ Grype not installed. Install: brew install grype${NC}" fi } # ============================================= # 4. SBOM Generation (Syft) # ============================================= run_sbom_generation() { echo -e "\n${YELLOW}[4/5] SBOM Generation (Syft)${NC}" if command -v syft &> /dev/null; then echo "Generating SBOM..." syft dir:. -o cyclonedx-json="$SCAN_RESULTS_DIR/sbom-${TIMESTAMP}.json" 2>/dev/null || true syft dir:. -o spdx-json="$SCAN_RESULTS_DIR/sbom-spdx-${TIMESTAMP}.json" 2>/dev/null || true echo -e "${GREEN}✓ SBOM generated (CycloneDX + SPDX)${NC}" else echo -e "${YELLOW}⚠ Syft not installed. Install: brew install syft${NC}" fi } # ============================================= # 5. Container Image Scanning (Trivy) # ============================================= run_image_scan() { echo -e "\n${YELLOW}[5/5] Container Image Scanning${NC}" if command -v trivy &> /dev/null; then # Scan each built image for IMAGE in breakpilot-pwa-backend breakpilot-pwa-consent-service breakpilot-pwa-school-service; do if docker image inspect "$IMAGE" &> /dev/null; then echo "Scanning $IMAGE..." trivy image "$IMAGE" \ --format json --output "$SCAN_RESULTS_DIR/trivy-image-${IMAGE}-${TIMESTAMP}.json" \ --severity HIGH,CRITICAL || true else echo -e "${YELLOW}⚠ Image $IMAGE not found, skipping${NC}" fi done echo -e "${GREEN}✓ Container image scans complete${NC}" else echo -e "${YELLOW}⚠ Trivy not installed. Install: brew install trivy${NC}" fi } # ============================================= # Run selected scans # ============================================= if $RUN_ALL || $RUN_SECRETS; then run_secrets_scan fi if $RUN_ALL || $RUN_SAST; then run_sast_scan fi if $RUN_ALL || $RUN_SCA; then run_sca_scan fi if $RUN_ALL || $RUN_SBOM; then run_sbom_generation fi if $RUN_ALL || $RUN_IMAGE; then run_image_scan fi # ============================================= # Summary # ============================================= echo -e "\n${BLUE}╔════════════════════════════════════════════════════════════╗${NC}" echo -e "${BLUE}║ Scan Summary ║${NC}" echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}" echo -e "Reports saved to: ${GREEN}$SCAN_RESULTS_DIR${NC}" ls -la "$SCAN_RESULTS_DIR"/*-${TIMESTAMP}.* 2>/dev/null || echo "No reports generated" if $CRITICAL_FOUND && $CI_MODE; then echo -e "\n${RED}✗ Critical security findings detected. CI pipeline should fail.${NC}" exit 1 else echo -e "\n${GREEN}✓ Security scan completed${NC}" exit 0 fi