build/Restore-MsalDependency.ps1

<#
.SYNOPSIS
    Rebuilds lib/ from the pinned MSAL.NET packages of Data/MsalDependency.psd1.
.DESCRIPTION
    Downloads every pinned package from nuget.org, checks its SHA-512 against the pin, extracts
    the pinned files into a staging folder and checks each one's SHA-256. On Windows it also
    checks that every DLL carries a valid Authenticode signature from Microsoft Corporation.
    Only when every check passes is lib/ replaced by the staging folder, so lib/ holds exactly the
    pinned files. Needs PowerShell only (5.1 or 7), no .NET SDK.
.EXAMPLE
    PS C:\>./build/Restore-MsalDependency.ps1
#>

[CmdletBinding()]
param (
    [string] $FeedUrl = 'https://api.nuget.org/v3-flatcontainer'
)
Set-StrictMode -Version 3.0
$ErrorActionPreference = 'Stop'

$RepositoryRoot = Split-Path -Parent $PSScriptRoot
$LibPath = Join-Path $RepositoryRoot 'lib'
$Pins = Import-PowerShellDataFile -Path (Join-Path (Join-Path $RepositoryRoot 'Data') 'MsalDependency.psd1')
$IsWindowsHost = -not (Get-Variable IsWindows -ErrorAction SilentlyContinue) -or $IsWindows
Add-Type -AssemblyName System.IO.Compression, System.IO.Compression.FileSystem
if ($PSVersionTable.PSEdition -eq 'Desktop') { [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 }

$WorkFolder = Join-Path ([System.IO.Path]::GetTempPath()) ('posh-msal-restore-' + [guid]::NewGuid().Guid.Substring(0, 8))
$StagingFolder = Join-Path $WorkFolder 'lib'
[void] (New-Item -ItemType Directory -Path $StagingFolder)
try {
    foreach ($Package in $Pins.Packages) {
        $Id = $Package.Id.ToLowerInvariant()
        $Version = $Package.Version.ToLowerInvariant()
        $PackageFile = Join-Path $WorkFolder "$Id.$Version.nupkg"
        Invoke-WebRequest -Uri "$FeedUrl/$Id/$Version/$Id.$Version.nupkg" -OutFile $PackageFile -UseBasicParsing

        $Sha512 = [System.Security.Cryptography.SHA512]::Create()
        $Stream = [System.IO.File]::OpenRead($PackageFile)
        try { $Actual = [System.Convert]::ToBase64String($Sha512.ComputeHash($Stream)) }
        finally { $Stream.Dispose(); $Sha512.Dispose() }
        if ($Actual -ne $Package.Sha512) { throw "SHA-512 mismatch for $($Package.Id) $($Package.Version): expected $($Package.Sha512), got $Actual." }

        $Archive = [System.IO.Compression.ZipFile]::OpenRead($PackageFile)
        try {
            foreach ($File in $Package.Files) {
                $Entry = $Archive.GetEntry($File.Source)
                if (-not $Entry) { throw "$($File.Source) not found in $($Package.Id) $($Package.Version)." }
                $Destination = Join-Path $StagingFolder $File.Target
                [void] (New-Item -ItemType Directory -Path (Split-Path -Parent $Destination) -Force)
                [System.IO.Compression.ZipFileExtensions]::ExtractToFile($Entry, $Destination, $true)
                $Hash = (Get-FileHash -LiteralPath $Destination -Algorithm SHA256).Hash
                if ($Hash -ne $File.Sha256) { throw "SHA-256 mismatch for $($File.Target): expected $($File.Sha256), got $Hash." }
                if ($IsWindowsHost -and $Destination -like '*.dll') {
                    $Signature = Get-AuthenticodeSignature -LiteralPath $Destination
                    if ($Signature.Status -ne 'Valid' -or $Signature.SignerCertificate.Subject -notmatch 'O=Microsoft Corporation') {
                        throw "$($File.Target) is not validly signed by Microsoft Corporation ($($Signature.Status))."
                    }
                }
            }
        }
        finally { $Archive.Dispose() }
        Write-Verbose ('{0} {1}: {2} file(s) checked.' -f $Package.Id, $Package.Version, @($Package.Files).Count)
    }

    if (Test-Path -LiteralPath $LibPath) { Remove-Item -LiteralPath $LibPath -Recurse -Force }
    Move-Item -LiteralPath $StagingFolder -Destination $LibPath
    Write-Verbose "lib/ restored from $(@($Pins.Packages).Count) pinned packages." -Verbose
}
finally {
    Remove-Item -LiteralPath $WorkFolder -Recurse -Force -ErrorAction SilentlyContinue
}