Framework/Listeners/SecurityRecommendationReport.ps1

Set-StrictMode -Version Latest 
#Listner to write CA scan status on completion of resource scan
class SecurityRecommendationReport: ListenerBase
{
    hidden static [SecurityRecommendationReport] $Instance = $null;
    static [SecurityRecommendationReport] GetInstance()
    {
        if ( $null -eq  [SecurityRecommendationReport]::Instance)
        {
            [SecurityRecommendationReport]::Instance = [SecurityRecommendationReport]::new();
        }    
        return [SecurityRecommendationReport]::Instance
    }

    [void] RegisterEvents()
    {
        $this.UnregisterEvents();       

        $this.RegisterEvent([AzSKRootEvent]::GenerateRunIdentifier, {
            $currentInstance = [SecurityRecommendationReport]::GetInstance();
            try 
            {
                $currentInstance.SetRunIdentifier([AzSKRootEventArgument] ($Event.SourceArgs | Select-Object -First 1));
            }
            catch 
            {
                $currentInstance.PublishException($_);
            }
        });

        

         $this.RegisterEvent([AzSKRootEvent]::CommandCompleted, {
            $currentInstance = [SecurityRecommendationReport]::GetInstance();
            try 
            {
                $messages = $Event.SourceArgs.Messages;
                if(($messages | Measure-Object).Count -gt 0 -and $Event.SourceArgs.Messages[0].Message -eq "RecommendationData")
                {
                    $reportTemplateFileContent = [ConfigurationHelper]::LoadOfflineConfigFile("SecurityRecommendationReport.html", $false);
                    $reportObject = [RecommendedSecurityReport] $Event.SourceArgs.Messages[0].DataObject;
                    
                    if([string]::IsNullOrWhiteSpace($reportObject.ResourceGroupName))
                    {
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#rgName#]", "Not Specified");
                    }
                    else {
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#rgName#]", "[$($reportObject.ResourceGroupName)]");
                    }

                    if(($reportObject.Input.Features | Measure-Object).Count -le 0)
                    {
                        #$currentInstance.WriteMessage("Features: Not Specified", [MessageType]::Default);
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#features#]", "Not Specified");
                    }
                    else {
                        $featuresString = [String]::Join(",", $reportObject.Input.Features);
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#features#]", "[$featuresString]");
                    }

                    if(($reportObject.Input.Categories | Measure-Object).Count -le 0)
                    {
                        #$currentInstance.WriteMessage("Categories: Not Specified", [MessageType]::Default);
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#categories#]", "Not Specified");
                    }
                    else {
                        $categoriesString = [String]::Join(",", $reportObject.Input.Categories);                        
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#categories#]", "[$categoriesString]");
                    }                    
                    if($null -ne $reportObject.Recommendations.CurrentFeatureGroup)
                    {
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#cgranking#]", "$($reportObject.Recommendations.CurrentFeatureGroup.Ranking)");
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#cgInstCount#]", "$($reportObject.Recommendations.CurrentFeatureGroup.TotalOccurances)");
                        $featuresString = [String]::Join(",", $reportObject.Recommendations.CurrentFeatureGroup.Features);
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#cgF#]", "$featuresString");
                        $categoriesString = [String]::Join(",", $reportObject.Recommendations.CurrentFeatureGroup.Categories);
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#cgC#]", "$categoriesString");
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#cgPass#]", "$($reportObject.Recommendations.CurrentFeatureGroup.TotalSuccessCount)");
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#cgFail#]", "$($reportObject.Recommendations.CurrentFeatureGroup.TotalFailCount)");
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#up#]", "$($reportObject.Recommendations.CurrentFeatureGroup.UsagePercentage)");
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#frate#]", "$($reportObject.Recommendations.CurrentFeatureGroup.FailureRate)");
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#omu#]", "$($reportObject.Recommendations.CurrentFeatureGroup.OtherMostUsed)");
                    }
                    else {
                        #TODO: need to hide the div
                        #$currentInstance.WriteMessage("Cannot find exact matching combination for the current user input.", [MessageType]::Default);
                    }
                    if(($reportObject.Recommendations.RecommendedFeatureGroups | Measure-Object).Count -gt 0)
                    {
                        $recommendationTemplate = @"
                        <table>
                    <tr><div class="dataPoint" id="cgranking[#i#]"><td> Category Group Ranking:</td><td> [#cgranking#]</td></b></div></tr>
              <tr><div class="dataPoint" id="instcount[#i#]"><td> No. of instances with same combination:</td><td> [#instcount#]</td></div></tr>
              <tr><div class="dataPoint" id="fc[#i#]"><td> Feature combination:</td><td>[#fc#]</td></div></tr>
              <tr><div class="dataPoint" id="cc[#i#]"> <td>Category Combination:</td><td> [#cc#]</td></div></tr>
              <tr><div class="dataPoint" id="measures[#i#]"><td> Measures:</td><td> [Total Pass: [#pass#] [Total Fail: [#fail#]]</td></div></tr>
              <tr><div class="dataPoint" id="up[#i#]"> <td>Usage Percentage:</td><td> [#up#]</td></div></tr>
              <tr><div class="dataPoint" id="fr[#i#]"> <td>Failure Rate:</td><td> [#frate#]</td></div></tr>
              <tr><div class="dataPoint" id="omu[#i#]"> <td>SimilarCombinations:</td><td> [#omu#]</td></div></tr>
              </table>
              <br />
"@
;
                        $recommendationHtml = "";
                        $i = 0;
                        $orderedRecommendations = $reportObject.Recommendations.RecommendedFeatureGroups | Sort-Object -Property Ranking
                        $orderedRecommendations | ForEach-Object {
                            $recommendation = $_;
                            $i = $i + 1;
                            $recommendationPart = $recommendationTemplate.Replace("[#i#]", $i);
                            $recommendationPart = $recommendationPart.Replace("[#cgranking#]","$($recommendation.Ranking)");
                            $recommendationPart = $recommendationPart.Replace("[#instcount#]","$($recommendation.TotalOccurances)");
                            $featuresString = [String]::Join(",", $recommendation.Features);
                            $recommendationPart = $recommendationPart.Replace("[#fc#]","$featuresString");
                            $categoriesString = [String]::Join(",", $recommendation.Categories);
                            $recommendationPart = $recommendationPart.Replace("[#cc#]","$categoriesString");
                            $recommendationPart = $recommendationPart.Replace("[#pass#]","$($recommendation.TotalSuccessCount) ]");
                            $recommendationPart = $recommendationPart.Replace("[#fail#]","$($recommendation.TotalFailCount)");
                            $recommendationPart = $recommendationPart.Replace("[#up#]","$($recommendation.UsagePercentage)");
                            $recommendationPart = $recommendationPart.Replace("[#frate#]","$($recommendation.FailureRate)");
                            $recommendationPart = $recommendationPart.Replace("[#omu#]","$($recommendation.OtherMostUsed)");
                            $recommendationHtml += $recommendationPart
                        }
                        $reportTemplateFileContent = $reportTemplateFileContent.Replace("[#recommendations#]", "$recommendationHtml");
                    
                    }
                    $reportFilePath = [WriteFolderPath]::GetInstance().FolderPath + "/SecurityRecommendationReport-" + $currentInstance.RunIdentifier + ".html";
                    $reportTemplateFileContent | Out-File -FilePath $reportFilePath -Force;

                   # $currentInstance.WriteMessage(($dataObject | ConvertTo-Json -Depth 10), [MessageType]::Info)
                }
            }
            catch 
            {
                $currentInstance.PublishException($_);
            }
        });
    }

}