From 41ee697f542441ac552b21ed49b2a25b9a40405c Mon Sep 17 00:00:00 2001 From: Atirna <288419661+atirna@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:36:31 +0530 Subject: [PATCH] fix(bootstrap): fail closed on verified checkouts --- kali/scripts/bootstrap-reverse.sh | 10 +-- skills/scripts/bootstrap-reverse.ps1 | 19 +++-- skills/scripts/test-bootstrap-manifest.sh | 5 +- .../scripts/test-bootstrap-supply-chain.ps1 | 73 ++++++++++++++++++- 4 files changed, 95 insertions(+), 12 deletions(-) diff --git a/kali/scripts/bootstrap-reverse.sh b/kali/scripts/bootstrap-reverse.sh index 651f630..b9ff643 100644 --- a/kali/scripts/bootstrap-reverse.sh +++ b/kali/scripts/bootstrap-reverse.sh @@ -661,17 +661,17 @@ EOF # ─── 服务启动 ────────────────────────────────────────────────────────────────────── start_anything_analyzer() { - if test_tcp_port 23816 2>/dev/null; then - log_ok "anything-analyzer 已在运行 (port 23816)" - return 0 - fi - local repo_dir="$HOME/tools/anything-analyzer" local repo commit repo=$(manifest_field anything-analyzer repoUrl) commit=$(manifest_field anything-analyzer pinnedCommit) install_git_commit "$repo" "$commit" "$repo_dir" || return 1 + if test_tcp_port 23816 2>/dev/null; then + log_ok "anything-analyzer 已在运行 (port 23816)" + return 0 + fi + local pnpm_package pnpm_version current_pnpm_version='' pnpm_package=$(manifest_dependency pnpm package) || return 1 pnpm_version=$(manifest_dependency pnpm version) || return 1 diff --git a/skills/scripts/bootstrap-reverse.ps1 b/skills/scripts/bootstrap-reverse.ps1 index c764186..0c12862 100644 --- a/skills/scripts/bootstrap-reverse.ps1 +++ b/skills/scripts/bootstrap-reverse.ps1 @@ -752,10 +752,6 @@ function Start-AnythingAnalyzerService { $AuthToken = Ensure-AnythingAnalyzerMcpConfig -Port ([int]$Definition.servicePort) } - if (Test-ReverseTcpPort -Port ([int]$Definition.servicePort)) { - return - } - $repoDir = [string]$Definition.installDir $checkoutDefinition = [pscustomobject]@{ repo = [string]$Definition.repoUrl @@ -763,6 +759,10 @@ function Start-AnythingAnalyzerService { } Ensure-GitCloneInstall -Definition $checkoutDefinition -TargetPath $repoDir | Out-Null + if (Test-ReverseTcpPort -Port ([int]$Definition.servicePort)) { + return + } + Ensure-Pnpm $vsBuildToolsError = '' if (Test-ReverseIsWindows) { @@ -842,7 +842,7 @@ function Ensure-Capability { } $existingState = Get-ReverseCapabilityState -Name $Name - if ($existingState -and -not $definition.PSObject.Properties['mcpNames']) { + if ($existingState -and -not $definition.PSObject.Properties['mcpNames'] -and $definition.bootstrapKind -ne 'git-clone') { $toolSpec = $null try { $toolSpec = Resolve-ReverseToolSpec -Name $Name @@ -1053,6 +1053,12 @@ function Expand-CapabilityDependencies { return $ordered } +function Test-BootstrapResultsSucceeded { + param([Parameter(Mandatory = $true)][object[]]$Results) + + return (@($Results | Where-Object { $_.status -eq 'failed' }).Count -eq 0) +} + $expandedCapabilities = Expand-CapabilityDependencies -Names $Capability $results = @() @@ -1112,3 +1118,6 @@ if (-not $SkipRefresh) { } $results | ConvertTo-Json -Depth 5 +if (-not (Test-BootstrapResultsSucceeded -Results $results)) { + exit 1 +} diff --git a/skills/scripts/test-bootstrap-manifest.sh b/skills/scripts/test-bootstrap-manifest.sh index 7234141..080e43a 100644 --- a/skills/scripts/test-bootstrap-manifest.sh +++ b/skills/scripts/test-bootstrap-manifest.sh @@ -49,6 +49,7 @@ case "$name:${1:-}" in esac ;; nc:-z) + [[ "${STUB_NC_PREOCCUPIED:-0}" != 1 ]] || exit 0 count=0; [[ ! -f "$STUB_NC_STATE" ]] || count="$(cat "$STUB_NC_STATE")" printf '%s\n' "$((count + 1))" > "$STUB_NC_STATE" (( count > 0 )) && exit 0 || exit 1 @@ -93,7 +94,7 @@ run_kali() { rm -f "$SCRATCH/nc-count" env PATH="$STUB_BIN:/opt/homebrew/bin:/usr/bin:/bin" HOME="$SCRATCH/home" \ CALL_LOG="$CALL_LOG" STUB_NC_STATE="$SCRATCH/nc-count" STUB_PNPM_VERSION="${STUB_PNPM_VERSION:-}" \ - STUB_FAIL_FETCH="${STUB_FAIL_FETCH:-0}" bash "$KALI_BOOTSTRAP" "$@" + STUB_FAIL_FETCH="${STUB_FAIL_FETCH:-0}" STUB_NC_PREOCCUPIED="${STUB_NC_PREOCCUPIED:-0}" bash "$KALI_BOOTSTRAP" "$@" } expect_line() { grep -Fqx "$1" "$CALL_LOG" || { echo "missing argv: $1" >&2; cat "$CALL_LOG" >&2; return 1; }; } expect_fragment() { grep -Fq "$1" "$CALL_LOG" || { echo "missing argv fragment: $1" >&2; cat "$CALL_LOG" >&2; return 1; }; } @@ -215,6 +216,8 @@ if (( BASH_VERSINFO[0] >= 4 )); then [[ $(grep -c '|status|--porcelain|--untracked-files=all' "$CALL_LOG") -ge 2 ]] touch "$kali_dir/.stub-dirty" rejects_without_pnpm run_kali anything-analyzer --start-services --skip-refresh + STUB_NC_PREOCCUPIED=1 rejects_without_pnpm run_kali anything-analyzer --start-services --skip-refresh + expect_fragment '|status|--porcelain|--untracked-files=all' rm -rf "$kali_dir" : > "$CALL_LOG" diff --git a/skills/scripts/test-bootstrap-supply-chain.ps1 b/skills/scripts/test-bootstrap-supply-chain.ps1 index 0647a46..e854367 100644 --- a/skills/scripts/test-bootstrap-supply-chain.ps1 +++ b/skills/scripts/test-bootstrap-supply-chain.ps1 @@ -46,7 +46,9 @@ try { $failedTarget = Join-Path $scratch 'failed' $badDefinition = [pscustomobject]@{ repo = (Join-Path $scratch 'missing'); pinnedCommit = $pin } - try { Ensure-GitCloneInstall -Definition $badDefinition -TargetPath $failedTarget | Out-Null; throw 'failed fetch accepted' } catch {} + $failedFetchRejected = $false + try { Ensure-GitCloneInstall -Definition $badDefinition -TargetPath $failedTarget | Out-Null } catch { $failedFetchRejected = $true } + Assert-True $failedFetchRejected 'failed fetch accepted' Assert-True (-not (Test-Path $failedTarget)) 'failed fetch poisoned final path' Assert-True (@(Get-ChildItem $scratch -Filter '.reverse-bootstrap-*').Count -eq 0) 'failed fetch left staging path' @@ -108,6 +110,75 @@ printf "pnpm|%s\n" "$*" >> "$BOOTSTRAP_PS_LOG" Assert-True $dirtyRejected 'post-install dirty checkout accepted or rejection reason changed' Assert-True (-not (Test-Path (Join-Path $target 'pnpm-workspace.yaml'))) 'generated workspace file was not removed' + Invoke-Git -Arguments @('-C', $target, 'config', 'user.email', 'test@example.invalid') + Invoke-Git -Arguments @('-C', $target, 'config', 'user.name', 'test') + Invoke-Git -Arguments @('-C', $target, 'commit', '--allow-empty', '--quiet', '-m', 'wrong checkout') + $wrongCommitRejected = $false + try { Ensure-GitCloneInstall -Definition $definition -TargetPath $target | Out-Null } catch { $wrongCommitRejected = $_.Exception.Message -match 'expected' } + Assert-True $wrongCommitRejected 'clean wrong-commit checkout accepted' + + $publicProfile = [Environment]::GetFolderPath([Environment+SpecialFolder]::UserProfile) + $publicTools = Join-Path $publicProfile 'Tools' + $publicTarget = Join-Path $publicTools 'SecLists' + if (Test-Path -LiteralPath $publicTarget) { + Write-Host 'SKIP: public bootstrap exit regression (existing SecLists checkout)' + } + else { + $createdPublicTools = -not (Test-Path -LiteralPath $publicTools) + try { + New-Item -ItemType Directory -Path $publicTarget -Force | Out-Null + Invoke-Git -Arguments @('-C', $publicTarget, 'init', '--quiet') + Invoke-Git -Arguments @('-C', $publicTarget, 'config', 'user.email', 'test@example.invalid') + Invoke-Git -Arguments @('-C', $publicTarget, 'config', 'user.name', 'test') + Set-Content (Join-Path $publicTarget 'fixture.txt') 'wrong checkout' + Invoke-Git -Arguments @('-C', $publicTarget, 'add', 'fixture.txt') + Invoke-Git -Arguments @('-C', $publicTarget, 'commit', '--quiet', '-m', 'fixture') + + $powerShellHost = if ($PSVersionTable.PSEdition -eq 'Desktop') { Join-Path $PSHOME 'powershell.exe' } else { Join-Path $PSHOME 'pwsh' } + $childOutput = @(& $powerShellHost -NoProfile -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot 'bootstrap-reverse.ps1') -Capability seclists -SkipRefresh) + $childExitCode = $LASTEXITCODE + $childResult = ($childOutput -join [Environment]::NewLine) | ConvertFrom-Json + Assert-True ($childExitCode -ne 0) 'failed public bootstrap exited successfully' + Assert-True ($childResult.status -eq 'failed') 'failed public bootstrap did not report failed status' + Assert-True ($childResult.error -match 'Checkout verification failed') 'failed public bootstrap did not report checkout verification' + } + finally { + Remove-Item -LiteralPath $publicTarget -Recurse -Force -ErrorAction SilentlyContinue + if ($createdPublicTools -and (Test-Path -LiteralPath $publicTools) -and (@(Get-ChildItem -LiteralPath $publicTools -Force).Count -eq 0)) { + Remove-Item -LiteralPath $publicTools -Force -ErrorAction SilentlyContinue + } + } + } + + . (Join-Path $PSScriptRoot 'bootstrap-reverse.ps1') -Capability '__test_missing__' -SkipRefresh | Out-Null + $script:gitCloneDefinition = [pscustomobject]@{ name = 'test-git-clone'; bootstrapKind = 'git-clone'; canAutoInstall = $true } + $script:gitCloneVerifierCalled = $false + function Get-ReverseBootstrapDefinition { param([string]$Name) return $script:gitCloneDefinition } + function Get-ReverseCapabilityState { param([string]$Name) return [pscustomobject]@{ Ready = $true } } + function Resolve-ReverseToolSpec { param([string]$Name) return [pscustomobject]@{ Available = $true } } + function Ensure-GitCloneInstall { + param($Definition, [string]$TargetPath) + $script:gitCloneVerifierCalled = $true + return [pscustomobject]@{ Verified = $true } + } + $gitCloneResult = Ensure-Capability -Name 'test-git-clone' + Assert-True $script:gitCloneVerifierCalled 'available git-clone capability skipped checkout verification' + Assert-True $gitCloneResult.Verified 'git-clone capability did not return checkout verification result' + + $script:serviceCheckoutVerifierCalled = $false + function Ensure-GitCloneInstall { + param($Definition, [string]$TargetPath) + $script:serviceCheckoutVerifierCalled = $true + } + function Test-ReverseTcpPort { param([int]$Port) return $true } + Start-AnythingAnalyzerService -Definition ([pscustomobject]@{ + installDir = (Join-Path $scratch 'anything-analyzer') + repoUrl = $source + pinnedCommit = $pin + servicePort = 23816 + }) -AuthToken 'test-token' + Assert-True $script:serviceCheckoutVerifierCalled 'running Anything Analyzer service skipped checkout verification' + Write-Host 'PowerShell bootstrap supply-chain regression passed' } finally {