Public/Test-SignedCommit.ps1

function Test-SignedCommit {
    [CmdletBinding()]
    [OutputType([PSCustomObject[]])]
    param(
        [Parameter(Mandatory)]
        [ValidatePattern('^[a-zA-Z0-9._-]+$')]
        [string]$Owner,

        [Parameter(Mandatory)]
        [ValidatePattern('^[a-zA-Z0-9._-]+$')]
        [string]$Repo,

        [Parameter(Mandatory)]
        [string]$Token
    )

    $target = "$Owner/$Repo"
    $results = [System.Collections.Generic.List[PSCustomObject]]::new()

    $defaultBranch = 'main'
    try {
        $repoInfo = Invoke-GitHubApi -Endpoint "repos/$Owner/$Repo" -Token $Token
        if ($repoInfo.default_branch) {
            $defaultBranch = $repoInfo.default_branch
        }
    }
    catch {
        $results.Add((Format-FylgyrResult `
            -CheckName 'SignedCommit' `
            -Status 'Error' `
            -Severity 'Medium' `
            -Resource $target `
            -Detail "Failed to retrieve repository info: $($_.Exception.Message)" `
            -Remediation 'Verify the repository exists and the token has contents:read access.' `
            -Target $target))
        return $results.ToArray()
    }

    $resource = "$target (branch: $defaultBranch)"

    try {
        $signatures = Invoke-GitHubApi `
            -Endpoint "repos/$Owner/$Repo/branches/$defaultBranch/protection/required_signatures" `
            -Token $Token
    }
    catch {
        $msg = $_.Exception.Message

        if ($msg -match '404') {
            $results.Add((Format-FylgyrResult `
                -CheckName 'SignedCommit' `
                -Status 'Warning' `
                -Severity 'Medium' `
                -Resource $resource `
                -Detail "Branch '$defaultBranch' does not require signed commits (no branch protection or required_signatures is disabled). Signed commits make it harder for an attacker with a stolen credential to impersonate a maintainer, as in the xz-utils backdoor." `
                -Remediation "Enable 'Require signed commits' in Settings > Branches > Branch protection rules. As a lower-friction first step, adopt commit signoff (git commit -s) and document a GPG/SSH signing policy. See: https://docs.github.com/authentication/managing-commit-signature-verification" `
                -AttackMapping @('xz-utils-backdoor') `
                -Target $target))
            return $results.ToArray()
        }

        if ($msg -match '403') {
            $results.Add((Format-FylgyrResult `
                -CheckName 'SignedCommit' `
                -Status 'Error' `
                -Severity 'Medium' `
                -Resource $resource `
                -Detail 'Insufficient permissions to read required signatures setting.' `
                -Remediation 'Use a fine-grained token with Administration:read permission, or a classic token with repo scope.' `
                -Target $target))
            return $results.ToArray()
        }

        $results.Add((Format-FylgyrResult `
            -CheckName 'SignedCommit' `
            -Status 'Error' `
            -Severity 'Medium' `
            -Resource $resource `
            -Detail "Unexpected error reading required_signatures: $($_.Exception.Message)" `
            -Remediation 'Re-run with a valid token and verify network access to api.github.com.' `
            -Target $target))
        return $results.ToArray()
    }

    if ($signatures.PSObject.Properties['enabled'] -and $signatures.enabled -eq $true) {
        $results.Add((Format-FylgyrResult `
            -CheckName 'SignedCommit' `
            -Status 'Pass' `
            -Severity 'Info' `
            -Resource $resource `
            -Detail "Branch '$defaultBranch' requires signed commits." `
            -Remediation 'No action needed.' `
            -Target $target))
    }
    else {
        $results.Add((Format-FylgyrResult `
            -CheckName 'SignedCommit' `
            -Status 'Warning' `
            -Severity 'Medium' `
            -Resource $resource `
            -Detail "Branch '$defaultBranch' does not require signed commits. Only a small minority of projects enforce this today, so this is a recommendation rather than a hard failure, but it is a meaningful defense against maintainer-impersonation attacks such as xz-utils." `
            -Remediation "Enable 'Require signed commits' in Settings > Branches > Branch protection rules. Publish a GPG/SSH signing policy for maintainers. Commit signoff (git commit -s) is a lower-friction intermediate step." `
            -AttackMapping @('xz-utils-backdoor') `
            -Target $target))
    }

    $results.ToArray()
}