docs: project docs, scripts, deployment configs, and evidence
This commit is contained in:
196
scripts/ops/run-sca-evidence.ps1
Normal file
196
scripts/ops/run-sca-evidence.ps1
Normal file
@@ -0,0 +1,196 @@
|
||||
param(
|
||||
[string]$EvidenceDate = (Get-Date -Format 'yyyy-MM-dd')
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$projectRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
||||
$frontendRoot = Join-Path $projectRoot 'frontend\admin'
|
||||
$evidenceRoot = Join-Path $projectRoot "docs\evidence\ops\$EvidenceDate\sca"
|
||||
$goCacheRoot = Join-Path $projectRoot '.cache'
|
||||
$goBuildCache = Join-Path $goCacheRoot 'go-build'
|
||||
$goModCache = Join-Path $goCacheRoot 'gomod'
|
||||
$goPath = Join-Path $goCacheRoot 'gopath'
|
||||
|
||||
New-Item -ItemType Directory -Force $evidenceRoot, $goBuildCache, $goModCache, $goPath | Out-Null
|
||||
|
||||
function Invoke-CapturedCommand {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$FilePath,
|
||||
[string[]]$ArgumentList = @(),
|
||||
[Parameter(Mandatory = $true)][string]$WorkingDirectory,
|
||||
[Parameter(Mandatory = $true)][string]$StdOutPath,
|
||||
[Parameter(Mandatory = $true)][string]$StdErrPath
|
||||
)
|
||||
|
||||
Remove-Item $StdOutPath, $StdErrPath -Force -ErrorAction SilentlyContinue
|
||||
$process = Start-Process `
|
||||
-FilePath $FilePath `
|
||||
-ArgumentList $ArgumentList `
|
||||
-WorkingDirectory $WorkingDirectory `
|
||||
-PassThru `
|
||||
-WindowStyle Hidden `
|
||||
-RedirectStandardOutput $StdOutPath `
|
||||
-RedirectStandardError $StdErrPath `
|
||||
-Wait
|
||||
|
||||
return $process.ExitCode
|
||||
}
|
||||
|
||||
function Get-NpmAuditCounts {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$JsonPath
|
||||
)
|
||||
|
||||
if (-not (Test-Path $JsonPath)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$raw = Get-Content $JsonPath -Raw
|
||||
if ([string]::IsNullOrWhiteSpace($raw)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$payload = $raw | ConvertFrom-Json
|
||||
if (-not $payload.metadata -or -not $payload.metadata.vulnerabilities) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return $payload.metadata.vulnerabilities
|
||||
}
|
||||
|
||||
function Get-GovulnFindingCount {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$JsonPath
|
||||
)
|
||||
|
||||
if (-not (Test-Path $JsonPath)) {
|
||||
return [pscustomobject]@{
|
||||
Count = 0
|
||||
IDs = @()
|
||||
}
|
||||
}
|
||||
|
||||
$count = 0
|
||||
$ids = New-Object System.Collections.Generic.HashSet[string]
|
||||
$insideFinding = $false
|
||||
foreach ($line in Get-Content $JsonPath) {
|
||||
if ($line -match '"finding"') {
|
||||
$insideFinding = $true
|
||||
$count++
|
||||
continue
|
||||
}
|
||||
if ($insideFinding -and $line -match '"osv":\s*"([^"]+)"') {
|
||||
[void]$ids.Add($Matches[1])
|
||||
$insideFinding = $false
|
||||
}
|
||||
}
|
||||
|
||||
return [pscustomobject]@{
|
||||
Count = $count
|
||||
IDs = @($ids)
|
||||
}
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||
$prodAuditJson = Join-Path $evidenceRoot "npm-audit-prod-$timestamp.json"
|
||||
$prodAuditErr = Join-Path $evidenceRoot "npm-audit-prod-$timestamp.stderr.txt"
|
||||
$fullAuditJson = Join-Path $evidenceRoot "npm-audit-full-$timestamp.json"
|
||||
$fullAuditErr = Join-Path $evidenceRoot "npm-audit-full-$timestamp.stderr.txt"
|
||||
$govulnJson = Join-Path $evidenceRoot "govulncheck-$timestamp.jsonl"
|
||||
$govulnErr = Join-Path $evidenceRoot "govulncheck-$timestamp.stderr.txt"
|
||||
$summaryPath = Join-Path $evidenceRoot "SCA_SUMMARY_$timestamp.md"
|
||||
|
||||
$prodAuditExit = Invoke-CapturedCommand `
|
||||
-FilePath 'npm.cmd' `
|
||||
-ArgumentList @('audit', '--omit=dev', '--json', '--registry=https://registry.npmjs.org/') `
|
||||
-WorkingDirectory $frontendRoot `
|
||||
-StdOutPath $prodAuditJson `
|
||||
-StdErrPath $prodAuditErr
|
||||
|
||||
$fullAuditExit = Invoke-CapturedCommand `
|
||||
-FilePath 'npm.cmd' `
|
||||
-ArgumentList @('audit', '--json', '--registry=https://registry.npmjs.org/') `
|
||||
-WorkingDirectory $frontendRoot `
|
||||
-StdOutPath $fullAuditJson `
|
||||
-StdErrPath $fullAuditErr
|
||||
|
||||
Push-Location $projectRoot
|
||||
try {
|
||||
$env:GOCACHE = $goBuildCache
|
||||
$env:GOMODCACHE = $goModCache
|
||||
$env:GOPATH = $goPath
|
||||
$govulnExit = Invoke-CapturedCommand `
|
||||
-FilePath 'go' `
|
||||
-ArgumentList @('run', 'golang.org/x/vuln/cmd/govulncheck@latest', '-json', './...') `
|
||||
-WorkingDirectory $projectRoot `
|
||||
-StdOutPath $govulnJson `
|
||||
-StdErrPath $govulnErr
|
||||
} finally {
|
||||
Pop-Location
|
||||
Remove-Item Env:GOCACHE, Env:GOMODCACHE, Env:GOPATH -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
$prodCounts = Get-NpmAuditCounts -JsonPath $prodAuditJson
|
||||
$fullCounts = Get-NpmAuditCounts -JsonPath $fullAuditJson
|
||||
$govulnFindings = Get-GovulnFindingCount -JsonPath $govulnJson
|
||||
$prodFindingSummary = if ($prodCounts) {
|
||||
"info=$($prodCounts.info) low=$($prodCounts.low) moderate=$($prodCounts.moderate) high=$($prodCounts.high) critical=$($prodCounts.critical) total=$($prodCounts.total)"
|
||||
} else {
|
||||
'unavailable'
|
||||
}
|
||||
$fullFindingSummary = if ($fullCounts) {
|
||||
"info=$($fullCounts.info) low=$($fullCounts.low) moderate=$($fullCounts.moderate) high=$($fullCounts.high) critical=$($fullCounts.critical) total=$($fullCounts.total)"
|
||||
} else {
|
||||
'unavailable'
|
||||
}
|
||||
$govulnIDsSummary = if ($govulnFindings.IDs.Count -gt 0) {
|
||||
($govulnFindings.IDs | Sort-Object) -join ', '
|
||||
} else {
|
||||
'none'
|
||||
}
|
||||
$prodAuditJsonName = Split-Path $prodAuditJson -Leaf
|
||||
$prodAuditErrName = Split-Path $prodAuditErr -Leaf
|
||||
$fullAuditJsonName = Split-Path $fullAuditJson -Leaf
|
||||
$fullAuditErrName = Split-Path $fullAuditErr -Leaf
|
||||
$govulnJsonName = Split-Path $govulnJson -Leaf
|
||||
$govulnErrName = Split-Path $govulnErr -Leaf
|
||||
|
||||
$summaryLines = @(
|
||||
'# SCA Summary',
|
||||
'',
|
||||
"- Generated at: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss zzz')",
|
||||
"- Project root: $projectRoot",
|
||||
'',
|
||||
'## Commands',
|
||||
'',
|
||||
'- `cd frontend/admin && npm.cmd audit --omit=dev --json --registry=https://registry.npmjs.org/`',
|
||||
'- `cd frontend/admin && npm.cmd audit --json --registry=https://registry.npmjs.org/`',
|
||||
'- `go run golang.org/x/vuln/cmd/govulncheck@latest -json ./...`',
|
||||
'',
|
||||
'## Exit Codes',
|
||||
'',
|
||||
"- npm audit production: $prodAuditExit",
|
||||
"- npm audit full: $fullAuditExit",
|
||||
"- govulncheck: $govulnExit",
|
||||
'',
|
||||
'## Findings',
|
||||
'',
|
||||
"- npm audit production: $prodFindingSummary",
|
||||
"- npm audit full: $fullFindingSummary",
|
||||
"- govulncheck reachable findings: $($govulnFindings.Count)",
|
||||
"- govulncheck reachable IDs: $govulnIDsSummary",
|
||||
'',
|
||||
'## Evidence Files',
|
||||
'',
|
||||
"- $prodAuditJsonName",
|
||||
"- $prodAuditErrName",
|
||||
"- $fullAuditJsonName",
|
||||
"- $fullAuditErrName",
|
||||
"- $govulnJsonName",
|
||||
"- $govulnErrName",
|
||||
''
|
||||
)
|
||||
|
||||
Set-Content -Path $summaryPath -Value ($summaryLines -join [Environment]::NewLine) -Encoding UTF8
|
||||
Get-Content $summaryPath
|
||||
Reference in New Issue
Block a user