Public/Integrity.ps1
|
using namespace System using namespace System.IO using namespace System.Security.Cryptography using namespace System.Collections.Immutable function Write-DirectoryHashes { #### Hash files under `Path` and write both `HashIndex.md` and `HashIndex.json` into that directory. #### #### JSON is a flat array of `{ File, Hash, Path }` records for agents and tooling. #### Markdown carries a header block and dash-prefixed `path,file,hash` rows for humans. #### Algorithm (`SHA256`), include list, and exclude list are fixed at the module scope #### (`$script:HashIndexAlgorithm`, `$script:HashIndexInclude`, `$script:HashIndexExclude`). #### #### Directory exclusion is enforced at traversal time by `Get-IndexableFile`, not after enumeration. #### Build output and similar trees (`bin`, `obj`, `node_modules`, `.git`) are never descended into. #### #### **Parameters** #### - `[string]`: __Path__ #### - *Root directory to walk.* #### #### **Returns** #### - *None. Writes `<Path>\HashIndex.md`, `<Path>\HashIndex.json`, and a count line to host.* [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Path ) $resolvedPath = (Resolve-Path -Path $Path -ErrorAction Stop).Path $pathRedactionPrefix = (Resolve-Path '~').Path $markdownFile = Join-Path $resolvedPath 'HashIndex.md' $jsonFile = Join-Path $resolvedPath 'HashIndex.json' $rootDirectory = [System.IO.DirectoryInfo]::new($resolvedPath) $records = Get-IndexableFile -Directory $rootDirectory -Include $script:HashIndexInclude -Exclude $script:HashIndexExclude | Where-Object { $_.FullName -ne $jsonFile -and $_.FullName -ne $markdownFile } | ForEach-Object { $hashObject = Get-FileHash -Path $_.FullName -Algorithm $script:HashIndexAlgorithm [PSCustomObject]@{ File = $_.Name Hash = $hashObject.Hash Path = $hashObject.Path.Replace($pathRedactionPrefix, '') } } $header = @" # Hash Index **TickStamp** : $((Get-Date).Ticks) **Algorithm** : $($script:HashIndexAlgorithm) **Include** : $($script:HashIndexInclude -join ' ') **RedactionPath** : [[REDACTED]] "@ $rows = foreach ($record in $records) { "- $($record.Path),$($record.File),$($record.Hash)" } ($header + ($rows -join "`n") + "`n") | Out-File -FilePath $markdownFile -Encoding utf8 @($records) | ConvertTo-Json -Depth 10 | Out-File -FilePath $jsonFile -Encoding utf8 Write-Host "Hashed $(@($records).Count) files into $markdownFile and $jsonFile" } #### # Get-Hash function Get-Hash { #### Hash a file using the specified algorithm. #### #### **Parameters** #### - `[string]`: __Path__ #### - *Path to the file.* #### - `[string]`: __Algorithm__ #### - *Hash algorithm. One of `MD5`, `SHA1`, `SHA256`, `SHA384`, `SHA512`. Defaults to `MD5`.* #### #### **Returns** #### - `[string]` #### - *Hex hash string.* #### #### **Throws** #### - When `Path` does not resolve to an existing file. param ( [Parameter(Mandatory)] [string]$Path, [ValidateSet('MD5', 'SHA1', 'SHA256', 'SHA384', 'SHA512')] [string]$Algorithm = 'MD5' ) if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { throw "Get-Hash: file not found: $Path" } $fileHash = Get-FileHash -LiteralPath $Path -Algorithm $Algorithm -ErrorAction Stop return $fileHash.Hash } |