tasks/analysis.tasks.ps1
$covenantVersion = "0.19.0" $CovenantIncludeSpdxReport = $true $CovenantIncludeCycloneDxReport = $true $CovenantMetadata = @{ git_repo = $(if (Get-Command "gh" -ErrorAction SilentlyContinue) { try { $ghRepo = & gh repo view --json nameWithOwner $ghRepo | ConvertFrom-Json | Select-Object -expandproperty nameWithOwner } catch { "" } }) git_branch = $(if (Get-Command "git" -ErrorAction SilentlyContinue) {git branch --show-current }) git_sha = $(if (Get-Command "git" -ErrorAction SilentlyContinue) { & git rev-parse HEAD }) } # Defaults for publishing-related variables $PublishCovenantOutputToStorage = $false $AnalysisOutputStorageAccountName = "" $AnalysisOutputContainerName = "" $AnalysisOutputBlobPath = "" task InstallCovenantTool { Install-DotNetTool -Name covenant -Version $covenantVersion } # Synopsis: Setup SBOM metadata for Covenant command-line task PrepareCovenantMetadata { $script:covenantMetadataArgs = @() foreach ($key in $CovenantMetadata.Keys) { # NOTE: No space after the '-m' switch otherwise the metadata key has a leading space in the report $script:covenantMetadataArgs += "-m$key=$($CovenantMetadata[$key])" } } task RunCovenantTool -If { $SolutionToBuild } Version, InstallCovenantTool, PrepareCovenantMetadata,{ $baseOutputName = [IO.Path]::GetFileNameWithoutExtension($SolutionToBuild) # Ensure we have a fully-qualified path, as this will be needed when uploading on build server $script:covenantJsonOutputFile = Join-Path $here ("/{0}.sbom.json" -f $baseOutputName) $script:covenantSpdxOutputFile = Join-Path $here ("/{0}.sbom.spdx.json" -f $baseOutputName) $script:covenantCycloneDxOutputFile = Join-Path $here ("/{0}.sbom.cyclonedx.xml" -f $baseOutputName) $script:covenantHtmlReportFile = Join-Path $here ("/{0}.sbom.html" -f $baseOutputName) Write-Verbose "covenantHtmlReportFile: $covenantHtmlReportFile" # Generate SBOM exec { & dotnet-covenant ` generate ` $SolutionToBuild ` -v $script:GitVersion.SemVer ` --output $covenantJsonOutputFile ` $covenantMetadataArgs } # Generate HTML report exec { & dotnet-covenant ` report ` $covenantJsonOutputFile ` --output $covenantHtmlReportFile } } # Synopsis: Generate SPDX-formatted report task GenerateCovenantSpdxReport -If { $SolutionToBuild -and $CovenantIncludeSpdxReport } RunCovenantTool,{ exec { & dotnet-covenant ` convert ` spdx ` $covenantJsonOutputFile ` --output $covenantSpdxOutputFile } Write-Verbose "covenantSpdxOutputFile: $covenantSpdxOutputFile" } # Synopsis: Generate CycloneDX-formatted report task GenerateCovenantCycloneDxReport -If { $SolutionToBuild -and $CovenantIncludeCycloneDxReport } RunCovenantTool,{ exec { & dotnet-covenant ` convert ` cyclonedx ` $covenantJsonOutputFile ` --output $covenantCycloneDxOutputFile } Write-Verbose "covenantCycloneDxOutputFile: $covenantCycloneDxOutputFile" } task PublishCovenantBuildArtefacts -If { $IsAzureDevops } { Write-Host "##vso[task.setvariable variable=SbomHtmlReportPath;isoutput=true]$covenantHtmlReportFile" Write-Host "##vso[artifact.upload artifactname=SBOM]$covenantHtmlReportFile" Write-Host "##vso[artifact.upload artifactname=SBOM]$covenantJsonOutputFile" if ($CovenantIncludeSpdxReport) { Write-Host "##vso[artifact.upload artifactname=SBOM]$covenantSpdxOutputFile" } if ($CovenantIncludeCycloneDxReport) { Write-Host "##vso[artifact.upload artifactname=SBOM]$covenantCycloneDxOutputFile" } } task PublishCovenantOutputToStorage -If { $SolutionToBuild -and $PublishCovenantOutputToStorage } { if ( (Test-Path $covenantJsonOutputFile) -and ` $AnalysisOutputStorageAccountName -and ` $AnalysisOutputContainerName -and ` $AnalysisOutputBlobPath) { $covenantJsonOutputFilename = (Split-Path -Leaf $covenantJsonOutputFile) $filename = "{0}-{1}.json" -f [IO.Path]::GetFileNameWithoutExtension($covenantJsonOutputFilename), ([DateTime]::Now).ToString('yyyyMMddHHmmssfff') Write-Information @" Publishing storage account: Source File: $covenantJsonOutputFile Account: $AnalysisOutputStorageAccountName Blob Path: "$AnalysisOutputContainerName/$AnalysisOutputBlobPath/$filename" "@ $uri = "https://{0}.blob.core.windows.net/{1}/{2}/{3}" -f $AnalysisOutputStorageAccountName, $AnalysisOutputContainerName, $AnalysisOutputBlobPath, $filename $authUri = & az storage blob generate-sas ` --auth-mode login ` --as-user ` --https-only ` --account-name $AnalysisOutputStorageAccountName ` --blob-url $uri ` --permissions c ` --start (Get-Date).ToUniversalTime().ToString("yyyy-M-d'T'H:m'Z'") ` --expiry (Get-Date).AddMinutes(10).ToUniversalTime().ToString("yyyy-M-d'T'H:m'Z'") ` --full-uri ` -o tsv if ($LASTEXITCODE -ne 0) { Write-Warning "Unable to generate a storage SAS token for publishing SBOM - have you run 'az login'?" } else { $headers = @{ "x-ms-date" = [System.DateTime]::UtcNow.ToString("R") "x-ms-blob-type" = "BlockBlob" } Invoke-RestMethod -Headers $headers ` -Uri $authUri ` -Method PUT ` -InFile $covenantJsonOutputFile ` -Verbose:$false | Out-Null Write-Information "Covenant JSON output published to storage account" } } else { Write-Information "Publishing of Covenant output skipped, due to absent configuration" } } task RunSBOMAnalysis -If { $SolutionToBuild -and $env:SBOM_ANALYSIS_RELEASE_READER_PAT } EnsureGitHubCli,RunCovenantTool,{ if (!(Get-Module Az.Storage -ListAvailable)){ Install-Module Az.Storage -Scope CurrentUser -Repository PSGallery -Force -Verbose } $AnalysisOutputContainerName = "data" $AnalysisOutputStorageAccountName = "endsynapsedatalake" # 1. Download JSON ruleset $uri = "https://{0}.blob.core.windows.net/{1}/{2}/{3}" -f $AnalysisOutputStorageAccountName, $AnalysisOutputContainerName, "openchain/license_rules", "license_rule_set.json" $isAuthenticated = $false try{ $authUri = exec {& az storage blob generate-sas ` --auth-mode login ` --as-user ` --https-only ` --account-name $AnalysisOutputStorageAccountName ` --blob-url $uri ` --permissions re ` --start (Get-Date).ToUniversalTime().ToString("yyyy-M-d'T'H:m'Z'") ` --expiry (Get-Date).AddMinutes(10).ToUniversalTime().ToString("yyyy-M-d'T'H:m'Z'") ` --full-uri ` -o tsv} $isAuthenticated = $true } catch{ Write-Warning "Skipping SBOM Analysis, unable to access the license rule set. Ensure you are logged into the Azure CLI" } if($isAuthenticated){ Write-Host $authUri $analysisFilesLocation = '.analysis' if(!(Test-Path $analysisFilesLocation)){ New-Item -ItemType Directory $analysisFilesLocation | Out-Null } Get-AzStorageBlobContent -Destination "$($analysisFilesLocation)/" -absoluteuri $authUri -Force | fl # Switch to a PAT that gives read access to the repo hosting the analysis tool $savedGhToken = $env:GH_TOKEN $env:GH_TOKEN = $env:SBOM_ANALYSIS_RELEASE_READER_PAT try { # Find latest version released on GitHub $latestVersion = exec { gh release list -R endjin/endjin-sbom-analyser --limit 1 } | ConvertFrom-Csv -Header title,type,"tag name",published -Delimiter `t | Select-Object -ExpandProperty "tag name" if (!$latestVersion) { throw "Unable to determine the latest version of the Python tool" } Write-Host $latestVersion $DownloadFileName = "sbom_analyser-$($latestVersion)-py3-none-any.whl" if(!(Test-Path (Join-Path $analysisFilesLocation $DownloadFileName))){ Write-Host "Downloading latest release of SBOM Analyser, version" $latestVersion exec { & gh release download -R "endjin/endjin-sbom-analyser" -p $DownloadFileName -D $analysisFilesLocation} } } finally { $env:GH_TOKEN = $savedGhToken } exec { pip install poetry pip install (Join-Path $analysisFilesLocation $DownloadFileName) } $sbomPath = $covenantJsonOutputFile Write-Host $sbomPath $jsonPath = Get-ChildItem -path "$($analysisFilesLocation)/openchain/license_rules/*.json" Write-Host $jsonPath Push-Location $analysisFilesLocation try{ exec{ generate_sbom_score $sbomPath $jsonPath } $summarisedContent = Get-Content 'sbom_analysis_summarised_scores.csv' | ConvertFrom-Csv if ($summarisedContent.Unknown -gt 0){ Write-Warning (Write-SBOMComponents -fileName 'sbom_analysis_unknown_components.csv' -sum $summarisedContent.Unknown -type 'unknown') } if ($summarisedContent.Rejected -gt 0){ throw (Write-SBOMComponents -fileName 'sbom_analysis_rejected_components.csv' -sum $summarisedContent.Rejected -type 'rejected') } } finally{ Pop-Location } } } task RunCovenant RunCovenantTool, GenerateCovenantSpdxReport, GenerateCovenantCycloneDxReport, PublishCovenantBuildArtefacts, PublishCovenantOutputToStorage, RunSBOMAnalysis |