audit: document legacy P_Rec_Annot loading chain

This commit is contained in:
codex
2026-07-31 13:39:29 +03:00
parent ea3a9e157e
commit 56dcce42f6
10 changed files with 264 additions and 0 deletions
@@ -0,0 +1,84 @@
param(
[string]$DeployedRoot = '\\phpsrv03\NBIM\save\Apps\Plant3D\DriveA\P_Rec_Annot',
[string]$SourceRoot = '\\phpsrv03\NBIM\code\CS\P_Rec_Annot\v1.0.4\repo',
[string]$OutputRoot = (Split-Path -Parent $PSCommandPath)
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Get-PeArchitecture([string]$Path) {
if ([IO.Path]::GetExtension($Path) -notin '.dll', '.exe') { return $null }
try {
$stream = [IO.File]::Open($Path, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::ReadWrite)
try {
$reader = [IO.BinaryReader]::new($stream)
$stream.Position = 0x3c; $offset = $reader.ReadInt32(); $stream.Position = $offset + 4
$machine = $reader.ReadUInt16()
switch ($machine) { 0x14c { 'x86' } 0x8664 { 'x64' } 0x1c4 { 'ARM' } default { 'Unknown' } }
} finally { $stream.Dispose() }
} catch { 'Unreadable' }
}
function Get-AssemblyMetadata([string]$Path) {
$result = [ordered]@{ AssemblyName = $null; AssemblyVersion = $null; TargetFramework = $null }
if ([IO.Path]::GetExtension($Path) -ne '.dll') { return [pscustomobject]$result }
try {
$name = [Reflection.AssemblyName]::GetAssemblyName($Path)
$result.AssemblyName = $name.Name; $result.AssemblyVersion = $name.Version.ToString()
try {
$assembly = [Reflection.Assembly]::ReflectionOnlyLoadFrom($Path)
$attribute = $assembly.GetCustomAttributesData() | Where-Object { $_.AttributeType.FullName -eq 'System.Runtime.Versioning.TargetFrameworkAttribute' } | Select-Object -First 1
if ($attribute) { $result.TargetFramework = [string]$attribute.ConstructorArguments[0].Value }
} catch { }
} catch { }
[pscustomobject]$result
}
$inventory = foreach ($file in Get-ChildItem -LiteralPath $DeployedRoot -Recurse -File) {
$hash = Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256
$version = try { [Diagnostics.FileVersionInfo]::GetVersionInfo($file.FullName) } catch { $null }
$meta = Get-AssemblyMetadata $file.FullName
[pscustomobject]@{
RelativePath = $file.FullName.Substring($DeployedRoot.Length).TrimStart('\')
Name = $file.Name; Extension = $file.Extension; Length = $file.Length; LastWriteTimeUtc = $file.LastWriteTimeUtc.ToString('o')
Sha256 = $hash.Hash; FileVersion = $version.FileVersion; ProductVersion = $version.ProductVersion
AssemblyName = $meta.AssemblyName; AssemblyVersion = $meta.AssemblyVersion; TargetFramework = $meta.TargetFramework
PeArchitecture = Get-PeArchitecture $file.FullName
}
}
$inventory | Sort-Object RelativePath | Export-Csv -LiteralPath (Join-Path $OutputRoot 'deployed-files.csv') -NoTypeInformation -Encoding utf8
$settings = [xml](Get-Content -LiteralPath (Join-Path $DeployedRoot 'Settings.xml'))
$sequence = 0
$configuration = foreach ($item in $settings.Main.LoadAssemblies.LoadAssembly) {
$sequence++
$configured = [string]$item.assemblyname; $resolved = Join-Path $DeployedRoot $configured
$entry = $inventory | Where-Object Name -eq $configured | Select-Object -First 1
[pscustomobject]@{
Sequence = $sequence; ConfigFile = 'Settings.xml'; ConfiguredPath = $configured; ResolvedPath = ('<deployed-root>\' + $configured)
Assembly = $entry.AssemblyName; Exists = Test-Path -LiteralPath $resolved; Sha256 = $entry.Sha256
LoadMethod = 'acDoc.Editor.Command("._netload", fullPath)'; Required = $true
FailureBehavior = 'Outer loader catch stops remaining sequence and shows MessageBox'; Notes = 'Path is combined with OrthoLoader assembly directory.'
}
}
$configuration | Export-Csv -LiteralPath (Join-Path $OutputRoot 'configuration-map.csv') -NoTypeInformation -Encoding utf8
$projects = Get-ChildItem -LiteralPath $SourceRoot -Recurse -File -Filter *.csproj | ForEach-Object {
$content = Get-Content -LiteralPath $_.FullName -Raw
$assemblyName = if ($content -match '<AssemblyName>([^<]+)</AssemblyName>') { $Matches[1] } else { [IO.Path]::GetFileNameWithoutExtension($_.Name) }
$framework = if ($content -match '<TargetFrameworkVersion>([^<]+)</TargetFrameworkVersion>') { $Matches[1] } else { $null }
$platform = if ($content -match '<PlatformTarget>([^<]+)</PlatformTarget>') { $Matches[1] } else { $null }
[pscustomobject]@{ AssemblyName = $assemblyName; Csproj = $_.FullName.Substring($SourceRoot.Length).TrimStart('\'); TargetFramework = $framework; PlatformTarget = $platform }
}
$sourceMap = foreach ($dll in $inventory | Where-Object Extension -eq '.dll') {
$matches = @($projects | Where-Object { $_.AssemblyName -eq $dll.AssemblyName -or $_.AssemblyName -eq [IO.Path]::GetFileNameWithoutExtension($dll.Name) })
$releaseCandidate = Join-Path (Join-Path $SourceRoot 'FilesRelease') $dll.Name
$exactReleaseMatch = (Test-Path -LiteralPath $releaseCandidate) -and ((Get-FileHash -LiteralPath $releaseCandidate -Algorithm SHA256).Hash -eq $dll.Sha256)
$status = if ($exactReleaseMatch) { 'EXACT_SOURCE_MATCH' } elseif ($matches.Count -eq 1) { 'LIKELY_SOURCE_MATCH' } elseif ($matches.Count -gt 1) { 'MULTIPLE_CANDIDATES' } elseif ($dll.Name -match '^PnP|^Ac') { 'AUTODESK_DEPENDENCY' } else { 'NO_SOURCE_FOUND' }
$project = $matches | Select-Object -First 1
[pscustomobject]@{ DeployedDll=$dll.Name; DeployedSha256=$dll.Sha256; FileVersion=$dll.FileVersion; AssemblyVersion=$dll.AssemblyVersion; LegacyProject=if($project){[IO.Path]::GetFileNameWithoutExtension($project.Csproj)}else{$null}; LegacyCsproj=if($project){$project.Csproj}else{$null}; LegacyOutputName=if($project){$project.AssemblyName}else{$null}; TargetFramework=if($project){$project.TargetFramework}else{$null}; PlatformTarget=if($project){$project.PlatformTarget}else{$null}; CommandCount=$null; RibbonRole=if($dll.AssemblyName -eq 'OrthoLoader'){'Central ribbon/loader'}else{$null}; ConfigurationSource=if($configuration.ConfiguredPath -contains $dll.Name){'Settings.xml'}else{$null}; MatchStatus=$status }
}
$sourceMap | Sort-Object DeployedDll | Export-Csv -LiteralPath (Join-Path $OutputRoot 'deployed-to-source-map.csv') -NoTypeInformation -Encoding utf8
"GeneratedUtc=$([DateTime]::UtcNow.ToString('o'))`nDeployedRoot=<network root redacted to report context>`nSourceRoot=<network source root redacted to report context>`nFiles=$($inventory.Count)" | Set-Content -LiteralPath (Join-Path $OutputRoot 'evidence\generation.txt') -Encoding utf8