$ErrorActionPreference = "Stop" $root = Split-Path $PSScriptRoot -Parent $bootstrap = Join-Path $root "Enroll-MinerOpsAgent.ps1" $tokens = $null $parseErrors = $null [System.Management.Automation.Language.Parser]::ParseFile( $bootstrap, [ref] $tokens, [ref] $parseErrors ) | Out-Null if ($parseErrors.Count -gt 0) { throw "Enroll-MinerOpsAgent.ps1 parser errors: $($parseErrors.Message -join '; ')" } $source = Get-Content -LiteralPath $bootstrap -Raw function Assert-SourceContains { param( [Parameter(Mandatory)][string] $Pattern, [Parameter(Mandatory)][string] $Message ) if ($source -notmatch $Pattern) { throw $Message } } Assert-SourceContains 'enrollment\.json' ` "Bootstrap enrollment file is not explicit." Assert-SourceContains 'workflow\s*-ne\s*"new_agent"' ` "New-Agent bootstrap does not reject transition packages." Assert-SourceContains '/api/agent/enroll' ` "Bootstrap does not use the secure enrollment endpoint." Assert-SourceContains 'Read-Host[\s\S]*-AsSecureString' ` "API3 password is not prompted as a SecureString." Assert-SourceContains 'agent-config\.json\.pending' ` "Atomic pending config path is missing." Assert-SourceContains 'agent-config\.recovery\.json' ` "Post-redemption credential recovery path is missing." Assert-SourceContains 'Open-MinerOpsEnrollmentCredentialFileCreateNew' ` "Handle-based credential creation is missing." Assert-SourceContains 'FileMode\]::CreateNew' ` "Credential files are not created with fail-if-present semantics." Assert-SourceContains 'FileShare\]::None' ` "Credential files are not held exclusively while being created." Assert-SourceContains 'Flush\(\$true\)' ` "Credential writes are not durably flushed through the trusted handle." Assert-SourceContains 'GetNumberOfLinks' ` "Enrollment does not reject hard-linked credential targets." Assert-SourceContains 'S-1-5-18' ` "SYSTEM ACL entry is missing." Assert-SourceContains 'S-1-5-32-544' ` "Administrators ACL entry is missing." Assert-SourceContains 'SetOwner\(\$administrators\)' ` "Stable Administrators ownership is missing from credential ACLs." Assert-SourceContains '\[IO\.File\]::Move\(\$PendingConfigPath, \$ConfigPath\)' ` "Atomic final config move is missing." Assert-SourceContains 'Assert-MinerOpsEnrollmentDirectory' ` "Enrollment does not revalidate the directory trust boundary." Assert-SourceContains 'minerops-filesystem-trust\.blocked-' ` "Enrollment directory repair lacks an external durable trust blocker." Assert-SourceContains 'ApproveFilesystemTrustResetAfterReboot' ` "Enrollment lacks an explicit post-reboot trust-reset gate." Assert-SourceContains 'windows-full-boot-event-v1:' ` "Enrollment blockers do not use canonical full-kernel-boot records." if ($source -match 'LastBootUpTime') { throw "Enrollment must not use a wall-clock boot timestamp." } if ($source -match 'Remove-MinerOpsEnrollmentTrustBlocker') { throw "Enrollment must not have independent trust-blocker deletion authority." } Assert-SourceContains 'Enrollment cannot clear an existing filesystem trust blocker' ` "Enrollment does not fail closed on retained filesystem trust blockers." Assert-SourceContains 'Assert-MinerOpsEnrollmentTrustedAncestors' ` "Enrollment does not validate ancestor namespace protection." foreach ($functionName in @( "Assert-MinerOpsEnrollmentNoReparsePath", "Assert-MinerOpsEnrollmentTrustedAncestors" )) { $functionSource = [regex]::Match( $source, "(?s)function $functionName \{.*?\n\}" ).Value if ([string]::IsNullOrWhiteSpace($functionSource)) { throw "$functionName source was not found." } if ($functionSource -notmatch '\$current\s*=\s*\$root') { throw "$functionName does not preserve the filesystem root." } if ($functionSource -notmatch '\$fullPath\.Equals\(\s*\$root') { throw "$functionName does not protect a root-valued full path from trimming." } if ($functionSource -match '\$root\.TrimEnd\(') { throw "$functionName converts a root-qualified path to drive-relative form." } if ($functionSource -match 'GetFullPath\(\$Path\)\.TrimEnd\(') { throw "$functionName trims the filesystem root before identifying it." } if ($functionName -eq "Assert-MinerOpsEnrollmentTrustedAncestors" -and $functionSource -notmatch '\$parent\.Equals\(\s*\$root') { throw "$functionName does not protect a root-valued parent from trimming." } } Assert-SourceContains 'Open-MinerOpsEnrollmentDirectoryLock' ` "Enrollment does not lock an insecure target during repair." Assert-SourceContains 'DirectoryInfo\(\$staging\)' ` "Enrollment does not securely stage a new directory." Assert-SourceContains '& \$Installer' ` "Bootstrap does not hand off to the safe installer." Assert-SourceContains 'Start-ScheduledTask -TaskName "MinerOpsAgent"' ` "Fresh Agent startup is missing." Assert-SourceContains 'Remove-Item -LiteralPath \$EnrollmentPath' ` "Consumed extracted enrollment token is not removed." if ($source -match '\[IO\.File\]::WriteAllText\(\s*\$(Pending|Recovery)ConfigPath') { throw "Enrollment must not truncate or adopt a credential pathname." } $createFunction = [regex]::Match( $source, '(?s)function Open-MinerOpsEnrollmentCredentialFileCreateNew \{.*?\n\}' ).Value $createIndex = $createFunction.IndexOf( 'New-MinerOpsEnrollmentProtectedFileCreateNewStream' ) $verifyIndex = $createFunction.IndexOf('Test-MinerOpsEnrollmentCredentialAcl') $linkIndex = $createFunction.IndexOf('Assert-MinerOpsEnrollmentSingleLinkStream') if ( $createIndex -lt 0 -or $verifyIndex -le $createIndex -or $linkIndex -le $verifyIndex -or $createFunction -match 'Set-MinerOpsEnrollmentStreamAcl' ) { throw "Enrollment credential creation must apply FileSecurity at CreateNew, verify it, then reject links before content." } $createStreamFunction = [regex]::Match( $source, '(?s)function New-MinerOpsEnrollmentProtectedFileCreateNewStream \{.*?\n\}' ).Value foreach ($requiredCreateContract in @( '\[IO\.FileStream\]::new', 'FileMode\]::CreateNew', 'FileSystemRights\]::WriteData', 'FileSystemRights\]::ReadPermissions', 'FileSystemRights\]::Synchronize', 'FileShare\]::None', '\$Acl' )) { if ($createStreamFunction -notmatch $requiredCreateContract) { throw "Enrollment protected CreateNew stream contract is incomplete." } } if ($createStreamFunction -match 'FullControl|SetAccessControl|Set-Acl') { throw "Enrollment protected creation requests excess rights or repairs by path." } $repairFunction = [regex]::Match( $source, '(?s)function Protect-MinerOpsEnrollmentExistingConfig \{.*?\n\}' ).Value foreach ($requiredRepairRight in @( 'ReadPermissions', 'ChangePermissions', 'TakeOwnership', 'Synchronize' )) { if ($repairFunction -notmatch $requiredRepairRight) { throw "Enrollment existing-config repair is missing $requiredRepairRight." } } if ($repairFunction -match 'Set-Acl|FileAccess') { throw "Enrollment existing-config repair must remain handle-bound." } $writeFunction = [regex]::Match( $source, '(?s)function Write-MinerOpsEnrollmentCredentialJson \{.*?\n\}' ).Value if ( $writeFunction -notmatch 'Assert-MinerOpsEnrollmentSingleLinkStream' -or $writeFunction -notmatch '\$Stream\.Write' -or $writeFunction -notmatch '\$Stream\.Flush\(\$true\)' ) { throw "Enrollment credential content is not written and flushed through the verified handle." } $existingConfigGuard = $source.IndexOf( 'Test-Path -LiteralPath $ConfigPath' ) $redeemCall = $source.IndexOf( '"$HubUrl/api/agent/enroll"' ) if ( $existingConfigGuard -lt 0 -or $redeemCall -lt 0 -or $existingConfigGuard -gt $redeemCall ) { throw "Existing Agent identity must be rejected before token redemption." } $configMove = $source.IndexOf( '[IO.File]::Move($PendingConfigPath, $ConfigPath)' ) $installerCall = $source.IndexOf( '& $Installer' ) if ( $configMove -lt 0 -or $installerCall -lt 0 -or $configMove -gt $installerCall ) { throw "Permanent Agent credentials must be persisted before installation." } foreach ($sensitiveVariable in @( 'EnrollmentToken', 'Api3PasswordPlain' )) { if ( $source -match ( '(?im)Write-(Host|Output|Verbose|Information).*?\$' + $sensitiveVariable ) ) { throw "$sensitiveVariable must not be written to output." } } Write-Host "MinerOps enrollment bootstrap source safety tests passed."