Octopus.Script exported 2025-11-02 by mcasperson belongs to ‘SBOM’ category.
This step extracts the Docker image, finds any bom.json files, and scans them for vulnerabilities using Trivy.
Parameters
When steps based on the template are included in a project’s deployment process, the parameters below can be set.
Application package containing the SBOM to scan
Sbom.Package =
Script body
Steps based on this template will execute the following PowerShell script.
Write-Host "Pulling Trivy Docker Image"
Write-Host "##octopus[stdout-verbose]"
docker pull ghcr.io/aquasecurity/trivy
Write-Host "##octopus[stdout-default]"
$SUCCESS = 0
Write-Host "##octopus[stdout-verbose]"
Get-ChildItem -Path "." | Out-String
Write-Host "##octopus[stdout-default]"
# Find all bom.json files
$bomFiles = Get-ChildItem -Path "." -Filter "bom.json" -Recurse -File
if ($bomFiles.Count -eq 0) {
Write-Host "No bom.json files found in the current directory."
exit 0
}
foreach ($file in $bomFiles) {
Write-Host "Scanning $($file.FullName)"
# Delete any existing report file
if (Test-Path "$($file.FullName)/depscan-bom.json") {
Remove-Item "$($file.FullName)/depscan-bom.json" -Force
}
# Generate the report, capturing the output
try {
$OUTPUT = docker run --rm -v "$($file.FullName):/input/$($file.Name)" ghcr.io/aquasecurity/trivy sbom -q "/input/$($file.Name)"
$exitCode = $LASTEXITCODE
}
catch {
$OUTPUT = $_.Exception.Message
$exitCode = 1
}
# Run again to generate the JSON output in the same directory as the bom.json file
docker run --rm -v "$($file.DirectoryName):/output" -v "$($file.FullName):/input/$($file.Name)" ghcr.io/aquasecurity/trivy sbom -q -f json -o /output/depscan-bom.json "/input/$($file.Name)"
# Parse JSON output to count vulnerabilities
$jsonContent = Get-Content -Path "$($file.DirectoryName)/depscan-bom.json" | ConvertFrom-Json
$CRITICAL = ($jsonContent.Results | ForEach-Object { $_.Vulnerabilities } | Where-Object { $_.Severity -eq "CRITICAL" }).Count
$HIGH = ($jsonContent.Results | ForEach-Object { $_.Vulnerabilities } | Where-Object { $_.Severity -eq "HIGH" }).Count
if ("#{Octopus.Environment.Name}" -eq "Security") {
Write-Highlight "🟥 $CRITICAL critical vulnerabilities"
Write-Highlight "🟧 $HIGH high vulnerabilities"
}
# Set success to 1 if exit code is not zero
if ($exitCode -ne 0) {
$SUCCESS = 1
}
# Print the output
$OUTPUT | ForEach-Object {
if ($_.Length -gt 0) {
Write-Host $_
}
}
}
# Find all depscan-bom.json files recursively
$depscanFiles = Get-ChildItem -Path "." -Filter "depscan-bom.json" -Recurse -File
if ($depscanFiles.Count -gt 0) {
$zipPath = "$PWD/depscan-bom.zip"
# Remove existing zip if present
if (Test-Path $zipPath) {
Remove-Item $zipPath -Force
}
# Create a temporary directory structure and copy files with relative paths
$tempDir = "$PWD/temp_zip"
if (Test-Path $tempDir) {
Remove-Item $tempDir -Recurse -Force
}
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
foreach ($file in $depscanFiles) {
$relativePath = $file.FullName.Substring($PWD.Path.Length + 1)
$targetPath = Join-Path $tempDir $relativePath
$targetDir = Split-Path $targetPath -Parent
Write-Host "Adding $relativePath to zip"
if (-not (Test-Path $targetDir)) {
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
}
Copy-Item $file.FullName -Destination $targetPath
}
# Compress with relative paths
Compress-Archive -Path "$tempDir/*" -DestinationPath $zipPath
# Cleanup temp directory
Remove-Item $tempDir -Recurse -Force
# Octopus Deploy artifact
New-OctopusArtifact $zipPath
} else {
Write-Host "No depscan-bom.json files found to zip."
}
# Cleanup
for ($i = 1; $i -le 10; $i++) {
try {
if (Test-Path "bundle") {
Set-ItemProperty -Path "bundle" -Name IsReadOnly -Value $false -Recurse -ErrorAction SilentlyContinue
Remove-Item -Path "bundle" -Recurse -Force -ErrorAction Stop
break
}
}
catch {
Write-Host "Attempting to clean up files"
Start-Sleep -Seconds 1
}
}
# Set Octopus variable
Set-OctopusVariable -Name "VerificationResult" -Value $SUCCESS
exit 0
Provided under the Apache License version 2.0.
To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.
{
"Id": "a38bfff8-8dde-4dd6-9fd0-c90bb4709d5a",
"Name": "Scan for Vulnerabilities",
"Description": "This step extracts the Docker image, finds any bom.json files, and scans them for vulnerabilities using Trivy.",
"Version": 3,
"ExportedAt": "2025-11-02T21:42:33.662Z",
"ActionType": "Octopus.Script",
"Author": "mcasperson",
"Packages": [
{
"Id": "9b495093-0020-4df8-bc44-2fd65ab13943",
"Name": "application",
"PackageId": "",
"FeedId": null,
"AcquisitionLocation": "Server",
"Properties": {
"Extract": "True",
"SelectionMode": "deferred",
"PackageParameterName": "Sbom.Package"
}
}
],
"Parameters": [
{
"Id": "9cea163a-a048-4ff5-9405-56fdcb9015c7",
"Name": "Sbom.Package",
"Label": "Application package containing the SBOM to scan",
"HelpText": null,
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "Package"
}
}
],
"Properties": {
"OctopusUseBundledTooling": "False",
"Octopus.Action.Script.ScriptSource": "Inline",
"Octopus.Action.Script.Syntax": "PowerShell",
"Octopus.Action.Script.ScriptBody": "Write-Host \"Pulling Trivy Docker Image\"\nWrite-Host \"##octopus[stdout-verbose]\"\ndocker pull ghcr.io/aquasecurity/trivy\nWrite-Host \"##octopus[stdout-default]\"\n\n$SUCCESS = 0\n\nWrite-Host \"##octopus[stdout-verbose]\"\nGet-ChildItem -Path \".\" | Out-String\nWrite-Host \"##octopus[stdout-default]\"\n\n# Find all bom.json files\n$bomFiles = Get-ChildItem -Path \".\" -Filter \"bom.json\" -Recurse -File\n\nif ($bomFiles.Count -eq 0) {\n Write-Host \"No bom.json files found in the current directory.\"\n exit 0\n}\n\nforeach ($file in $bomFiles) {\n Write-Host \"Scanning $($file.FullName)\"\n\n # Delete any existing report file\n if (Test-Path \"$($file.FullName)/depscan-bom.json\") {\n Remove-Item \"$($file.FullName)/depscan-bom.json\" -Force\n }\n\n # Generate the report, capturing the output\n try {\n $OUTPUT = docker run --rm -v \"$($file.FullName):/input/$($file.Name)\" ghcr.io/aquasecurity/trivy sbom -q \"/input/$($file.Name)\"\n $exitCode = $LASTEXITCODE\n }\n catch {\n $OUTPUT = $_.Exception.Message\n $exitCode = 1\n }\n\n # Run again to generate the JSON output in the same directory as the bom.json file\n docker run --rm -v \"$($file.DirectoryName):/output\" -v \"$($file.FullName):/input/$($file.Name)\" ghcr.io/aquasecurity/trivy sbom -q -f json -o /output/depscan-bom.json \"/input/$($file.Name)\"\n\n # Parse JSON output to count vulnerabilities\n $jsonContent = Get-Content -Path \"$($file.DirectoryName)/depscan-bom.json\" | ConvertFrom-Json\n $CRITICAL = ($jsonContent.Results | ForEach-Object { $_.Vulnerabilities } | Where-Object { $_.Severity -eq \"CRITICAL\" }).Count\n $HIGH = ($jsonContent.Results | ForEach-Object { $_.Vulnerabilities } | Where-Object { $_.Severity -eq \"HIGH\" }).Count\n\n if (\"#{Octopus.Environment.Name}\" -eq \"Security\") {\n Write-Highlight \"🟥 $CRITICAL critical vulnerabilities\"\n Write-Highlight \"🟧 $HIGH high vulnerabilities\"\n }\n\n # Set success to 1 if exit code is not zero\n if ($exitCode -ne 0) {\n $SUCCESS = 1\n }\n\n # Print the output\n $OUTPUT | ForEach-Object {\n if ($_.Length -gt 0) {\n Write-Host $_\n }\n }\n}\n\n# Find all depscan-bom.json files recursively\n$depscanFiles = Get-ChildItem -Path \".\" -Filter \"depscan-bom.json\" -Recurse -File\n\nif ($depscanFiles.Count -gt 0) {\n $zipPath = \"$PWD/depscan-bom.zip\"\n\n # Remove existing zip if present\n if (Test-Path $zipPath) {\n Remove-Item $zipPath -Force\n }\n\n # Create a temporary directory structure and copy files with relative paths\n $tempDir = \"$PWD/temp_zip\"\n\n if (Test-Path $tempDir) {\n Remove-Item $tempDir -Recurse -Force\n }\n\n New-Item -ItemType Directory -Path $tempDir -Force | Out-Null\n\n foreach ($file in $depscanFiles) {\n $relativePath = $file.FullName.Substring($PWD.Path.Length + 1)\n $targetPath = Join-Path $tempDir $relativePath\n $targetDir = Split-Path $targetPath -Parent\n\n Write-Host \"Adding $relativePath to zip\"\n\n if (-not (Test-Path $targetDir)) {\n New-Item -ItemType Directory -Path $targetDir -Force | Out-Null\n }\n\n Copy-Item $file.FullName -Destination $targetPath\n }\n\n # Compress with relative paths\n Compress-Archive -Path \"$tempDir/*\" -DestinationPath $zipPath\n\n # Cleanup temp directory\n Remove-Item $tempDir -Recurse -Force\n\n # Octopus Deploy artifact\n New-OctopusArtifact $zipPath\n\n} else {\n Write-Host \"No depscan-bom.json files found to zip.\"\n}\n\n# Cleanup\nfor ($i = 1; $i -le 10; $i++) {\n try {\n if (Test-Path \"bundle\") {\n Set-ItemProperty -Path \"bundle\" -Name IsReadOnly -Value $false -Recurse -ErrorAction SilentlyContinue\n Remove-Item -Path \"bundle\" -Recurse -Force -ErrorAction Stop\n break\n }\n }\n catch {\n Write-Host \"Attempting to clean up files\"\n Start-Sleep -Seconds 1\n }\n}\n\n# Set Octopus variable\nSet-OctopusVariable -Name \"VerificationResult\" -Value $SUCCESS\n\nexit 0"
},
"Category": "SBOM",
"HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/a381802920158308/step-templates/sbom-scan.json",
"Website": "/step-templates/a38bfff8-8dde-4dd6-9fd0-c90bb4709d5a",
"Logo": "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",
"$Meta": {
"Type": "ActionTemplate"
}
}
Page updated on Sunday, November 2, 2025