param([Parameter(Mandatory=$true)][string]$SourceRoot,[Parameter(Mandatory=$true)][string]$OutputDirectory) $ErrorActionPreference='Stop' $source=(Resolve-Path -LiteralPath $SourceRoot).Path $out=[IO.Path]::GetFullPath($OutputDirectory) New-Item -ItemType Directory -Force -Path $out,(Join-Path $out 'evidence')|Out-Null function Rel([string]$p){$p.Substring($source.Length).TrimStart('\','/')} function Csv($p,$rows){$rows|Export-Csv -LiteralPath $p -NoTypeInformation -Encoding UTF8} function Lines([string]$p){try{[IO.File]::ReadAllLines($p)}catch{@()}} $files=@(Get-ChildItem -LiteralPath $source -Recurse -File -Force) $inv=foreach($f in $files){$e=$f.Extension.ToLowerInvariant();$c=if($e -in '.cs','.vb','.fs'){'source'}elseif($e -in '.sln','.csproj','.vbproj','.props','.targets','.config','packages.config'){'project/config'}elseif($e -in '.dll','.arx','.dbx','.lsp','.cuix'){'autodesk/binary'}elseif($e -in '.resx','.xaml','.png','.jpg','.bmp','.ico'){'ui/resource'}elseif($e -in '.dwg','.dcf','.db','.sqlite','.doc','.docx','.dot','.dotx','.pdf','.zip'){'document/binary'}else{'data/other'};[pscustomobject]@{RelativePath=Rel $f.FullName;Extension=$e;SizeBytes=$f.Length;LastWriteTime=$f.LastWriteTime.ToString('o');Sha256=(Get-FileHash $f.FullName -Algorithm SHA256).Hash;Classification=$c}} Csv (Join-Path $out 'evidence\file-inventory.csv') $inv Csv (Join-Path $out 'evidence\hashes.csv') ($inv|Select RelativePath,SizeBytes,Sha256) $projects=Get-ChildItem -LiteralPath $source -Recurse -File|Where-Object {$_.Extension -in '.csproj','.vbproj' -and $_.FullName -notmatch '\\(bin|obj|packages|lib)\\'} $pr=@();foreach($p in $projects){[xml]$x=Get-Content -LiteralPath $p.FullName -Raw;$refs=@($x.Project.ItemGroup.Reference);if(!$refs.Count){$pr+=[pscustomobject]@{Project=Rel $p.FullName;Reference='';HintPath='';Private='';Category='';Specific2019='';Risk2026=''}};foreach($r in $refs){$n=[string]$r.Include;$cat=if($n -match 'Autodesk|AcMgd|AcDbMgd|AcCoreMgd|PnP|ProcessPower'){'Autodesk API'}elseif($n -match 'SQLite|Excel|Office'){'external'}else{'framework/other'};$pr+=[pscustomobject]@{Project=Rel $p.FullName;Reference=$n;HintPath=[string]$r.HintPath;Private=[string]$r.Private;Category=$cat;Specific2019=([string]$r.HintPath -match '2019');Risk2026=if([string]$r.HintPath -match '2019'){'hard-coded 2019 path'}elseif($cat -eq 'Autodesk API'){'verify 2026 API/reference'}else{''}}}} Csv (Join-Path $out 'evidence\project-references.csv') $pr $src=Get-ChildItem -LiteralPath $source -Recurse -File|Where-Object {$_.Extension -in '.cs','.vb' -and $_.FullName -notmatch '\\(bin|obj|packages|lib)\\'} $cmd=@();$rib=@();$ev=@();$db=@();$wr=@();$tel=@();$cfg=@();$paths=@() foreach($f in $src){$ls=Lines $f.FullName;$rel=Rel $f.FullName;for($i=0;$i -lt $ls.Count;$i++){$l=$ls[$i] if($l -match '\[CommandMethod\("([^"]+)"(?:,\s*([^\)]*))?\)'){$cmd+=[pscustomobject]@{File=$rel;Line=$i+1;Command=$Matches[1];Flags=$Matches[2];Class='see source';Method='see source';Purpose='registered AutoCAD command';Selection='unknown';PlantProject='verify body';ReadsDrawing='yes';WritesDrawing='unknown';Telemetry='not evidenced'}} if($l -match 'Ribbon(Button|Panel|Tab)|ComponentManager|CurrentProjectOpenedEvent'){$rib+=[pscustomobject]@{File=$rel;Line=$i+1;Element=$Matches[0];Context=($l -replace '[\r\n]+',' ');Action=if($l -match '\+='){'subscribe/create'}elseif($l -match '-='){'unsubscribe'}else{'use'}}} foreach($tok in 'CommandWillStart','CommandEnded','CommandCancelled','CommandFailed','ObjectAppended','ObjectModified','ObjectErased','BeginSave','SaveComplete','DocumentCreated','DocumentActivated','DocumentToBeDestroyed','SystemVariableChanged','SelectionAdded','Idle','PointMonitor','CurrentProjectOpenedEvent'){if($l -match [regex]::Escape($tok)){$ev+=[pscustomobject]@{Publisher='AutoCAD/Plant/UI';Event=$tok;Subscriber=$rel;Line=$i+1;Unsubscribe='unknown';CallsAnnotation=if($l -match 'Annot|Ortho|PXP'){'possible'}else{'unknown'};CanRepeat='yes/unknown';Risk='lifecycle/repeat check'}}} foreach($tok in 'PnPDrawings','PnPDatalinks','EngineeringItems','P3dLineGroupPartRelationship','P3dLineGroup','P3dPartConnection','DataLinksManager','GetPnPDatabase'){if($l -match [regex]::Escape($tok)){$db+=[pscustomobject]@{File=$rel;Line=$i+1;Database='Plant PnP/DCF';Table=$tok;Operation=if($l -match 'Select|Get|Read'){'read'}elseif($l -match 'Set|Update|Insert|Delete'){'write/unknown'}else{'access'};ReadOrWrite=if($l -match 'Set|Update|Insert|Delete'){'write/unknown'}else{'read/unknown'};Risk='version/API coupling'}}} foreach($tok in 'OpenMode.ForWrite','UpgradeOpen','AppendEntity','AddNewlyCreatedDBObject','Erase','SetXData','StartTransaction','MLeader','BlockReference','AttributeReference','MText','DBText','Table'){if($l -match [regex]::Escape($tok)){$wr+=[pscustomobject]@{File=$rel;Line=$i+1;Operation=$tok;DWG='possible/confirmed by context';DCF='not proven by token';ProjectXml='not proven';CreatedOrModified='see context';Undo='transaction-dependent';DocumentLock='unknown';CommitAbort='see source';PartialWriteRisk='yes if exception'}}} foreach($tok in 'Environment.MachineName','Environment.UserName','Usage','Counter','Telemetry','AppendAllText','WriteAllText','Excel','Xlsx','EPPlus','ClosedXML','Interop.Excel','P_Rec_Annot'){if($l -match [regex]::Escape($tok)){$tel+=[pscustomobject]@{File=$rel;Line=$i+1;Token=$tok;Trigger='unknown';PluginName=if($l -match 'P_Rec_Annot|Plugin'){'possible'}else{'not evidenced'};Count=if($l -match 'Usage|Counter'){'possible'}else{'not evidenced'};Computer=if($l -match 'MachineName'){'yes'}else{'not evidenced'};User=if($l -match 'User'){'possible'}else{'not evidenced'};Project=if($l -match 'Project'){'possible'}else{'not evidenced'};Aggregation='not determined'}}} if($l -match '(?i)([A-Z]:\\[^"\r\n]+|\\\\[^"\r\n]+)'){$paths+=[pscustomobject]@{File=$rel;Line=$i+1;ValueMasked='';Purpose='hard-coded path';Network=if($Matches[1]-match '^\\\\'){'yes'}else{'no'};Writable='unknown';Risk2026='review'}} if($l -match '(?i)Settings\.xml|annotation_settings\.xml|users_indexes_list\.xml|Template\.dwg|FilesRelease'){$cfg+=[pscustomobject]@{File=$rel;Line=$i+1;KeyOrFile=($l -replace '[\r\n]+',' ');Purpose='configuration/deployment';Risk='path coupling'}}}} Csv (Join-Path $out 'evidence\commands.csv') $cmd;Csv (Join-Path $out 'evidence\ribbon-items.csv') $rib;Csv (Join-Path $out 'evidence\event-subscriptions.csv') $ev;Csv (Join-Path $out 'evidence\database-access.csv') $db;Csv (Join-Path $out 'evidence\drawing-write-operations.csv') $wr;Csv (Join-Path $out 'evidence\telemetry-calls.csv') $tel;Csv (Join-Path $out 'evidence\configuration-keys.csv') $cfg;Csv (Join-Path $out 'evidence\external-paths.csv') $paths Set-Content -LiteralPath (Join-Path $out 'evidence\audit-source-summary.txt') -Encoding UTF8 -Value @("SourceRoot=$source","FileCount=$($files.Count)","ProjectCount=$($projects.Count)","SourceCodeFileCount=$($src.Count)","ReadOnlyAnalysis=TRUE")