ارزیابی و پایش سلامت وابستگیهای NuGet در پروژههای NET.
نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۴/۲۱ ۰۹:۰۵
آدرس: www.dntips.ir
.NET CLI برای بررسی وضعیت سلامت بستهها پرداخته و یک اسکریپت جامع در دو نگارش متناسب با محیطهای ویندوز (PowerShell) و لینوکس/مک (Bash) ارائه میدهیم که علاوه بر تولید گزارش چاپی، قابلیت مسدودسازی فرآیند Build را در صورت کشف آسیبپذیری دارا میباشند.--vulnerable: لیست بستههایی که دارای آسیبپذیریهای امنیتی ثبت شده هستند را استخراج میکند. استفاده از سوئیچ --include-transitive تضمین میکند که وابستگیهای غیرمستقیم (بستههایی که خود توسط بستههای اصلی فراخوانی شدهاند) نیز مورد بررسی قرار گیرند.--deprecated: بستههایی را نشان میدهد که توسط توسعهدهنده اصلی منسوخ اعلام شدهاند و استفاده از آنها دیگر توصیه نمیشود.--outdated: وضعیت نسخههای مصرفی را با آخرین نسخههای منتشر شده در سرور NuGet مقایسه کرده و بستههای قدیمی را لیست میکند..sln و ساختار جدید .slnx) را شناسایی کرده، خروجی گزارش را به صورت یک فایل متنی با برچسب زمان (Timestamp) ذخیره میکنند و در صورت وجود خطای امنیتی، با بازگرداندن Exit Code 1 مانع از ادامه یافتن فرآیند CI/CD میشوند.# nuget-health-check.ps1
# اجرا از ریشه مخزن - نیازمند .NET SDK 8 یا بالاتر
$ErrorActionPreference = "Stop"
# پیدا کردن خودکار اولین فایل Solution (پشتیبانی از .sln و .slnx)
$Solution = Get-ChildItem -Filter *.slnx | Select-Object -First 1
if ($null -eq $Solution) {
$Solution = Get-ChildItem -Filter *.sln | Select-Object -First 1
}
if ($null -eq $Solution) {
Write-Error "خطا: هیچ فایل راهکاری (.sln یا .slnx) در این پوشه یافت نشد."
Exit 1
}
$Timestamp = Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ"
$ReportPath = "reports/nuget-health-$(Get-Date -Format 'yyyy-MM-dd').txt"
# ایجاد پوشه گزارش در صورت عدم وجود
if (-not (Test-Path "reports")) { New-Item -ItemType Directory -Path "reports" | Out-Null }
$ReportContent = New-Object System.Text.StringBuilder
$null = $ReportContent.AppendLine("========================================================")
$null = $ReportContent.AppendLine(" NuGet Dependency Health Report")
$null = $ReportContent.AppendLine(" Generated : $Timestamp")
$null = $ReportContent.AppendLine(" Solution : $($Solution.Name)")
$null = $ReportContent.AppendLine("========================================================")
$null = $ReportContent.AppendLine("")
$null = $ReportContent.AppendLine("--- Packages with known CVEs (including transitive) ---")
$VulnerableOutput = dotnet list $Solution.FullName package --vulnerable --include-transitive 2>&1
$null = $ReportContent.AppendLine($VulnerableOutput)
$null = $ReportContent.AppendLine("")
$null = $ReportContent.AppendLine("--- Packages that have newer versions available ---")
$OutdatedOutput = dotnet list $Solution.FullName package --outdated 2>&1
$null = $ReportContent.AppendLine($OutdatedOutput)
$null = $ReportContent.AppendLine("")
$null = $ReportContent.AppendLine("--- Packages marked deprecated by their authors ---")
$DeprecatedOutput = dotnet list $Solution.FullName package --deprecated 2>&1
$null = $ReportContent.AppendLine($DeprecatedOutput)
$null = $ReportContent.AppendLine("")
$null = $ReportContent.AppendLine("========================================================")
$null = $ReportContent.AppendLine(" Report complete. Review all findings and create ADRs")
$null = $ReportContent.AppendLine(" for any deferral decisions before closing this report.")
$null = $ReportContent.AppendLine("========================================================")
# خروجی روی کنسول و ذخیره در فایل
Write-Output $ReportContent.ToString()
$ReportContent.ToString() | Out-File -FilePath $ReportPath -Encoding utf8
# بررسی وجود آسیبپذیری برای متوقف کردن گیت CI
$VulnCount = ($VulnerableOutput | Select-String -Pattern ">\s+").Count
if ($VulnCount -gt 0) {
Write-Host "ERROR: $VulnCount vulnerable package(s) found. Resolve before merging." -ForegroundColor Red
Exit 1
}bash توسعه یافته و برای استفاده در رانرهای لینوکسی، کانتینرها و محیطهای ابری بهینهسازی شده است:#!/usr/bin/env bash
# scripts/nuget-health-check.sh
# اجرا از ریشه مخزن - نیازمند .NET SDK 8 یا بالاتر (Linux, macOS, WSL)
set -euo pipefail
# پیدا کردن خودکار فایل راهکار (.slnx یا .sln)
SOLUTION="${1:-$(ls *.slnx *.sln 2>/dev/null | head -1)}"
[[ -z "$SOLUTION" ]] && { echo "Error: No solution file (.sln or .slnx) found."; exit 1; }
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
mkdir -p reports
REPORT_FILE="reports/nuget-health-$(date +%F).txt"
{
echo "========================================================"
echo " NuGet Dependency Health Report"
echo " Generated : $TIMESTAMP"
echo " Solution : $SOLUTION"
echo "========================================================"
echo ""
echo "--- Packages with known CVEs (including transitive) ---"
dotnet list "$SOLUTION" package --vulnerable --include-transitive 2>/dev/null || true
echo ""
echo "--- Packages that have newer versions available ---"
dotnet list "$SOLUTION" package --outdated 2>/dev/null || true
echo ""
echo "--- Packages marked deprecated by their authors ---"
dotnet list "$SOLUTION" package --deprecated 2>/dev/null || true
echo ""
echo "========================================================"
echo " Report complete. Review all findings and create ADRs"
echo " for any deferral decisions before closing this report."
echo "========================================================"
} | tee "$REPORT_FILE"
# بررسی آسیبپذیریها جهت مسدودسازی گیت CI
VULN_COUNT=$(dotnet list "$SOLUTION" package --vulnerable --include-transitive 2>/dev/null | grep -c "^ >" || true)
if [ "$VULN_COUNT" -gt 0 ]; then
echo "ERROR: $VULN_COUNT vulnerable package(s) found. Resolve before merging." >&2
exit 1
fiتوصیه معماری (ADR): پیشنهاد میشود در صورت مواجهه با بستههای قدیمی یا منسوخ که در حال حاضر به دلایل فنی (مانند عدم سازگاری کد) امکان ارتقای آن آنی وجود ندارد، تصمیمات به صورت مستند در قالب ساختارهای ثبت تصمیمات معماری (Architecture Decision Records) در پروژه نگهداری شوند تا بدهی فنی (Technical Debt) تیم کاملاً شفاف و قابل رهگیری باشد.