procmap.psm1

$ErrorActionPreference = "Stop"
function checkForAdmin {
    # Create a new WindowsPrincipal object for the current user
    $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = New-Object System.Security.Principal.WindowsPrincipal($currentUser)

    # Define the administrator role
    $adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator

    # Check if the current user is in the administrator role
    if ($principal.IsInRole($adminRole)) {
        return $true
    } else {
        break
        exit 1
    }
}
checkForAdmin





Write-Host "ProcMap Version v6.2 - Written by PB - a.ka cybersamurai.co.uk" -ForegroundColor Gray
#-------------------------------------------- DRAW.IO SCRIPT-------------------------------------
$global:drawioScript = @"
## Track Executables file from XDR Report like a BOSS!!!
# 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":"processCmd", "to":"objectUser","invert": false, "style":"fontSize=18;curved=1;endArrow=blockThin;endFill=3;"}
# connect: {"from":"parentFilePath", "to":"objectUser","invert": false, "style":"fontSize=18;curved=1;endArrow=blockThin;endFill=3;"}
# connect: {"from":"processFilePath", "to":"objectUser","invert": false, "style":"fontSize=18;curved=1;endArrow=blockThin;endFill=3;"}
# connect: {"from":"objectFilePath", "to":"objectUser","invert": false, "style":"fontSize=18;curved=1;endArrow=blockThin;endFill=3;"}
# wsourceth: auto
# height: auto
# padding: 5
# ignore: shape,fill,stroke,image
# nodespacing: 30
# levelspacing: 120
# edgespacing: 40
# layout: horizontalflow
## CSV data starts below this line
"@

#-------------------------------------------- VARIABLES DRAW.IO -------------------------------------
$imageLevel1 = "https://cdn4.iconfinder.com/data/icons/mexican-curious-1/400/emojiDF_angry_comp-512.png"
$imageLevel2 = "https://cdn2.iconfinder.com/data/icons/data-organization-and-management-7/64/vector_525_04-512.png"
$imageLevel3 = "https://cdn0.iconfinder.com/data/icons/call-center-and-service-2/32/06-Question-512.png"
$imageLevel4 = "https://cdn2.iconfinder.com/data/icons/module/512/nfc-chip-chipset-pay-service-mobile-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 = '%objectUser%'
$label4 = '<i style="color:#004d80;">%objectUser%</i>'
$labelUnknownFile  = '%objectUser%'
$imageUnknown = "https://cdn2.iconfinder.com/data/icons/module/512/nfc-chip-chipset-pay-service-mobile-512.png"
$imageSigned = "https://cdn-icons-png.flaticon.com/128/1949/1949225.png"
$labelsigned= '%objectUser%<br>Signed:<i style=color:blue;>%signed%</i>'
#-----------------------------------------------DRAW.IO FIle TYPE ICONS -------------------------------------
###############################################################################################################
$global:imageExe ="https://cdn0.iconfinder.com/data/icons/100-file-document-format-type/48/EXE-512.png"
$global:imageDll = "https://cdn2.iconfinder.com/data/icons/file-types-3/32/file_DLL-512.png"
$global:imagePs1 = "https://cdn2.iconfinder.com/data/icons/azure-1/512/Console-512.png"
$global:imageJson = "https://cdn3.iconfinder.com/data/icons/file-document-format-folder-5/64/Document_file_extension_office_work_paper_information_folder_documentation_json-256.png"
$global:imageTxt = "https://cdn4.iconfinder.com/data/icons/files-110/64/txt-text-file-format-extension-document-archive-256.png"
$global:imageJs = "https://cdn4.iconfinder.com/data/icons/file-extension-names-vol-8/512/27-256.png"
$global:pdf = "https://cdn4.iconfinder.com/data/icons/smashicons-file-types-flat/56/28_-_PDF_File_Flat-256.png"
$global:macroFiles = "https://cdn3.iconfinder.com/data/icons/file-document-15/512/file_danger_alert_warning-256.png"
$global:imageIcon = "https://cdn1.iconfinder.com/data/icons/multimedia-and-entertainment-3/48/25-Photo-256.png"






#--------------------------------------------------- GLOBAL VARIABLES -------------------------------------
$scriptPath = $MyInvocation.MyCommand.Path
$wd= [System.IO.Path]::GetDirectoryName($scriptPath) ##old version "$wd ="C:\Users\user.name\OneDrive - NEC Software Solutions\Documents\Projects\Auto-Diagram\ProcessMap\v_04"

$objectFilePathList = @()
$dstList = @()
$sourceList = @()

##----------------Open File Dialog function
Add-Type -AssemblyName System.Windows.Forms
$userProfile = $env:USERPROFILE
$Path =$wd
$global:filePath = ""
#-------------------------------------------------- TEMP FILES -------------------------------------
$temp1 = "$wd\temp1.csv"
$cookedFile = "$wd\cooked.csv"
$cleanFile = "$wd\cleanFile.csv"
$finalFile = "$wd\finalScript.txt"
#------------------------------------------------------------------------
#####
function removeSillyCommans{
#LEGACY FUNCTION NOT BEING USED
$data = Import-Csv $cookedFile

forEach ($row in $data) {
    $row.Psobject.Properties | ForEach-Object {
    $_.value = $_.value -replace'\["|\"]'
    if($_.value -like "*,*") {$_.value = ""}
        }
}
$data | Export-Csv $cookedFile -NoTypeInformation
} #removeSillyCommans kind not working nor its needed, kept for future reference only.
###-----------------------------------------------------------------------
##########################################################################################

function OpenFile{

$initialPath = Join-Path $env:USERPROFILE "Downloads"        
        $FileDialogObject = [System.Windows.Forms.OpenFileDialog]
 # Create Object and add properties

        
        $OpenFileDialog = New-Object $FileDialogObject
        $OpenFileDialog.InitialDirectory = $initialPath
        $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
        }
 #subFunction located inside generateTemp1, used to retrieve only the last binary of an executable path. switches -v and -verbose
function carveString ($string) {
$pattern = "[^\\]+$"

    if ($string -match $pattern) {
      $captured= $matches[0]
    }
return $captured
}



function generateTemp1{
        param(
        [switch]$debugger = $false,
        [switch]$simple = $false,
        [switch]$s = $false)

$track = @()
$trackHost =@()
$export = @()

foreach ($x in $data) {
    if($s -or $simple){
    #this is the default value and will show only the last executable binary on the active path
    $logged = carveString $x.Logged
    $parentName = carveString $x.parentName
    $parentCmd = carveString $x.parentCmd
    $parentFilePath = carveString $x.parentFilePath
    $osDescription = carveString $x.osDescription
    $objectUser = carveString $x.objectUser
    $processFilePath = carveString $x.processFilePath
    $processCmd = carveString $x.processCmd
    $objectFilePath = carveString $x.objectFilePath
    $processSigner = carveString $x.processSigner 
    
    }
    Else{
    #if verbose switch is active will show the entire path of the binaries
    $logged = $x.Logged
    $parentName = $x.parentName
    $parentCmd = $x.parentCmd
    $parentFilePath = $x.parentFilePath
    $osDescription = $x.osDescription
    $objectUser = $x.objectUser
    $processFilePath = $x.processFilePath
    $processCmd =$x.processCmd
    $objectFilePath = $x.objectFilePath
    $processSigner = $x.processSigner
    }


        $trackString = "$($objectUser) --> $($parentFilePath) --> $($processFilePath) --> $($objectFilePath)"


    # Check if the trackString is not already in the $track array
    if ($trackString -notin $track) {
        # Add the trackString to the $track array
        $track += $trackString
        #$trackString
        ######Process Records here Display the trackString in Cyan
       # Write-Host $trackString -ForegroundColor Cyan


###------------------------------------------

        #Add PARENT
        $newRow = [PSCustomObject]@{
            objectUser = $objectUser
            parentFilePath = $parentFilePath
            processFilePath = ""
            objectFilePath = ""
            processCmd = $processCmd
            fill = "#ccffe6"
            stroke = "#009900"
            shape = "ellipse"
            label = $label1   
            image = $imageLevel1
            signed= $processSigner
      
        }
        $export += $newRow
        #Add CHILD
        $newRow = [PSCustomObject]@{
            objectUser = $parentFilePath
            parentFilePath = ""
            processFilePath = $processFilePath
            objectFilePath = ""
            processCmd = ""
            fill = "#ffe6ff"
            stroke = "#d9d9d9"
            shape = "ellipse"
            label = $label1   
            image = $imageLevel2
            signed = $processSigner
      
        }
        $export += $newRow
        #Add CHILDCMD
        if($processSigner -like $null -or $processSigner -like ""){$labelSelected = $labelUnknownFile;$imageSelected =$imageUnknown}
        else{$labelSelected = $labelsigned;$imageSelected = $imageSigned}

        $newRow = [PSCustomObject]@{
            objectUser = $processFilePath
            parentFilePath = ""
            processFilePath = ""
            objectFilePath = $objectFilePath
            processCmd = $processCmd
            fill = "#dae8fc"
            stroke = "#6c8ebf"
            shape = "ellipse"
            label = $labelSelected   
            image = $imageSelected
            signed = $x.processSigner
     
        }
        $export += $newRow
        $dstList += $x.parentFilePath
        $objectFilePathList += $x.objectFilePath
        $sourceList += $x.objectUser

    } 
    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
if ($debugger){
$x = Import-Csv $temp1
$x[3] | Select-Object objectUser, processCmd
}
else{}
}
function mergeData{
        param([switch]$debugger = $false)

Import-Csv $temp1 | 
Group-Object objectUser | 
ForEach-Object {
    [PsCustomObject]@{
        objectUser = $_.name | Select-Object -First 1
        parentFilePath = ($_.Group.parentFilePath | Sort-Object -Unique) -join ','  -replace '^,'
        processFilePath = ($_.Group.processFilePath | Sort-Object -Unique) -join ',' -replace '^,'
        objectFilePath = ($_.Group.objectFilePath | Sort-Object -Unique) -join ',' -replace '^,'
        processCmd = ($_.Group.processCmd | Sort-Object -Unique) -join ',' -replace '^,'
            fill = $_.Group.fill | Select-Object -First 1
            stroke = $_.Group.stroke | Select-Object -First 1
            shape = $_.Group.shape | Select-Object -First 1
            label = $_.Group.label | Select-Object -First 1
            image = $_.Group.image | Select-Object -First 1
            signed = ($_.Group.signed | Where-Object { $_ -notlike $null -or $_ -notlike "" }| Select-Object -First 1)   
    }

} | 
Export-Csv $cookedFile -NoTypeInformation

if($debugger){
Import-Csv $cookedFile | Format-Table
}
Else{}
}
function addMissingDestinations{
        param([switch]$debugger = $false)

$MissingList = Import-Csv $temp1
#import all sources from temp1 file
$sources = $MissingList.objectUser

#For Each objectFilePath that is not yet added to the objectUser list, add
#This function is to create the unique cell on the Draw.io
$childCmdMissing = Import-Csv $temp1 | 
Where-Object {$_.objectFilePath -notlike $null -or 
$_.objectFilePath -notlike "" -and $_.objectFilePath -notin $sources}
$childRepetitionTracker =@() #this function was adding double records, this list is to keep track of them and avoid
$newRowList = @()
    forEach($x in $childCmdMissing){
        
        if($x.objectFilePath -notin $childRepetitionTracker) {
        $tracker = $x.objectFilePath
        $childRepetitionTracker += $tracker
       
        if($tracker -like "*.exe"){$selectImage = $global:imageExe}
        elseif($tracker -like "*.dll"){$selectImage = $global:imageDll}
        elseif($tracker -like "*.ps1*" -or $tracker -like "*.psm1*"){$selectImage = $global:imagePs1}
        elseif($tracker -like "*.json"){$selectImage = $global:imageJson}
        elseif($tracker -like "*.pdf"){$selectImage = $global:pdf}
        elseif($tracker -like "*.txt" -or $tracker -like "*.log"){$selectImage = $global:imageTxt}
        elseif($tracker -like "*.js"){$selectImage = $global:imageJs}
        elseif($tracker -like "*.docm" -or $tracker -like "*.xlxm" -or $tracker -like "*.pptm" -or $tracker -like "*.bat"){$selectedImage = $global:macroFiles}
        elseif($tracker -like "*.png" -or $tracker -like "*.jpeg" -or $tracker -like "*.jpg" -or $tracker -like "*.gif" -or $tracker -like "*.webp"){$selectImage = $global:imageIcon}
        else{$selectImage = $imageLevel4}
        $row =      [PsCustomObject]@{
                objectUser = $x.objectFilePath
                parentFilePath = ""
                processFilePath = ""
                objectFilePath = ""
                processCmd = $x.processCmd
                fill = " #e6e6e6"
                stroke = "#0d0d0d"
                shape = "ellipse"
                label = $label4
                image = $selectImage
                signed = $x.processSigner 

        }
        $newRowList += $row
                }#-close if
    }#-close for Each
$newRowList | Export-Csv -Append $cookedFile -NoTypeInformation

if($debugger){
Import-Csv $cookedFile | Format-Table
}
}
function addMissingProcessCmd{
        param([switch]$debugger = $false)

$MissingList = Import-Csv $temp1
#import all sources from temp1 file
$processCmd = $MissingList.processCmd

#Imports Temp1, to focus on the processCmd
$processCmdMissing = Import-Csv $temp1 | Where-Object {$_.processCmd -notlike $null -or $_.processCmd -notlike ""}
$processTracker =@() #this function was adding double records, this list is to keep track of them and avoid
$newRowList = @()
    forEach($x in $processCmdMissing){

        
        if($x.processCmd -notin $processTracker) {
        $tracker = $x.processCmd
        $processTracker += $tracker
        if($tracker -like "*.exe"){$selectImage = $global:imageExe}
        elseif($tracker -like "*.dll"){$selectImage = $global:imageDll}
        elseif($tracker -like "*.ps1*" -or $tracker -like "*.psm1"){$selectImage = $global:imagePs1}
        elseif($tracker -like "*.json"){$selectImage = $global:imageJson}
        elseif($tracker -like "*.pdf"){$selectImage = $global:pdf}
        elseif($tracker -like "*.txt" -or $tracker -like "*.log"){$selectImage = $global:imageTxt}
        elseif($tracker -like "*.js"){$selectImage = $global:imageJs}
        elseif($tracker -like "*.docm" -or $tracker -like "*.xlxm" -or $tracker -like "*.pptm" -or $tracker -like "*.bat"){$selectedImage = $global:macroFiles}
        elseif($tracker -like "*.png" -or $tracker -like "*.jpeg" -or $tracker -like "*.jpg" -or $tracker -like "*.gif" -or $tracker -like "*.webp"){$selectImage = $global:imageIcon}

        else{$selectImage = $imageLevel4}
        $row =      [PsCustomObject]@{
                objectUser = $x.processCmd
                parentFilePath = ""
                processFilePath = ""
                objectFilePath = ""
                processCmd = ""
                fill = "#e6e6e6"
                stroke = "#0d0d0d"
                shape = "ellipse"
                label = $label4
                image = $selectImage
                signed = $x.processSigner 

        }
        $newRowList += $row
                }#-close if
    }#-close for Each
$newRowList | Export-Csv -Append $cookedFile -NoTypeInformation

if($debugger){
Import-Csv $cookedFile | Format-Table
}
}
function mergeScriptData{
# Import the CSV file and filter the data and Export the filtered data to a new CSV file
$data = Import-Csv $cookedFile | Where-Object {($_.objectUser -ne $null) -and ($_.objectUser -ne "")}
$data | Export-Csv $cleanFile -NoTypeInformation

$data = Get-Content $cleanFile
#$script = Get-Content "$wd\drawIo.txt"
$script = $global:drawioScript
$script > $finalFile
$data >> $finalFile

Get-Content "$finalFile" | Set-Clipboard
}

function cleanUp {
Remove-Item $temp1
Remove-Item $cookedFile
Remove-Item $cleanFile
Remove-Item $finalFile
}
function showHelp {Write-Host " ----- ProcMap Manual ----- " -ForegroundColor Yellow -BackgroundColor Black
Write-Host " Please make sure the file has the following headers " -ForegroundColor Green
Write-Host '"objectUser","parentFilePath","processFilePath","objectFilePath","processSigner"'  -ForegroundColor Yellow -BackgroundColor Black
Write-Host ""
Write-Host "If you run without any switches you will create the connection using the full path of the files" -ForegroundColor Gray
Write-Host "To make it easier to read the connections and to help keep the mindMap small, to see the full path on the mindMap use the switch -s" -ForegroundColor Gray
Write-Host ""
Write-Host " -----COMMANDS----- " -ForegroundColor Yellow -BackgroundColor Black
Write-Host "-s or -simple : This will make the connections using the full path from the file"
Write-Host "-h or -help : Guess what this command does.."

Write-Host " -----EXAMPLES----- " -ForegroundColor Yellow -BackgroundColor Black
Write-Host "Just running bake command you will be prompt with a open file dialog box to choose your file,and will run with default switches, read above for more info"
Write-Host "procmap`n" -ForegroundColor Gray
Write-Host "You can also import the report file directly passing the first argument as the path of your .csv report"
Write-Host "procmap $userProfile\Downloads\report.csv"  -ForegroundColor Gray
Write-Host "procmap -filePath $userProfile\Downloads\report.csv`n"  -ForegroundColor Gray
Write-Host "To Simplify the output of the path use the -s or -simple switch" 
Write-Host "procmap -s "  -ForegroundColor Gray
Write-Host "procmap -s \.report.csv`n"  -ForegroundColor Gray

}
function procmap{
    param(
        [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] #this let file be the first parameter
        [string]$FilePath,
        [switch]$debugger = $false,
        [switch]$simple = $false,
        [switch]$s = $false,
        [switch]$h = $false,
        [switch]$help = $false
    )

if($help -or $h) {showHelp}
Else{
Write-Host "[START OF SCRIPT]" -ForegroundColor White -BackgroundColor Blue

Write-Host "Please make sure the file has the following headers" -ForegroundColor Gray
Write-Host '"objectUser","parentFilePath","processFilePath","objectFilePath",processSigner'-BackgroundColor Black -ForegroundColor Yellow


Try {

if($filePath){
$data = Import-Csv $filePath

} #if $filePath switch was used do not open file Dialog
Else{
OpenFile
$data = Import-Csv $global:filePath
}

if ($s -or $simple){generateTemp1 -simple}
else{generateTemp1}

                    Write-Host "[SUCCESS] Generating Temp1 file..." -ForegroundColor Gray
                    Start-Sleep -Seconds 1

mergeData 
                    Write-Host "[SUCCESS] Merging the columns together..." -ForegroundColor Gray
                    Start-Sleep -Seconds 1

addMissingDestinations
                    Start-Sleep -Seconds 2
addMissingProcessCmd
                    Write-Host "[SUCCESS] Adding Draw.IO components.." -ForegroundColor Gray
                    Start-Sleep -Seconds 1


mergeScriptData
                    Write-Host "[SUCCESS] Sprinkling a bit of magic.." -ForegroundColor Gray
                    Start-Sleep -Seconds 1

Try{
                    Write-Host "[ INFO ] Deleting Temporary files.." -ForegroundColor Gray
                    Start-Sleep -Seconds 1
    cleanUp 
                    Write-Host "[SUCCESS] Deleting Temporary files" -ForegroundColor Gray

}#close try
Catch{Write-Host "[ERROR] - Could not delete temporary files, please delete them manually" -ForegroundColor Red}


                    Write-Host "[SUCCESS] Script finished running`n" -ForegroundColor Gray
                    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 OF SCRIPT]" -ForegroundColor White -BackgroundColor Blue
}#-close try

Catch {Write-Host "An error occurred: $_"
    Write-Host "Please select a .csv file" -BackgroundColor Black -ForegroundColor Red
Write-Host "Please make sure the file has the following headers" -BackgroundColor Black -ForegroundColor red
Write-Host '"objectUser","parentFilePath","processFilePath","objectFilePath",processSigner' -BackgroundColor Black -ForegroundColor Yellow
    
    }


}#-close if checking if user selected -h or -help
    }#---close if Check if -help switch was selected



function runDebug{
Try{
#$data = Import-Csv "C:\Users\paulo.bazzo\OneDrive - NEC Software Solutions\Documents\Projects\Auto-Diagram\ProcessMap\v_5.6_newFeature\24hours_bypass.csv"
generateTemp1 -debugger $true
mergeData -debugger $true
addMissingDestinations -debugger $true
addMissingProcessCmd -debugger $true
mergeScriptData
#cleanUp
}
Catch{Write-Host "Well.. Before you start debugging, you you have do debug the debugger first... " -ForegroundColor Yellow}
}


Export-ModuleMember -Function procmap