250 lines
8.8 KiB
YAML
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"
|