Framework/BugLog/AutoBugLog.ps1

Set-StrictMode -Version Latest
class AutoBugLog {
    hidden [ControlStateExtension] $ControlStateExt;
    hidden [SubscriptionContext] $SubscriptionContext;
    hidden [InvocationInfo] $InvocationContext;
    hidden [PSObject] $ControlSettings; 
    hidden [SVTEventContext[]] $ControlResults;
    hidden [bool] $isBugLogCustomFlow = $false;
    hidden [bool] $ShowBugsInS360 = $false;
    
    
    AutoBugLog([SubscriptionContext] $subscriptionContext, [InvocationInfo] $invocationContext, [SVTEventContext[]] $ControlResults, [ControlStateExtension] $ControlStateExt) {
        $this.SubscriptionContext = $subscriptionContext;
        $this.InvocationContext = $invocationContext;    
        $this.ControlResults = $ControlResults;        
        $this.ControlSettings = [ConfigurationManager]::LoadServerConfigFile("ControlSettings.json");
        $this.ControlStateExt = $ControlStateExt      
        
        #flag to check if pluggable bug logging interface (service tree)
        if ([Helpers]::CheckMember($this.ControlSettings.BugLogging, "BugAssigneeAndPathCustomFlow", $null)) {
            $this.isBugLogCustomFlow = $this.ControlSettings.BugLogging.BugAssigneeAndPathCustomFlow;
        }
    }  

    static [string] ComputeHashX([string] $dataToHash)
    {
        return [Helpers]::ComputeHashShort($dataToHash, [Constants]::AutoBugLogTagLen)
    }

    #main function where bug logging takes place
    hidden [void] LogBugInADO([SVTEventContext[]] $ControlResults, [string] $BugLogParameterValue) {
        #check if user has permissions to log bug for the current resource
        if (($ControlResults.ControlResults.VerificationResult -contains "Failed" -or $ControlResults.ControlResults.VerificationResult -contains "Verify") -and $this.CheckPermsForBugLog($ControlResults[0])) {
            #retrieve the project name for the current resource
            $ProjectName = $this.GetProjectForBugLog($ControlResults[0])

            #check if the area and iteration path are valid
            if ([BugLogPathManager]::CheckIfPathIsValid($this.SubscriptionContext.SubscriptionName,$ProjectName,$this.InvocationContext,  $this.ControlSettings.BugLogging.BugLogAreaPath, $this.ControlSettings.BugLogging.BugLogIterationPath, $this.isBugLogCustomFlow)) {
                #Obtain the assignee for the current resource, will be same for all the control failures for this particular resource
                $metaProviderObj = [BugMetaInfoProvider]::new();        
                $AssignedTo = $metaProviderObj.GetAssignee($ControlResults[0], $this.ControlSettings.BugLogging)
                $serviceId = $metaProviderObj.ServiceId

                #Set ShowBugsInS360 if customebuglog is enabled and sericeid not null and ShowBugsInS360 enabled in policy
                if ($this.isBugLogCustomFlow  -and (-not [string]::IsNullOrEmpty($serviceId)) -and ([Helpers]::CheckMember($this.ControlSettings.BugLogging, "ShowBugsInS360") -and $this.ControlSettings.BugLogging.ShowBugsInS360) ) {
                    $this.ShowBugsInS360 = $true;
                }
                else {
                    $this.ShowBugsInS360 = $false;
                }

                #Obtain area and iteration paths
                $AreaPath = [BugLogPathManager]::GetAreaPath()
                $IterationPath = [BugLogPathManager]::GetIterationPath()       
                $BugLoggingProject = [BugLogPathManager]::GetBugLoggingProject() #This project should be used to check if current bug exists or not

                #this falg is added to restrict 'Determining bug logging' message should print only once
                $printLogBugMsg = $true;
                #Loop through all the control results for the current resource
                $ControlResults | ForEach-Object {            
                    
                    $control = $_;                   

                    #filter controls on basis of whether they are baseline or not depending on the value given in autobuglog flag
                    $LogControlFlag=$false
                    if ($BugLogParameterValue -eq "All") {
                        $LogControlFlag = $true
                    }
                    elseif ($BugLogParameterValue -eq "BaselineControls") {
                        $LogControlFlag = $this.CheckBaselineControl($control.ControlItem.ControlID)                
                    }
                    else {
                        $LogControlFlag = $this.CheckPreviewBaselineControl($control.ControlItem.ControlID)
                    }
            
            
                    if ($LogControlFlag -and ($control.ControlResults[0].VerificationResult -eq "Failed" -or $control.ControlResults[0].VerificationResult -eq "Verify") ) {
                
                        #compute hash of control Id and resource Id
                        $hash = $this.GetHashedTag($control.ControlItem.Id, $control.ResourceContext.ResourceId)
                        #check if a bug with the computed hash exists
                        $workItem = $this.GetWorkItemByHash($hash, $BugLoggingProject)
                        if ($workItem[0].results.count -gt 0) {
                            #a work item with the hash exists, find if it's state and reactivate if resolved bug
                            $this.ManageActiveAndResolvedBugs($ProjectName, $control, $workItem, $AssignedTo)
                        }
                        else {
                            if($printLogBugMsg)
                            {
                                Write-Host "Determining bugs to log..." -ForegroundColor Cyan
                            }
                            $printLogBugMsg = $false;

                            #filling the bug template
                            $Title = "[ADOScanner] Control failure - {0} for resource {1} {2}"
                            $Description = "Control failure - {3} for resource {4} {5} </br></br> <b>Control Description: </b> {0} </br></br> <b> Control Result: </b> {6} </br> </br> <b> Rationale:</b> {1} </br></br> <b> Recommendation:</b> {2}"
            
                            $Title = $Title.Replace("{0}", $control.ControlItem.ControlID)
                            $Title = $Title.Replace("{1}", $control.ResourceContext.ResourceTypeName)
                            $Title = $Title.Replace("{2}", $control.ResourceContext.ResourceName)
                            
                            $Description = $Description.Replace("{0}", $control.ControlItem.Description)
                            $Description = $Description.Replace("{1}", $control.ControlItem.Rationale)
                            $Description = $Description.Replace("{2}", $control.ControlItem.Recommendation)
                            $Description = $Description.Replace("{3}", $control.ControlItem.ControlID)
                            $Description = $Description.Replace("{4}", $control.ResourceContext.ResourceTypeName)
                            $Description = $Description.Replace("{5}", $control.ResourceContext.ResourceName)
                            $Description = $Description.Replace("{6}", $control.ControlResults[0].VerificationResult)
                            $RunStepsForControl = " </br></br> <b>Control Scan Command:</b> Run: {0}"
                            $RunStepsForControl = $RunStepsForControl.Replace("{0}", $this.GetControlReproStep($control))
                            $Description += $RunStepsForControl
                            
                
                            #check and append any detailed log and state data for the control failure
                            if ($this.GetDetailedLogForControl($control)) {
                                $Description += "<hr></br><b>Some other details for your reference</b> </br><hr> {7} "
                                $log = $this.GetDetailedLogForControl($control).Replace("\", "\\")
                                $Description = $Description.Replace("{7}", $log)
                    
                            }                
                            $Description = $Description.Replace("`"", "'")
                            $Severity = $this.GetSeverity($control.ControlItem.ControlSeverity)        
                    
                            #function to attempt bug logging
                            $this.AddWorkItem($Title, $Description, $AssignedTo, $AreaPath, $IterationPath, $Severity, $ProjectName, $control, $hash, $serviceId)
                    
                        }
                    }
                }
            }
        }

    }

    #function to get the security command for repro of this bug
    hidden [string] GetControlReproStep([SVTEventContext []] $ControlResult){
        $StepsForRepro=""
        if ($ControlResult.FeatureName -eq "Organization") {
            $StepsForRepro="Get-AzSKADOSecurityStatus -OrganizationName '{0}' -ControlIds '{1}'"
            $StepsForRepro=$StepsForRepro.Replace("{0}",$ControlResult.ResourceContext.ResourceName)
            $StepsForRepro=$StepsForRepro.Replace("{1}",$ControlResult.ControlItem.ControlID)
        }
        elseif ($ControlResult.ResourceContext.ResourceTypeName -eq "Project") {
            $StepsForRepro="Get-AzSKADOSecurityStatus -OrganizationName '{0}' -ProjectNames '{1}' -ControlIds '{2}'"
            $StepsForRepro=$StepsForRepro.Replace("{0}",$ControlResult.ResourceContext.ResourceGroupName)
            $StepsForRepro=$StepsForRepro.Replace("{1}",$ControlResult.ResourceContext.ResourceName)
            $StepsForRepro=$StepsForRepro.Replace("{2}",$ControlResult.ControlItem.ControlID)
        }
        else {
            $StepsForRepro="Get-AzSKADOSecurityStatus -OrganizationName '{0}' -ProjectNames '{1}' -{2}Names '{3}' -ControlIds '{4}'"
            $StepsForRepro=$StepsForRepro.Replace("{0}",$this.SubscriptionContext.SubscriptionName)
            $StepsForRepro=$StepsForRepro.Replace("{1}",$ControlResult.ResourceContext.ResourceGroupName)
            $StepsForRepro=$StepsForRepro.Replace("{2}",$ControlResult.FeatureName)
            $StepsForRepro=$StepsForRepro.Replace("{3}",$ControlResult.ResourceContext.ResourceName)
            $StepsForRepro=$StepsForRepro.Replace("{4}",$ControlResult.ControlItem.ControlID)
        }
        return $StepsForRepro
    }
    
    #function to retrieve project name according to the resource
    hidden [string] GetProjectForBugLog([SVTEventContext[]] $ControlResult) {
        $ProjectName = ""
        #if resource is the organization, call control state extension to retreive attestation host project
        if ($ControlResult.FeatureName -eq "Organization") {
            $ProjectName = $this.ControlStateExt.GetProject()
        }
        #for all the other resource types, retrieve the project name from the control itself
        elseif ($ControlResult.ResourceContext.ResourceTypeName -eq "Project") {
            $ProjectName = $ControlResult.ResourceContext.ResourceName
        }
        else {
            $ProjectName = $ControlResult.ResourceContext.ResourceGroupName
        }
        return $ProjectName
    }
    
    #function to check if the bug can be logged for the current resource type
    hidden [bool] CheckPermsForBugLog([SVTEventContext[]] $ControlResult) {
        switch -regex ($ControlResult.FeatureName) {
            'Organization' {
                #check if any host project can be retrieved, if not use getHostProject to return the correct behaviour output
                if (!($this.GetHostProject($ControlResult))) {
                    return $false
                }                
            }
            'Project' {
                #check if user is member of PA/PCA
                if (!$this.ControlStateExt.GetControlStatePermission($ControlResult.FeatureName, $ControlResult.ResourceContext.ResourceName)) {
                    Write-Host "`nAuto bug logging denied due to insufficient permissions. Make sure you are a project administrator. " -ForegroundColor Red
                    return $false
                }
            }
            'User' {
                #TODO: User controls dont have a project associated with them, can be rectified in future versions
                Write-Host "`nAuto bug logging for user control failures is currently not supported." -ForegroundColor Yellow
                return $false
            }
        }
        return $true
    }
    
    #function to retrive the attestation host project for organization level control failures
    hidden [string] GetHostProject([SVTEventContext[]] $ControlResult) {
        $Project = $null
        
        #check if attestationhost project has been specified along with the command
        if ($this.InvocationContext.BoundParameters["AttestationHostProjectName"]) {
            #check if the user has permission to log bug at org level
            if ($this.ControlStateExt.GetControlStatePermission("Organization", "")) { 
                #user is PCA member, set the host project and return the project name
                $this.ControlStateExt.SetProjectInExtForOrg()    
                $Project = $this.ControlStateExt.GetProject()
                return $Project
            }
            #user is not a member of PCA, invalidate the bug log
            else {
                Write-Host "Error: Could not configure host project to log bugs for organization-specific control failures.`nThis may be because you may not have correct privilege (requires 'Project Collection Administrator')." -ForegroundColor Red
                return $null
            }
        }
        
        else {
            #check if the user is a member of PCA after validating that the host project name was not provided
            if (!$this.ControlStateExt.GetControlStatePermission("Organization", "") ) {
                Write-Host "Error: Auto bug logging denied.`nThis may be because you are attempting to log bugs for areas you do not have RBAC permission to." -ForegroundColor Red
                return $null
                      
            }
            else{
                $Project = $this.ControlStateExt.GetProject()
                #user is a PCA member but the project has not been set for org control failures
                if (!$Project) { 
                    Write-Host "`nNo project defined to log bugs for organization-specific controls." -ForegroundColor Red
                    Write-Host "Use the '-AttestationHostProjectName' parameter with this command to configure the project that will host bug logging details for organization level controls.`nRun 'Get-Help -Name Get-AzSKADOSecurityStatus -Full' for more info." -ForegroundColor Yellow
                    return $null
                }
            }
        }
        return $Project

    }

    #function to check any detailed log and state data for the control failure
    hidden [string] GetDetailedLogForControl([SVTEventContext[]] $ControlResult) {
        $log = ""
        #retrieve the message data for control result
        $Messages = $ControlResult.ControlResults[0].Messages

        $Messages | ForEach-Object {
            if ($_.Message) {
                $log += "<b>$($_.Message)</b> </br></br>"
            }
            #check for state data
            if ($_.DataObject) {
                $log += "<hr>"

                #beautify state data for bug template
                $stateData = [Helpers]::ConvertObjectToString($_, $false)
                $stateData=$stateData.Replace("`"","'")
                $stateData = $stateData.Replace("@{", "@{</br>")
                $stateData = $stateData.Replace("@(", "@(</br>")
                $stateData = $stateData.Replace(";", ";</br>")
                $stateData = $stateData.Replace("},", "</br>},</br>")
                $stateData = $stateData.Replace(");", "</br>});</br>")
                    
                $log += "$($stateData) </br></br>"    
                    
                
            }
        }
        
        #sanitizing input for JSON
        $log = $log.Replace("\", "\\")    

        return $log
    }
    
    #function to retrieve the person to whom the bug will be assigned
    hidden [string] GetAssignee([SVTEventContext[]] $ControlResult) 
    {
        $metaProviderObj = [BugMetaInfoProvider]::new();        
        return $metaProviderObj.GetAssignee($ControlResult, $this.ControlSettings.BugLogging);   
    }

    #function to map severity of the control item
    hidden [string] GetSeverity([string] $ControlSeverity) {
        $Severity = ""
        switch -regex ($ControlSeverity) {
            'Critical' {
                $Severity = "1 - Critical"
            }
            'High' {
                $Severity = "2 - High"
            }
        'Important' {
                $Severity = "2 - High"
            }
            'Medium' {
                $Severity = "3 - Medium"
            }
        'Moderate' {
                $Severity = "3 - Medium"
            }
            'Low' {
                $Severity = "4 - Low"
            }

        }

        return $Severity
    }

    hidden [string] GetSecuritySeverity([string] $ControlSeverity) {
        $Severity = ""
        switch -regex ($ControlSeverity) {
            'Critical' {
                $Severity = "1 - Critical"
            }
            'High' {
                $Severity = "2 - Important"
            }
            'Important' {
                $Severity = "2 - Important"
            }
            'Moderate' {
                $Severity = "3 - Moderate"
            }
            'Medium' {
                $Severity = "3 - Moderate"
            }
            'Low' {
                $Severity = "4 - Low"
            }

        }

        return $Severity
    }
    
    #function to find active bugs and reactivate resolved bugs
    hidden [void] ManageActiveAndResolvedBugs([string]$ProjectName, [SVTEventContext[]] $control, [object] $workItem, [string] $AssignedTo) {
        
        
        $state = ($workItem[0].results.values[0].fields | where { $_.name -eq "State" })
        $id = ($workItem[0].results.values[0].fields | where { $_.name -eq "ID" }).value

        #bug url that redirects user to bug logged in ADO, this is not available via the API response and thus has to be created via the ID of bug
        $bugUrl = "https://{0}.visualstudio.com/{1}/_workitems/edit/{2}" -f $($this.SubscriptionContext.SubscriptionName), $ProjectName , $id

        #TODO : whether the bug is active or resolved, we have to ensure the state of the bug remains active after this function
        #if a PCA assigns this to a non PCA, the control can never be fixed for org/project controls. to tackle this, reassign it to the original owner PCA
        #do this for both active and resolved bugs, as we need it to be assigned to the actual person who can fix this control
        #for other control results, we need not changed the assignee
        <# $url = "https://dev.azure.com/{0}/{1}/_apis/wit/workitems/{2}?api-version=5.1" -f $($this.SubscriptionContext.SubscriptionName), $ProjectName, $id
            $BugTemplate = [ConfigurationManager]::LoadServerConfigFile("TemplateForResolvedBug.json")
            $BugTemplate = $BugTemplate | ConvertTo-Json -Depth 10
            $BugTemplate=$BugTemplate.Replace("{0}",$AssignedTo)
            $header = [WebRequestHelper]::GetAuthHeaderFromUriPatch($url)
            try {
                #TODO: shift all this as a patch request in webrequesthelper class and manage accented characters as well
                $responseObj = Invoke-RestMethod -Uri $url -Method Patch -ContentType "application/json-patch+json ; charset=utf-8" -Headers $header -Body $BugTemplate
            }
            catch {
                #if the user to whom the bug has been assigneed is not a member of org any more
                if ($_.ErrorDetails.Message -like '*System.AssignedTo*') {
                    $body = $BugTemplate | ConvertFrom-Json
                    #let it remain assigned
                    $body[2].value = "";
                    $body = $body | ConvertTo-Json
                    try {
                        $responseObj = Invoke-RestMethod -Uri $url -Method Patch -ContentType "application/json-patch+json ; charset=utf-8" -Headers $header -Body $body
                        $bugUrl = "https://{0}.visualstudio.com/_workitems/edit/{1}" -f $($this.SubscriptionContext.SubscriptionName), $responseObj.id
                    }
                    catch {
                        Write-Host "Could not reactivate the bug" -ForegroundColor Red
                    }
                }
                else {
                    Write-Host "Could not reactivate the bug" -ForegroundColor Red
 
                }
            }
 
        #if the bug state was intially resolved, add in the state data to be referenced later
        if ($state.value -eq "Resolved") {
            $control.ControlResults.AddMessage("Resolved Bug", $bugUrl)
        }
        #if the bug state was initially active
        else {
            $control.ControlResults.AddMessage("Active Bug", $bugUrl)
        }#>



        #change the assignee for resolved bugs only
        if ($state.value -eq "Resolved") {
            $url = "https://dev.azure.com/{0}/{1}/_apis/wit/workitems/{2}?api-version=5.1" -f $($this.SubscriptionContext.SubscriptionName), $ProjectName, $id
            $BugTemplate = [ConfigurationManager]::LoadServerConfigFile("TemplateForResolvedBug.json")
            $BugTemplate = $BugTemplate | ConvertTo-Json -Depth 10 
            $BugTemplate=$BugTemplate.Replace("{0}",$AssignedTo)           
            $header = [WebRequestHelper]::GetAuthHeaderFromUriPatch($url)                
            try {
                #TODO: shift all this as a patch request in webrequesthelper class and manage accented characters as well
                $responseObj = Invoke-RestMethod -Uri $url -Method Patch  -ContentType "application/json-patch+json ; charset=utf-8" -Headers $header -Body $BugTemplate
                $control.ControlResults.AddMessage("Resolved Bug", $bugUrl)
            }
            catch {
                #if the user to whom the bug has been assigneed is not a member of org any more
                if ($_.ErrorDetails.Message -like '*System.AssignedTo*') {
                    $body = $BugTemplate | ConvertFrom-Json
                    #let it remain assigned
                    $body[2].value = "";
                    $body = $body | ConvertTo-Json
                    try {
                        $responseObj = Invoke-RestMethod -Uri $url -Method Patch -ContentType "application/json-patch+json ; charset=utf-8" -Headers $header -Body $body
                        $control.ControlResults.AddMessage("Resolved Bug", $bugUrl)
                    }
                    catch {
                        Write-Host "Could not reactivate the bug" -ForegroundColor Red
                    }
                }
                else {
                    Write-Host "Could not reactivate the bug" -ForegroundColor Red

                }
            }
        }
        else{
            $control.ControlResults.AddMessage("Active Bug", $bugUrl)
        }
    
    }

    #function to search for existing bugs based on the hash
    hidden [object] GetWorkItemByHash([string] $hash, [string] $ProjectName) {
        
        $url = "https://{0}.almsearch.visualstudio.com/{1}/_apis/search/workItemQueryResults?api-version=5.1-preview" -f $($this.SubscriptionContext.SubscriptionName), $ProjectName;

        #TODO: validate set to allow only two values : ReactiveOldBug and CreateNewBug
        #check for ResolvedBugBehaviour in control settings
        if ($this.ControlSettings.BugLogging.ResolvedBugLogBehaviour -ne "ReactiveOldBug") {
            #new bug is to be logged for every resolved bug, hence search for only new/active bug
            $body = '{"searchText":"{0}","skipResults":0,"takeResults":25,"sortOptions":[],"summarizedHitCountsNeeded":true,"searchFilters":{"Projects":["{1}"],"Work Item Types":["Bug"],"States":["Active","New"]},"filters":[],"includeSuggestions":false}' | ConvertFrom-Json
        }
        else {
            #resolved bug needs to be reactivated, hence search for new/active/resolved bugs
            $body = '{"searchText":"{0}","skipResults":0,"takeResults":25,"sortOptions":[],"summarizedHitCountsNeeded":true,"searchFilters":{"Projects":["{1}"],"Work Item Types":["Bug"],"States":["Active","New","Resolved"]},"filters":[],"includeSuggestions":false}' | ConvertFrom-Json
        }

        #tag to be searched
        $body.searchText = "Tags: " + $hash
        $body.searchFilters.Projects = $ProjectName

        $response = [WebRequestHelper]:: InvokePostWebRequest($url, $body)
    
        return  $response

    }

    #function to compute hash and return the tag
    hidden [string] GetHashedTag([string] $ControlId, [string] $ResourceId) {
        $hashedTag = $null
        $stringToHash = "{0}#{1}"
        #create a hash of resource id and control id
        $stringToHash = $stringToHash.Replace("{0}", $ResourceId)
        $stringToHash = $stringToHash.Replace("{1}", $ControlId)
        #return the bug tag
        $hashedTag="ADOScanID: " + [AutoBugLog]::ComputeHashX($stringToHash)
        return $hashedTag
    }

    hidden [void] AddWorkItem([string] $Title, [string] $Description, [string] $AssignedTo, [string] $AreaPath, [string] $IterationPath, [string]$Severity, [string]$ProjectName, [SVTEventContext[]] $control, [string] $hash, [string] $serviceId) {
        
        
        #logging new bugs
        
        $apiurl = 'https://dev.azure.com/{0}/{1}/_apis/wit/workitems/$bug?api-version=5.1' -f $($this.SubscriptionContext.SubscriptionName), $ProjectName;

        $BugTemplate = $null;
        $SecuritySeverity = "";
        
        if ($this.ShowBugsInS360) {
            $BugTemplate = [ConfigurationManager]::LoadServerConfigFile("TemplateForNewBugS360.json")
            $SecuritySeverity = $this.GetSecuritySeverity($control.ControlItem.ControlSeverity)        
        }
        else {
            $BugTemplate = [ConfigurationManager]::LoadServerConfigFile("TemplateForNewBug.json")
        }

        # Replace the field reference name for bug description if it is customized
        if ([Helpers]::CheckMember($this.controlsettings.BugLogging, 'BugDescriptionField') -and -not ([string]::IsNullOrEmpty($this.ControlSettings.BugLogging.BugDescriptionField))) {
            $BugTemplate[1].path = "/fields/"+$this.ControlSettings.BugLogging.BugDescriptionField
        }

        if ($this.InvocationContext.BoundParameters['BugDescriptionField']) {
            $BugTemplate[1].path = "/fields/"+$this.InvocationContext.BoundParameters['BugDescriptionField']
        }
        $BugTemplate = $BugTemplate | ConvertTo-Json -Depth 10 
        $BugTemplate=$BugTemplate.Replace("{0}",$Title)
        $BugTemplate=$BugTemplate.Replace("{1}",$Description)
        $BugTemplate=$BugTemplate.Replace("{2}",$Severity)
        $BugTemplate=$BugTemplate.Replace("{3}",$AreaPath)
        $BugTemplate=$BugTemplate.Replace("{4}",$IterationPath)
        $BugTemplate=$BugTemplate.Replace("{5}",$hash)
        $BugTemplate=$BugTemplate.Replace("{6}",$AssignedTo)

        if ($this.ShowBugsInS360) 
        {
            $BugTemplate=$BugTemplate.Replace("{7}", $this.controlsettings.BugLogging.HowFound)
            #ComplianceArea
            $BugTemplate=$BugTemplate.Replace("{8}", $this.controlsettings.BugLogging.ComplianceArea)
            #ServiceHierarchyId
            $BugTemplate=$BugTemplate.Replace("{9}", $serviceId)
            #ServiceHierarchyIdType
            $BugTemplate=$BugTemplate.Replace("{10}", $this.controlsettings.BugLogging.ServiceTreeIdType)
            
            #Severity
            $BugTemplate=$BugTemplate.Replace("{11}", $SecuritySeverity)
        }

        $responseObj = $null
        $header = [WebRequestHelper]::GetAuthHeaderFromUriPatch($apiurl)

        try {
            $responseObj = Invoke-RestMethod -Uri $apiurl -Method Post -ContentType "application/json-patch+json ; charset=utf-8" -Headers $header -Body $BugTemplate
            $bugUrl = "https://{0}.visualstudio.com/_workitems/edit/{1}" -f $($this.SubscriptionContext.SubscriptionName), $responseObj.id
            $control.ControlResults.AddMessage("New Bug", $bugUrl)
        }
        catch {
            #handle assignee users who are not part of org any more
            if ($_.ErrorDetails.Message -like '*System.AssignedTo*') {
                $BugTemplate = $BugTemplate | ConvertFrom-Json
                $BugTemplate[6].value = "";
                $BugTemplate = $BugTemplate | ConvertTo-Json
                try {
                    $responseObj = Invoke-RestMethod -Uri $apiurl -Method Post -ContentType "application/json-patch+json ; charset=utf-8" -Headers $header -Body $BugTemplate
                    $bugUrl = "https://{0}.visualstudio.com/_workitems/edit/{1}" -f $($this.SubscriptionContext.SubscriptionName), $responseObj.id
                    $control.ControlResults.AddMessage("New Bug", $bugUrl)
                }
                catch {
                    Write-Host "Could not log the bug" -ForegroundColor Red
                }


            }
            #handle the case wherein due to global search area/ iteration paths from different projects passed the checkvalidpath function
            elseif ($_.ErrorDetails.Message -like '*Invalid Area/Iteration id*') {
                Write-Host "Please verify the area and iteration path. They should belong under the same project area." -ForegroundColor Red
            }
            elseif ($_.ErrorDetails.Message -like '*The current user does not have permissions to save work items under the specified area path*') {
                Write-Host "Could not log the bug. You do not have permissions to save work items under the area path [$($AreaPath)]." -ForegroundColor Red
            }
            else {
                Write-Host "Could not log the bug" -ForegroundColor Red
            }
        }
        
        
    }

    #the next two functions to check baseline and preview baseline, are duplicate controls that are present in ADOSVTBase as well.
    #they have been added again, due to behaviour of framework, where the file that needs to called in a certain file has to be mentioned
    #above the other file as it is dumped in the memory before the second file. This behaviour will effectively create a deadlock
    #in this case, as we have to create autobuglog object in adosvtbase, making it be declared first in framework and hence the following controls
    #cant be accessed here from adosvtbase.

    #function to check if the current control is a baseline control or not
    hidden [bool] CheckBaselineControl($controlId) {
        if (($null -ne $this.ControlSettings) -and [Helpers]::CheckMember($this.ControlSettings, "BaselineControls.ResourceTypeControlIdMappingList")) {
            $baselineControl = $this.ControlSettings.BaselineControls.ResourceTypeControlIdMappingList | Where-Object { $_.ControlIds -contains $controlId }
            if (($baselineControl | Measure-Object).Count -gt 0 ) {
                return $true
            }
        }

        if (($null -ne $this.ControlSettings) -and [Helpers]::CheckMember($this.ControlSettings, "BaselineControls.SubscriptionControlIdList")) {
            $baselineControl = $this.ControlSettings.BaselineControls.SubscriptionControlIdList | Where-Object { $_ -eq $controlId }
            if (($baselineControl | Measure-Object).Count -gt 0 ) {
                return $true
            }
        }
        return $false
    }
    
    #function to check if the current control is a preview baseline control or not
    hidden [bool] CheckPreviewBaselineControl($controlId) {
        if (($null -ne $this.ControlSettings) -and [Helpers]::CheckMember($this.ControlSettings, "PreviewBaselineControls.ResourceTypeControlIdMappingList")) {
            $PreviewBaselineControls = $this.ControlSettings.PreviewBaselineControls.ResourceTypeControlIdMappingList | Where-Object { $_.ControlIds -contains $controlId }
            if (($PreviewBaselineControls | Measure-Object).Count -gt 0 ) {
                return $true
            }
        }

        if (($null -ne $this.ControlSettings) -and [Helpers]::CheckMember($this.ControlSettings, "PreviewBaselineControls.SubscriptionControlIdList")) {
            $PreviewBaselineControls = $this.ControlSettings.PreviewBaselineControls.SubscriptionControlIdList | Where-Object { $_ -eq $controlId }
            if (($PreviewBaselineControls | Measure-Object).Count -gt 0 ) {
                return $true
            }
        }
        return $false
    }

    
    
}