Public/Test-CCSecurityConfig.ps1
|
function Test-CCSecurityConfig { <# .SYNOPSIS Validates GitHub security configuration (dependabot, secret scanning, alerts). .PARAMETER Repository GitHub repository in format "owner/repo". Auto-detected if not specified. .PARAMETER Token GitHub token. Falls back to GITHUB_TOKEN, GH_TOKEN, or gh CLI. .PARAMETER Standard Standard tier: core, active, minimal. Default: core. .PARAMETER Config Path to per-repo config file for overrides. .OUTPUTS [PSCustomObject[]] Array of check results. .EXAMPLE Test-CCSecurityConfig -Repository 'The-Code-Kitchen/LeadForge' #> [CmdletBinding()] [OutputType([PSCustomObject[]])] param( [Parameter()] [string]$Repository, [Parameter()] [string]$Token, [Parameter()] [ValidateSet('core', 'active', 'minimal')] [string]$Standard = 'core', [Parameter()] [string]$Config ) $results = [System.Collections.ArrayList]::new() $token = Resolve-CCToken -Token $Token if (-not $token) { throw "No GitHub token available. Set GITHUB_TOKEN or run 'gh auth login'." } $repo = Resolve-CCRepository -Repository $Repository if (-not $repo) { throw "Cannot determine repository. Specify -Repository 'owner/repo'." } $stdConfig = (Get-CCStandardConfig -Standard $Standard -ConfigPath $Config).security # GH-SEC-001: Vulnerability alerts (Dependabot security updates) if ($stdConfig.vulnerability_alerts) { $alertsEnabled = $true try { Invoke-CCGitHubApi -Endpoint "repos/$repo/vulnerability-alerts" -Token $token } catch { if ($_.Exception.Response.StatusCode.value__ -eq 404) { $alertsEnabled = $false } } if ($alertsEnabled) { [void]$results.Add((New-CCCheckResult -CheckId 'GH-SEC-005' -Category 'Security' -Item 'Vulnerability Alerts' ` -Status 'Pass' -Severity 'Info' -Message 'Vulnerability alerts enabled')) } else { [void]$results.Add((New-CCCheckResult -CheckId 'GH-SEC-005' -Category 'Security' -Item 'Vulnerability Alerts' ` -Status 'Fail' -Severity 'Error' ` -Message 'Vulnerability alerts not enabled' ` -FixAvailable $true -FixId 'EnableVulnerabilityAlerts' -Current $false -Expected $true)) } } # GH-SEC-001: Dependabot security updates if ($stdConfig.dependabot_security) { # Check if automated security fixes are enabled via the repo endpoint $repoInfo = Invoke-CCGitHubApi -Endpoint "repos/$repo" -Token $token # Note: This is inferred from the security_and_analysis field if available $secAnalysis = $repoInfo.security_and_analysis if ($secAnalysis -and $secAnalysis.dependabot_security_updates -and $secAnalysis.dependabot_security_updates.status -eq 'enabled') { [void]$results.Add((New-CCCheckResult -CheckId 'GH-SEC-001' -Category 'Security' -Item 'Dependabot Security' ` -Status 'Pass' -Severity 'Info' -Message 'Dependabot security updates enabled')) } else { [void]$results.Add((New-CCCheckResult -CheckId 'GH-SEC-001' -Category 'Security' -Item 'Dependabot Security' ` -Status 'Fail' -Severity 'Error' ` -Message 'Dependabot security updates not enabled' ` -FixAvailable $true -FixId 'EnableDependabotSecurity' -Current $false -Expected $true)) } } # GH-SEC-002: Dependabot version updates (checks for dependabot.yml in repo) if ($stdConfig.dependabot_versions) { $dependabotFile = Invoke-CCGitHubApi -Endpoint "repos/$repo/contents/.github/dependabot.yml" -Token $token -AllowNotFound if (-not $dependabotFile) { $dependabotFile = Invoke-CCGitHubApi -Endpoint "repos/$repo/contents/.github/dependabot.yaml" -Token $token -AllowNotFound } if ($dependabotFile) { [void]$results.Add((New-CCCheckResult -CheckId 'GH-SEC-002' -Category 'Security' -Item 'Dependabot Versions' ` -Status 'Pass' -Severity 'Info' -Message 'Dependabot version updates configured (.github/dependabot.yml)')) } else { [void]$results.Add((New-CCCheckResult -CheckId 'GH-SEC-002' -Category 'Security' -Item 'Dependabot Versions' ` -Status 'Fail' -Severity 'Warning' ` -Message 'No .github/dependabot.yml — version updates not configured' ` -FixAvailable $true -FixId 'EnableDependabotVersions' -Current $false -Expected $true)) } } # GH-SEC-003: Secret scanning if ($stdConfig.secret_scanning) { $secAnalysis = if (-not $secAnalysis) { (Invoke-CCGitHubApi -Endpoint "repos/$repo" -Token $token).security_and_analysis } else { $secAnalysis } if ($secAnalysis -and $secAnalysis.secret_scanning -and $secAnalysis.secret_scanning.status -eq 'enabled') { [void]$results.Add((New-CCCheckResult -CheckId 'GH-SEC-003' -Category 'Security' -Item 'Secret Scanning' ` -Status 'Pass' -Severity 'Info' -Message 'Secret scanning enabled')) } else { [void]$results.Add((New-CCCheckResult -CheckId 'GH-SEC-003' -Category 'Security' -Item 'Secret Scanning' ` -Status 'Fail' -Severity 'Error' ` -Message 'Secret scanning not enabled' ` -FixAvailable $true -FixId 'EnableSecretScanning' -Current $false -Expected $true)) } } # GH-SEC-004: Push protection if ($stdConfig.push_protection) { $secAnalysis = if (-not $secAnalysis) { (Invoke-CCGitHubApi -Endpoint "repos/$repo" -Token $token).security_and_analysis } else { $secAnalysis } if ($secAnalysis -and $secAnalysis.secret_scanning_push_protection -and $secAnalysis.secret_scanning_push_protection.status -eq 'enabled') { [void]$results.Add((New-CCCheckResult -CheckId 'GH-SEC-004' -Category 'Security' -Item 'Push Protection' ` -Status 'Pass' -Severity 'Info' -Message 'Secret scanning push protection enabled')) } else { [void]$results.Add((New-CCCheckResult -CheckId 'GH-SEC-004' -Category 'Security' -Item 'Push Protection' ` -Status 'Fail' -Severity 'Warning' ` -Message 'Secret scanning push protection not enabled' ` -FixAvailable $true -FixId 'EnablePushProtection' -Current $false -Expected $true)) } } return $results.ToArray() } |