netmap.psm1
$global:drawioScript = @"
## Habit Tracker UML use case diagram # label: <i style="fontSize:16;"> ip:%ruleName%</i><br>hostName:%hostName%<br><i style="color:gray;">DNS:%peerHost%</i><br> ## style: shape=%shape%;rounded=1;fillColor=%fill%;strokeColor=%stroke%; # style: label;image=%image%;whiteSpace=wrap;html=1;rounded=1;fillColor=%fill%;strokeColor=%stroke%;fontSize=16 # namespace: csvimport- ##fromlabel + sourcelabel + label + tolabel + targetlabel # connect: {"from":"dst", "to":"source","invert": false,"fromlabel":"%connectionLegend%","sourcelabel":"%connectionLegend%", \ #"style":"fontSize=18;curved=1;endArrow=blockThin;endFill=3;"} # connect: {"from":"dst2", "to":"source","label":"%connectionLegend%", "style": \ # "fontSize=18;curved=1;endArrow=blockThin;endFill=1;dashed=1;"} # connect: {"from":"dst3", "to":"source","sourcelabel":"%connectionLegend%", "style": \ # "fontSize=18;curved=1;endArrow=blockThin;endFill=1;dashed=1;"} # connect: {"from":"dst4", "to":"source","sourcelabel":"%connectionLegend%", "style": \ # "fontSize=18;curved=1;endArrow=blockThin;endFill=1;dashed=1;"} # connect: {"from":"dst5", "to":"source","sourcelabel":"%connectionLegend%","targetlabel":"%connectionLegend%", "style": \ # "fontSize=18;curved=1;endArrow=blockThin;endFill=1;dashed=1;strokeColor=#1a75ff"} # wsourceth: auto # height: auto # padding: 5 # ignore: shape,fill,stroke,dst,image # nodespacing: 40 # levelspacing: 120 # edgespacing: 40 # layout: horizontalflow ## CSV data starts below this line "@ #------------------------ GLOBAL VARIABLES$global:filePath #$scriptPath = $MyInvocation.MyCommand.Path #$wd= [System.IO.Path]::GetDirectoryName($scriptPath) ##old version #$wd = "C:\Users\user.name\location\folder\" $wd = $env:TEMP ##----------------Open File Dialog function Add-Type -AssemblyName System.Windows.Forms $userProfile = $env:USERPROFILE $Path ="$userProfile\Downloads" $global:filePath = "" function OpenFile{ $FileDialogObject = [System.Windows.Forms.OpenFileDialog] # Create Object and add properties $OpenFileDialog = New-Object $FileDialogObject $OpenFileDialog.InitialDirectory = $Path $OpenFileDialog.CheckPathExists = $true $OpenFileDialog.CheckFileExists = $true $OpenFileDialog.Title = "Load a XDR Report that contains at least the headers'dhost' and 'src'" $OpenFileDialog.Filter = "csv files (*.csv)|*.csv" #|All files (*.*)|*.*" $OpenFileDialog.ShowDialog() if ($openFileDialog.CheckFileExists -eq $true) { $filePath = $openFileDialog.FileName} $global:filePath = $filePath } #------------------------------------------------------------- TEMP FILES $temp1 = "$wd\temp1.csv" $cookedFile = "$wd\cooked.csv" $finalFile = "$wd\finalFile.txt" #--------------------------------------VARIABLES FOR DRAW.IO $imageFireWall = "https://cdn1.iconfinder.com/data/icons/unigrid-phantom-security-vol-1/60/013_032_firewall_protection_security_defense_brick_wall_threat_fire-512.png" $imageLevel1 = "https://cdn0.iconfinder.com/data/icons/computer-technology-16/5083/2-Search-512.png" $imageLevel2 = "https://cdn3.iconfinder.com/data/icons/server-rack/64/search-512.png" $imageLevel3 = "https://cdn0.iconfinder.com/data/icons/computer-technology-16/5083/2-Search-512.png" $imageLevel4 = "https://cdn0.iconfinder.com/data/icons/computer-technology-16/5083/2-Search-512.png" $imageLevel5 = "https://cdn0.iconfinder.com/data/icons/computer-technology-16/5083/2-Search-512.png" $imageInternet = "https://cdn3.iconfinder.com/data/icons/network-and-communications-8/32/network_web_internet_network-512.png" $label1 = 'ip:%source%<br>hostName:%hostName%<br><i style="color:gray;">DNS:%peerHost%</i><br><i style="color:red;">Rule:%ruleName%</i>' $label2 = 'Destination<br><i style="color:green;">IP:%source%</i><br>Rule:%ruleName%' #main function creates .csv file on DrawIO format 1st pass function generateTemp1{ $track = @() $trackHost =@() $export = @() $dstList = @() $dstList2 =@() $dstList3 = @() $dstList4 = @() $dstList5 = @() foreach ($x in $data) { $trackString = "$($x.src) --> $($x.dhost)" $source = $x.src $connectionLegend = $x.app + " " + $x.act + " " + $x.deviceDirection #$x.hostName ;sleep 1 # Check if the trackString is not already in the $track array if ($trackString -notin $track) { # Add the trackString to the $track array $track += $trackString ######Process Records here Display the trackString in Cyan # Write-Host $trackString -ForegroundColor Cyan if($source -notin $dstList) { #Write-Host "level 1" -ForegroundColor Yellow ###------------------------------------------ $newRow = [PSCustomObject]@{ hostName = $x.hostName source = $x.src dst = $x.dhost dst2 = "" dst3 = "" dst4 = "" dst5 = "" logged = $x.Logged remakrs = $x.remarks peerHost = $x.peerHost ruleName = $x.ruleName connectionLegend = $connectionLegend fill = "#dae8fc" stroke = "#6c8ebf" shape = "ellipse" image = $imageLevel1 label = $label1 } $export += $newRow $dstList += $x.dhost ###-------------------------------------------------- } elseif($source -in $dstList -and $source -notin $dstList2) { #Write-Host "level 2" -ForegroundColor Yellow ###------------------------------------------ $newRow = [PSCustomObject]@{ hostName = $x.hostName source = $x.src dst = "" dst2 = $x.dhost dst3 = "" dst4 = "" dst5 = "" logged = $x.Logged remakrs = $x.remarks peerHost = $x.peerHost ruleName = $x.ruleName connectionLegend = $connectionLegend fill = "#dae8fc" stroke = "#6c8ebf" shape = "ellipse" image = $imageLevel2 label = $label1 } $export += $newRow $dstList += $x.dhost $dstList2 += $x.dhost ###-------------------------------------------------- } elseif($source -in $dstList -or $source -in $dstList2 -and $source -notin $dstList3) { #Write-Host "level 3" -ForegroundColor Yellow ###------------------------------------------ $newRow = [PSCustomObject]@{ hostName = $x.hostName source = $x.src dst = "" dst2 = "" dst3 = $x.dhost dst4 = "" dst5 = "" logged = $x.Logged remakrs = $x.remarks peerHost = $x.peerHost ruleName = $x.ruleName connectionLegend = $connectionLegend fill = "#dae8fc" stroke = "#6c8ebf" shape = "ellipse" image = $imageLevel3 label = $label1 } $export += $newRow $dstList += $x.dhost $dstList2 += $x.dhost $dstList3 += $x.dhost ###-------------------------------------------------- } elseif($source -in $dstList -or $source -in $dstList2 -or $source -in $dstList3 -and $source -notin $dstList4) { #Write-Host "level 4" -ForegroundColor Yellow ###------------------------------------------ $newRow = [PSCustomObject]@{ hostName = $x.hostName source = $x.src dst = "" dst2 = "" dst3 = "" dst4 = $x.dhost dst5 = "" logged = $x.Logged remakrs = $x.remarks peerHost = $x.peerHost ruleName = $x.ruleName connectionLegend = $connectionLegend fill = "#dae8fc" stroke = "#6c8ebf" shape = "ellipse" image = $imageLevel4 label = $label1 } $export += $newRow $dstList += $x.dhost $dstList2 += $x.dhost $dstList3 += $x.dhost $dstList4 += $x.dhost ###-------------------------------------------------- } elseif($source -in $dstList -or $source -in $dstList2 -or $source -in $dstList3 -or $source -in $dstList4 -and $source -notin $dstList5) { #Write-Host "level 5" -ForegroundColor Yellow ###------------------------------------------ $newRow = [PSCustomObject]@{ hostName = $x.hostName source = $x.src dst = "" dst2 = "" dst3 = "" dst4 = "" dst5 = $x.dhost logged = $x.Logged remakrs = $x.remarks peerHost = $x.peerHost ruleName = $x.ruleName connectionLegend = $connectionLegend fill = "#dae8fc" stroke = "#6c8ebf" shape = "ellipse" image = $imageLevel5 label = $label1 } $export += $newRow $dstList += $x.dhost $dstList2 += $x.dhost $dstList3 += $x.dhost $dstList4 += $x.dhost $dstList5 += $x.dhost ###-------------------------------------------------- } } else { # Check if the trackString is in the $done array if ($trackString -in $done) { # Skip the current record if the trackString is in $done continue } # Display the trackString in Yellow if it's not in $done #Show records that were skipped for being repetitive #Write-Host $trackString -ForegroundColor Yellow } # Add the current trackString to the $done array to mark it as processed $done += $trackString } $export | Export-Csv -Path $temp1 -NoTypeInformation } #Combine same rows of data into a single cell function mergeData{ Import-Csv $temp1 | Group-Object source | ForEach-Object { [PsCustomObject]@{ hostName = $_.Group.hostName | Select-Object -First 1 source = $_.name | Select-Object -First 1 dst = $_.Group.dst -join ',' dst2 = $_.Group.dst2 -join ',' dst3 = $_.Group.dst3 -join ',' dst4 = $_.Group.dst4 -join ',' dst5 = $_.Group.dst5 -join ',' logged = $_.Group.Logged | Select-Object -First 1 remakrs = $_.Group.remarks | Select-Object -First 1 peerHost = $_.Group.peerHost | Select-Object -First 1 ruleName = $_.Group.ruleName | Select-Object -First 1 connectionLegend = $_.Group.ConnectionLegend | Select-Object -First 1 fill = $_.Group.fill | Select-Object -First 1 stroke = $_.Group.stroke | Select-Object -First 1 shape = $_.Group.shape | Select-Object -First 1 image = $_.Group.image | Select-Object -First 1 label = $label1 | Select-Object -First 1 } } | Export-Csv $cookedFile -NoTypeInformation } #not all destinations are added on the first pass function addMissingDestinations{ $data = Import-Csv $temp1 #$allSources = @() $allSources = $data.source $missingOnes = @() foreach($x in $data) { if($x.dst -notin $allSources -and $x.dst -notin $missingOnes -and $x.dst -ne $null -and $x.dst -ne ""){ $destination = $x.dst $hostName = $x.hostName $missingOnes += $x.dst $ruleName = $x.ruleName #debug #Write-Host " $destination added "; sleep 1 } } $newRowList = @() forEach($x in $missingOnes){ $row = [PsCustomObject]@{ hostName = "" source = $x dst = "" dst2 = "" dst3 = "" dst4 = "" dst5 = "Internet" fill = "#dae8fc" stroke = "#6c8ebf" shape = "ellipse" image = $imageLevel1 logged = "" remakrs = "" peerHost = "" ruleName = $ruleName connectionLegend = "" label = $label2 } $newRowList += $row } $newRowList | Export-Csv -Append $cookedFile -NoTypeInformation } #adds a final Internet connection to the final destinations that dont have a 'from' #does not mean 100% that they are connecting to the internet, it means a 'FROM' destination was not found on those IPs from the loaded data file. function addInternetNode { $row = [PsCustomObject]@{ hostName = "Internet" source = "Internet" dst = "" dst2 = "" dst3 = "" dst4 = "" dst5 = "www" fill = "#80b3ff" stroke = "#1a75ff" shape = "circle" image = $imageInternet logged = "" remakrs = "" peerHost = "" ruleName = "" connectionLegend = "Outbound to the Internet" label = "The Internet" } $newRowList += $row $newRowList | Export-Csv -Append $cookedFile -NoTypeInformation } #output has silly ',' on empty cells, this function removes them function removeSillyCommans{ $data = Import-Csv $cookedFile forEach ($row in $data) { $row.Psobject.Properties | ForEach-Object { $_.value = $_.value -replace'\["|\"]' if($_.value -eq ",") {$_.value = ""} } } $data | Export-Csv $cookedFile -NoTypeInformation } #merge Draw.IO.txt with cooked.csv function mergeScriptData{ $data = Get-Content $cookedFile #$script = Get-Content "$wd\drawIo.txt" $script = $global:drawioScript $script > $finalFile $data >> $finalFile } #delete temporary files from folder function cleanUp { Remove-Item $temp1 Remove-Item $cookedFile Remove-Item $finalFile } function netmap{ Write-Host "netmap v1.0 - netmap - A tool to help visualize connections using Draw.IO application." -ForegroundColor Gray Write-Host "Copyright (C) 2023 Cyber Samurai - Paulo Bazzo" -ForegroundColor Gray Write-Host "Cybersamurai - https://cybersamurai.co.uk" -ForegroundColor Gray Write-Host "`n" Write-Host "For better results please ensure your report.csv has the following headers" -ForegroundColor Yellow Write-Host "hostName,dhost,remarks,src,peerHost,ruleName,shost,act,app,deviceDirection" -ForegroundColor Yellow Try{OpenFile $data = Import-Csv $global:filePath $check = $true Write-Host "File loaded successfully" -ForegroundColor Green;sleep 1} Catch{Write-Host "Please select a file to ingest" -ForegroundColor Yellow} if($check){ Write-Host "[START SCRIPT] -----Generating Diagram Script`n" -ForegroundColor white -BackgroundColor Blue generateTemp1 Write-Host "[SUCESS] - Temp file generated." -ForegroundColor Gray; Start-Sleep -Seconds .5 mergeData Write-Host "[SUCESS] - Merging data together" -ForegroundColor Gray ; Start-Sleep -Seconds .5 addMissingDestinations Write-Host "[SUCESS] - Modifying format to match DrawIO requirements" -ForegroundColor Gray; Start-Sleep -Seconds .5 addInternetNode Write-Host "[SUCESS] - Adding internet routing" -ForegroundColor Gray; Start-Sleep -Seconds 2 removeSillyCommans Write-Host "[SUCESS] - Sprinking some magic..." -ForegroundColor Gray ; Start-Sleep -Seconds .5 mergeScriptData Get-Content "$wd\finalFile.txt" | Set-Clipboard sleep -seconds 2; Try{cleanup}Catch{Write-Host "could not clean up temporary files"} Write-Host "[SUCCESS] - Script saved to your Clipboard! - Ready to paste(Ctrl+V) into Draw.IO"-ForegroundColor Green Write-Host "[INSTRUCTIONS] - Go to Draw.IO and paste your clipboard on 'Arrange --> Insert --> Advance --> CSV -->Import'" -ForegroundColor Yellow Write-Host "[END SCRIPT] -----Script Completed" -ForegroundColor white -BackgroundColor Blue } else{Write-Host "Please select a .csv file to ingest" -ForegroundColor Yellow} } Export-ModuleMember -Function netmap |