name: Security Testing (SAST & DAST) on: push: branches: [ main, develop ] pull_request: branches: [ main, develop ] schedule: # Run security scans daily at 2 AM - cron: '0 2 * * *' jobs: sast-eslint-security: name: SAST - ESLint Security Plugin runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: | frontend/package-lock.json backend/package-lock.json - name: Install backend dependencies run: cd backend && npm ci - name: Install frontend dependencies run: cd frontend && npm ci - name: Run ESLint Security Scan (Backend) run: cd backend && npx eslint . --ext .js --format json --output-file ../eslint-backend-report.json || true - name: Run ESLint Security Scan (Frontend) run: cd frontend && npx eslint . --ext .js,.jsx --format json --output-file ../eslint-frontend-report.json || true - name: Upload ESLint Reports uses: actions/upload-artifact@v4 if: always() with: name: eslint-security-reports path: | eslint-backend-report.json eslint-frontend-report.json sast-semgrep: name: SAST - Semgrep runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Run Semgrep uses: returntocorp/semgrep-action@v1 with: config: >- p/security-audit p/nodejs p/javascript p/express p/sql-injection p/xss p/command-injection generateSarif: true - name: Upload SARIF file uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: semgrep.sarif sast-npm-audit: name: SAST - NPM Audit runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Run NPM Audit (Backend) run: | cd backend npm audit --json > ../npm-audit-backend.json || true npm audit --audit-level=high continue-on-error: true - name: Run NPM Audit (Frontend) run: | cd frontend npm audit --json > ../npm-audit-frontend.json || true npm audit --audit-level=high continue-on-error: true - name: Upload NPM Audit Reports uses: actions/upload-artifact@v4 if: always() with: name: npm-audit-reports path: | npm-audit-backend.json npm-audit-frontend.json sast-snyk: name: SAST - Snyk runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Run Snyk to check for vulnerabilities uses: snyk/actions/node@master continue-on-error: true env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: args: --severity-threshold=high --all-projects docker-security-scan: name: SAST - Docker Image Security runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build Docker image run: docker build -t streamflow-test:latest . - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: image-ref: 'streamflow-test:latest' format: 'sarif' output: 'trivy-results.sarif' severity: 'CRITICAL,HIGH' - name: Upload Trivy results to GitHub Security uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: 'trivy-results.sarif' - name: Run Dockle run: | wget -q https://github.com/goodwithtech/dockle/releases/download/v0.4.14/dockle_0.4.14_Linux-64bit.tar.gz tar zxf dockle_0.4.14_Linux-64bit.tar.gz ./dockle streamflow-test:latest dast-zap-scan: name: DAST - OWASP ZAP runs-on: ubuntu-latest needs: [sast-eslint-security, sast-semgrep] steps: - name: Checkout code uses: actions/checkout@v4 - name: Start application in Docker run: | docker compose up -d sleep 30 - name: Wait for application to be ready run: | timeout 60 bash -c 'until curl -f http://localhost:12345/api/health; do sleep 2; done' - name: Run OWASP ZAP Baseline Scan uses: zaproxy/action-baseline@v0.11.0 with: target: 'http://localhost:12345' rules_file_name: '.zap/rules.tsv' cmd_options: '-a' fail_action: false - name: Run OWASP ZAP Full Scan uses: zaproxy/action-full-scan@v0.9.0 with: target: 'http://localhost:12345' rules_file_name: '.zap/rules.tsv' cmd_options: '-a' fail_action: false - name: Stop application if: always() run: docker compose down - name: Upload ZAP Reports uses: actions/upload-artifact@v4 if: always() with: name: zap-scan-reports path: | report_html.html report_json.json report_md.md security-report: name: Generate Security Report runs-on: ubuntu-latest needs: [sast-eslint-security, sast-semgrep, sast-npm-audit, docker-security-scan, dast-zap-scan] if: always() steps: - name: Download all artifacts uses: actions/download-artifact@v4 - name: Generate summary report run: | echo "# Security Scan Summary" > security-report.md echo "" >> security-report.md echo "## Scan Date: $(date)" >> security-report.md echo "" >> security-report.md echo "### SAST Scans Completed:" >> security-report.md echo "- ✅ ESLint Security Plugin" >> security-report.md echo "- ✅ Semgrep" >> security-report.md echo "- ✅ NPM Audit" >> security-report.md echo "- ✅ Docker Image Scan (Trivy & Dockle)" >> security-report.md echo "" >> security-report.md echo "### DAST Scans Completed:" >> security-report.md echo "- ✅ OWASP ZAP Baseline Scan" >> security-report.md echo "- ✅ OWASP ZAP Full Scan" >> security-report.md echo "" >> security-report.md echo "### Artifacts:" >> security-report.md echo "All scan reports have been uploaded as workflow artifacts." >> security-report.md - name: Upload Security Report uses: actions/upload-artifact@v4 with: name: security-summary-report path: security-report.md