mirror of
https://dev.azure.com/effectory/Survey%20Software/_git/Cloud%20Engineering
synced 2026-02-27 18:52:18 +01:00
410 lines
18 KiB
PowerShell
410 lines
18 KiB
PowerShell
<#
|
||
.SYNOPSIS
|
||
Generates a comprehensive Software Bill of Materials (SBOM) by consolidating Snyk dependency exports with enhanced package metadata.
|
||
|
||
.DESCRIPTION
|
||
This script processes multiple Snyk CSV dependency exports to create a unified Software Bill of Materials
|
||
with enriched package information. It combines vulnerability data from Snyk with additional metadata
|
||
from package repositories (NuGet) to provide comprehensive dependency insights.
|
||
|
||
Features:
|
||
• Multi-file CSV processing from Snyk dependency exports
|
||
• Enhanced NuGet package metadata enrichment (version history, deprecation status)
|
||
• Vulnerability aggregation across all projects and dependencies
|
||
• License information consolidation and analysis
|
||
• Deprecation detection for NuGet packages
|
||
• Comprehensive SBOM generation with timestamped output
|
||
• Support for npm, NuGet, and other package types
|
||
• Latest version tracking and publication date analysis
|
||
|
||
.PARAMETER None
|
||
This script does not accept parameters. Input files are processed from a predefined directory path.
|
||
|
||
.EXAMPLE
|
||
.\SBOM.ps1
|
||
Processes all Snyk CSV exports in c:\temp\snyk\ and generates a consolidated SBOM.
|
||
|
||
.EXAMPLE
|
||
# Prepare Snyk CSV exports first
|
||
# Export dependencies from Snyk UI or CLI to c:\temp\snyk\
|
||
.\SBOM.ps1
|
||
Creates enhanced SBOM with NuGet metadata enrichment.
|
||
|
||
.INPUTS
|
||
System.IO.FileInfo[]
|
||
Requires Snyk CSV dependency export files in c:\temp\snyk\ directory.
|
||
Expected CSV columns: id, name, version, type, issuesCritical, issuesHigh, issuesMedium,
|
||
issuesLow, dependenciesWithIssues, licenses, projects, license urls
|
||
|
||
.OUTPUTS
|
||
System.IO.FileInfo
|
||
Generates a timestamped CSV file containing enriched SBOM data with the following columns:
|
||
- FileName: Source CSV file name for traceability
|
||
- id: Package unique identifier from Snyk
|
||
- name: Package name
|
||
- version: Package version
|
||
- type: Package type (npm, nuget, maven, etc.)
|
||
- issuesCritical/High/Medium/Low: Vulnerability counts by severity
|
||
- dependenciesWithIssues: Count of vulnerable dependencies
|
||
- licenses: License information from Snyk
|
||
- projects: Projects using this dependency
|
||
- license_urls: URLs to license information
|
||
- latestVersion: Most recent available version (NuGet packages)
|
||
- latestVersionUrl: URL to latest version (NuGet packages)
|
||
- latestVersionPublishedDate: Publication date of latest version
|
||
- firstPublishedDate: Initial publication date of current version
|
||
- versionUrl: URL to current version information
|
||
- isDeprecated: Boolean indicating deprecation status
|
||
|
||
.NOTES
|
||
Requires PowerShell 5.1 or later
|
||
Requires PackageManagement module for NuGet package queries
|
||
|
||
Prerequisites:
|
||
- Snyk CSV dependency exports must be placed in c:\temp\snyk\ directory
|
||
- Network connectivity to nuget.org for package metadata enrichment
|
||
- PowerShell execution policy must allow script execution
|
||
|
||
Performance Considerations:
|
||
- Processing time depends on number of unique NuGet packages
|
||
- Each NuGet package requires API calls to nuget.org
|
||
- Large SBOM files may take several minutes to process
|
||
- Progress indicators show current processing status
|
||
|
||
Input File Requirements:
|
||
- Files must be CSV format with standard Snyk dependency export structure
|
||
- All CSV files in the source directory will be processed
|
||
- Files should contain complete dependency information from Snyk scans
|
||
|
||
.LINK
|
||
https://docs.snyk.io/products/snyk-open-source/dependency-management
|
||
https://docs.microsoft.com/en-us/nuget/api/overview
|
||
|
||
.COMPONENT
|
||
PackageManagement PowerShell Module, Snyk Dependency Exports
|
||
|
||
.ROLE
|
||
Software Composition Analysis, Security Governance, Compliance Reporting
|
||
|
||
.FUNCTIONALITY
|
||
SBOM generation, dependency analysis, vulnerability aggregation, package metadata enrichment
|
||
#>
|
||
|
||
Write-Host "========================================================================================================================================================================"
|
||
Write-Host "🔍 Software Bill of Materials (SBOM) Generator" -ForegroundColor Green
|
||
Write-Host "========================================================================================================================================================================"
|
||
Write-Host "📋 Processing Snyk dependency exports and enriching with package metadata..." -ForegroundColor Cyan
|
||
Write-Host ""
|
||
|
||
# Data structure class for enhanced SBOM entries
|
||
class CSVItem {
|
||
[string] $FileName = ""
|
||
[string] $id = ""
|
||
[string] $name = ""
|
||
[string] $version = ""
|
||
[string] $type = ""
|
||
[string] $issuesCritical = ""
|
||
[string] $issuesHigh = ""
|
||
[string] $issuesMedium = ""
|
||
[string] $issuesLow = ""
|
||
[string] $dependenciesWithIssues = ""
|
||
[string] $licenses = ""
|
||
[string] $projects = ""
|
||
[string] $license_urls = ""
|
||
[string] $latestVersion = ""
|
||
[string] $latestVersionUrl = ""
|
||
[string] $latestVersionPublishedDate = ""
|
||
[string] $firstPublishedDate = ""
|
||
[string] $versionUrl = ""
|
||
[string] $isDeprecated = ""
|
||
}
|
||
|
||
<#
|
||
.SYNOPSIS
|
||
Enriches package entries with additional metadata from package repositories.
|
||
|
||
.DESCRIPTION
|
||
This function queries package repositories (currently NuGet) to gather additional metadata
|
||
such as publication dates, latest versions, deprecation status, and repository URLs.
|
||
This enrichment provides comprehensive package lifecycle information for SBOM analysis.
|
||
|
||
.PARAMETER allItems
|
||
Array of CSVItem objects representing all packages in the SBOM.
|
||
|
||
.PARAMETER name
|
||
Name of the package to enrich with metadata.
|
||
|
||
.PARAMETER version
|
||
Version of the package to enrich.
|
||
|
||
.PARAMETER type
|
||
Package type (npm, nuget, maven, etc.). Only NuGet packages are currently enriched.
|
||
|
||
.PARAMETER progress
|
||
Progress indicator string showing current processing status.
|
||
|
||
.NOTES
|
||
Currently supports NuGet package enrichment only.
|
||
Makes API calls to NuGet.org which may impact performance for large SBOMs.
|
||
Handles deprecated packages by checking multiple metadata fields.
|
||
#>
|
||
function PropagatePackage {
|
||
param (
|
||
[CSVItem[]] $allItems,
|
||
[string] $name,
|
||
[string] $version,
|
||
[string] $type,
|
||
[string] $progress
|
||
)
|
||
|
||
# Find all SBOM entries matching this package
|
||
$foundItems = $allItems | Where-Object { ($_.name -eq $name) -and ($_.version -eq $version) -and ($_.type -eq $type)}
|
||
|
||
Write-Host " [$progress] 📦 Enriching $type package: $name ($version) - Found $($foundItems.Length) entries" -ForegroundColor Gray
|
||
|
||
# Currently only supports NuGet package enrichment
|
||
if ($type -ne "nuget") {
|
||
Write-Host " ⏭️ Skipping $type package (enrichment not supported)" -ForegroundColor DarkGray
|
||
return
|
||
}
|
||
|
||
# Query NuGet repository for specific version metadata
|
||
try {
|
||
$nuget = Find-Package $name -RequiredVersion $version -ProviderName Nuget -ErrorAction Stop
|
||
Write-Host " ✅ Found NuGet package metadata for $name $version" -ForegroundColor DarkGreen
|
||
}
|
||
catch {
|
||
Write-Host " ❌ Failed to find NuGet package: $name $version - $($_.Exception.Message)" -ForegroundColor DarkRed
|
||
return
|
||
}
|
||
|
||
# Query for latest version information
|
||
$lastNuget = $null
|
||
try {
|
||
$lastNuget = Find-Package $name -ProviderName Nuget -ErrorAction Stop
|
||
Write-Host " 📈 Latest version found: $($lastNuget.Version)" -ForegroundColor DarkGreen
|
||
}
|
||
catch {
|
||
Write-Host " ⚠️ Could not determine latest version for $name" -ForegroundColor DarkYellow
|
||
}
|
||
|
||
# Enrich all matching SBOM entries with NuGet metadata
|
||
foreach ($propagateItem in $foundItems) {
|
||
# Set publication date for current version
|
||
$propagateItem.firstPublishedDate = $nuget.metadata["published"]
|
||
|
||
# Generate NuGet.org URL for current version
|
||
$propagateItem.versionUrl = "https://www.nuget.org/packages/$name/$version"
|
||
|
||
# Add latest version information if available
|
||
if ($null -ne $lastNuget) {
|
||
$propagateItem.latestVersion = $lastNuget.Version
|
||
$propagateItem.latestVersionPublishedDate = $lastNuget.metadata["published"]
|
||
$propagateItem.latestVersionUrl = "https://www.nuget.org/packages/$name/$($lastNuget.Version)"
|
||
}
|
||
|
||
# Determine deprecation status by checking multiple metadata fields
|
||
$isDeprecated = ($null -eq $lastNuget) -or
|
||
($nuget.metadata["summary"] -like "*Deprecated*") -or
|
||
($nuget.metadata["title"] -like "*Deprecated*") -or
|
||
($nuget.metadata["tags"] -like "*Deprecated*") -or
|
||
($nuget.metadata["description"] -like "*Deprecated*")
|
||
|
||
$propagateItem.isDeprecated = $isDeprecated
|
||
|
||
if ($isDeprecated) {
|
||
Write-Host " ⚠️ Package marked as deprecated: $name" -ForegroundColor Yellow
|
||
}
|
||
}
|
||
|
||
Write-Host " ✅ Successfully enriched $($foundItems.Length) SBOM entries" -ForegroundColor DarkGreen
|
||
return
|
||
}
|
||
|
||
# Generate timestamped output filename
|
||
[string] $date = Get-Date -Format "yyyy-MM-dd HHmm"
|
||
$fileName = ".\$date snyk_npm_nuget_sbom.csv"
|
||
|
||
Write-Host "📁 Output file: $fileName" -ForegroundColor Gray
|
||
Write-Host ""
|
||
|
||
Write-Host "========================================================================================================================================================================"
|
||
Write-Host "📋 Phase 1: Processing Snyk CSV Dependencies" -ForegroundColor Cyan
|
||
Write-Host "========================================================================================================================================================================"
|
||
|
||
# Define source directory for Snyk CSV exports
|
||
$csvDependenciesExportPath = "c:\temp\snyk\*.csv"
|
||
|
||
# Locate all CSV files in the source directory
|
||
try {
|
||
$files = Get-ChildItem $csvDependenciesExportPath -ErrorAction Stop
|
||
Write-Host "✅ Found $($files.Count) Snyk CSV file(s) to process" -ForegroundColor Green
|
||
}
|
||
catch {
|
||
Write-Host "❌ No CSV files found in $csvDependenciesExportPath" -ForegroundColor Red
|
||
Write-Host " Please ensure Snyk dependency exports are placed in the directory" -ForegroundColor Yellow
|
||
exit 1
|
||
}
|
||
|
||
# Initialize SBOM collection
|
||
[CSVItem[]]$CSVItems = @()
|
||
$totalEntries = 0
|
||
|
||
# Process each CSV file
|
||
foreach($file in $files) {
|
||
Write-Host ""
|
||
Write-Host "📄 Processing file: $($file.Name)" -ForegroundColor Yellow
|
||
Write-Host " 📍 Path: $($file.FullName)" -ForegroundColor Gray
|
||
|
||
try {
|
||
$csv = Import-Csv -Path $file.FullName -ErrorAction Stop
|
||
Write-Host " 📊 Found $($csv.Count) dependency entries" -ForegroundColor White
|
||
|
||
# Process each dependency entry in the CSV file
|
||
$entryCount = 0
|
||
foreach ($csvLine in $csv) {
|
||
$entryCount++
|
||
$totalEntries++
|
||
|
||
# Create new SBOM entry
|
||
[CSVItem] $CSVItem = [CSVItem]::new()
|
||
$CSVItem.FileName = $file.Name
|
||
|
||
# Map Snyk CSV data to SBOM structure
|
||
$CSVItem.id = $csvLine.id
|
||
$CSVItem.name = $csvLine.name
|
||
$CSVItem.version = $csvLine.version
|
||
$CSVItem.type = $csvLine.type
|
||
|
||
# Vulnerability information
|
||
$CSVItem.issuesCritical = $csvLine.issuesCritical
|
||
$CSVItem.issuesHigh = $csvLine.issuesHigh
|
||
$CSVItem.issuesMedium = $csvLine.issuesMedium
|
||
$CSVItem.issuesLow = $csvLine.issuesLow
|
||
$CSVItem.dependenciesWithIssues = $csvLine.dependenciesWithIssues
|
||
|
||
# License and project information
|
||
$CSVItem.licenses = $csvLine.licenses
|
||
$CSVItem.projects = $csvLine.projects
|
||
$CSVItem.license_urls = $csvLine."license urls"
|
||
|
||
# Version and metadata (will be enriched later for NuGet packages)
|
||
$CSVItem.latestVersion = $csvLine.latestVersion
|
||
$CSVItem.latestVersionPublishedDate = $csvLine.latestVersionPublishedDate
|
||
$CSVItem.firstPublishedDate = $csvLine.firstPublishedDate
|
||
$CSVItem.isDeprecated = $csvLine.isDeprecated
|
||
|
||
$CSVItems += $CSVItem
|
||
}
|
||
|
||
Write-Host " ✅ Successfully processed $entryCount entries from $($file.Name)" -ForegroundColor Green
|
||
}
|
||
catch {
|
||
Write-Host " ❌ Error processing $($file.Name): $($_.Exception.Message)" -ForegroundColor Red
|
||
Write-Host " Skipping this file and continuing..." -ForegroundColor Yellow
|
||
continue
|
||
}
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Host "📊 CSV Processing Summary:" -ForegroundColor Cyan
|
||
Write-Host "=========================="
|
||
Write-Host "• Files Processed: $($files.Count)" -ForegroundColor White
|
||
Write-Host "• Total Dependencies: $totalEntries" -ForegroundColor White
|
||
Write-Host ""
|
||
|
||
# Analyze package types
|
||
$packageTypes = $CSVItems | Group-Object type | Sort-Object Count -Descending
|
||
Write-Host "🔍 Package Type Distribution:" -ForegroundColor Cyan
|
||
foreach ($type in $packageTypes) {
|
||
Write-Host " $($type.Name): $($type.Count) packages" -ForegroundColor White
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Host "========================================================================================================================================================================"
|
||
Write-Host "🔧 Phase 2: NuGet Package Metadata Enrichment" -ForegroundColor Cyan
|
||
Write-Host "========================================================================================================================================================================"
|
||
|
||
# Identify unique NuGet packages that need enrichment
|
||
$nugetPackages = $CSVItems | Where-Object { $_.type -eq "nuget" -and $_.latestVersion -eq "" } |
|
||
Sort-Object -Property name, version -Unique
|
||
$nugetCount = $nugetPackages.Count
|
||
|
||
if ($nugetCount -eq 0) {
|
||
Write-Host "ℹ️ No NuGet packages require enrichment (all already have metadata)" -ForegroundColor Blue
|
||
} else {
|
||
Write-Host "📦 Found $nugetCount unique NuGet packages requiring metadata enrichment" -ForegroundColor White
|
||
Write-Host "⏱️ This process may take several minutes depending on network connectivity..." -ForegroundColor Yellow
|
||
Write-Host ""
|
||
|
||
$counter = 0
|
||
foreach ($package in $nugetPackages) {
|
||
$counter++
|
||
$progressPercent = [math]::Round(($counter / $nugetCount) * 100, 1)
|
||
|
||
Write-Host "🔄 [$counter/$nugetCount - $progressPercent%] Processing NuGet package: $($package.name) v$($package.version)" -ForegroundColor Cyan
|
||
|
||
PropagatePackage -allItems $CSVItems -name $package.name -type $package.type -version $package.version -progress ("{0:D4}/{1:D4}" -f $counter, $nugetCount)
|
||
}
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Host "========================================================================================================================================================================"
|
||
Write-Host "💾 Phase 3: SBOM Export and Analysis" -ForegroundColor Cyan
|
||
Write-Host "========================================================================================================================================================================"
|
||
|
||
Write-Host "📄 Exporting enhanced SBOM to CSV..." -ForegroundColor White
|
||
|
||
try {
|
||
$CSVItems | Export-Csv -Path $fileName -NoTypeInformation -ErrorAction Stop
|
||
|
||
if (Test-Path $fileName) {
|
||
$fileSize = [math]::Round((Get-Item $fileName).Length / 1KB, 2)
|
||
Write-Host "✅ SBOM export completed successfully!" -ForegroundColor Green
|
||
Write-Host " 📁 File: $fileName" -ForegroundColor Gray
|
||
Write-Host " 📏 Size: $fileSize KB" -ForegroundColor Gray
|
||
Write-Host " 📊 Records: $($CSVItems.Count)" -ForegroundColor Gray
|
||
}
|
||
}
|
||
catch {
|
||
Write-Host "❌ Failed to export SBOM: $($_.Exception.Message)" -ForegroundColor Red
|
||
exit 1
|
||
}
|
||
|
||
Write-Host ""
|
||
Write-Host "📈 SBOM Analysis Summary:" -ForegroundColor Cyan
|
||
Write-Host "========================"
|
||
|
||
# Vulnerability summary
|
||
$criticalCount = ($CSVItems | Where-Object { [int]$_.issuesCritical -gt 0 }).Count
|
||
$highCount = ($CSVItems | Where-Object { [int]$_.issuesHigh -gt 0 }).Count
|
||
$mediumCount = ($CSVItems | Where-Object { [int]$_.issuesMedium -gt 0 }).Count
|
||
$lowCount = ($CSVItems | Where-Object { [int]$_.issuesLow -gt 0 }).Count
|
||
|
||
Write-Host "🚨 Vulnerability Summary:" -ForegroundColor Yellow
|
||
Write-Host " Critical Issues: $criticalCount packages" -ForegroundColor $(if($criticalCount -gt 0) {'Red'} else {'Green'})
|
||
Write-Host " High Issues: $highCount packages" -ForegroundColor $(if($highCount -gt 0) {'Red'} else {'Green'})
|
||
Write-Host " Medium Issues: $mediumCount packages" -ForegroundColor $(if($mediumCount -gt 0) {'Yellow'} else {'Green'})
|
||
Write-Host " Low Issues: $lowCount packages" -ForegroundColor $(if($lowCount -gt 0) {'Yellow'} else {'Green'})
|
||
|
||
# Deprecation summary
|
||
$deprecatedCount = ($CSVItems | Where-Object { $_.isDeprecated -eq $true -or $_.isDeprecated -eq "True" }).Count
|
||
Write-Host ""
|
||
Write-Host "⚠️ Deprecation Summary:" -ForegroundColor Yellow
|
||
Write-Host " Deprecated Packages: $deprecatedCount" -ForegroundColor $(if($deprecatedCount -gt 0) {'Yellow'} else {'Green'})
|
||
|
||
# License summary
|
||
$unlicensedCount = ($CSVItems | Where-Object { $_.licenses -eq "" -or $_.licenses -eq $null }).Count
|
||
Write-Host ""
|
||
Write-Host "📋 License Summary:" -ForegroundColor Cyan
|
||
Write-Host " Packages with License Info: $($CSVItems.Count - $unlicensedCount)" -ForegroundColor Green
|
||
Write-Host " Packages without License Info: $unlicensedCount" -ForegroundColor $(if($unlicensedCount -gt 0) {'Yellow'} else {'Green'})
|
||
|
||
Write-Host ""
|
||
Write-Host "========================================================================================================================================================================"
|
||
Write-Host "✅ Software Bill of Materials generation completed successfully!" -ForegroundColor Green
|
||
Write-Host "========================================================================================================================================================================"
|
||
|
||
|
||
|