# kb_bootpack_acceptance_v1.2.ps1 # PS 5.1 — Lecture seule. Valide les pointeurs BUG_KB_JSON_POINTER et la KB JSON. # Sortie attendue (ligne 1): # entries=|blocking=|ptr_exists=|ptr_sha_match=|ptr_entries_note=|json_entries= # Puis si m>0 : "[GATE-BLOCK] blocking ids: ,,..." param( [Parameter(Mandatory=$true)] [string]$Root, [switch]$Utf8Console ) $ErrorActionPreference = 'Stop' function Read-TextUtf8NoBom([string]$path){ [byte[]]$b = [IO.File]::ReadAllBytes($path) if($b.Length -ge 3 -and $b[0] -eq 239 -and $b[1] -eq 187 -and $b[2] -eq 191){ $b = $b[3..($b.Length-1)] } $enc = New-Object System.Text.UTF8Encoding($false) return $enc.GetString($b) } function Parse-PointerSection([string]$text){ $lines = $text -split "`r`n|`n|`r" $start = -1 for($i=0;$i -lt $lines.Length;$i++){ $t = $lines[$i].Trim() if($t -match '^\[BUG_KB_JSON_POINTER\]'){ $start = $i break } } if($start -lt 0){ return @{ exists = $false; path=$null; sha=$null; sha256=$null; entries_note=$null; size_note=$null } } $end = $lines.Length for($j=$start+1;$j -lt $lines.Length;$j++){ if($lines[$j].Trim() -match '^\[.+\]'){ $end = $j; break } } $path=$null; $sha=$null; $sha256=$null; $entries=$null; $size=$null for($k=$start+1;$k -lt $end;$k++){ $u = $lines[$k].Trim() if($u -match '^\s*Path\s*=\s*(.+)$'){ $path = $Matches[1].Trim() } elseif($u -match '^\s*SHA256\s*=\s*([0-9A-Fa-f]{64})'){ $sha256 = $Matches[1].ToUpper() } elseif($u -match '^\s*SHA\s*=\s*([0-9A-Fa-f]{64})'){ $sha = $Matches[1].ToUpper() } elseif($u -match '^\s*(Entries|ENTRIES)\s*=\s*(\d+)$'){ $entries= [int]$Matches[2] } elseif($u -match '^\s*(Size|SIZE)\s*=\s*(\d+)$'){ $size = [int]$Matches[2] } } return @{ exists = $true; path=$path; sha=$sha; sha256=$sha256; entries_note=$entries; size_note=$size } } function Resolve-KBPath([string]$root,[string]$rel){ if([string]::IsNullOrWhiteSpace($rel)){ return $null } # Normalise doubles antislash $rel2 = $rel -replace '\\\\','\' if([System.IO.Path]::IsPathRooted($rel2)){ return $rel2 } return (Join-Path $root $rel2) } function Get-FileSHA256([string]$p){ if(-not (Test-Path -LiteralPath $p)){ return $null } return (Get-FileHash -LiteralPath $p -Algorithm SHA256).Hash.ToUpper() } function Load-KBJson([string]$p){ if(-not (Test-Path -LiteralPath $p)){ return $null } $raw = Read-TextUtf8NoBom $p # Tolérance footer: on coupe du 1er '{' au dernier '}' pour ne garder que le JSON $start = $raw.IndexOf('{') $end = $raw.LastIndexOf('}') if(($start -lt 0) -or ($end -lt $start)){ return $null } $clean = $raw.Substring($start, ($end - $start + 1)) try{ return ($clean | ConvertFrom-Json) } catch { return $null } } # 1) Charger bootpack + paste $bp1 = Join-Path $Root 'bootpack\bootpack.txt' $bp2 = Join-Path $Root 'bootpack\bootpack_paste.txt' $txt1 = if(Test-Path -LiteralPath $bp1){ Read-TextUtf8NoBom $bp1 } else { "" } $txt2 = if(Test-Path -LiteralPath $bp2){ Read-TextUtf8NoBom $bp2 } else { "" } $ptr1 = Parse-PointerSection $txt1 $ptr2 = Parse-PointerSection $txt2 $ptr_exists = ($ptr1.exists -or $ptr2.exists) # 2) Choix du pointeur effectif: priorité bootpack.txt, sinon paste $eff = $null if($ptr1.exists){ $eff = $ptr1 } elseif($ptr2.exists){ $eff = $ptr2 } $entries_first = -1 if($eff -ne $null -and $eff.entries_note -ne $null){ $entries_first = [int]$eff.entries_note } # 3) Résoudre chemin KB, calcul SHA, charger JSON $kb_path = $null if($eff -ne $null){ $kb_path = Resolve-KBPath $Root $eff.path } $kb_sha_calc = $null $json = $null $json_entries = -1 $blocking = 0 if($kb_path -ne $null){ $kb_sha_calc = Get-FileSHA256 $kb_path $json = Load-KBJson $kb_path if($json -ne $null -and $json.entries -ne $null){ try { $json_entries = ($json.entries | Measure-Object).Count $blocking = ($json.entries | Where-Object { $_.blocking -eq $true } | Measure-Object).Count } catch { $json_entries = -1 $blocking = 0 } } } # 4) Comparaison SHA (SHA256 prioritaire, sinon SHA) $ptr_sha = $null if($eff -ne $null){ if(-not [string]::IsNullOrEmpty($eff.sha256)){ $ptr_sha = $eff.sha256.ToUpper() } elseif(-not [string]::IsNullOrEmpty($eff.sha)){ $ptr_sha = $eff.sha.ToUpper() } } $ptr_sha_match = $false if(($kb_sha_calc -ne $null) -and ($ptr_sha -ne $null)){ if($kb_sha_calc.ToUpper() -eq $ptr_sha.ToUpper()){ $ptr_sha_match = $true } } # 5) Ligne d'acceptance $entries_out = $json_entries $ptr_entries_note = if($entries_first -ge 0){ [string]$entries_first } else { "" } Write-Host ("entries={0}|blocking={1}|ptr_exists={2}|ptr_sha_match={3}|ptr_entries_note={4}|json_entries={5}" -f ` $entries_out, $blocking, $ptr_exists.ToString().ToLower(), $ptr_sha_match.ToString().ToLower(), $ptr_entries_note, $json_entries) # 6) Liste des blocking si > 0 if($json -ne $null -and $blocking -gt 0){ $ids = ($json.entries | Where-Object { $_.blocking -eq $true } | Select-Object -ExpandProperty id) if($ids -ne $null){ $line = [string]::Join(',', $ids) Write-Host ("[GATE-BLOCK] blocking ids: {0}" -f $line) } } if($Utf8Console){ $OutputEncoding = New-Object System.Text.UTF8Encoding($false) }