Files
git.stella-ops.org/.gitea/workflows/dependency-security-scan.yml

250 lines
8.8 KiB
YAML

# 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"