functions/ConvertFrom-ThunderScanToCodeDX.ps1

<#
.SYNOPSIS
Converts a ThunderScan JSON report to a Code DX report for importing results.
 
.DESCRIPTION
Used to convert a Thunderscan JSON report file to a Code Dx XML format so it can be imported.
  
.EXAMPLE
ConvertFrom-ThunderScanToCodeDX [scan_file_to_process] [output_directory]
#>

    
Function ConvertFrom-ThunderScanToCodeDX
{
    [cmdletbinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$ThunderScanFilePath,
        [Parameter(Mandatory=$true)]
        [string]$OutputDir
    )
    
    #Setup variables
    $CDate = Get-Date -format "yyyy-MM-dd-HHmmss"
    $OutputFilePath = $OutputDir.Trim("""") + "\TS2CDX-" + $CDate + ".xml"
    
    # Enable for Debugging
    #
    #$ThunderScanFilePath = "Thunderscan.json"
    #$OutputFilePath = "C:\Users\aacuna\Documents\repos\powershell\Code DX\TS2CDX-" + $CDate + ".xml"
    
    $SourceScanFile = $ThunderScanFilePath.Trim("""")
    $ToolName = "ThunderScan"
    $NativeIDName = "Thunder Scan uniqueId"
    $NativeID
    $cwe
    $description
    $locationFile
    $locationLine
    $reportDate

    #Setup Code DX output doc
    [xml]$doc = New-Object System.Xml.XmlDocument
    $dec = $doc.CreateXmlDeclaration("1.0","UTF-8",$null)
    $updateXML= $doc.AppendChild($dec)
    $reportComment = "ThunderScan to Code DX - Generated $CDate"
    $updateXML= $doc.AppendChild($doc.CreateComment($reportComment))
    $root = $doc.CreateNode("element","report",$null)
    
    #read source ThunderScan file and create custom PSO
    $SourceScanData = Get-Content -Encoding UTF8 -Raw -Path $SourceScanFile | ConvertFrom-Json

    #pull report date attributes and reformat for Code DX file
    $reportDate = $SourceScanData.createdTime.SubString(0,10)
    $reportDate = [datetime]::parseexact($reportDate, 'dd/MM/yyyy', $null)
    $reportDate = $reportDate.ToString('yyyy-MM-dd')

    #Set Root attributes
    $root.SetAttribute("date",$reportDate)
    $root.SetAttribute("tool",$ToolName)

    #create findings Element
    $fds = $doc.CreateNode("element","findings",$null)

    #Get parent array of results
    $Results = $SourceScanData.results

    $Results | ForEach-Object{

        #capture language for use in finding metadata
        $resultLanguage = $_.language

        #fetch results object from within results object.
        $subResults = $_.results
        
        If ($subResults.Count -gt 0){
            $subResults | ForEach-Object{

                #fetch vulns
                $vulns = $_.vulnerabilities

                If ($vulns.Count -gt 0){
                    #capture result level information
                    $sev = $_.risk.ToLower()
                    $code = $_.name
                    $resDesc = $_.description
                    $resRec = $_.recommendation

                    #drill into each vulnerability
                    $vulns | ForEach-Object{
                        #Create Code DX elements
                        $fd = $doc.CreateNode("element","finding",$null)
                        $id = $doc.CreateNode("element","native-id",$null)
                        $cwe = $doc.CreateNode("element","cwe",$null)
                        $desc = $doc.CreateNode("element","description",$null)
                        $tl = $doc.CreateNode("element","tool",$null)
                        $loc = $doc.CreateNode("element","location",$null)
                        $ln = $doc.CreateNode("element","line",$null)
                        $md = $doc.CreateNode("element","metadata",$null)

                        #Set finding "fd" attributes
                        $fd.SetAttribute("severity", $_.risk.ToLower())
                        $fd.SetAttribute("type","static")
                        If ($_.vulnerabilities.falsePositive){
                                $fd.SetAttribute("status", "false-positive")
                            } else {
                                $fd.SetAttribute("status", "new")
                        }

                        #Set Native ID attributes
                        $id.SetAttribute("name",$NativeIDName)
                        $id.SetAttribute("value", $_.uniqueId)

                        #Set CWE attributes
                        $cwe.SetAttribute("id", $_.cwe.SubString(4))

                        #Set Tool attributes
                        $tl.SetAttribute("name",$ToolName)
                        $tl.SetAttribute("category","Security")
                        $tl.SetAttribute("code", $_.vulnerabilityType)

                        #set line attributes
                        $ln.SetAttribute("end", $_.functionCalls.line)
                        $ln.SetAttribute("start", $_.functionCalls.line)
                        $updateXML= $loc.AppendChild($ln)

                        #build location node and attributes
                        $loc.SetAttribute("type","file")
                        $loc.SetAttribute("path", $_.vulnerableProjectFile)

                        #Set description attributes
                        $desc.SetAttribute("format", "plain-text")
                        $desc.InnerText = $resDesc

                        #Capture current vuln for use later
                        $vo = $_

                        #Build most of the Metadata Node
                        "vulnerableFunction","vulnerableFunctionCall","inputVariable","functionCalls" | forEach {
                            If($_ -ne "functionCalls"){
                                $e = $doc.CreateNode("element","value",$null)
                                $e.SetAttribute("key",$_)
                                $e.InnerText = $vo.$_
                                $updateXML= $md.AppendChild($e)
                            } else {
                                $fc = $vo.$_
                                "name","line","file" | forEach{
                                    $e = $doc.CreateNode("element","value",$null)
                                    $e.SetAttribute("key","Function Call $_")
                                    $e.InnerText = $fc.$_
                                    $updateXML= $md.AppendChild($e)
                                }
                            }
                        }

                        #Add recommendation element to Metadata
                        $e = $doc.CreateNode("element","value",$null)
                        $e.SetAttribute("key","Recommendation")
                        $e.InnerText = $resRec
                        $updateXML= $md.AppendChild($e)

                        #Build Dataflow metadata
                        $flow = $_.userInputFlow
                        $e = $doc.CreateNode("element","value",$null)
                        $e.SetAttribute("key","User Input Flow")
                        $dataflowtext = ""
                        for ($i=0; $i -lt $flow.length; $i++) {
                            $dataflowtext = $dataflowtext + $flow[$i].name
                            $dataflowtext = $dataflowtext + " at line " + $flow[$i].line
                            $dataflowtext = $dataflowtext + " in file " + $flow[$i].file 
                            if(($i+1) -lt $flow.length){
                                $dataflowtext = $dataflowtext + " --> "
                            }
                        }
                        $e.InnerText = $dataflowtext
                        $updateXML= $md.AppendChild($e)

                        #append remaining children to finding
                        $updateXML= $fd.AppendChild($id)
                        $updateXML= $fd.AppendChild($cwe)
                        $updateXML= $fd.AppendChild($tl)
                        $updateXML= $fd.AppendChild($loc)
                        $updateXML= $fd.AppendChild($desc)
                        $updateXML= $fd.AppendChild($md)

                        #append finding to findings
                        $updateXML= $fds.AppendChild($fd)
                    }
                }
            }
        }
    }


    $updateXML= $root.AppendChild($fds)
    $updateXML= $doc.AppendChild($root) | Out-Null
    Write-Host "Outputing file to: $OutputFilePath"
    $doc.Save($OutputFilePath)
}