# Dependency Security Scan # Sprint: CI/CD Enhancement - Dependency Management Automation # # Purpose: Scan dependencies for known vulnerabilities # Schedule: Weekly and on PRs modifying package files name: Dependency Security Scan on: schedule: # Run weekly on Sundays at 02:00 UTC - cron: '0 2 * * 0' pull_request: paths: - 'src/Directory.Packages.props' - '**/package.json' - '**/package-lock.json' - '**/*.csproj' workflow_dispatch: inputs: fail_on_vulnerabilities: description: 'Fail if vulnerabilities found' required: false type: boolean default: true env: DOTNET_VERSION: '10.0.100' jobs: scan-nuget: name: NuGet Vulnerability Scan runs-on: ubuntu-latest outputs: vulnerabilities_found: ${{ steps.scan.outputs.vulnerabilities_found }} critical_count: ${{ steps.scan.outputs.critical_count }} high_count: ${{ steps.scan.outputs.high_count }} steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} include-prerelease: true - name: Restore packages run: dotnet restore src/StellaOps.sln - name: Scan for vulnerabilities id: scan run: | mkdir -p security-reports echo "Scanning NuGet packages for vulnerabilities..." # Run vulnerability check dotnet list src/StellaOps.sln package --vulnerable --include-transitive \ > security-reports/nuget-vulnerabilities.txt 2>&1 || true # Parse results CRITICAL=$(grep -c "Critical" security-reports/nuget-vulnerabilities.txt 2>/dev/null || echo "0") HIGH=$(grep -c "High" security-reports/nuget-vulnerabilities.txt 2>/dev/null || echo "0") MEDIUM=$(grep -c "Medium" security-reports/nuget-vulnerabilities.txt 2>/dev/null || echo "0") LOW=$(grep -c "Low" security-reports/nuget-vulnerabilities.txt 2>/dev/null || echo "0") TOTAL=$((CRITICAL + HIGH + MEDIUM + LOW)) echo "=== Vulnerability Summary ===" echo "Critical: $CRITICAL" echo "High: $HIGH" echo "Medium: $MEDIUM" echo "Low: $LOW" echo "Total: $TOTAL" echo "critical_count=$CRITICAL" >> $GITHUB_OUTPUT echo "high_count=$HIGH" >> $GITHUB_OUTPUT echo "medium_count=$MEDIUM" >> $GITHUB_OUTPUT echo "low_count=$LOW" >> $GITHUB_OUTPUT if [[ $TOTAL -gt 0 ]]; then echo "vulnerabilities_found=true" >> $GITHUB_OUTPUT else echo "vulnerabilities_found=false" >> $GITHUB_OUTPUT fi # Show detailed report echo "" echo "=== Detailed Report ===" cat security-reports/nuget-vulnerabilities.txt - name: Upload NuGet security report uses: actions/upload-artifact@v4 with: name: nuget-security-report path: security-reports/ retention-days: 90 scan-npm: name: npm Vulnerability Scan runs-on: ubuntu-latest outputs: vulnerabilities_found: ${{ steps.scan.outputs.vulnerabilities_found }} critical_count: ${{ steps.scan.outputs.critical_count }} high_count: ${{ steps.scan.outputs.high_count }} steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Find and scan package.json files id: scan run: | mkdir -p security-reports TOTAL_CRITICAL=0 TOTAL_HIGH=0 TOTAL_MEDIUM=0 TOTAL_LOW=0 VULNERABILITIES_FOUND=false # Find all package.json files PACKAGES=$(find . -name "package.json" -not -path "*/node_modules/*" -not -path "*/bin/*" -not -path "*/obj/*") for pkg in $PACKAGES; do DIR=$(dirname "$pkg") if [[ ! -f "$DIR/package-lock.json" ]] && [[ ! -f "$DIR/yarn.lock" ]]; then continue fi echo "Scanning $DIR..." cd "$DIR" # Install dependencies npm install --ignore-scripts 2>/dev/null || true # Run npm audit REPORT_FILE="${GITHUB_WORKSPACE}/security-reports/npm-audit-$(basename $DIR).json" npm audit --json > "$REPORT_FILE" 2>/dev/null || true # Parse results if [[ -f "$REPORT_FILE" ]]; then CRITICAL=$(jq '.metadata.vulnerabilities.critical // 0' "$REPORT_FILE" 2>/dev/null || echo "0") HIGH=$(jq '.metadata.vulnerabilities.high // 0' "$REPORT_FILE" 2>/dev/null || echo "0") MEDIUM=$(jq '.metadata.vulnerabilities.moderate // 0' "$REPORT_FILE" 2>/dev/null || echo "0") LOW=$(jq '.metadata.vulnerabilities.low // 0' "$REPORT_FILE" 2>/dev/null || echo "0") TOTAL_CRITICAL=$((TOTAL_CRITICAL + CRITICAL)) TOTAL_HIGH=$((TOTAL_HIGH + HIGH)) TOTAL_MEDIUM=$((TOTAL_MEDIUM + MEDIUM)) TOTAL_LOW=$((TOTAL_LOW + LOW)) if [[ $((CRITICAL + HIGH + MEDIUM + LOW)) -gt 0 ]]; then VULNERABILITIES_FOUND=true fi fi cd "$GITHUB_WORKSPACE" done echo "=== npm Vulnerability Summary ===" echo "Critical: $TOTAL_CRITICAL" echo "High: $TOTAL_HIGH" echo "Medium: $TOTAL_MEDIUM" echo "Low: $TOTAL_LOW" echo "critical_count=$TOTAL_CRITICAL" >> $GITHUB_OUTPUT echo "high_count=$TOTAL_HIGH" >> $GITHUB_OUTPUT echo "vulnerabilities_found=$VULNERABILITIES_FOUND" >> $GITHUB_OUTPUT - name: Upload npm security report uses: actions/upload-artifact@v4 with: name: npm-security-report path: security-reports/ retention-days: 90 summary: name: Security Summary runs-on: ubuntu-latest needs: [scan-nuget, scan-npm] if: always() steps: - name: Generate summary run: | NUGET_VULNS="${{ needs.scan-nuget.outputs.vulnerabilities_found }}" NPM_VULNS="${{ needs.scan-npm.outputs.vulnerabilities_found }}" NUGET_CRITICAL="${{ needs.scan-nuget.outputs.critical_count }}" NUGET_HIGH="${{ needs.scan-nuget.outputs.high_count }}" NPM_CRITICAL="${{ needs.scan-npm.outputs.critical_count }}" NPM_HIGH="${{ needs.scan-npm.outputs.high_count }}" echo "## Dependency Security Scan Results" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### NuGet Packages" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Severity | Count |" >> $GITHUB_STEP_SUMMARY echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY echo "| Critical | ${NUGET_CRITICAL:-0} |" >> $GITHUB_STEP_SUMMARY echo "| High | ${NUGET_HIGH:-0} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### npm Packages" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Severity | Count |" >> $GITHUB_STEP_SUMMARY echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY echo "| Critical | ${NPM_CRITICAL:-0} |" >> $GITHUB_STEP_SUMMARY echo "| High | ${NPM_HIGH:-0} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY # Determine overall status TOTAL_CRITICAL=$((${NUGET_CRITICAL:-0} + ${NPM_CRITICAL:-0})) TOTAL_HIGH=$((${NUGET_HIGH:-0} + ${NPM_HIGH:-0})) if [[ $TOTAL_CRITICAL -gt 0 ]]; then echo "### ⚠️ Critical Vulnerabilities Found" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Please review and remediate critical vulnerabilities before merging." >> $GITHUB_STEP_SUMMARY elif [[ $TOTAL_HIGH -gt 0 ]]; then echo "### ⚠️ High Severity Vulnerabilities Found" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Please review high severity vulnerabilities." >> $GITHUB_STEP_SUMMARY else echo "### ✅ No Critical or High Vulnerabilities" >> $GITHUB_STEP_SUMMARY fi - name: Check gate if: github.event.inputs.fail_on_vulnerabilities == 'true' || github.event_name == 'pull_request' run: | NUGET_CRITICAL="${{ needs.scan-nuget.outputs.critical_count }}" NPM_CRITICAL="${{ needs.scan-npm.outputs.critical_count }}" TOTAL_CRITICAL=$((${NUGET_CRITICAL:-0} + ${NPM_CRITICAL:-0})) if [[ $TOTAL_CRITICAL -gt 0 ]]; then echo "::error::$TOTAL_CRITICAL critical vulnerabilities found in dependencies" exit 1 fi echo "Security scan passed - no critical vulnerabilities"