Public/Get-MsrcVulnerabilityReportHtml.ps1

Function Get-MsrcVulnerabilityReportHtml {
<#

    .SYNOPSIS
        Use a CVRF document to create a Vulnerability summary

    .DESCRIPTION
        Use a CVRF document to create a Vulnerability summary

    .PARAMETER Vulnerability
        The Vulnerability node of a CVRF document
    
    .PARAMETER ProductTree
        The ProductTree node of a CVRF document

    .EXAMPLE
        Get-MsrcCvrfDocument -ID 2016-Aug -ApiKey 'YOUR API KEY' |
        Get-MsrcVulnerabilityReportHtml |
        Out-File -FilePath Cvrf-CVE-Summary.html

        It creates a report with all the Vulnerabilities in a CVRF document

    .EXAMPLE
        $cvrfDoc = Get-MsrcCvrfDocument -ID 2016-Nov
        $cvrfDoc.Vulnerability | Foreach-Object {
    
            Write-Verbose "Dealing with CVE: $($_.CVE)" -Verbose
            Get-MsrcVulnerabilityReportHtml -Vulnerability $_ -ProductTree $cvrfDoc.ProductTree |
            Out-File -FilePath "Cvrf-$($vulnerability.CVE)-Summary.html"
        }

        It creates a report for each of the Vulnerabilities in a CVRF document

    .EXAMPLE
        $cvrfDoc = Get-MsrcCvrfDocument -ID 2016-Nov
        $HT = @{
            Vulnerability = ($cvrfDoc.Vulnerability | Where-Object {$_.CVE -In @('CVE-2016-0026','CVE-2016-7202','CVE-2016-3343')})
            ProductTree = $cvrfDoc.ProductTree
        }
        Get-MsrcVulnerabilityReportHtml @HT | Out-File -FilePath Cvrf-CVE-Summary.html

        It creates a report for specific Vulnerabilities in a CVRF document
#>

[CmdletBinding()]
[OutputType([string])]
Param(

    [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
    $Vulnerability,

    [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
    $ProductTree
)
Begin{

}
Process {
    $htmlDocumentTemplate = @'
<html>
<head>
    <!-- this is the css from the old bulletin site. Change this to better style your report to your liking -->
    <link rel="stylesheet" href="https://i-technet.sec.s-msft.com/Combined.css?resources=0:ImageSprite,0:TopicResponsive,0:TopicResponsive.MediaQueries,1:CodeSnippet,1:ProgrammingSelector,1:ExpandableCollapsibleArea,0:CommunityContent,1:TopicNotInScope,1:FeedViewerBasic,1:ImageSprite,2:Header.2,2:HeaderFooterSprite,2:Header.MediaQueries,2:Banner.MediaQueries,3:megabladeMenu.1,3:MegabladeMenu.MediaQueries,3:MegabladeMenuSpriteCluster,0:Breadcrumbs,0:Breadcrumbs.MediaQueries,0:ResponsiveToc,0:ResponsiveToc.MediaQueries,1:NavSidebar,0:LibraryMemberFilter,4:StandardRating,2:Footer.2,5:LinkList,2:Footer.MediaQueries,0:BaseResponsive,6:MsdnResponsive,0:Tables.MediaQueries,7:SkinnyRatingResponsive,7:SkinnyRatingV2;/Areas/Library/Content:0,/Areas/Epx/Content/Css:1,/Areas/Epx/Themes/TechNet/Content:2,/Areas/Epx/Themes/Shared/Content:3,/Areas/Global/Content:4,/Areas/Epx/Themes/Base/Content:5,/Areas/Library/Themes/Msdn/Content:6,/Areas/Library/Themes/TechNet/Content:7&amp;v=9192817066EC5D087D15C766A0430C95">
    
    <!-- this style section changes cell widths in the exec header table so that the affected products at the end are wide enough to read -->
    <style>
        #execHeader td:first-child {{ width: 10% ;}}
        #execHeader td:nth-child(5) {{ width: 37% ;}}
    </style>

    <!-- this section defines explicit width for all cells in the affected software tables. This is so the column width is the same across each product -->
    <style>
        .affected_software td:first-child {{ width: 35% ; }}
        .affected_software td:nth-child(2) {{ width: 6% ; }}
        .affected_software td:nth-child(3) {{ width: 7.5% ; }}
        .affected_software td:nth-child(4) {{ width: 7.5% ; }}
        .affected_software td:nth-child(5) {{ width: 7.5% ; }}
        .affected_software td:nth-child(6) {{ width: 32.5% ; }}
        .affected_software td:nth-child(7) {{ width: 4% ; }}
    </style>

</head>

<body lang=EN-US link=blue>
<div id="documentWrapper" style="width: 90%; margin-left: auto; margin-right: auto;">

<h1 id="top">Microsoft CVE Summary</h1>

<p>This report contains detail for the following vulnerabilities:</p>
<ul>
{0}
</ul>
{1}
</div>
<br>
 </body>
</html>
'@
    
    $cveListHtml = ''
    
    $cveSectionHtml = ''

    $Vulnerability | ForEach-Object {

        $v = $_

        Write-Verbose -Message "Dealing with $($_.CVE)"

        #region CVE Summary Table

        $cveSummaryTableHtml = @'
<table id="execHeader" border=1 cellpadding=0 width="99%">
 <thead style="background-color: #ededed">
  <tr>
   <td><b>CVE ID</b></td>
   <td><b>Vulnerability Description</b></td>
   <td><b>Maximum Severity Rating</b></td>
   <td><b>Vulnerability Impact</b></td>
  </tr>
 </thead>
 <tr>
     <td>{0}</td>
     <td>{1}</td>
     <td>{2}</td>
     <td>{3}</td>
 </tr>
</table>
'@


        $MaximumSeverity = Switch (
            ($_.Threats | Where-Object {$_.Type -eq 3 }).Description.Value | Select-Object -Unique
        ) {
            'Critical'  { 'Critical'  ; break }
            'Important' { 'Important' ; break }
            'Moderate'  { 'Moderate'  ; break }
            'Low'       { 'Low'       ; break }
            default {
                Write-Warning "Could not determine the Maximum Severity from the Threats for $($v.CVE)"
                'Unkwown'
            }
        }          
        if (-not($MaximumSeverity)) {
            $MaximumSeverity = 'Unknown'
        }
        

        if ($ImpactValues = ($v.Threats | Where-Object { $_.Type -eq 0 }).Description.Value | Select-Object -Unique) {
            $impactColumn = $ImpactValues -join ',<br>'
        } else {
            Write-Warning "Could not determine the Impact from the Threats for $($v.CVE)"
            $impactColumn = 'Unknown'
        }

        $vulnDescriptionColumnTemplate = @'
        <b>CVE Title:</b> {0}
        <br>
        <b>Description:</b> <br>{1}
        <br>
        <b>FAQ:</b><br>{2}
        <br>
        <b>Mitigations:</b><br>{3}
        <br>
        <b>Workarounds:</b><br>{4}
        <br>
        <b>Revision:</b><br>{5}
        <br>
'@

        
        $vulnDescriptionColumn = $vulnDescriptionColumnTemplate -f @(
            # $cveTitle
            $(
                if ($cveTitle = $v.Title.Value) {
                    $cveTitle
                } else {
                    Write-Warning "Missing Title for $($v.CVE)"
                    ($cveTitle = 'Unknown')
                }
            ),
            # $cveDescription
            $(
                if ($cveDescription = ($v.Notes | Where-Object { $_.Title -eq 'Description'}).Value) {
                    $cveDescription
                } else {
                    Write-Warning "Missing Description for $($v.CVE)"
                    'Unknown'
                }
            ),
            # $cveFaq
            $(
                if (($cveFaq = $v.Notes | Where-Object {$_.Title -eq 'FAQ'}).Value) {
                    $cveFaq -join '<br>'
                } else {
                    'None'
                }
            ),
            # $cveMitigation
            $(
                if ($cveMitigation = $v.Remediations | Where-Object { $_.Type -eq 1 }) {
                    ($cveMitigation).URL -join '<br>'
                } else {
                    'None'
                }
            ),
            # $cveWorkaround
            $(
                if ( $cveWorkaround = ($v.Remediations | Where-Object {$_.Type -EQ 0}).Description.Value) {
                    $cveWorkaround -join '<br>'
                } else {
                    'None'
                }
            ),
            # $Revision
            $(
                $RevisionStrings = @()
                ForEach($rev in  $v.RevisionHistory)
                {
                    if ( $revision = $($rev.Number, $rev.Date, $rev.Description.Value) ) {
                        $RevisionStrings += $($revision -join "&nbsp&nbsp&nbsp&nbsp")
                    }
                }

                if ( $RevisionStrings ) {
                    $RevisionStrings -join "<br />"
                } else {
                    'Unknown'
                }
            )
        )
        
        $cveSectionHtml += '<h1 id="{0}">{0} - {1}</h1> (<a href="#top">top</a>)' -f $v.CVE, $cveTitle

        #region CVE Summary List
        $cveListHtml += '<li><a href="#{0}">{0}</a> - {1}</li>' -f $v.CVE, $cveTitle
        #endregion

        $cveSectionHtml += $cveSummaryTableHtml -f @(
            "$($_.CVE)<br><a href=`"http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=$($_.CVE)`">MITRE</a><br><a href=`"https://web.nvd.nist.gov/view/vuln/detail?vulnId=$($_.CVE)`">NVD</a>",
            $vulnDescriptionColumn,
            $MaximumSeverity,
            $impactColumn
        )
        #endregion

        #region Exploitability Index Table
        $exploitabilityIndexTableHtml = @'
<h2>Exploitability Index</h2>

<p>The following table provides an exploitability assessment of each of the vulnerabilities addressed this month. The vulnerabilities are listed in order of bulletin ID then CVE ID. Only vulnerabilities that have a severity rating of Critical or Important in the bulletins are included.</p>

<table border=1 cellpadding=0 width="99%">
 <thead style="background-color: #ededed">
  <tr>
   <td><b>Exploitability Assessment for Latest Software Release</b></td>
   <td><b>Exploitability Assessment for Older Software Release</b></td>
   <td><b>Denial of Service Exploitability Assessment</b></td>
   <td><b>Publicly Disclosed</b></td>
   <td><b>Exploited</b></td>
  </tr>
 </thead>
 <tr>
     <td>{0}</td>
     <td>{1}</td>
     <td>{2}</td>
     <td>{3}</td>
     <td>{4}</td>
 </tr>
</table>
'@


        if ($ExploitStatusThreat = ($v.Threats | Where-Object { $_.Type -eq 1 } | Select-Object -Last 1).Description.Value) {
            $ExploitStatus = Get-MsrcThreatExploitStatus -ExploitStatusString $ExploitStatusThreat
        } else {
            Write-Warning "Missing ExploitStatus for $($v.CVE)"
        }       

        $cveSectionHtml += $exploitabilityIndexTableHtml -f @(
            # $LatestSoftwareRelease
            $(
                if ($ExploitStatus.LatestSoftwareRelease) {
                    $ExploitStatus.LatestSoftwareRelease
                } else {
                    'Not Found'
                }
            ),
            # $OlderSoftwareRelease
            $(
                if ($ExploitStatus.OlderSoftwareRelease) {
                    $ExploitStatus.OlderSoftwareRelease
                } else {
                    'Not Found'
                }
            ),
            # $DenialOfService
            $(
                if ($ExploitStatus.DenialOfService) {
                    $ExploitStatus.DenialOfService
                } else {
                    'Not Applicable'
                }
            ),
            # $publicly disclosed
            $(
                if ($ExploitStatus.PubliclyDisclosed) {
                    $ExploitStatus.PubliclyDisclosed
                } else {
                    'Not Found'
                }
            ),
            # $Exploited
            $(
                if ($ExploitStatus.Exploited) {
                    $ExploitStatus.Exploited
                } else {
                    'Not Found'
                }
            )
        )    
        #endregion

        #region Affected Software Table
    
        $affectedSoftwareTableTemplate = @'
<table class="affected_software" border=1 cellpadding=0 width="99%">
    <thead style="background-color: #ededed">
        <tr>
            <td colspan="7"><b>{0}</b></td>
        </tr>
    </thead>
        <tr>
            <td><b>Product</b></td>
            <td><b>KB Article</b></td>
            <td><b>Severity</b></td>
            <td><b>Impact</b></td>
            <td><b>Supersedence</b></td>
            <td><b>CVSS Score Set</b></td>
            <td><b>Restart Required</b></td>
        </tr>
        {1}
</table>
<br>
'@


        $affectedSoftwareRowTemplate = @'
        <tr>
                <td>{0}</td>
                <td>{1}</td>
                <td>{2}</td>
                <td>{3}</td>
                <td>{4}</td>
                <td>{5}</td>
                <td>{6}</td>
        </tr>
'@


        $cveSectionHtml += @'
<h2>Affected Software</h2>

<p>The following tables list the affected software details for the vulnerability.</p>
'@

        $affectedSoftware = Get-MsrcCvrfAffectedSoftware -Vulnerability $v -ProductTree $ProductTree
        $affectedSoftwareTableHtml = ''
        
        
        $affectedSoftware.FullProductName | Sort-Object -Unique | ForEach-Object {
            
            $PN = $_

            $affectedSoftware | Where-Object {$_.FullProductName -eq $PN} | ForEach-Object {

                $affectedSoftwareTableHtml += $affectedSoftwareRowTemplate -f @(
                    $PN,
                    $( 
                        if (-not($_.KBArticle)){
                            'None'
                        } else {
                            $($_.KBArticle | select -uniq) -join "<br />"
                        } 
                    ),
                    $( 
                        if (-not($_.Severity)) {
                            'Unknown'
                        } else {
                            $($_.Severity | select -uniq) -join "<br />"
                        } 
                    ),
                    $(
                        if (-not($_.Impact)) { 
                            'Unknown'
                        } else {
                            $($_.Impact | select -uniq) -join "<br />"
                        }
                    ),
                    $( 
                        if (-not($_.Supercedence)) {
                            'None'
                        } else { 
                            $($_.Supercedence | select -uniq) -join "<br />"
                        }
                    ),
                    $(

                        "Base: " + $(if(-not($_.CvssScoreSet.base)){'Unknown'}    else{ $_.CvssScoreSet.base })     + "<br />" +
                        "Temporal: " + $(if(-not($_.CvssScoreSet.temporal)){'Unknown'}else{ $_.CvssScoreSet.temporal }) + "<br />" +
                        "Vector: " + $(if(-not($_.CvssScoreSet.vector)){'Unknown'}  else{ $_.CvssScoreSet.vector })   + "<br />"
                    ),
                    $(
                        if (-not($_.RestartRequired)) {
                            'Unknown'
                        } else {
                            $($_.RestartRequired | select -uniq) -join "<br />"
                        } 
                    )
                )
            

            }
        }

        $cveSectionHtml += $affectedSoftwareTableTemplate -f @(
            $v.CVE,
            $affectedSoftwareTableHtml
        )
        #endregion

        #region Acknowledgments Table
        $acknowledgmentsTableTemplate = @'
<h2>Acknowledgements</h2>
<table border=1 cellpadding=0 width="99%">
 <thead style="background-color: #ededed">
    <tr>
        <td><b>CVE ID</b></td>
        <td><b>Acknowledgements</b></td>
    </tr>
    </thead>
 <tr>
     <td>{0}</td>
     <td>{1}</td>
 </tr>
</table>
'@


        if ($v.Acknowledgments) {
            $ackVal = ''
            $v.Acknowledgments | ForEach-Object {
             
                if ($_.Name.Value) {
                    $ackVal += $_.Name.Value
                    $ackVal += '<br>'
                }
                if ($_.URL) {
                    $ackVal += $_.URL
                    $ackVal += '<br>'
                }
                $ackVal += '<br><br>'
            }
        } else {
            Write-Warning "No Acknowledgments for $($v.CVE)"
            $ackVal = 'None'
        }

        $cveSectionHtml += $acknowledgmentsTableTemplate -f @(
            $v.CVE,
            $ackVal
        )
    }
    #endregion

    (
        $htmlDocumentTemplate -f @( 
            $cveListHtml,
            $cveSectionHtml
        )
    )
}
End {}
}