Private/Get-BsiCatalogData.ps1
|
#Requires -Version 5.1 <# .SYNOPSIS BSI OSCAL catalog management — download, cache, parse. .DESCRIPTION Handles the BSI Grundschutz++ OSCAL catalog lifecycle: downloading from GitHub, caching locally, extracting controls. #> function Sync-BsiCatalog { <# .SYNOPSIS Downloads the BSI OSCAL catalog from GitHub. .PARAMETER OutPath Path to write the catalog JSON. .PARAMETER MappingPath Path to mapping file containing catalogUrl. #> param( [Parameter(Mandatory)][string]$OutPath, [Parameter(Mandatory)][string]$MappingPath ) if (-not (Test-Path -LiteralPath $MappingPath)) { throw "Mapping file not found: $MappingPath" } $mapping = Get-Content -Raw -Path $MappingPath | ConvertFrom-Json $url = $mapping.catalogUrl if (-not $url) { throw "catalogUrl is not defined in $MappingPath" } $urls = @($url) if ($mapping.catalogFallbackUrls) { $urls += @($mapping.catalogFallbackUrls) } $downloaded = $false foreach ($u in $urls) { Write-Verbose "[Catalog] Trying URL: $u" try { $cacheDir = Split-Path -Parent $OutPath if (-not (Test-Path -LiteralPath $cacheDir)) { New-Item -ItemType Directory -Path $cacheDir -Force | Out-Null } $wc = New-Object System.Net.WebClient $wc.Encoding = [System.Text.Encoding]::UTF8 $catalogJson = $wc.DownloadString($u) $wc.Dispose() if (-not ($catalogJson -match '^\s*[\{\[]')) { throw "Downloaded content does not look like JSON." } $null = $catalogJson | ConvertFrom-Json [System.IO.File]::WriteAllText($OutPath, $catalogJson, [System.Text.UTF8Encoding]::new($false)) $downloaded = $true Write-Verbose "[Catalog] Cached to $OutPath" break } catch { Write-Warning "Failed to download catalog from $u : $_" } } if (-not $downloaded) { throw "Failed to download BSI catalog from any configured URL." } $mapping.lastSynced = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ") $mapping | ConvertTo-Json -Depth 10 | Set-Content -Path $MappingPath -Encoding UTF8 } function Get-BsiCatalog { <# .SYNOPSIS Loads the cached BSI OSCAL catalog. .PARAMETER Path Path to catalog JSON. .OUTPUTS Parsed catalog object or $null. #> param([string]$Path) if (-not (Test-Path -LiteralPath $Path)) { return $null } return Get-Content -Raw -Path $Path | ConvertFrom-Json } function Get-BsiCatalogControls { <# .SYNOPSIS Extracts all controls from an OSCAL catalog recursively. .PARAMETER Catalog Parsed OSCAL catalog object. .OUTPUTS Array of control objects. #> param($Catalog) $controls = [System.Collections.ArrayList]::new() if (-not $Catalog) { return $controls } if ($Catalog.catalog) { $Catalog = $Catalog.catalog } if ($Catalog.controls) { foreach ($c in $Catalog.controls) { $null = $controls.Add($c) } } function Add-ControlsRecursive { param($Node) if ($Node.controls) { foreach ($c in $Node.controls) { $null = $controls.Add($c) } } if ($Node.groups) { foreach ($group in $Node.groups) { Add-ControlsRecursive -Node $group } } } if ($Catalog.groups) { foreach ($group in $Catalog.groups) { Add-ControlsRecursive -Node $group } } return $controls } function Test-BsiCatalogExists { param([string]$Path) if (-not (Test-Path -LiteralPath $Path)) { return $false } try { $json = Get-Content -Raw -Path $Path | ConvertFrom-Json return $null -ne $json } catch { return $false } } |