Framework/Managers/AzSKPDFExtension.ps1
Set-StrictMode -Version Latest class AzSKPDFExtension { static [void] GeneratePDF([string] $reportFolderPath, [PSObject] $organizationObject, [PSObject] $dataObject, [bool] $isLandscape) { # Get Context Info $executedBy = ([ContextHelper]::GetCurrentContext()).Account # Verify whether word is installed on machine If (test-path HKLM:SOFTWARE\Classes\Word.Application) { # Initialize word file try { $Word = New-Object -ComObject word.application $Word.Visible = $false; $AzSKReportDoc = $Word.Documents.Add(); if($isLandscape) { $AzSKReportDoc.PageSetup.Orientation = 1 } else { $AzSKReportDoc.PageSetup.Orientation = 0 } $pdfPath = "$reportFolderPath\SecurityReport.pdf" $margin = 36 # 1.26 cm $AzSKReportDoc.PageSetup.LeftMargin = $margin $AzSKReportDoc.PageSetup.RightMargin = $margin #$AzSKReportDoc.PageSetup.TopMargin = $margin $AzSKReportDoc.PageSetup.BottomMargin = $margin $isOrganizationCore = $false $selection = $Word.Selection $selection.WholeStory $selection.Style = "No Spacing" # Region Front Page [AzSKPDFExtension]::WriteText($selection, 'DevSecOps Kit for Azure (AzSK)','Title', $true, $true, $false) [AzSKPDFExtension]::WriteText($selection, 'Security Report','TOC Heading', $true, $true, $false) $selection.InsertBreak(6) $selection.InsertBreak(6) $selection.InsertBreak(6) $selection.InsertBreak(6) $selection.InsertBreak(6) $TitleTableRange = $selection.Range(); $AzSKReportDoc.Tables.Add($TitleTableRange,11,2) | Out-Null $AzSKTitleTable = $AzSKReportDoc.Tables.item(1) [AzSKPDFExtension]::WriteHeaderTableCell($AzSKTitleTable, 1, 'Organization Name', $organizationObject.OrganizationName) [AzSKPDFExtension]::WriteHeaderTableCell($AzSKTitleTable, 2, 'OrganizationId', $organizationObject.OrganizationId) [AzSKPDFExtension]::WriteHeaderTableCell($AzSKTitleTable, 3, 'AzSK.ADO Version', $dataObject.MyCommand.Version.ToString()) [AzSKPDFExtension]::WriteHeaderTableCell($AzSKTitleTable, 4, 'Generated by', $dataObject.MyCommand.ModuleName.ToString()) [AzSKPDFExtension]::WriteHeaderTableCell($AzSKTitleTable, 5, 'Generated on', (get-date).ToUniversalTime().ToString("MMMM dd, yyyy HH:mm") + " (UTC)") [AzSKPDFExtension]::WriteHeaderTableCell($AzSKTitleTable, 6, 'Requested by', $executedBy.Id.ToString() + " (" + $executedBy.Type.ToString() + ")") [AzSKPDFExtension]::WriteHeaderTableCell($AzSKTitleTable, 7, 'Command Executed', $dataObject.Line.Trim()) [AzSKPDFExtension]::WriteHeaderTableCell($AzSKTitleTable, 8, 'Documentation', 'http://aka.ms/azskdocs') [AzSKPDFExtension]::WriteHeaderTableCell($AzSKTitleTable, 9, 'FAQ', 'http://aka.ms/azskdocs/faq') [AzSKPDFExtension]::WriteHeaderTableCell($AzSKTitleTable, 10, 'Support DL', [ConfigurationManager]::GetAzSKConfigData().SupportDL) $AzSKTitleTable.Borders.OutsideLineStyle = 1 $AzSKTitleTable.Style = 'Table Grid Light' $AzSKTitleTable.Borders.OutsideLineStyle = 1 $AzSKTitleTable.Borders.InsideLineStyle = 0 $AzSKTitleTable.Columns.AutoFit() $Word.Selection.Start= $AzSKReportDoc.Content.End $selection.InsertBreak(7) #end region # Region TOC [AzSKPDFExtension]::WriteText($selection, 'Contents','TOC Heading', $false, $true, $false) $range = $Selection.Range $toc = $AzSKReportDoc.TablesOfContents.Add($range) $selection.TypeParagraph() $selection.InsertBreak(7) # End region TOC # Region Headers/Footers #$Section = $AzSKReportDoc.Sections.Item(1) #$Header = $Section.Footers.Item(1) #$Header.Range.Text = (get-date).ToUniversalTime().ToString("HH:mm MMMM dd, yyyy") + "(UTC)" #$Header.Range.Font.Size = 9 #$Header.Range.ParagraphFormat.Alignment = 2 $AzSKReportDoc.Sections(1).Footers(1).PageNumbers.Add(2) # End region Headers/Footers #region -> Add the CSV report $selection.TypeText("Security Report Summary"); $selection.Style = 'Heading 1' $selection.TypeParagraph() $selection.Style = 'No Spacing' $selection.InsertBreak(6) $ReportRange = $selection.Range(); $reportCSVFilePath = @(); $reportCSVFilePath += Get-ChildItem -Path $reportFolderPath -Filter "*.CSV" -Recurse if($reportCSVFilePath.Length -le 0) { [AzSKPDFExtension]::WriteText($selection, 'Unable to find the required security report under the report folder.','No Spacing', $false, $true, $false) [AzSKPDFExtension]::WriteText($selection, 'Or','No Spacing', $true, $true, $false) [AzSKPDFExtension]::WriteText($selection, 'No controls have been found to evaluate for the organization.','No Spacing', $false, $true, $false) #throw "Didn't find the required security report under the report folder."; } else { $controls = Import-Csv -Path $reportCSVFilePath[0].FullName $isAttestedResult = $false if(($controls | Measure-Object).Count -gt 0) { $Number_Of_Controls = (($controls | Measure-Object).Count +1) if($controls[0] | Get-Member -Name "AttestedSubStatus") { $isAttestedResult = $true } if($isAttestedResult) { $Number_Of_Columns = 7 # ControlID, Status, RG, ResourceName, Control Severity } else { $Number_Of_Columns = 6 } $x = 2 $AzSKReportDoc.Tables.Add($ReportRange,$Number_Of_Controls,$Number_Of_Columns) | Out-Null $AzSKReportTable = $AzSKReportDoc.Tables.item(2) $AzSKReportTable.Cell(1,1).Range.Text = "ControlId" $AzSKReportTable.Cell(1,2).Range.Text = "Status" $AzSKReportTable.Cell(1,3).Range.Text = "ResourceGroup" $AzSKReportTable.Cell(1,4).Range.Text = "Resource" $AzSKReportTable.Cell(1,5).Range.Text = "Severity" $AzSKReportTable.Cell(1,6).Range.Text = "Description" if($isAttestedResult) { $AzSKReportTable.Cell(1,7).Range.Text = "Attestation Description" } Foreach($control in $controls) { $AzSKReportTable.Cell($x,1).Range.Text=$control.ControlId $AzSKReportTable.Cell($x,2).Range.Text=$control.Status if($control | Get-Member -Name "ResourceGroupName") { $AzSKReportTable.Cell($x,3).Range.Text=$control.ResourceGroupName if(($control | Get-Member -Name "ChildResourceName") -and (-Not [string]::IsNullOrEmpty($control.ChildResourceName))) { $AzSKReportTable.Cell($x,4).Range.Text=$control.ResourceName + "/" + $control.ChildResourceName } else { $AzSKReportTable.Cell($x,4).Range.Text=$control.ResourceName } } else { $isOrganizationCore = $true $AzSKReportTable.Cell($x,3).Range.Text="Organization" $AzSKReportTable.Cell($x,4).Range.Text="Organization" } $AzSKReportTable.Cell($x,5).Range.Text=$control.ControlSeverity $AzSKReportTable.Cell($x,6).Range.Text=$control.Description $AzSKReportTable.Cell($x,6).Range.Font.Size = 9 if($isAttestedResult -and ($control.AttestedSubStatus)) { #$AzSKReportTable.Cell($x,7).Range.Text=$control.ActualStatus $attstionDescription = "Attested Status: " + $control.AttestedSubStatus + "`vAttested By: " + $control.AttestedBy + "`vJustification: " + $control.AttesterJustification $AzSKReportTable.Cell($x,7).Range.Text = $attstionDescription $AzSKReportTable.Cell($x,7).Range.Font.Size = 9 } $x++ #if(($control | Get-Member -Name "AttestedSubStatus") -and ($control.AttestedSubStatus)) #{ #$AzSKReportTable.Cell($x,2).Range.Text= "Actual Status : " + $control.ActualStatus #$attstionDescription = "Attestation Description`vAttested Status: " + $control.AttestedSubStatus + "`vAttested By: " + $control.AttestedBy + "`vJustification: " + $control.AttesterJustification #$AzSKReportTable.Cell($x,6).Range.Text = $attstionDescription #$AzSKReportTable.Cell($x,6).Range.Font.Size = 9 #$x++; # } } $AzSKReportTable.Style = 'Grid Table 4 - Accent 1' $AzSKReportTable.Columns.Autofit() $selection = $Word.Selection $selection.WholeStory $selection.Style = "No Spacing" $wdStory = 6 $wdMove = 0 $ret = $selection.EndKey($wdStory, $wdMove) $selection.TypeParagraph() $selection.InsertBreak(7) } #end region #region -> Adding PowerShell output Get-ChildItem -Path $reportFolderPath -Directory | Where-Object {($_.Name -eq "etc")} | ForEach-Object { $rootfolder = $_ [AzSKPDFExtension]::WriteText($selection, 'PowerShell Output','Heading 1', $false, $true, $false) Get-ChildItem -Path $rootfolder.FullName -Recurse -Filter "PowerShellOutput.LOG" | ForEach-Object { $logfilepath = $_ $log = Get-Content $logfilepath.FullName | Out-String [AzSKPDFExtension]::WriteText($selection, $log,'No Spacing', $false, $true, $false) $selection.TypeText("#################################################################"); $selection.TypeParagraph() } } $selection.InsertBreak(7) #end region -> Adding PowerShell output #region -> Adding detailed logs [AzSKPDFExtension]::WriteText($selection, 'Detailed Output','Heading 1', $false, $true, $false) $selection.InsertBreak(6) Get-ChildItem -Path $reportFolderPath -Directory | Where-Object {-not ($_.Name -eq "etc")} | ForEach-Object { $rootfolder = $_ if($isOrganizationCore) { [AzSKPDFExtension]::WriteText($selection, 'Organization Name: '+ ($rootfolder.Name),'Heading 2', $false, $true, $false) } else { [AzSKPDFExtension]::WriteText($selection, 'Resource Group Name: ' + ($rootfolder.Name),'Heading 2', $false, $true, $false) } Get-ChildItem -Path $rootfolder.FullName -Recurse -Filter "*.LOG" | ForEach-Object { $logfilepath = $_ [AzSKPDFExtension]::WriteText($selection, 'Resource Type: ' + ($logfilepath.BaseName),'Heading 3', $false, $true, $false) $logs = Get-Content $logfilepath.FullName ForEach($log in $logs) { [AzSKPDFExtension]::WriteText($selection, ($log | Out-String),'No Spacing', $false, $false, $false) } $selection.TypeParagraph() $selection.InsertBreak(7) } } #end region # Update table of content $toc.Update() } } catch { throw $_.Exception } finally { $wdExportFormatPDF = 17 $wdDoNotSaveChanges = 0 $AzSKReportDoc.ExportAsFixedFormat($pdfPath,$wdExportFormatPDF) $AzSKReportDoc.close([ref]$wdDoNotSaveChanges) $Word.Quit() if (test-path variable:AzSKReportDoc) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($AzSKReportDoc) | Out-Null } if (test-path variable:word) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null } if (test-path variable:range) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($range) | Out-Null } if (test-path variable:ReportRange) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ReportRange) | Out-Null } if (test-path variable:AzSKReportTable) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($AzSKReportTable) | Out-Null } if (test-path variable:TitleTableRange) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($TitleTableRange) | Out-Null } if (test-path variable:AzSKTitleTable) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($AzSKTitleTable) | Out-Null } Remove-Variable range [gc]::collect() [gc]::WaitForPendingFinalizers() } } else { throw ([SuppressedException]::new(("You must have Microsoft Word application installed on machine to generate PDF report."), [SuppressedExceptionType]::Generic)) } } static [void] WriteText([PSObject] $selectionObj, [string] $textToWrite, [string] $style, [bool] $bold, [bool] $newParagraph, [bool] $newLine) { $selectionObj.TypeText($textToWrite); $selectionObj.Style = $style if($bold) { $selectionObj.Range.Font.Bold = 1 } else { $selectionObj.Range.Font.Bold = 0 } if($newParagraph) { $selectionObj.TypeParagraph() } if($newLine) { $selectionObj.TypeText("`v"); } $selectionObj.WholeStory $selectionObj.Style = "No Spacing" } static [void] WriteHeaderTableCell([PSObject] $tableObj, [int] $row, [string] $title, [string] $value) { $tableObj.Cell($row,1).Range.Text = $title $tableObj.Cell($row,1).Range.Bold = 1 $tableObj.Cell($row,2).Range.Text = $value } } # SIG # Begin signature block # MIIjiAYJKoZIhvcNAQcCoIIjeTCCI3UCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA9VpovVuIfOw+c # U7H6uUsl8zj5W9S+rOToqQKhFf8HoKCCDYEwggX/MIID56ADAgECAhMzAAAB32vw # LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn # s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw # PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS # yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG # 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh # EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH # tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS # 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp # TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok # t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4 # b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao # mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD # Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt # VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G # CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+ # Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82 # oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVXTCCFVkCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN # BglghkgBZQMEAgEFAKCBsDAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg4BVSSM5P # 9yqPkR4EtAvOd7c93gu2PyCKRnVMmQsG/CkwRAYKKwYBBAGCNwIBDDE2MDSgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRyAGmh0dHBzOi8vd3d3Lm1pY3Jvc29mdC5jb20g # MA0GCSqGSIb3DQEBAQUABIIBACQCRdSUlekv2UFx5Dnuwecw5x2X/LSmzxv01m67 # KM+B198kh14/7Rj4m/UJyjVAVb+Uzz86dpOf93PioOAh3y/Hqrf2LD8qH6T+GikB # V/t5iEpbmEu66px4e06gsFPzbj1GDhOfSZVQYedtLmwt0nbZt8+93Q3RcBCCL3Yn # 0hPy0fLZeHraZp5FDQm0LnHc3Hnpc3xUksyQumtuiKtKz+se9N0yM+hralkSfVUF # 6W3AMXU5Nrt+o9mbIqIYz21F5OmJMiH8qk/L+hBF3VowAXnGeaUU1JSA50OZKiXc # IRFEkkv06gntW7ptvDJSxK1NH+/feKcnz/Cu/9TicS0S28+hghLlMIIS4QYKKwYB # BAGCNwMDATGCEtEwghLNBgkqhkiG9w0BBwKgghK+MIISugIBAzEPMA0GCWCGSAFl # AwQCAQUAMIIBUQYLKoZIhvcNAQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkK # AwEwMTANBglghkgBZQMEAgEFAAQgfPw3HBLejIE6H8iTMd8AAJFbWKfBw3EaH1VL # eSM34UgCBmD5hU0iwBgTMjAyMTA4MTYwNTUzNDIuNjcyWjAEgAIB9KCB0KSBzTCB # yjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMc # TWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRT # UyBFU046OEE4Mi1FMzRGLTlEREExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFNlcnZpY2Wggg48MIIE8TCCA9mgAwIBAgITMwAAAUtPsqZI1eTCUQAAAAAB # SzANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAe # Fw0yMDExMTIxODI1NTlaFw0yMjAyMTExODI1NTlaMIHKMQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmlj # YSBPcGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4QTgyLUUzNEYt # OUREQTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKE2elDHdi4mv+K+hs+gu2lD16BQ # WXxMd1ZnpIAogl20/cvbgPf93reiaaaNmMLKtCb6P/W0cMDCNAa47Bi+fv15w8JB # 8AH3UmcSn/A/gEwXZJfIx/yT1HzhG2Eh18Yc9dNarOkIJ81aiVURxRWbwB3+vUuu # KRE77goqjqyUNAkqyAoCl8FT/0ntG52+HDWsRDDQ2TUFEZaOsinv+5ahQh9HityX # pTW606JgiicLzs8+kAlBcZGwN0qdUUXg2la8yLJ66Syfm3863DPzawaWd78c1CmY # zOKBHxxnx5cQMkk0hnGi/1YAcePbyBQTb0PyK8BPvTqKHG9O/nRljxbnW7ECAwEA # AaOCARswggEXMB0GA1UdDgQWBBRSqmp+0BKW57orct4+VNOfTUrrxjAfBgNVHSME # GDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRw # Oi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQ # Q0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5o # dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8y # MDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMI # MA0GCSqGSIb3DQEBCwUAA4IBAQAW2rnVlz87UB8kri0QHY2vxsYRUPmpDyXyBchA # ysxli110cf5waKqAX/gaa+Y9+XkUBiH6B//xh3erj+IPb4rgu0luz/e/qanIGXWZ # Di+6wrrl0DKlaaJPVbcWJeOyYIiSNIMOwosUFgfnIYWc0U4QyAv47u7iiwfjZ/zS # dzZZ2dlXr469bTflc9Xpm21QF8VYd0htSR04bU7afjImbXQ59pwi1nTx/OAwyoT5 # /9JOBVY0IdtHYRipNZrKsY/r2MzC1UP0EYZNa2LVeOm8TrIp07wf2e5GLcv4LqNi # e19oSYFNudMURX6RHHUI1ylJv2izzoIBR6FlTVpHNDoJD+mPMIIGcTCCBFmgAwIB # AgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2Vy # dGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAx # MjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYw # JAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZI # hvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoA # goX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiE # VEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+B # VLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3w # V3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXo # eByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYw # ggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNo # WoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBW # BgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUH # AQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp # L2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGV # MIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWlj # cm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIw # NB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4A # dAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM # 9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0 # YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgP # F/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/62 # 5Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZq # kHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96 # LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5v # vfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiF # AR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduW # sqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV # 42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto2 # 29Nfj950iEkSoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNh # IE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjhBODItRTM0Ri05 # RERBMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEw # BwYFKw4DAhoDFQCROjP3t+x4fE05RJDk79sFVIX57qCBgzCBgKR+MHwxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m # dCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA5MRPvTAiGA8y # MDIxMDgxNjEwNDUxN1oYDzIwMjEwODE3MTA0NTE3WjB3MD0GCisGAQQBhFkKBAEx # LzAtMAoCBQDkxE+9AgEAMAoCAQACAikNAgH/MAcCAQACAhE4MAoCBQDkxaE9AgEA # MDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAI # AgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAbh0v03TcugWN4bHyKFwrikKfEsbd # CtRo4EcNbKzsTAy3FTZeJdqMKy5J3L0l1jelRUjK2X0H+VuYYJ1MDbGo+OePpTVZ # VLhpsLqd68C/jYdvv1yDBgWiVPffin5awTevc8dQtsGjTVVEyavPmzg2BG0jlHjz # UIzjrPENudQfsX0xggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ # Q0EgMjAxMAITMwAAAUtPsqZI1eTCUQAAAAABSzANBglghkgBZQMEAgEFAKCCAUow # GgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCDdqCR7 # 0LWYP9Bq4BACyqZL/HHHizggrbns9MYXzaGUXTCB+gYLKoZIhvcNAQkQAi8xgeow # gecwgeQwgb0EIGv27oQieexlgS2z8WP+sgW/RhlbXKeFco4/aFU9RTkjMIGYMIGA # pH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAFLT7KmSNXkwlEA # AAAAAUswIgQgS1mGnjMfr/kFoqfsPPw2exacwidDI8udX+hMNj9UE7swDQYJKoZI # hvcNAQELBQAEggEAhGkEXFVfScBkc1gNDqLIbZ2I67Ckg3kdCpkixF0kiCILmtFL # VRVoVegvw+ESn/yTymqRKFPPwV4suJ7LaWSW5FfqZGjEB6qZJh/GE7tR+IzMKfze # tbgRpYdfTiomD+dxsODvoMFWcsUKIMPwEWMF/OIhjrsY1GIiFsENsPSJH2oLinH8 # AQehueSqDnPHLvD9u1iL0P32Ev4C8hhRyo7flxnu+t5Li9DvClzh87JmSifbCcjS # I0+0juD8ni/5aQReHufQVg2uE/g143srrFErgZXWWdBXjFNPA46IXkmQnktdHuVP # +R5youRoz7p+WFVnu07xCIaLp0QIGOvqkPUlhg== # SIG # End signature block |