param( [string]$Root="\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry" ) $ErrorActionPreference="Stop" function NowIso { (Get-Date).ToString('s') } function ByteHead([string]$p,[int]$n){ [byte[]](Get-Content -LiteralPath $p -Encoding Byte -TotalCount $n) } function HasUtf8Bom([string]$p){ $b=ByteHead $p 3 return ($b.Length -ge 3 -and $b[0]-eq 239 -and $b[1]-eq 187 -and $b[2]-eq 191) } $bin = Join-Path $Root "bin" $scr = Join-Path $Root "scripts" $boot= Join-Path $Root "bootpack\bootpack.txt" $logs= Join-Path $Root "logs" if(!(Test-Path -LiteralPath $logs)){ New-Item -ItemType Directory -Path $logs | Out-Null } $ts = (Get-Date).ToString("yyyyMMdd_HHmmss") $log = Join-Path $logs ("precommit_audit_"+$ts+".log") "check|rel|status|note" | Out-File -LiteralPath $log -Encoding ascii $err=0 # -- WRAPPERS ------------------------------------------------------------ $wraps = @( @{ path = Join-Path $bin "gov_pipeline_guard_latest.cmd"; target="gov_pipeline_guard_v1.2.8.ps1" }, @{ path = Join-Path $bin "gov_profile_launcher_latest.cmd"; target="gov_profile_launcher_v1.9.ps1" }, @{ path = Join-Path $bin "kb_bulk_ingest_latest.cmd"; target="kb_bulk_ingest_v1.3.ps1" } ) foreach($w in $wraps){ $p=$w.path; $rel=$p.Substring($Root.Length+1) if(!(Test-Path -LiteralPath $p)){ "WRAPPER|$rel|NOK|absent" | Out-File $log -Append -Encoding ascii; $err++; continue } $raw = Get-Content -LiteralPath $p -Raw $hasNonAscii = $raw -match "[^\x00-\x7F]" if($hasNonAscii){ "WRAPPER|$rel|NOK|non-ascii" | Out-File $log -Append -Encoding ascii; $err++ } else { "WRAPPER|$rel|OK|ascii" | Out-File $log -Append -Encoding ascii } $m = [regex]::Match($raw,'powershell\.exe\s+-ExecutionPolicy\s+Bypass\s+-File\s+([^\r\n]+\.ps1)') if($m.Success){ $t=[IO.Path]::GetFileName($m.Groups[1].Value.Trim('"')) if($t -ne $w.target){ "WRAPPER_TARGET|$rel|NOK|found=$t expected=$($w.target)" | Out-File $log -Append -Encoding ascii; $err++ } else { "WRAPPER_TARGET|$rel|OK|$t" | Out-File $log -Append -Encoding ascii } } else { "WRAPPER_TARGET|$rel|NOK|no powershell.exe call" | Out-File $log -Append -Encoding ascii; $err++ } } # -- CORE SCRIPTS BOM ---------------------------------------------------- $core=@( (Join-Path $scr "gov_pipeline_guard_v1.2.8.ps1"), (Join-Path $scr "gov_profile_launcher_v1.9.ps1"), (Join-Path $scr "kb_bulk_ingest_v1.3.ps1") ) foreach($p in $core){ $rel=$p.Substring($Root.Length+1) if(!(Test-Path -LiteralPath $p)){ "BOM|$rel|NOK|absent" | Out-File $log -Append -Encoding ascii; $err++; continue } if(HasUtf8Bom $p){ "BOM|$rel|OK|UTF8-BOM" | Out-File $log -Append -Encoding ascii } else { "BOM|$rel|NOK|no-bom" | Out-File $log -Append -Encoding ascii; $err++ } } # -- BOOTPACK: KB POINTERS ---------------------------------------------- if(Test-Path -LiteralPath $boot){ $t=Get-Content -LiteralPath $boot -Raw -Encoding UTF8 $kb=[regex]::Match($t,'(?ms)^\[BUG_KB_JSON_POINTER\]\s*(.*?)(?=^\[|\Z)').Groups[1].Value if([string]::IsNullOrWhiteSpace($kb)){ "BOOTPACK|bootpack.txt|NOK|no-kb-pointer" | Out-File $log -Append -Encoding ascii; $err++ } else { $kp = [regex]::Match($kb,'(?mi)^\s*Path\s*=\s*(.+)$').Groups[1].Value.Trim() $ks = [regex]::Match($kb,'(?mi)^\s*SHA256\s*=\s*([0-9a-f]+)$').Groups[1].Value.Trim().ToLower() $ke = [regex]::Match($kb,'(?mi)^\s*Entries\s*=\s*([0-9]+)$').Groups[1].Value.Trim() if(Test-Path -LiteralPath $kp){ $rawkb=Get-Content -LiteralPath $kp -Raw $i=$rawkb.LastIndexOf(']}'); if($i -ge 0){ $rawkb=$rawkb.Substring(0,$i+2) } $cnt=-1; try{$cnt=(($rawkb|ConvertFrom-Json).entries).Count}catch{$cnt=([regex]::Matches($rawkb,'"id"\s*:')).Count} $sha=(Get-FileHash -LiteralPath $kp -Algorithm SHA256).Hash.ToLower() if($sha -ne $ks){ "BOOTPACK_SHA|bootpack.txt|NOK|ptr=$ks real=$sha" | Out-File $log -Append -Encoding ascii; $err++ } else { "BOOTPACK_SHA|bootpack.txt|OK|$sha" | Out-File $log -Append -Encoding ascii } if([string]$cnt -ne $ke){ "BOOTPACK_ENTRIES|bootpack.txt|NOK|ptr=$ke real=$cnt" | Out-File $log -Append -Encoding ascii; $err++ } else { "BOOTPACK_ENTRIES|bootpack.txt|OK|$cnt" | Out-File $log -Append -Encoding ascii } } else { "BOOTPACK|bootpack.txt|NOK|kb-path-missing" | Out-File $log -Append -Encoding ascii; $err++ } } } else { "BOOTPACK|bootpack.txt|NOK|absent" | Out-File $log -Append -Encoding ascii; $err++ } # -- TOKEN SCAN (PS5.1 safe) -------------------------------------------- $glob = Get-ChildItem -LiteralPath $scr -Filter "*.ps1" -File foreach($f in $glob){ $rel=$f.FullName.Substring($Root.Length+1) $txt = Get-Content -LiteralPath $f.FullName -Raw $code=$txt $code=[regex]::Replace($code,'"(?:[^"\\]|\\.)*"','""') $code=[regex]::Replace($code,"'(?:[^'\\]|\\.)*'","''") $code=[regex]::Replace($code,'\s*#.*$','',[System.Text.RegularExpressions.RegexOptions]::Multiline) $issues=@() if([regex]::IsMatch($code,'(?s)\?\s*[^:\r\n]{1,160}:\s*[^\r\n]{1,160}',[System.Text.RegularExpressions.RegexOptions]::Singleline)){ $issues+="ternary" } if($code -match '\?\.'){$issues+="safe_nav"} if($code -match '\?\?'){$issues+="null_coalesce"} if($code -match 'Select-String\s+[^\r\n]*-Recurse'){$issues+="ss_recurse"} if($issues.Count -gt 0){ "TOKENS|$rel|NOK|"+($issues -join ",") | Out-File $log -Append -Encoding ascii; $err++ } } # -- UNC HEALTH ---------------------------------------------------------- try{ $hp=Join-Path $logs ("unc_health_"+$ts+".txt") $payload="probe-"+$ts [IO.File]::WriteAllText($hp,$payload,(New-Object System.Text.UTF8Encoding($true))) $sha=(Get-FileHash -LiteralPath $hp -Algorithm SHA256).Hash "UNC|logs|OK|sha="+$sha | Out-File $log -Append -Encoding ascii } catch { "UNC|logs|NOK|"+$_.Exception.Message | Out-File $log -Append -Encoding ascii; $err++ } Write-Host ("SUMMARY | errors="+$err+" | log="+$log) if($err -gt 0){ exit 5 } else { exit 0 }