From 7a65a8eed940809164f77ccb2d68e9d915c0daad Mon Sep 17 00:00:00 2001 From: SINIKI Date: Thu, 2 Jan 2020 15:04:49 +0530 Subject: [PATCH 01/57] SQLDatabase SVT control error bugfix 1. Added error handling for SQLAudit command. Expected result: Control should return Verify state if storage account is not found. --- .../Core/SVT/Services/SQLDatabase.ps1 | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 b/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 index 025b909c4..69cfa42dc 100644 --- a/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 +++ b/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 @@ -85,10 +85,17 @@ class SQLDatabase: AzSVTBase hidden [ControlResult] CheckSqlServerAuditing([ControlResult] $controlResult) { - $serverAudit = Get-AzSqlServerAudit -ResourceGroupName $this.ResourceContext.ResourceGroupName -ServerName $this.ResourceContext.ResourceName -ErrorAction Stop - - $controlResult.AddMessage([MessageData]::new("Current audit status for SQL server [$($this.ResourceContext.ResourceName)]:", $serverAudit)) - + $serverAudit = $null + try { + $serverAudit = Get-AzSqlServerAudit -ResourceGroupName $this.ResourceContext.ResourceGroupName -ServerName $this.ResourceContext.ResourceName -ErrorAction Stop + $controlResult.AddMessage([MessageData]::new("Current audit status for SQL server [$($this.ResourceContext.ResourceName)]:", $serverAudit)) + } + catch + { + $controlResult.AddMessage("$($_.Exception.Message)") + $controlResult.AddMessage([VerificationResult]::Verify, ""); + } + if($null -ne $serverAudit){ $isCompliant = (($serverAudit.BlobStorageTargetState -eq [AuditStateType]::Enabled) ` -and ($serverAudit.RetentionInDays -eq $this.ControlSettings.SqlServer.AuditRetentionPeriod_Min -or $serverAudit.RetentionInDays -eq $this.ControlSettings.SqlServer.AuditRetentionPeriod_Forever)) @@ -104,7 +111,7 @@ class SQLDatabase: AzSVTBase } else{ - $controlResult.AddMessage("Unable to get audit details for SQL server [$($this.ResourceContext.ResourceName)]"); + $controlResult.AddMessage("Unable to get audit details for SQL server [$($this.ResourceContext.ResourceName)]."); } return $controlResult; @@ -202,10 +209,18 @@ class SQLDatabase: AzSVTBase hidden [ControlResult] CheckSqlServerThreatDetection([ControlResult] $controlResult) { $isCompliant = $false - + $serverAudit = $null #First check if the server auditing is enabled, without which TD does not work - $serverAudit = Get-AzSqlServerAudit -ResourceGroupName $this.ResourceContext.ResourceGroupName -ServerName $this.ResourceContext.ResourceName.ToLower() -ErrorAction Stop - + try + { + $serverAudit = Get-AzSqlServerAudit -ResourceGroupName $this.ResourceContext.ResourceGroupName -ServerName $this.ResourceContext.ResourceName.ToLower() -ErrorAction Stop + } + catch + { + $controlResult.AddMessage("$($_.Exception.Message)") + $controlResult.AddMessage([VerificationResult]::Verify, ""); + } + if($null -ne $serverAudit){ #Check if Audit is Enabled if($serverAudit.BlobStorageTargetState -eq [AuditStateType]::Enabled){ @@ -400,26 +415,6 @@ class SQLDatabase: AzSVTBase return $controlResult; } - # TODO: This function is not being called. We can delete this function if not being used. - hidden [bool] IsServerThreatDetectionEnabled(){ - $isCompliant = $false - $serverAudit = Get-AzSqlServerAudit -ResourceGroupName $this.ResourceContext.ResourceGroupName -ServerName $this.ResourceContext.ResourceName -ErrorAction Stop - if($null -ne $serverAudit){ - if($serverAudit.BlobStorageTargetState -eq 'Enabled'){ - # TODO: We are temporarily suppressing the alias deprecation warning message given by the below Az.SQL cmdlet. - $serverThreat = Get-AzSqlServerAdvancedThreatProtectionSettings ` - -ResourceGroupName $this.ResourceContext.ResourceGroupName ` - -ServerName $this.ResourceContext.ResourceName ` - -ErrorAction Stop -WarningAction SilentlyContinue - $excludedTypeCount = ($serverThreat.ExcludedDetectionTypes | Measure-Object ).Count - $isCompliant = (($serverThreat.ThreatDetectionState -eq [ThreatDetectionStateType]::Enabled) ` - -and ($excludedTypeCount -eq 0) ` - -and (($serverThreat.EmailAdmins -eq $True) -or ($null -ne $serverThreat.NotificationRecipientsEmails))) - } - } - return $isCompliant -} - hidden [PSObject[]] GetSqlServerFirewallRules() { if ($null -eq $this.SqlFirewallDetails) { From 8cc566b6b22428014c045f8acde2f717fb93a439 Mon Sep 17 00:00:00 2001 From: "v-arbagh@microsoft.com" Date: Thu, 2 Jan 2020 17:48:48 +0530 Subject: [PATCH 02/57] Linux VM Antimalware control is erroring out fixed --- src/AzSK/Framework/Core/SVT/Services/VirtualMachine.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzSK/Framework/Core/SVT/Services/VirtualMachine.ps1 b/src/AzSK/Framework/Core/SVT/Services/VirtualMachine.ps1 index ec9842bec..3d4302181 100644 --- a/src/AzSK/Framework/Core/SVT/Services/VirtualMachine.ps1 +++ b/src/AzSK/Framework/Core/SVT/Services/VirtualMachine.ps1 @@ -326,7 +326,7 @@ class VirtualMachine: AzSVTBase $LinuxAntimalwareStatusWSQuery =[string]::Format($this.VMControlSettings.QueryForLinuxAntimalwareStatus,($this.ResourceContext.ResourceId).ToLower()); $queryStatusResult = [LogAnalyticsHelper]::QueryStatusfromWorkspace($this.Workspace, $LinuxAntimalwareStatusWSQuery); - if($queryStatusResult.Count -gt 0 ) + if($queryStatusResult -ne $null -and ($queryStatusResult | Measure-Object).Count -gt 0 ) { $controlResult.AddMessage([VerificationResult]::Passed,"Antimalware is configured correctly on the VM. Validated the status through ASC workspace query."); } From ebadd051fcacb44b17b55ee45a0386847fbcaeaf Mon Sep 17 00:00:00 2001 From: SINIKI Date: Fri, 3 Jan 2020 07:19:33 +0530 Subject: [PATCH 03/57] Fixed Azure_ServiceBus_AuthZ_Use_Minimum_Access_Policies --- src/AzSK/Framework/Core/SVT/Services/ServiceBus.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/Services/ServiceBus.ps1 b/src/AzSK/Framework/Core/SVT/Services/ServiceBus.ps1 index ca3d10ff5..526017a9b 100644 --- a/src/AzSK/Framework/Core/SVT/Services/ServiceBus.ps1 +++ b/src/AzSK/Framework/Core/SVT/Services/ServiceBus.ps1 @@ -202,22 +202,22 @@ class ServiceBus: AzSVTBase $faliedClients = New-Object -TypeName PSObject if(($fullPermissionQueues | Measure-Object).count -gt 0) { - $faliedClients | Add-Member -NotePropertyName FailedQueues -NotePropertyValue $fullPermissionQueues + $faliedClients | Add-Member -NotePropertyName FailedQueuesWithFullPermission -NotePropertyValue $fullPermissionQueues $controlResult.AddMessage([MessageData]::new("Validate the authorization rules for the Queue are defined with limited permissions.", $fullPermissionQueues)); } if(($noPolicyQueues | Measure-Object).count -gt 0) { - $faliedClients | Add-Member -NotePropertyName FailedQueues -NotePropertyValue $noPolicyQueues + $faliedClients | Add-Member -NotePropertyName FailedQueuesWithNoAccessPolicy -NotePropertyValue $noPolicyQueues $controlResult.AddMessage([MessageData]::new("No Authorization rules defined for following Queue. Either Queue is not in use or namespace level access policy is used. Applications (senders/receivers) must not use access policies defined at Service Bus namespace level.", $noPolicyQueues)); } if(($fullPermissionTopics | Measure-Object).count -gt 0) { - $faliedClients | Add-Member -NotePropertyName FailedQueues -NotePropertyValue $fullPermissionTopics + $faliedClients | Add-Member -NotePropertyName FailedTopicsWithFullPermission -NotePropertyValue $fullPermissionTopics $controlResult.AddMessage([MessageData]::new("Validate the authorization rules for the Topic are defined with limited permissions.", $fullPermissionTopics)); } if(($noPolicyTopics | Measure-Object).count -gt 0) { - $faliedClients | Add-Member -NotePropertyName FailedQueues -NotePropertyValue $noPolicyTopics + $faliedClients | Add-Member -NotePropertyName FailedTopicsWithNoAccessPolicy -NotePropertyValue $noPolicyTopics $controlResult.AddMessage([MessageData]::new("No Authorization rules defined for following Topic. Either Topic is not in use or namespace level access policy is used. Applications (senders/receivers) must not use access policies defined at Service Bus namespace level.", $noPolicyTopics)); } From 757e9b22dbdfd58044c346b3d9bad9f45bd8513f Mon Sep 17 00:00:00 2001 From: Tarun Shukla Date: Fri, 3 Jan 2020 18:23:27 +0530 Subject: [PATCH 04/57] Bug Fix for control SQLDatabase_Audit_Enable_Threat_Detection_Server' --- src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 b/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 index 025b909c4..465d6f8b8 100644 --- a/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 +++ b/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 @@ -95,12 +95,16 @@ class SQLDatabase: AzSVTBase if ($isCompliant){ $controlResult.VerificationResult = [VerificationResult]::Passed - } + }elseif (($serverAudit.EventHubTargetState -eq [AuditStateType]::Enabled) -or ($serverAudit.LogAnalyticsTargetState -eq [AuditStateType]::Enabled)) { + #Mark control as 'Verify' if Audit settings other than Storage is enabled as in such case log retention data is not available + $controlResult.AddMessage([VerificationResult]::Verify, + "Please verify that audit logs are retained for at least $($this.ControlSettings.SqlServer.AuditRetentionPeriod_Min) days for SQL server - [$($this.ResourceContext.ResourceName)]"); + } else{ $controlResult.EnableFixControl = $true; $controlResult.AddMessage([VerificationResult]::Failed, "Audit settings are either disabled OR not retaining logs for at least $($this.ControlSettings.SqlServer.AuditRetentionPeriod_Min) days for SQL server - [$($this.ResourceContext.ResourceName)]"); - } + } } else{ @@ -208,7 +212,7 @@ class SQLDatabase: AzSVTBase if($null -ne $serverAudit){ #Check if Audit is Enabled - if($serverAudit.BlobStorageTargetState -eq [AuditStateType]::Enabled){ + if(($serverAudit.BlobStorageTargetState -eq [AuditStateType]::Enabled) -or ($serverAudit.EventHubTargetState -eq [AuditStateType]::Enabled) -or ($serverAudit.LogAnalyticsTargetState -eq [AuditStateType]::Enabled)){ # TODO: We are temporarily suppressing the alias deprecation warning message given by the below Az.SQL cmdlet. $serverThreat = Get-AzSqlServerAdvancedThreatProtectionSettings ` -ResourceGroupName $this.ResourceContext.ResourceGroupName ` From 20994a8f09fb264d9801408f7a8a66d73acb57d9 Mon Sep 17 00:00:00 2001 From: Tarun Shukla Date: Fri, 3 Jan 2020 18:28:58 +0530 Subject: [PATCH 05/57] Bug Fix GOP : Read-Host reads input in next line --- src/AzSK/Framework/Core/PolicySetup/PolicySetup.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AzSK/Framework/Core/PolicySetup/PolicySetup.ps1 b/src/AzSK/Framework/Core/PolicySetup/PolicySetup.ps1 index ccfe8032a..1aa040b2d 100644 --- a/src/AzSK/Framework/Core/PolicySetup/PolicySetup.ps1 +++ b/src/AzSK/Framework/Core/PolicySetup/PolicySetup.ps1 @@ -1487,8 +1487,8 @@ class PolicySetup: AzCommandBase $existingPolicyFolderContent= Get-ChildItem -Path $($this.FolderPath) -ErrorAction SilentlyContinue if(($existingPolicyFolderContent | Measure-Object).Count -gt 0) { - $this.PublishCustomMessage("Warning: Policy folder already contains files. Downloading policies can override existing files. `nDo you want to continue(Y/N):", $([MessageType]::Warning)) - $answer= Read-Host + $this.PublishCustomMessage("Warning: Policy folder already contains files. Downloading policies can override existing files.", $([MessageType]::Warning)) + $answer= Read-Host "Do you want to continue(Y/N)" if($answer.ToLower() -ne "y" ) { $downloadPolicy = $false From 6472e693f9ba58dbb77d4a2c1243561119334eed Mon Sep 17 00:00:00 2001 From: "Ritika Singh (Tata Consultancy Services Ltd)" Date: Tue, 7 Jan 2020 00:36:38 +0530 Subject: [PATCH 06/57] BugFix for GeneratePDF switch None as parameter not required --- src/AzSK.Framework/Models/Enums.ps1 | 1 - src/AzSK/SVT/SVT.ps1 | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/AzSK.Framework/Models/Enums.ps1 b/src/AzSK.Framework/Models/Enums.ps1 index a720e9cde..95f392ad4 100644 --- a/src/AzSK.Framework/Models/Enums.ps1 +++ b/src/AzSK.Framework/Models/Enums.ps1 @@ -92,7 +92,6 @@ enum MonitoringSolutionInstallationOption enum GeneratePDF { - None Landscape Portrait } diff --git a/src/AzSK/SVT/SVT.ps1 b/src/AzSK/SVT/SVT.ps1 index 973050837..0fa977056 100644 --- a/src/AzSK/SVT/SVT.ps1 +++ b/src/AzSK/SVT/SVT.ps1 @@ -161,9 +161,10 @@ function Get-AzSKAzureServicesSecurityStatus $DoNotOpenOutputFolder, [GeneratePDF] + [ValidateSet("Landscape", "portrait")] [Parameter(Mandatory = $false)] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::None, + $GeneratePDF = [GeneratePDF]::portrait, [switch] [Parameter(Mandatory = $false)] @@ -379,9 +380,10 @@ function Get-AzSKSubscriptionSecurityStatus $DoNotOpenOutputFolder, [GeneratePDF] + [ValidateSet("Landscape", "portrait")] [Parameter(Mandatory = $false)] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::None, + $GeneratePDF = [GeneratePDF]::portrait, [switch] [Parameter(Mandatory = $false)] From 2d0e4e6e94f6da61197a9fdb8aecaf4956c8d5bf Mon Sep 17 00:00:00 2001 From: "Ritika Singh (Tata Consultancy Services Ltd)" Date: Tue, 7 Jan 2020 15:56:44 +0530 Subject: [PATCH 07/57] Bug fix for GAI-HostInfo: AzSK description --- src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 | 2 +- src/AzSK.Framework/Managers/AzSKPDFExtension.ps1 | 2 +- src/AzSK/AzSKStaging.psd1 | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 b/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 index 2a66e614f..ce6fe9255 100644 --- a/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 +++ b/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 @@ -27,7 +27,7 @@ Copyright = '(c) 2017 Microsoft Corporation. All rights reserved.' # Description of the functionality provided by this module - Description = 'DevSecOps Kit for AzureDevOps(AzSK) - Preview' + Description = 'Secure DevOps Kit for Azure (AzSK) - Preview' # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '5.0' diff --git a/src/AzSK.Framework/Managers/AzSKPDFExtension.ps1 b/src/AzSK.Framework/Managers/AzSKPDFExtension.ps1 index a882054a6..96b25608a 100644 --- a/src/AzSK.Framework/Managers/AzSKPDFExtension.ps1 +++ b/src/AzSK.Framework/Managers/AzSKPDFExtension.ps1 @@ -40,7 +40,7 @@ class AzSKPDFExtension $selection.Style = "No Spacing" # Region Front Page - [AzSKPDFExtension]::WriteText($selection, 'DevSecOps Kit for Azure (AzSK)','Title', $true, $true, $false) + [AzSKPDFExtension]::WriteText($selection, 'Secure DevOps Kit for Azure (AzSK)','Title', $true, $true, $false) [AzSKPDFExtension]::WriteText($selection, 'Security Report','TOC Heading', $true, $true, $false) $selection.InsertBreak(6) $selection.InsertBreak(6) diff --git a/src/AzSK/AzSKStaging.psd1 b/src/AzSK/AzSKStaging.psd1 index 6e04d25a8..71fc80069 100644 --- a/src/AzSK/AzSKStaging.psd1 +++ b/src/AzSK/AzSKStaging.psd1 @@ -1,4 +1,4 @@ -# +# # Module manifest for module 'AzSK' # # Generated by: Microsoft AzSK Team @@ -12,7 +12,7 @@ RootModule = '.\AzSK.psm1' # Version number of this module. - ModuleVersion = '4.0.0.0' + ModuleVersion = '4.4.4' # ID used to uniquely identify this module GUID = 'a34e9875-e00a-477f-a41d-0410da399e54' @@ -27,7 +27,7 @@ Copyright = '(c) 2017 Microsoft Corporation. All rights reserved.' # Description of the functionality provided by this module - Description = 'DevSecOps Kit for Azure (AzSK)' + Description = 'Secure DevOps Kit for Azure (AzSK)' # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '5.0' From 27fca5b41e223a9d40f79c3e2488542c4f5104fe Mon Sep 17 00:00:00 2001 From: Garima Singh Date: Wed, 8 Jan 2020 17:49:13 +0530 Subject: [PATCH 08/57] Bug Fix: Open output folder after rescan is complete for attested controls --- src/AzSK.Framework/Abstracts/CommandBase.ps1 | 76 ++++++++++++-------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/src/AzSK.Framework/Abstracts/CommandBase.ps1 b/src/AzSK.Framework/Abstracts/CommandBase.ps1 index c3ea2c7c0..c0b7aeb4b 100644 --- a/src/AzSK.Framework/Abstracts/CommandBase.ps1 +++ b/src/AzSK.Framework/Abstracts/CommandBase.ps1 @@ -102,6 +102,15 @@ class CommandBase: AzSKRoot { { $folderPath = $this.GetOutputFolderPath(); $methodResult = $methodToCall.Invoke(@()); + + if((-not $this.DoNotOpenOutputFolder) -and (-not [string]::IsNullOrEmpty($folderPath))) { + try { + Invoke-Item -Path $folderPath; + } + catch { + #ignore if any exception occurs + } + } } else { @@ -147,43 +156,54 @@ else { # #Generate PDF report - $GeneratePDFReport = $this.InvocationContext.BoundParameters["GeneratePDF"]; - try { - if (-not [string]::IsNullOrEmpty($folderpath)) { - switch ($GeneratePDFReport) { - None { - # Do nothing - } - Landscape { - [AzSKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $true); - } - Portrait { - [AzSKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $false); - } - } - } - } - catch { - # Unwrapping the first layer of exception which is added by Invoke function - $this.CommandError($_); - } + $GeneratePDFReport = $this.InvocationContext.BoundParameters["GeneratePDF"]; + try { + if (-not [string]::IsNullOrEmpty($folderpath)) { + switch ($GeneratePDFReport) { + None { + # Do nothing + } + Landscape { + [AzSKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $true); + } + Portrait { + [AzSKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $false); + } + } + } + } + catch { + # Unwrapping the first layer of exception which is added by Invoke function + $this.CommandError($_); + } # $AttestControlParamFound = $this.InvocationContext.BoundParameters["AttestControls"]; if($null -eq $AttestControlParamFound) { - if((-not $this.DoNotOpenOutputFolder) -and (-not [string]::IsNullOrEmpty($folderPath))) + #If controls are attested then open folder when rescan of attested controls is complete + $controlAttested = $false + if (Get-Variable AttestationValue -Scope Global){ + if ( $Global:AttestationValue){ + $controlAttested = $true + } + } + + if ( !$controlAttested) { - try + if((-not $this.DoNotOpenOutputFolder) -and (-not [string]::IsNullOrEmpty($folderPath))) { - Invoke-Item -Path $folderPath; - } - catch - { - #ignore if any exception occurs + try + { + Invoke-Item -Path $folderPath; + } + catch + { + #ignore if any exception occurs + } } } - } + } } return $folderPath; } From 19272885b169d8d3e3ab3da453095d068be094c1 Mon Sep 17 00:00:00 2001 From: Garima Singh Date: Wed, 8 Jan 2020 17:56:11 +0530 Subject: [PATCH 09/57] corrected indentation --- src/AzSK.Framework/Abstracts/CommandBase.ps1 | 60 ++++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/AzSK.Framework/Abstracts/CommandBase.ps1 b/src/AzSK.Framework/Abstracts/CommandBase.ps1 index c0b7aeb4b..9e8b34def 100644 --- a/src/AzSK.Framework/Abstracts/CommandBase.ps1 +++ b/src/AzSK.Framework/Abstracts/CommandBase.ps1 @@ -156,31 +156,32 @@ else { # #Generate PDF report - $GeneratePDFReport = $this.InvocationContext.BoundParameters["GeneratePDF"]; - try { - if (-not [string]::IsNullOrEmpty($folderpath)) { - switch ($GeneratePDFReport) { - None { - # Do nothing - } - Landscape { - [AzSKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $true); - } - Portrait { - [AzSKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $false); - } - } - } - } - catch { - # Unwrapping the first layer of exception which is added by Invoke function - $this.CommandError($_); - } + $GeneratePDFReport = $this.InvocationContext.BoundParameters["GeneratePDF"]; + try { + if (-not [string]::IsNullOrEmpty($folderpath)) { + switch ($GeneratePDFReport) { + None { + # Do nothing + } + Landscape { + [AzSKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $true); + } + Portrait { + [AzSKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $false); + } + } + } + } + catch { + # Unwrapping the first layer of exception which is added by Invoke function + $this.CommandError($_); + } # $AttestControlParamFound = $this.InvocationContext.BoundParameters["AttestControls"]; if($null -eq $AttestControlParamFound) { + #If controls are attested then open folder when rescan of attested controls is complete $controlAttested = $false if (Get-Variable AttestationValue -Scope Global){ @@ -189,22 +190,21 @@ else { } } - if ( !$controlAttested) + if ( !$controlAttested){ + if((-not $this.DoNotOpenOutputFolder) -and (-not [string]::IsNullOrEmpty($folderPath))) { - if((-not $this.DoNotOpenOutputFolder) -and (-not [string]::IsNullOrEmpty($folderPath))) + try { - try - { - Invoke-Item -Path $folderPath; - } - catch - { - #ignore if any exception occurs - } + Invoke-Item -Path $folderPath; + } + catch + { + #ignore if any exception occurs } } } } + } return $folderPath; } #EndRegion From 00e8281784331c722c1a6f7575971285bfd84571 Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Wed, 8 Jan 2020 18:48:23 +0530 Subject: [PATCH 10/57] Bug fix for typo and enabling MFA on activation --- src/AzSK/Framework/Core/PIM/PIMScript.ps1 | 5 +++-- src/AzSK/PIM/PIM.ps1 | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/AzSK/Framework/Core/PIM/PIMScript.ps1 b/src/AzSK/Framework/Core/PIM/PIMScript.ps1 index ec1b51c80..2b7b509ac 100644 --- a/src/AzSK/Framework/Core/PIM/PIMScript.ps1 +++ b/src/AzSK/Framework/Core/PIM/PIMScript.ps1 @@ -955,10 +955,11 @@ class PIM: AzCommandBase { { if($RequireMFAOnActivation) { - - $policyString= '{"ruleIdentifier":"AcrsRule","setting":"{\"acrsRequired\":false,\"acrs\":\"'+$policyTag+'\"}"}' + # TODO: if we turn on MFA on activation CA policy cannot be simultaneously applied. Need to check if the API still throws the error + $policyString= '' } + } # 5) Create json body for patch request diff --git a/src/AzSK/PIM/PIM.ps1 b/src/AzSK/PIM/PIM.ps1 index 1bc7d3a85..b7cfaaf68 100644 --- a/src/AzSK/PIM/PIM.ps1 +++ b/src/AzSK/PIM/PIM.ps1 @@ -161,7 +161,8 @@ function Set-AzSKPIMConfiguration { [Parameter(Mandatory = $false, ParameterSetName = "ConfigureRoleSettings")] [bool] - $ApplyConditonalAccessPolicyForRoleActivation, + $ApplyConditionalAccessPolicyForRoleActivation, + [Alias("ApplyConditonalAccessPolicyForRoleActivation")] [Parameter(Mandatory = $false, ParameterSetName = "RemovePermanentAssignment")] [Parameter(Mandatory = $false, ParameterSetName = "AssignEligibleforPermanentAssignments")] @@ -203,9 +204,9 @@ function Set-AzSKPIMConfiguration { } elseif ($PSCmdlet.ParameterSetName -eq 'ConfigureRoleSettings') { - if($null -ne $PSCmdlet.MyInvocation.BoundParameters["RequireMFAOnActivation"] -and $null -ne $PSCmdlet.MyInvocation.BoundParameters["ApplyConditonalAccessPolicyForRoleActivation"]) + if($null -ne $PSCmdlet.MyInvocation.BoundParameters["RequireMFAOnActivation"] -and $null -ne $PSCmdlet.MyInvocation.BoundParameters["ApplyConditionalAccessPolicyForRoleActivation"]) { - throw [SuppressedException] "'RequireMFAOnActivation' and 'ApplyConditonalAccessPolicyForRoleActivation' are exclusive switches. Please use only one of them in the command" + throw [SuppressedException] "'RequireMFAOnActivation' and 'ApplyConditionalAccessPolicyForRoleActivation' are exclusive switches. Please use only one of them in the command" return; } elseif ($null -ne $PSCmdlet.MyInvocation.BoundParameters["RequireMFAOnActivation"]) @@ -220,9 +221,9 @@ function Set-AzSKPIMConfiguration { $pimconfig.InvokeFunction($pimconfig.ConfigureRoleSettings,@($SubscriptionId, $ResourceGroupName, $ResourceName, $RoleName, $ExpireEligibleAssignmentsInDays, $RequireJustificationOnActivation, $MaximumActivationDuration, $false, $null)); } } - elseif ($null -ne $PSCmdlet.MyInvocation.BoundParameters["ApplyConditonalAccessPolicyForRoleActivation"]) + elseif ($null -ne $PSCmdlet.MyInvocation.BoundParameters["ApplyConditionalAccessPolicyForRoleActivation"]) { - if($ApplyConditonalAccessPolicyForRoleActivation) + if($ApplyConditionalAccessPolicyForRoleActivation) { #Both Conditional Access policy and MFA can not be applied simultaneously $pimconfig.InvokeFunction($pimconfig.ConfigureRoleSettings,@($SubscriptionId, $ResourceGroupName, $ResourceName, $RoleName, $ExpireEligibleAssignmentsInDays, $RequireJustificationOnActivation, $MaximumActivationDuration, $false, $true)); From 70351b1769d88816af10d9e252a74fedfc0fc080 Mon Sep 17 00:00:00 2001 From: Garima Singh Date: Thu, 9 Jan 2020 14:45:49 +0530 Subject: [PATCH 11/57] Review comment to use test-path --- src/AzSK.Framework/Abstracts/CommandBase.ps1 | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/AzSK.Framework/Abstracts/CommandBase.ps1 b/src/AzSK.Framework/Abstracts/CommandBase.ps1 index 9e8b34def..e7a623166 100644 --- a/src/AzSK.Framework/Abstracts/CommandBase.ps1 +++ b/src/AzSK.Framework/Abstracts/CommandBase.ps1 @@ -103,13 +103,10 @@ class CommandBase: AzSKRoot { $folderPath = $this.GetOutputFolderPath(); $methodResult = $methodToCall.Invoke(@()); - if((-not $this.DoNotOpenOutputFolder) -and (-not [string]::IsNullOrEmpty($folderPath))) { - try { + if(-not $this.DoNotOpenOutputFolder) { + if (Test-Path $folderPath) { Invoke-Item -Path $folderPath; } - catch { - #ignore if any exception occurs - } } } else { @@ -184,10 +181,12 @@ else { #If controls are attested then open folder when rescan of attested controls is complete $controlAttested = $false - if (Get-Variable AttestationValue -Scope Global){ - if ( $Global:AttestationValue){ - $controlAttested = $true - } + if( ([FeatureFlightingManager]::GetFeatureStatus("EnableScanAfterAttestation","*"))) { + if (Get-Variable AttestationValue -Scope Global){ + if ( $Global:AttestationValue){ + $controlAttested = $true + } + } } if ( !$controlAttested){ From b6a57a87990fdc452ccd4837d1e342d7b60a3627 Mon Sep 17 00:00:00 2001 From: Aboli Bhangale Date: Thu, 9 Jan 2020 16:00:08 +0530 Subject: [PATCH 12/57] Persist storage blob per resource - large subs CA --- src/AzSK.Framework/Abstracts/ServicesSecurityStatus.ps1 | 2 +- src/AzSK.Framework/Managers/PartialScanManager.ps1 | 8 ++++++-- src/AzSK/Framework/Configurations/FeatureFlighting.json | 9 +++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/AzSK.Framework/Abstracts/ServicesSecurityStatus.ps1 b/src/AzSK.Framework/Abstracts/ServicesSecurityStatus.ps1 index 1f2c80a61..b491dae2e 100644 --- a/src/AzSK.Framework/Abstracts/ServicesSecurityStatus.ps1 +++ b/src/AzSK.Framework/Abstracts/ServicesSecurityStatus.ps1 @@ -569,7 +569,7 @@ class ServicesSecurityStatus: AzSVTCommandBase #If Scan source is in supported sources or UsePartialCommits switch is available if ($this.UsePartialCommits -or ($baselineControlsDetails.SupportedSources -contains $scanSource)) { - $partialScanMngr.UpdateResourceScanRetryCount($_.ResourceId); + $partialScanMngr.UpdateResourceScanRetryCount($_.ResourceId,$this.SubscriptionContext.SubscriptionId); } } diff --git a/src/AzSK.Framework/Managers/PartialScanManager.ps1 b/src/AzSK.Framework/Managers/PartialScanManager.ps1 index f9eaa1b78..b4ed3350b 100644 --- a/src/AzSK.Framework/Managers/PartialScanManager.ps1 +++ b/src/AzSK.Framework/Managers/PartialScanManager.ps1 @@ -156,7 +156,7 @@ class PartialScanManager } } - [void] UpdateResourceScanRetryCount([string] $resourceId) + [void] UpdateResourceScanRetryCount([string] $resourceId,[string] $subscriptionId) { $resourceValues = @(); $this.GetResourceScanTrackerObject(); @@ -171,7 +171,11 @@ class PartialScanManager { $resourceValue.State = [ScanState]::ERR } - #$this.PersistStorageBlob(); + if([FeatureFlightingManager]::GetFeatureStatus("EnableStorageBlobPersistPerResource",$($subscriptionId)) -eq $true) + { + $this.PersistStorageBlob(); + } + } else { diff --git a/src/AzSK/Framework/Configurations/FeatureFlighting.json b/src/AzSK/Framework/Configurations/FeatureFlighting.json index 13710ff58..1c8f3c2ec 100644 --- a/src/AzSK/Framework/Configurations/FeatureFlighting.json +++ b/src/AzSK/Framework/Configurations/FeatureFlighting.json @@ -172,6 +172,15 @@ "DisabledForSubs": [], "UnderPreview": false, "IsEnabled": true + }, + { + "Name": "EnableStorageBlobPersistPerResource", + "Description": "", + "Sources": ["*"], + "EnabledForSubs": ["254ad434-e2e6-45c0-a32b-34bf24cb7479","0e265216-bc29-40b0-8759-07d7cb75497f"], + "DisabledForSubs": [], + "UnderPreview": false, + "IsEnabled": true } ] } \ No newline at end of file From 14125e0918521f48cf713b465558df5a6b4e2e9d Mon Sep 17 00:00:00 2001 From: Aboli Bhangale Date: Thu, 9 Jan 2020 16:29:08 +0530 Subject: [PATCH 13/57] updating feature flag- removing sub ids --- src/AzSK/Framework/Configurations/FeatureFlighting.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzSK/Framework/Configurations/FeatureFlighting.json b/src/AzSK/Framework/Configurations/FeatureFlighting.json index 1c8f3c2ec..b79f45072 100644 --- a/src/AzSK/Framework/Configurations/FeatureFlighting.json +++ b/src/AzSK/Framework/Configurations/FeatureFlighting.json @@ -177,7 +177,7 @@ "Name": "EnableStorageBlobPersistPerResource", "Description": "", "Sources": ["*"], - "EnabledForSubs": ["254ad434-e2e6-45c0-a32b-34bf24cb7479","0e265216-bc29-40b0-8759-07d7cb75497f"], + "EnabledForSubs": [], "DisabledForSubs": [], "UnderPreview": false, "IsEnabled": true From ed4f26db654d38552d5f90f10eb05febfb8b9a41 Mon Sep 17 00:00:00 2001 From: Garima Singh Date: Thu, 9 Jan 2020 16:38:56 +0530 Subject: [PATCH 14/57] Subscription lock details: Remove notes and name --- .../Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 b/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 index 6a2dff336..95365d806 100644 --- a/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 +++ b/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 @@ -611,9 +611,9 @@ class SubscriptionCore: AzSVTBase if($foundLocks) { - $controlResult.SetStateData("Resource Locks on subscription", $lockDtls); + $controlResult.SetStateData("Resource Locks on subscription", ($lockDtls | Select-Object @{Name="LockLevel";Expression={$_.Properties.level}}, LockId)) #$controlResult.AddMessage([VerificationResult]::Verify, "Subscription lock details :", ($lockDtls | Select-Object Name, @{Name="Lock Level";Expression={$_.Properties.level}}, LockId, @{Name="Notes";Expression={$_.Properties.notes}} ), $true, "SubscriptionLocks") - $controlResult.AddMessage([VerificationResult]::Verify, "Subscription lock details :", ($lockDtls | Select-Object Name, @{Name="Lock Level";Expression={$_.Properties.level}}, LockId, @{Name="Notes";Expression={$_.Properties.notes}} )) + $controlResult.AddMessage([VerificationResult]::Verify, "Subscription lock details :", ($lockDtls | Select-Object @{Name="LockLevel";Expression={$_.Properties.level}}, LockId)) } else { From bac5d29ff4528b068d6cfde2aaf7cd44c504bc5f Mon Sep 17 00:00:00 2001 From: Vishal H Date: Thu, 9 Jan 2020 17:16:10 +0530 Subject: [PATCH 15/57] Resource Tracker Inventory --- src/.vscode/launch.json | 24 ++++++++++++++ .../Abstracts/ServicesSecurityStatus.ps1 | 32 ++++++++++++++++++- src/AzSK/AzSKStaging.psd1 | 2 +- .../Configurations/FeatureFlighting.json | 9 ++++++ 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/.vscode/launch.json diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json new file mode 100644 index 000000000..47c551204 --- /dev/null +++ b/src/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "PowerShell: Launch Current File", + "type": "PowerShell", + "request": "launch", + "script": "${file}", + "cwd": "${file}" + }, + { + "type": "PowerShell", + "request": "launch", + "name": "PowerShell Launch Current File in Temporary Console", + "script": "${file}", + "args": [], + "cwd": "${file}", + "createTemporaryIntegratedConsole": true + } + ] +} \ No newline at end of file diff --git a/src/AzSK.Framework/Abstracts/ServicesSecurityStatus.ps1 b/src/AzSK.Framework/Abstracts/ServicesSecurityStatus.ps1 index 1f2c80a61..f9566686a 100644 --- a/src/AzSK.Framework/Abstracts/ServicesSecurityStatus.ps1 +++ b/src/AzSK.Framework/Abstracts/ServicesSecurityStatus.ps1 @@ -485,7 +485,37 @@ class ServicesSecurityStatus: AzSVTCommandBase } } - [AIOrgTelemetryHelper]::PublishEvent( "Partial Commit Details",@{"TotalSVTResources"= $($ScanResourcesList |Measure-Object).Count;"ScanCompletedResourcesCount"=$CompletedResources; "NonScannedResourcesCount" = $IncompleteScans;"ErrorStateResourcesCount"= $InErrorResources;"SubscriptionId"=$this.SubscriptionContext.SubscriptionId;"PartialScanIdentifier"=$this.PartialScanIdentifier;}, $null) + [AIOrgTelemetryHelper]::PublishEvent( "Partial Commit Details",@{"TotalSVTResources"= $($ScanResourcesList |Measure-Object).Count;"ScanCompletedResourcesCount"=$CompletedResources; "NonScannedResourcesCount" = $IncompleteScans;"ErrorStateResourcesCount"= $InErrorResources;"SubscriptionId"=$this.SubscriptionContext.SubscriptionId;"PartialScanIdentifier"=$this.PartialScanIdentifier;}, $null) + + #By default below detail partial scan tracker telemetry will be in disabled state + # and only be enabled using feature flag for perticular subscriptions to analaze the CA scan issues + # Register/Deregister all listeners to cleanup the memory + if([FeatureFlightingManager]::GetFeatureStatus("EnableDetailedResourceTrackerTelemetry",$this.SubscriptionContext.SubscriptionId) -eq $true) + { + $resourceTrackerEvents = [System.Collections.ArrayList]::new() + #Loop through all resource list present in tracker and prepare array of events with common properties like RunIdentifier, SubId,etc + foreach($resource in $ScanResourcesList){ + $resourceEvent = "" | Select-Object Name, Properties, Metrics + #RunIdentifier value is not set at this stage. Its value is default. + #Investigation needs to be done base don partialScanIdentifier + #"RunIdentifier" = $this.RunIdentifier; + $Properties = @{ + "SubscriptionId"= $this.SubscriptionContext.SubscriptionId; + "PartialScanIdentifier"=$this.PartialScanIdentifier; + "ResourceId" = $resource.Id; + "ScanRetryCount" = $resource.ScanRetryCount; + "State" = $resource.State; + "StateModifiedDate" = $resource.ModifiedDate + "TrackerId" = $partialScanMngr.ResourceScanTrackerObj.Id + } + $resourceEvent.Name = "Partial Tracker Resource Details" + $resourceEvent.Properties = $properties + $resourceTrackerEvents.Add($resourceEvent) | Out-Null + } + #Push array of resourcelist to AI telemetry + [AIOrgTelemetryHelper]::TrackEvents($resourceTrackerEvents); + } + } catch{ #Continue exexution if telemetry is not sent diff --git a/src/AzSK/AzSKStaging.psd1 b/src/AzSK/AzSKStaging.psd1 index 6e04d25a8..cffda17ef 100644 --- a/src/AzSK/AzSKStaging.psd1 +++ b/src/AzSK/AzSKStaging.psd1 @@ -12,7 +12,7 @@ RootModule = '.\AzSK.psm1' # Version number of this module. - ModuleVersion = '4.0.0.0' + ModuleVersion = '4.4.0' # ID used to uniquely identify this module GUID = 'a34e9875-e00a-477f-a41d-0410da399e54' diff --git a/src/AzSK/Framework/Configurations/FeatureFlighting.json b/src/AzSK/Framework/Configurations/FeatureFlighting.json index 13710ff58..2b7e880ad 100644 --- a/src/AzSK/Framework/Configurations/FeatureFlighting.json +++ b/src/AzSK/Framework/Configurations/FeatureFlighting.json @@ -172,6 +172,15 @@ "DisabledForSubs": [], "UnderPreview": false, "IsEnabled": true + }, + { + "Name": "EnableDetailedResourceTrackerTelemetry", + "Description": "", + "Sources": ["*"], + "EnabledForSubs": [], + "DisabledForSubs": [], + "UnderPreview": false, + "IsEnabled": false } ] } \ No newline at end of file From 02bb854e12cfed8f4e7cd318e577edaecc547e2b Mon Sep 17 00:00:00 2001 From: Vishal H Date: Thu, 9 Jan 2020 18:17:00 +0530 Subject: [PATCH 16/57] reverted unwanted changes --- src/.vscode/launch.json | 24 ------------------------ src/AzSK/AzSKStaging.psd1 | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 src/.vscode/launch.json diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json deleted file mode 100644 index 47c551204..000000000 --- a/src/.vscode/launch.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "PowerShell: Launch Current File", - "type": "PowerShell", - "request": "launch", - "script": "${file}", - "cwd": "${file}" - }, - { - "type": "PowerShell", - "request": "launch", - "name": "PowerShell Launch Current File in Temporary Console", - "script": "${file}", - "args": [], - "cwd": "${file}", - "createTemporaryIntegratedConsole": true - } - ] -} \ No newline at end of file diff --git a/src/AzSK/AzSKStaging.psd1 b/src/AzSK/AzSKStaging.psd1 index cffda17ef..6e04d25a8 100644 --- a/src/AzSK/AzSKStaging.psd1 +++ b/src/AzSK/AzSKStaging.psd1 @@ -12,7 +12,7 @@ RootModule = '.\AzSK.psm1' # Version number of this module. - ModuleVersion = '4.4.0' + ModuleVersion = '4.0.0.0' # ID used to uniquely identify this module GUID = 'a34e9875-e00a-477f-a41d-0410da399e54' From 3b0dcb94f6f41772e68139bb288563c7c679ef3c Mon Sep 17 00:00:00 2001 From: Tarun Shukla Date: Thu, 9 Jan 2020 19:38:35 +0530 Subject: [PATCH 17/57] Added new checks in GCA for suspended job and large subs --- .../Core/ContinuousAssurance/CAAutomation.ps1 | 168 +++++++++++++++++- 1 file changed, 160 insertions(+), 8 deletions(-) diff --git a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 index 9de76e881..aef3a0a5e 100644 --- a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 +++ b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 @@ -2087,7 +2087,7 @@ class CCAutomation: AzCommandBase $resultMsg = "Active job schedule(s) found." $resultStatus = "OK" } - $messages += ($this.FormatGetCACheckMessage($stepCount,$checkDescription,$resultStatus,$resultMsg,$detailedMsg,$caOverallSummary)) + $messages += ($this.FormatGetCACheckMessage($stepCount,$checkDescription,$resultStatus,$resultMsg,$detailedMsg)) if($shouldReturn) { return $messages @@ -2095,19 +2095,28 @@ class CCAutomation: AzCommandBase $detailedMsg = $Null #endregion + #region: Step 12: Check if last job is not successful or job hasn't run in last 2 days - $stepCount++ - $recentJobs = Get-AzAutomationJob -ResourceGroupName $this.AutomationAccount.ResourceGroup ` + $stepCount++ + $recentJobs = $null + $allJobs = Get-AzAutomationJob -ResourceGroupName $this.AutomationAccount.ResourceGroup ` -AutomationAccountName $this.AutomationAccount.Name ` - -RunbookName $this.RunbookName | - Sort-Object LastModifiedTime -Descending | - Select-Object -First 10 + -RunbookName $this.RunbookName + if($null -ne $allJobs -and ($allJobs | Measure-Object).Count -gt 0){ + $recentJobs = $allJobs | Sort-Object LastModifiedTime -Descending | Where-Object {$_.LastModifiedTime.UtcDateTime -ge [DateTime]::UtcNow.AddDays(-3)} + } + if(($recentJobs|Measure-Object).Count -gt 0) { + $checkDescription = "Inspecting CA executed jobs." + #Get the last job $lastJob = $recentJobs[0] + #Get suspended job in last 3 days + $suspendedJobs = $recentJobs | Where-Object { $_.Status -eq 'Suspended'} + + #Check 12.1 Last job run in last 48 hrs if(($(get-date).ToUniversalTime() - $lastJob.LastModifiedTime.UtcDateTime).TotalHours -gt 48) { - $checkDescription = "Inspecting CA executed jobs." $failMsg = "The CA scanning automation runbook (job) has not run in the last 48 hours. In normal functioning, CA scans run once every $($this.defaultScanIntervalInHours) hours by default." $resolvemsg = "Please contact AzSK support team for a resolution." $resultMsg = "$failMsg`r`n$resolvemsg" @@ -2115,8 +2124,20 @@ class CCAutomation: AzCommandBase $shouldReturn = $true $messages += ($this.FormatGetCACheckMessage($stepCount,$checkDescription,$resultStatus,$resultMsg,$detailedMsg,$caOverallSummary)) } + #Check 12.2 Suspended job in last 3 days + elseif($null -ne $suspendedJobs -and ($suspendedJobs|Measure-Object).Count -gt 0){ + $failMsg = "One or more CA scanning automation runbook (job) has been in suspended state in the last 3 days." + $resolvemsg = "Please contact AzSK support team for a resolution." + $resultMsg = "$failMsg`r`n$resolvemsg" + $resultStatus = "Warning" + $shouldReturn = $false + $messages += ($this.FormatGetCACheckMessage($stepCount,$checkDescription,$resultStatus,$resultMsg,$detailedMsg)) + } else { + $resultStatus = "OK" + $resultMsg = "CA jobs are functioning normal." + $messages += ($this.FormatGetCACheckMessage($stepCount,$checkDescription,$resultStatus,$resultMsg,$detailedMsg)) #display job summary $jobSummary = $recentJobs | Format-Table Status,@{Label="Duration (in Minutes)"; Expression={[math]::Round(($_.EndTime - $_.StartTime).TotalMinutes)}} | Out-String $messages += [MessageData]::new("Summary of recent jobs ($($this.RunbookName)):", $jobSummary); @@ -2126,8 +2147,126 @@ class CCAutomation: AzCommandBase { $messages += [MessageData]::new("Job history not found."); } - + + if($shouldReturn) + { + return $messages + } + + $detailedMsg = $Null #endregion + + + #region: Step 13: Check if storage account rec'd scan logs in last 3 days + $stepCount++ + $checkDescription = "Inspecting AzSK storage account contains logs for recent jobs." + $tgtSubsLogsStatus = @() + $isScanLogsPresent = $true + if($this.IsCentralScanModeOn) + { + try + { + if(-not [string]::IsNullOrWhiteSpace($this.TargetSubscriptionIds)) + { + $scanobjects | ForEach-Object { + try + { + $tgtSubStorageAccount = "" | Select-Object TargetSubscriptionId, LoggingOption, IsScanLogsPresent + $tgtSubStorageAccount.TargetSubscriptionId = $_.SubscriptionId; + $tgtSubStorageAccount.LoggingOption = $_.LoggingOption; + $tgtSubStorageAccount.IsScanLogsPresent = $false + + if($_.LoggingOption -eq [CAReportsLocation]::IndividualSubs) + { + # Set context for target sub + Set-AzContext -SubscriptionId $tgtSubStorageAccount.TargetSubscriptionId | Out-Null + }else{ + # Set context for master sub + Set-AzContext -SubscriptionId $this.SubscriptionContext.SubscriptionId | Out-Null + } + # Get Scan logs from storage + $scanLogsPrefixPattern = $this.AutomationAccount.ResourceGroup + "/" + "$($tgtSubStorageAccount.TargetSubscriptionId)" + "/" + $CAScanDataBlobObject = $this.GetScanLogsFromStorageAccount($this.CAScanOutputLogsContainerName,$scanLogsPrefixPattern) + if($null -ne $CAScanDataBlobObject -and ($CAScanDataBlobObject| Measure-Object).Count -gt 0){ + $CAScanDataBlobObject = $CAScanDataBlobObject | Where-Object { $_.LastModified.UtcDateTime -ge [DateTime]::UtcNow.AddDays(-3)} + } + if($null -ne $CAScanDataBlobObject -and ($CAScanDataBlobObject| Measure-Object).Count -gt 0) + { + # Scan logs found in storage for last 3 days + $tgtSubStorageAccount.IsScanLogsPresent = $true + }else{ + # No scan logs found in storage for last 3 days + $tgtSubStorageAccount.IsScanLogsPresent = $false + $isScanLogsPresent = $false + } + $tgtSubsLogsStatus += $tgtSubStorageAccount + + } + catch + { + $isScanLogsPresent = $false + $tgtSubsLogsStorage += $tgtSubStorageAccount + $currentMessage = [MessageData]::new("Failed to fetch the storage account details $($this.SubscriptionContext.SubscriptionId)"); + $messages += $currentMessage; + $this.PublishCustomMessage($currentMessage); + $this.PublishException($_) + + } + } + + } + } + catch + { + $this.PublishException($_) + $isScanLogsPresent = $false + } + finally + { + #setting the context back to the parent subscription + Set-AzContext -SubscriptionId $this.SubscriptionContext.SubscriptionId | Out-Null + } + $detailedMsg = [MessageData]::new("Target Subscriptions scan logs details:", $tgtSubsLogsStatus); + } + else + { + # Get AzSK storage of the current sub + $scanLogsPrefixPattern = $this.AutomationAccount.ResourceGroup + "/" + "$($this.SubscriptionContext.SubscriptionId)" + "/" + $CAScanDataBlobObject = $this.GetScanLogsFromStorageAccount($this.CAScanOutputLogsContainerName,$scanLogsPrefixPattern) + if($null -ne $CAScanDataBlobObject -and ($CAScanDataBlobObject| Measure-Object).Count -gt 0){ + $CAScanDataBlobObject = $CAScanDataBlobObject | Where-Object { $_.LastModified.UtcDateTime -ge [DateTime]::UtcNow.AddDays(-3)} + } + if($null -ne $CAScanDataBlobObject -and ($CAScanDataBlobObject| Measure-Object).Count -gt 0) + { + $isScanLogsPresent = $true + }else{ + $isScanLogsPresent = $false + } + } + $resolveMsg = "Please contact AzSK support team for a resolution." + if($isScanLogsPresent) + { + $resultMsg = "Scan logs are present in storage account for last 3 days." + $resultStatus = "OK" + } + else + { + $failMsg = "Scan logs are missing in storage account for last 3 days." + $resultMsg = "$failMsg`r`n$resolvemsg" + $resultStatus = "Failed" + $shouldReturn = $true + } + + $messages += ($this.FormatGetCACheckMessage($stepCount,$checkDescription,$resultStatus,$resultMsg,$detailedMsg,$caOverallSummary)) + + if($shouldReturn) + { + return $messages + } + + $detailedMsg = $Null + #endregion + if($this.ExhaustiveCheck) { @@ -3874,6 +4013,19 @@ class CCAutomation: AzCommandBase return $null } } + + #get scan logs from storage + hidden [PSObject] GetScanLogsFromStorageAccount($containerName, $scanLogsPrefixPattern) + { + # Get AzSK storage of the current master sub + $reportsStorageAccount = [UserSubscriptionDataHelper]::GetUserSubscriptionStorage() + $keys = Get-AzStorageAccountKey -ResourceGroupName $reportsStorageAccount.ResourceGroupName -Name $reportsStorageAccount.Name + $currentContext = New-AzStorageContext -StorageAccountName $reportsStorageAccount.Name -StorageAccountKey $keys[0].Value -Protocol Https + #$scanLogsPrefixPattern = $this.AutomationAccount.CoreResourceGroup + "/" + "$($this.SubscriptionContext.SubscriptionId)" + "/" + $CAScanDataBlobObject = Get-AzStorageBlob -Container $containerName -Prefix $scanLogsPrefixPattern -Context $currentContext -ErrorAction SilentlyContinue + return $CAScanDataBlobObject + } + #get App RGs hidden [PSObject] GetAppRGs() { From ce60baeebca3e74b99bb5a166110d5cb12da4b6a Mon Sep 17 00:00:00 2001 From: Garima Singh Date: Thu, 9 Jan 2020 21:31:21 +0530 Subject: [PATCH 18/57] Bom Char issue --- .../Helpers/AIOrgTelemetryHelper.ps1 | 34 ++++++++++++++----- .../Helpers/RemoteApiHelper.ps1 | 22 ++++++++++++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 b/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 index 710b41880..72222ed5a 100644 --- a/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 +++ b/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 @@ -498,19 +498,25 @@ static [void] PublishEvent([System.Collections.ArrayList] $servicescantelemetryE if($type -eq "Usage") { - Invoke-WebRequest -Uri "https://dc.services.visualstudio.com/v2/track" ` + $uri = "https://dc.services.visualstudio.com/v2/track" + } + else { + $uri = [WebRequestHelper]::GetApplicationInsightsEndPoint() + } + try { + Invoke-WebRequest -Uri $uri ` -Method Post ` -ContentType "application/x-json-stream" ` -Body $eventJson ` -UseBasicParsing | Out-Null } - else { - $uri = [WebRequestHelper]::GetApplicationInsightsEndPoint() - Invoke-WebRequest -Uri $uri ` - -Method Post ` - -ContentType "application/x-json-stream" ` - -Body $eventJson ` - -UseBasicParsing | Out-Null + catch{ + # Error while sending events to telemetry. Encode content to UTF8 and make API call again + if (($null -ne $eventJson)-and ($eventJson.length -gt 0)) { + if ($_.Exception.Response.StatusCode -eq "BadRequest") { + [AIOrgTelemetryHelper]::PostUTF8Content($uri, $eventJson); + } + } } } catch { @@ -518,5 +524,17 @@ static [void] PublishEvent([System.Collections.ArrayList] $servicescantelemetryE # Error while sending CA events to telemetry. No need to break the execution. } } +hidden static PostUTF8Content($uri, $eventJson) +{ + try { + Invoke-WebRequest -Uri $uri ` + -Method Post ` + -Body ([System.Text.Encoding]::UTF8.GetBytes($eventJson)) ` + -ContentType "application/x-json-stream" ` + -UseBasicParsing + } + catch { + } +} } diff --git a/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 b/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 index 800f46fcc..5baa52ebb 100644 --- a/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 +++ b/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 @@ -22,9 +22,31 @@ class RemoteApiHelper { return $result } catch { + #Error while sending events to Database. Encode content to UTF8 and make API call again + if (($null -ne $content)-and ($content.length -gt 0)) { + if ($_.Exception.Response.StatusCode -eq "BadRequest") { + [RemoteApiHelper]::PostUTF8Content($uri, $content, "application/json") + } + } return "ERROR" } } + hidden static [psobject] PostUTF8Content($uri, $content, $type) + { + try { + $accessToken = [RemoteApiHelper]::GetAccessToken() + $result = Invoke-WebRequest -Uri $([RemoteApiHelper]::ApiBaseEndpoint + $uri) ` + -Method Post ` + -Body ([System.Text.Encoding]::UTF8.GetBytes($content)) ` + -ContentType $type ` + -Headers @{"Authorization" = "Bearer $accessToken"} ` + -UseBasicParsing + return $result + } + catch { + return "ERROR" + } + } hidden static [psobject] GetContent($uri, $content, $type) { From 69e1c1945d7728f60757d5cff1b845151ae82ecf Mon Sep 17 00:00:00 2001 From: "Ritika Singh (Tata Consultancy Services Ltd)" Date: Thu, 9 Jan 2020 23:13:45 +0530 Subject: [PATCH 19/57] Bug fix for GAI-HostInfo: AzSK description --- src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 | 2 +- src/AzSK/AzSKStaging.psd1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 b/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 index ce6fe9255..2f25ba9f8 100644 --- a/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 +++ b/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 @@ -27,7 +27,7 @@ Copyright = '(c) 2017 Microsoft Corporation. All rights reserved.' # Description of the functionality provided by this module - Description = 'Secure DevOps Kit for Azure (AzSK) - Preview' + Description = 'DevSecOps Kit for Azure (AzSK) - Preview' # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '5.0' diff --git a/src/AzSK/AzSKStaging.psd1 b/src/AzSK/AzSKStaging.psd1 index 71fc80069..0422f1f73 100644 --- a/src/AzSK/AzSKStaging.psd1 +++ b/src/AzSK/AzSKStaging.psd1 @@ -12,7 +12,7 @@ RootModule = '.\AzSK.psm1' # Version number of this module. - ModuleVersion = '4.4.4' + ModuleVersion = '4.0.0.0' # ID used to uniquely identify this module GUID = 'a34e9875-e00a-477f-a41d-0410da399e54' From 839067f4548ec024cb4ed99387e166223f10c001 Mon Sep 17 00:00:00 2001 From: "Ritika Singh (Tata Consultancy Services Ltd)" Date: Thu, 9 Jan 2020 23:26:10 +0530 Subject: [PATCH 20/57] Rolling back the changes made in file AzSK.AzureDevOps.psd1 as it is not required fo the fix --- src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 b/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 index 2f25ba9f8..5aad1af53 100644 --- a/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 +++ b/src/AzSK.AzureDevOps/AzSK.AzureDevOps.psd1 @@ -27,7 +27,7 @@ Copyright = '(c) 2017 Microsoft Corporation. All rights reserved.' # Description of the functionality provided by this module - Description = 'DevSecOps Kit for Azure (AzSK) - Preview' + Description = 'DevSecOps Kit for AzureDevOps (AzSK) - Preview' # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '5.0' From b4ab5efcf1bdfedd15eda9ebd778eddd45062a11 Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Fri, 10 Jan 2020 00:26:34 +0530 Subject: [PATCH 21/57] Large Sub issues resolution for Vnet and Resource Inventory --- src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 | 120 +++++++++++++----- .../Listeners/AzResourceInventoryListener.ps1 | 7 +- 2 files changed, 93 insertions(+), 34 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 index cb9b50eed..fd8a2f63c 100644 --- a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 +++ b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 @@ -38,39 +38,60 @@ class SVTIaasBase: AzSVTBase hidden [PSObject[]] GetvNetNics($VNetSubnets) { - if (-not $this.vNetNics) + if([FeatureFlightingManager]::GetFeatureStatus("EnableVnetFixForSub",$($this.SubscriptionContext.SubscriptionId))) { - $this.vNetNicsWIssues = @(); - $VNetSubnets | ForEach-Object{ - Set-Variable -Name currentsubnet -Scope Local -Value $_ - if($null -ne $currentsubnet.IpConfigurations ) + if (-not $this.vNetNics) + { + $nics = Get-AzNetworkInterface #-ResourceGroupName $rgname + $ipc = $VNetSubnets| Select-Object -Property 'IpConfigurations' -ExpandProperty 'IpConfigurations' + + if($null -ne $ipc -and ($ipc.IpConfigurations | Measure-Object).Count -gt 0) { - $currentsubnet.IpConfigurations | ForEach-Object{ - Set-Variable -Name currentipconfig -Scope Local -Value $_ - if($currentipconfig.Id.Contains("Microsoft.Network/networkInterfaces")) - { - $currentipconfig = $currentipconfig.Id.ToLower() - $nicresourceid = $currentipconfig.Substring(0,$currentipconfig.LastIndexOf("ipconfigurations")-1) - try - { - # - $nic = Get-AzResource -ResourceId $nicresourceid - $this.vNetNics += $nic - } - catch - { - $this.vNetNicsWIssues += $nicresourceid; - } + $this.vNetNics = $nics| Where-Object{($_.IpConfigurations.Id) -in $ipc.IpConfigurations.Id} + } + } + + return $this.vNetNics; + + } + else + { + if (-not $this.vNetNics) + { + $this.vNetNicsWIssues = @(); + $VNetSubnets | ForEach-Object{ + Set-Variable -Name currentsubnet -Scope Local -Value $_ + if($null -ne $currentsubnet.IpConfigurations ) + { + $currentsubnet.IpConfigurations | ForEach-Object{ + Set-Variable -Name currentipconfig -Scope Local -Value $_ + if($currentipconfig.Id.Contains("Microsoft.Network/networkInterfaces")) + { + $currentipconfig = $currentipconfig.Id.ToLower() + $nicresourceid = $currentipconfig.Substring(0,$currentipconfig.LastIndexOf("ipconfigurations")-1) + try + { + # + $nic = Get-AzResource -ResourceId $nicresourceid + $this.vNetNics += $nic + } + catch + { + $this.vNetNicsWIssues += $nicresourceid; + } + } } } } } - } + } return $this.vNetNics; } hidden [PSObject[]] GetvnetNicsProperties($vNetNics) { + + if(-not $this.vNetNicsOutput) { if($null -ne $vNetNics ) @@ -88,25 +109,58 @@ class SVTIaasBase: AzSVTBase $out.EnableIPForwarding = $nicproperties.EnableIPForwarding $PublicIpAddresses = @() $PrivateIpAddresses = @() - $nicproperties.IpConfigurations | ForEach-Object{ - Set-Variable -Name ipconfiguration -Scope Local -Value $_ - try + if([FeatureFlightingManager]::GetFeatureStatus("EnableVnetFixForSub",$($this.SubscriptionContext.SubscriptionId))) + { + + + $NICPublicIpAddresses = $nic.ipconfigurations.PublicIpAddress + $PrivateIpAddresses = $nic.ipconfigurations.PrivateIpAddress + $PublicIpAddresses =@() + if(($PublicIpAddresses |Measure-Object).Count -gt 0) { - if(($ipconfiguration | Get-Member -Name "Properties") -and ($ipconfiguration.Properties | Get-Member -Name "PublicIpAddress") -and $ipconfiguration.Properties.PublicIpAddress) - { - $IPResource = Get-AzResource -ResourceId $ipconfiguration.Properties.PublicIpAddress.Id + $NICPublicIpAddresses | ForEach-Object{ + try + { + + $IPResource = Get-AzResource -ResourceId $_.Id $pubResourceName = Get-AzPublicIpAddress -Name $IPResource.Name -ResourceGroupName $IPResource.ResourceGroupName $PublicIpAddresses += $pubResourceName.IpAddress + } + catch + { + + $this.vNetPIPIssues += $nic + } + + + } + } + + + } + else + { + $nicproperties.IpConfigurations | ForEach-Object{ + Set-Variable -Name ipconfiguration -Scope Local -Value $_ + try + { + if(($ipconfiguration | Get-Member -Name "Properties") -and ($ipconfiguration.Properties | Get-Member -Name "PublicIpAddress") -and $ipconfiguration.Properties.PublicIpAddress) + { + $IPResource = Get-AzResource -ResourceId $ipconfiguration.Properties.PublicIpAddress.Id + $pubResourceName = Get-AzPublicIpAddress -Name $IPResource.Name -ResourceGroupName $IPResource.ResourceGroupName + $PublicIpAddresses += $pubResourceName.IpAddress + } + $PrivateIpAddresses += $ipconfiguration.Properties.PrivateIpAddress + } + catch + { + $this.vNetPIPIssues += $ipconfiguration } - $PrivateIpAddresses += $ipconfiguration.Properties.PrivateIpAddress - } - catch - { - $this.vNetPIPIssues += $ipconfiguration } } $out.PublicIpAddress = ([System.String]::Join(";",$PublicIpAddresses)) $out.PrivateIpAddress = ([System.String]::Join(";",$PrivateIpAddresses)) + if(($nicproperties | Get-Member -Name "VirtualMachine") -and $nicproperties.VirtualMachine ) { diff --git a/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 b/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 index 6eebae516..7af0db700 100644 --- a/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 +++ b/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 @@ -33,11 +33,16 @@ class AzResourceInventoryListener: ListenerBase try { $scanSource = [RemoteReportHelper]::GetScanSource(); - if($scanSource -ne [ScanSource]::Runbook) { return; } + if($scanSource -ne [ScanSource]::Runbook -or $event.Sender.InvocationContext.MyCommand.Name -ne 'Get-AzSKAzureServicesSecurityStatus') { return; } $SubscriptionId = ([ContextHelper]::GetCurrentRMContext()).Subscription.Id; [ResourceInventory]::FetchResources(); [AzResourceInventoryListener]::PostAzResourceInventory(); $resources= [ResourceInventory]::RawResources + if($resources.Count -gt 5000) + { + $resources= [ResourceInventory]::FilteredResources + [AIOrgTelemetryHelper]::TrackEvent("Raw Resource Inventory Aborted", $resources.Count, $null) + } $resourceGroups = Get-AzResourceGroup $resourceDetails = @(); $telemetryEvents = [System.Collections.ArrayList]::new() From a842817222e6e0d6539fc983c4aef613da31e385 Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Fri, 10 Jan 2020 00:44:20 +0530 Subject: [PATCH 22/57] Adding PIM application id for org users --- src/AzSK/Framework/Configurations/SVT/ControlSettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json index b702064f2..e3c6717bb 100644 --- a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json +++ b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json @@ -224,7 +224,7 @@ }, "CheckPIMCAPolicyTags": false, "PIMCAPolicyTags":[], - "PIMAppId":"", + "PIMAppId":"01fc33a7-78ba-4d2f-a4b7-768e336e890e", "ASCAlertsSeverityLevels": [ "High" ], "ASCAlertsThresholdInDays": 30, "WhitelistedMgmtCerts": { From db50f4c6cca326c3f3d8de631d4e6c45d64f192b Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Fri, 10 Jan 2020 10:11:12 +0530 Subject: [PATCH 23/57] minor changes for large sub --- src/AzSK/Framework/Configurations/SVT/ControlSettings.json | 3 ++- src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 | 7 +++---- .../Framework/Listeners/AzResourceInventoryListener.ps1 | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json index e3c6717bb..ce573f9dc 100644 --- a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json +++ b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json @@ -532,5 +532,6 @@ "RequiredImagePath": "azsktest/akstest", "RequiredImageTag": "latest" } - } + }, + "MaxResourceInventoryObjectsCount": 5000 } diff --git a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 index fd8a2f63c..c3f10c341 100644 --- a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 +++ b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 @@ -99,14 +99,13 @@ class SVTIaasBase: AzSVTBase $this.vNetPIPIssues = @(); $tempVNetNICS = [array]($vNetNics) $tempVNetNICS | ForEach-Object{ - Set-Variable -Name nic -Scope Local -Value $_ - Set-Variable -Name nicproperties -Scope Local -Value $_.Properties try { + Set-Variable -Name nic -Scope Local -Value $_ $out = ""| Select-Object NICName, VMName, VMId, PrimaryStatus, NetworkSecurityGroupName,NetworkSecurityGroupId, PublicIpAddress, PrivateIpAddress, EnableIPForwarding, IpConfigurations $out.NICName = $nic.Name - $out.IpConfigurations = $nicproperties.IpConfigurations - $out.EnableIPForwarding = $nicproperties.EnableIPForwarding + $out.IpConfigurations = $nic.IpConfigurations + $out.EnableIPForwarding = $nic.EnableIPForwarding $PublicIpAddresses = @() $PrivateIpAddresses = @() if([FeatureFlightingManager]::GetFeatureStatus("EnableVnetFixForSub",$($this.SubscriptionContext.SubscriptionId))) diff --git a/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 b/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 index 7af0db700..afe745f93 100644 --- a/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 +++ b/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 @@ -38,7 +38,9 @@ class AzResourceInventoryListener: ListenerBase [ResourceInventory]::FetchResources(); [AzResourceInventoryListener]::PostAzResourceInventory(); $resources= [ResourceInventory]::RawResources - if($resources.Count -gt 5000) + $controlSettings = [ConfigurationManager]::LoadServerConfigFile("ControlSettings.json"); + $maxResourceCount = $controlSettings.MaxResourceInventoryObjectsCount + if($resources.Count -gt $maxResourceCount) { $resources= [ResourceInventory]::FilteredResources [AIOrgTelemetryHelper]::TrackEvent("Raw Resource Inventory Aborted", $resources.Count, $null) From 09ef7e6d240ac75136603af83273337ec7a4aa6a Mon Sep 17 00:00:00 2001 From: Garima Singh Date: Fri, 10 Jan 2020 10:59:56 +0530 Subject: [PATCH 24/57] Changes for review comments --- src/AzSK.Framework/Abstracts/CommandBase.ps1 | 1 + src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 | 10 +++++++--- src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 | 8 +++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/AzSK.Framework/Abstracts/CommandBase.ps1 b/src/AzSK.Framework/Abstracts/CommandBase.ps1 index e7a623166..9e6c381b6 100644 --- a/src/AzSK.Framework/Abstracts/CommandBase.ps1 +++ b/src/AzSK.Framework/Abstracts/CommandBase.ps1 @@ -182,6 +182,7 @@ else { #If controls are attested then open folder when rescan of attested controls is complete $controlAttested = $false if( ([FeatureFlightingManager]::GetFeatureStatus("EnableScanAfterAttestation","*"))) { + #Global variable "AttestationValue" is set to true when one or more controls are attested in current scan if (Get-Variable AttestationValue -Scope Global){ if ( $Global:AttestationValue){ $controlAttested = $true diff --git a/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 b/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 index 72222ed5a..c2ae55cec 100644 --- a/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 +++ b/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 @@ -503,6 +503,7 @@ static [void] PublishEvent([System.Collections.ArrayList] $servicescantelemetryE else { $uri = [WebRequestHelper]::GetApplicationInsightsEndPoint() } + try { Invoke-WebRequest -Uri $uri ` -Method Post ` @@ -511,10 +512,12 @@ static [void] PublishEvent([System.Collections.ArrayList] $servicescantelemetryE -UseBasicParsing | Out-Null } catch{ - # Error while sending events to telemetry. Encode content to UTF8 and make API call again + # Error while sending events to telemetry. Encode content to UTF8 and make API call again to handle BOM/special characters if (($null -ne $eventJson)-and ($eventJson.length -gt 0)) { - if ($_.Exception.Response.StatusCode -eq "BadRequest") { - [AIOrgTelemetryHelper]::PostUTF8Content($uri, $eventJson); + if([Helpers]::CheckMember($_.Exception,"Response.StatusCode")){ + if ($_.Exception.Response.StatusCode -eq "BadRequest") { + [AIOrgTelemetryHelper]::PostUTF8Content($uri, $eventJson); + } } } } @@ -534,6 +537,7 @@ hidden static PostUTF8Content($uri, $eventJson) -UseBasicParsing } catch { + # Error while sending events to telemetry after UTF8 encoding. } } diff --git a/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 b/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 index 5baa52ebb..6fda5907d 100644 --- a/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 +++ b/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 @@ -22,10 +22,12 @@ class RemoteApiHelper { return $result } catch { - #Error while sending events to Database. Encode content to UTF8 and make API call again + #Error while sending events to Database. Encode content to UTF8 and make API call again to handle BOM/special characters if (($null -ne $content)-and ($content.length -gt 0)) { - if ($_.Exception.Response.StatusCode -eq "BadRequest") { - [RemoteApiHelper]::PostUTF8Content($uri, $content, "application/json") + if([Helpers]::CheckMember($_.Exception,"Response.StatusCode")){ + if ($_.Exception.Response.StatusCode -eq "BadRequest") { + [RemoteApiHelper]::PostUTF8Content($uri, $content, "application/json") + } } } return "ERROR" From fcd653e972465838c596b9cf30adbb3539e03ada Mon Sep 17 00:00:00 2001 From: SINIKI Date: Fri, 10 Jan 2020 11:04:53 +0530 Subject: [PATCH 25/57] APIM large sub fixes - Fixed stream issue - Added a check for number of APIs in an APIM service --- .../Configurations/SVT/ControlSettings.json | 3 +- .../SVT/Services/APIManagement.json | 90 +-- .../Core/SVT/Services/APIManagement.ps1 | 580 +++++++++++------- 3 files changed, 417 insertions(+), 256 deletions(-) diff --git a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json index ce573f9dc..22c100571 100644 --- a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json +++ b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json @@ -41,7 +41,8 @@ "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11", "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30", "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168" - ] + ], + "MaxAllowedAPICount": "30" }, "VirtualMachine": { "Windows": { diff --git a/src/AzSK/Framework/Configurations/SVT/Services/APIManagement.json b/src/AzSK/Framework/Configurations/SVT/Services/APIManagement.json index 29f414080..317d0f291 100644 --- a/src/AzSK/Framework/Configurations/SVT/Services/APIManagement.json +++ b/src/AzSK/Framework/Configurations/SVT/Services/APIManagement.json @@ -205,29 +205,29 @@ ], "Enabled": true }, - { - "ControlID": "Azure_APIManagement_DP_Restrict_CORS_Access", - "Description": "Ensure that CORS access is granted to a minimal set of trusted origins and only required verbs are supported", - "Id": "APIManagement200", - "ControlSeverity": "Medium", - "Automated": "Yes", - "MethodName": "CheckAPIManagementCORSAllowed", - "Rationale": "CORS enables an operation or an API to allow cross-domain calls from browser-based clients. Using '*' (allow all) for CORS setting means that all cross-origin requests are allowed. Restricting allowed origins to the specific set that needs access aligns with the principle of least privilege.", - "Recommendation": "For steps to add CORS policy to API's inbound policies please refer https://docs.microsoft.com/en-us/azure/api-management/api-management-cross-domain-policies#CORS. Note: No action is needed if you are not using CORS for your service.", - "Tags": [ - "SDL", - "TCP", - "Automated", - "DP", - "DeveloperSku", - "BasicSku", - "StandardSku", - "PremiumSku", - "APIManagement", - "APIMAPIs" - ], - "Enabled": true - }, + { + "ControlID": "Azure_APIManagement_DP_Restrict_CORS_Access", + "Description": "Ensure that CORS access is granted to a minimal set of trusted origins and only required verbs are supported", + "Id": "APIManagement200", + "ControlSeverity": "Medium", + "Automated": "Yes", + "MethodName": "CheckAPIManagementCORSAllowed", + "Rationale": "CORS enables an operation or an API to allow cross-domain calls from browser-based clients. Using '*' (allow all) for CORS setting means that all cross-origin requests are allowed. Restricting allowed origins to the specific set that needs access aligns with the principle of least privilege.", + "Recommendation": "Run command 'Get-AzApiManagementPolicy' to check CORS setting for your APIM service. For steps to add CORS policy to API's inbound policies please refer https://docs.microsoft.com/en-us/azure/api-management/api-management-cross-domain-policies#CORS. Note: No action is needed if you are not using CORS for your service.", + "Tags": [ + "SDL", + "TCP", + "Automated", + "DP", + "DeveloperSku", + "BasicSku", + "StandardSku", + "PremiumSku", + "APIManagement", + "APIMAPIs" + ], + "Enabled": true + }, { "ControlID": "Azure_APIManagement_AuthN_Verify_Delegated_Authentication", "Description": "If delegated authentication is enabled, ensure that it is implemented securely", @@ -315,28 +315,28 @@ ], "Enabled": true }, - { - "ControlID": "Azure_APIManagement_AuthZ_Restrict_Caller_IPs", - "Description": "Use Restrict caller IPs policies for additional protection", - "Id": "APIManagement250", - "ControlSeverity": "Medium", - "Automated": "Yes", - "MethodName": "CheckRestrictedCallerIPs", - "Rationale": "Using the IP filter policy feature ensures that access to the backend service is restricted to a specific set/group of clients. NOTE: While this control does provide an extra layer of access control protection, it may not always be feasible to implement in all scenarios.", - "Recommendation": "Use 'ip-filter' policy filters to allow and deny calls from specific IP addresses and/or address ranges. Do not add IP range $($this.ControlSettings.UniversalIPRange) as that allows access to all possible IPs. Please refer: https://docs.microsoft.com/en-us/azure/api-management/api-management-access-restriction-policies#RestrictCallerIPs", - "Tags": [ - "SDL", - "TCP", - "Automated", - "AuthZ", - "DeveloperSku", - "BasicSku", - "StandardSku", - "PremiumSku", - "APIManagement" - ], - "Enabled": true - }, + { + "ControlID": "Azure_APIManagement_AuthZ_Restrict_Caller_IPs", + "Description": "Use Restrict caller IPs policies for additional protection", + "Id": "APIManagement250", + "ControlSeverity": "Medium", + "Automated": "Yes", + "MethodName": "CheckRestrictedCallerIPs", + "Rationale": "Using the IP filter policy feature ensures that access to the backend service is restricted to a specific set/group of clients. NOTE: While this control does provide an extra layer of access control protection, it may not always be feasible to implement in all scenarios.", + "Recommendation": "a) Run command 'Get-AzApiManagementPolicy' to check IP addresses filter configured in your APIM service. b) Use 'ip-filter' policy filters to allow and deny calls from specific IP addresses and/or address ranges. c) Do not add IP range $($this.ControlSettings.UniversalIPRange) as that allows access to all possible IPs. Please refer: https://docs.microsoft.com/en-us/azure/api-management/api-management-access-restriction-policies#RestrictCallerIPs", + "Tags": [ + "SDL", + "TCP", + "Automated", + "AuthZ", + "DeveloperSku", + "BasicSku", + "StandardSku", + "PremiumSku", + "APIManagement" + ], + "Enabled": true + }, { "ControlID": "Azure_APIManagement_AuthN_Disable_Management_API", "Description": "Do not use API Management REST API", diff --git a/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 b/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 index e1718a8b3..6710931fd 100644 --- a/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 +++ b/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 @@ -6,6 +6,7 @@ class APIManagement: AzSVTBase hidden [PSObject] $APIMInstance = $null; hidden [PSObject] $APIMAPIs = $null; hidden [PSObject] $APIMProducts = $null; + hidden [PSObject] $APIUserAuth = $null; hidden [PSObject] $ResourceObject; @@ -173,8 +174,8 @@ class APIManagement: AzSVTBase $Product = $this.APIMProducts | Where-Object { ($_.State -eq [Microsoft.Azure.Commands.ApiManagement.ServiceManagement.Models.PsApiManagementProductState]::Published) -and ($_.SubscriptionRequired -eq $false )} if(($Product | Measure-Object).Count -gt 0) { - $controlResult.AddMessage([VerificationResult]::Failed, "'Requires Subscription' option is turned OFF for below Products in '$($this.ResourceContext.ResourceName)' API Management instance.", $Product ) - $controlResult.SetStateData("API product(s) open for public access without the requirement of subscriptions", $Product); + $controlResult.AddMessage([VerificationResult]::Failed, "'Requires Subscription' option is turned OFF for below Products in '$($this.ResourceContext.ResourceName)' API Management instance.", $Product.ProductId ) + $controlResult.SetStateData("API product(s) open for public access without the requirement of subscriptions", $Product.ProductId); } else { @@ -193,8 +194,8 @@ class APIManagement: AzSVTBase if(($null -ne $Product) -and ($Product.ApprovalRequired -contains $false)) { $Product = $Product | Where-Object { $_.ApprovalRequired -eq $false} - $controlResult.AddMessage([VerificationResult]::Verify, "'Requires Approval' option is turned OFF for below Products in '$($this.ResourceContext.ResourceName)' API Management instance.", $Product) - $controlResult.SetStateData("API product(s) where subscription attempts are auto-approved", $Product); + $controlResult.AddMessage([VerificationResult]::Verify, "'Requires Approval' option is turned OFF for below Products in '$($this.ResourceContext.ResourceName)' API Management instance.", $Product.ProductId) + $controlResult.SetStateData("API product(s) where subscription attempts are auto-approved", $Product.ProductId); } else { @@ -321,8 +322,8 @@ class APIManagement: AzSVTBase $Product = $this.APIMProducts | Where-Object { ($_.ProductId -eq 'starter') -or ($_.ProductId -eq 'unlimited') } if(($Product | Measure-Object).Count -gt 0) { - $controlResult.AddMessage([VerificationResult]::Failed, "APIM contains sample products. Delete the two sample products: Starter and Unlimited.",$Product) - $controlResult.SetStateData("APIM service sample product", $Product); + $controlResult.AddMessage([VerificationResult]::Failed, "APIM contains sample products. Delete the two sample products: Starter and Unlimited.", $Product.ProductId) + $controlResult.SetStateData("APIM service sample product", $Product.ProductId); } else { @@ -337,8 +338,17 @@ class APIManagement: AzSVTBase if(($null -ne $this.APIMContext) -and ($null -ne $this.APIMAPIs)) { $ClientCertAuthDisabledInAPIs = ($this.APIMAPIs).ApiId | ForEach-Object { - $apiPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_ - $certThumbprint = $apiPolicy | Select-Xml -XPath "//inbound//authentication-certificate" | foreach { $_.Node.thumbprint } + $apiPolicy = $null + try { + $apiPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_ -ErrorAction Stop + } + catch { + # This block has been intentionally left blank to avoid breaking the code flow + } + $certThumbprint = $null + if (-not [String]::IsNullOrEmpty($apiPolicy)) { + $certThumbprint = $apiPolicy | Select-Xml -XPath "//inbound//authentication-certificate" | foreach { $_.Node.thumbprint } + } if($certThumbprint -eq $null) { $_ @@ -374,226 +384,358 @@ class APIManagement: AzSVTBase $MaxApiCount = $this.ControlSettings.MaxApiCount } $Counter = 0 - $this.APIMAPIs | Select-Object ApiId, Name | ForEach-Object { - #Policy Scope: API - - if($Counter -ge $MaxApiCount) - { - sleep($SleepTime) - $Counter = 0 - } - $Counter ++ - $APIPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_.ApiId - $AllowedOrigins = "" - $AllowedOrigins = $APIPolicy | Select-Xml -XPath "//inbound//cors//origin" | foreach { $_.Node.InnerXML } - if($null -ne $AllowedOrigins) - { - $Policy = "" | Select-Object Scope, Name, Id, AllowedOrigins - $Policy.Scope = "API" - $Policy.Name = $_.Name - $Policy.Id = $_.ApiId - $Policy.AllowedOrigins = $($AllowedOrigins -join ",") - - $Result += $Policy - } - - #Policy Scope: Operation - Get-AzApiManagementOperation -Context $this.APIMContext -ApiId $_.ApiId | ForEach-Object { - $OperationPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_.ApiId -OperationId $_.OperationId - $AllowedOrigins = "" - $AllowedOrigins = $OperationPolicy | Select-Xml -XPath "//inbound//cors//origin" | foreach { $_.Node.InnerXML } - if($null -ne $AllowedOrigins) - { - $Policy = "" | Select-Object Scope, ScopeName, ScopeId, AllowedOrigins - $Policy.Scope = "Operation" - $Policy.ScopeName = $_.Name - $Policy.ScopeId = $_.OperationId - $Policy.AllowedOrigins = $($AllowedOrigins -join ",") - - $Result += $Policy - } - } - } - - $FailedResult = $Result | Where-Object { $_.AllowedOrigins.Split(",") -contains "*" } - - if(($FailedResult | Measure-Object).Count -gt 0) - { - $controlResult.AddMessage([VerificationResult]::Failed , - [MessageData]::new("CORS is enabled in APIM with access from all domains ('*') " + $this.ResourceContext.ResourceName, $FailedResult)); - $controlResult.SetStateData("CORS setting Allowed Origins", $FailedResult); - } - elseif(($Result | Measure-Object).Count -gt 0) + if ( ($this.APIMAPIs | Measure-Object).Count -gt $this.ControlSettings.APIManagement.MaxAllowedAPICount) { + # If number of APIs is higher than MaxAllowedAPICount, the control is marked as Verify. + # This check has been implemented because of the execution time of control in case of large subscription + # In this case, user must follow the FAQ provided in recommendation to check API/Operation level policy + $controlResult.AddMessage([VerificationResult]::Verify, - [MessageData]::new("CORS is enabled in APIM with access from below custom domains."),$Result); - $controlResult.SetStateData("CORS setting Allowed Origins",$Result); + [MessageData]::new("Total API count: $(($this.APIMAPIs | Measure-Object).Count)`n`rVerify that CORS access is granted to a minimal set of trusted origins and only required verbs are supported.`n`rEnsure that CORS is not enabled in APIM with access from all domains ('*'). Using '*' (allow all) for CORS setting means that all cross-origin requests are allowed.")); } else { - $controlResult.AddMessage([VerificationResult]::Manual, - [MessageData]::new("No CORS settings found for "+$this.ResourceContext.ResourceName)); + #Policy Scope: API + #Policy Scope: Operation + $this.APIMAPIs | Select-Object ApiId, Name | ForEach-Object { + #Policy Scope: API + + if($Counter -ge $MaxApiCount) + { + sleep($SleepTime) + $Counter = 0 + } + $Counter ++ + $APIPolicy = $null + try + { + $APIPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_.ApiId -ErrorAction Stop + } + catch + { + # This block has been intentionally left blank to avoid breaking the code flow + } + + $AllowedOrigins = $null + if (-not [String]::IsNullOrEmpty($APIPolicy)) + { + $AllowedOrigins = $APIPolicy | Select-Xml -XPath "//inbound//cors//origin" | foreach { $_.Node.InnerXML } + } + if($null -ne $AllowedOrigins) + { + $Policy = "" | Select-Object Scope, Name, Id, AllowedOrigins + $Policy.Scope = "API" + $Policy.Name = $_.Name + $Policy.Id = $_.ApiId + $Policy.AllowedOrigins = $($AllowedOrigins -join ",") + + $Result += $Policy + } + + #Policy Scope: Operation + Get-AzApiManagementOperation -Context $this.APIMContext -ApiId $_.ApiId | ForEach-Object { + $OperationPolicy = $null + try + { + $OperationPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_.ApiId -OperationId $_.OperationId -ErrorAction Stop + } + catch + { + # This block has been intentionally left blank to avoid breaking the code flow + } + + $AllowedOrigins = $null + if (-not [String]::IsNullOrEmpty($OperationPolicy)) + { + $AllowedOrigins = $OperationPolicy | Select-Xml -XPath "//inbound//cors//origin" | foreach { $_.Node.InnerXML } + } + if ($null -ne $AllowedOrigins) + { + $Policy = "" | Select-Object Scope, ScopeName, ScopeId, AllowedOrigins + $Policy.Scope = "Operation" + $Policy.ScopeName = $_.Name + $Policy.ScopeId = $_.OperationId + $Policy.AllowedOrigins = $($AllowedOrigins -join ",") + + $Result += $Policy + } + } + } + + $FailedResult = $Result | Where-Object { $_.AllowedOrigins.Split(",") -contains "*" } + + if(($FailedResult | Measure-Object).Count -gt 0) + { + $controlResult.AddMessage([VerificationResult]::Failed , + [MessageData]::new("CORS is enabled in APIM with access from all domains ('*') " + $this.ResourceContext.ResourceName, $FailedResult)); + $controlResult.SetStateData("CORS setting Allowed Origins", $FailedResult); + } + elseif(($Result | Measure-Object).Count -gt 0) + { + $controlResult.AddMessage([VerificationResult]::Verify, + [MessageData]::new("CORS is enabled in APIM with access from below custom domains."),$Result); + $controlResult.SetStateData("CORS setting Allowed Origins",$Result); + } + else + { + $controlResult.AddMessage([VerificationResult]::Manual, + [MessageData]::new("No CORS settings found for "+$this.ResourceContext.ResourceName)); + } } + } return $controlResult; } hidden [ControlResult] CheckRestrictedCallerIPs([ControlResult] $controlResult) { - if( $null -ne $this.APIMContext) + if ( $null -ne $this.APIMContext) { - $Result = @() + $IsAPILevelPolicyEvaluated = $false + $Message = "" + $Index = 1 + $RestrictedCallerIPsInfo = @() #Policy Scope: Gobal - $GlobalPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext - $RestrictedIPs = "" - $RestrictedIPs = $GlobalPolicy | Select-Xml -XPath "//inbound//ip-filter" | foreach { $_.Node } - $Policy = "" | Select Scope, ScopeName, ScopeId, Action, AddressRange, Status - $Policy.Scope = "Global" - $Policy.ScopeName = "NA" - $Policy.ScopeId = "NA" - $Policy.Action = "" - $Policy.AddressRange = "" - if($null -ne $RestrictedIPs) - { - $Policy.Action = $RestrictedIPs.Action - $Policy.AddressRange = $RestrictedIPs | Select-Object Address, Address-Range - $Policy.Status = 'Enabled' + $GlobalPolicy = $null + try + { + $GlobalPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ErrorAction Stop + } + catch + { + # This block has been intentionally left blank to avoid breaking the code flow + } + + $RestrictedIPs = $null + if (-not [String]::IsNullOrEmpty($GlobalPolicy)) + { + $RestrictedIPs = $GlobalPolicy | Select-Xml -XPath "//inbound//ip-filter" | foreach { $_.Node } + } + + if ($null -ne $RestrictedIPs) + { + $Policy = "" | Select Scope, ScopeName, ScopeId, Action, AddressRange, Status + $Policy.Scope = "Global" + $Policy.Action = $RestrictedIPs.Action + $Policy.AddressRange = $RestrictedIPs | Select-Object Address, Address-Range + $Policy.Status = 'Enabled' + $RestrictedCallerIPsInfo += $Policy + $Message += "[$($Index)] Scope: Global;`tIsCallerIPRestrictionConfigured: True" + } else { - $Policy.Status = 'Not Enabled' + $Message += "[$($Index)] Scope: Global;`tIsCallerIPRestrictionConfigured: False" } - $Result += $Policy + #Policy Scope: Product - if($null -ne $this.APIMProducts) + if ($null -ne $this.APIMProducts) { + $TotalProduct = ($this.APIMProducts | Measure-Object).Count + $ProductsWithIPFilter = 0 $this.APIMProducts | ForEach-Object { - $ProductPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ProductId $_.ProductId - $RestrictedIPs = "" - $RestrictedIPs = $ProductPolicy | Select-Xml -XPath "//inbound//ip-filter" | foreach { $_.Node } + $ProductPolicy = $null + try + { + $ProductPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ProductId $_.ProductId -ErrorAction Stop + } + catch + { + # This block has been intentionally left blank to avoid breaking the code flow + } + + $RestrictedIPs = $null + if (-not [String]::IsNullOrEmpty($ProductPolicy)) + { + $RestrictedIPs = $ProductPolicy | Select-Xml -XPath "//inbound//ip-filter" | foreach { $_.Node } + } - $Policy = "" | Select Scope, ScopeName, ScopeId, Action, AddressRange, Status - $Policy.Scope = "Product" - $Policy.ScopeName = $_.Title - $Policy.ScopeId = $_.ProductId - $Policy.Action = "" - $Policy.AddressRange = "" - if($null -ne $RestrictedIPs) - { - $Policy.Action = $RestrictedIPs.Action - $Policy.AddressRange = $RestrictedIPs | Select-Object Address, Address-Range - $Policy.Status = 'Enabled' - } + if ($null -ne $RestrictedIPs) + { + $Policy = "" | Select Scope, ScopeName, ScopeId, Action, AddressRange, Status + $Policy.Scope = "Product" + $Policy.ScopeName = $_.Title + $Policy.ScopeId = $_.ProductId + $Policy.Action = $RestrictedIPs.Action + $Policy.AddressRange = $RestrictedIPs | Select-Object Address, Address-Range + $Policy.Status = 'Enabled' + $ProductWithIPFilter = $ProductWithIPFilter + 1 + + $RestrictedCallerIPsInfo += $Policy + } + + } + + $Index = $Index + 1 + if ($ProductsWithIPFilter -gt 0) + { + $Message += "`n`r[$($Index)] Scope: Product;`tIsCallerIPRestrictionConfigured: True;`tTotalProducts: $($TotalProduct);`tProductsWithIPRestriction: $($ProductsWithIPFilter)" + } else { - $Policy.Status = 'Not Enabled' + $Message += "`n`r[$($Index)] Scope: Product;`tIsCallerIPRestrictionConfigured: False;`tTotalProducts: $($TotalProduct);`tProductsWithIPRestriction: $($ProductsWithIPFilter)" } - $Result += $Policy - } } - #Policy Scope: API - #Policy Scope: Operation - $MaxApiCount = 10 - $SleepTime = 30 - if([Helpers]::CheckMember($this.ControlSettings,"SleepTime")) - { - $SleepTime = $this.ControlSettings.SleepTime - } - if([Helpers]::CheckMember($this.ControlSettings,"MaxApiCount")) + + $TotalApis = ($this.APIMAPIs | Measure-Object).Count + if ( $TotalApis -gt $this.ControlSettings.APIManagement.MaxAllowedAPICount) { - $MaxApiCount = $this.ControlSettings.MaxApiCount + # If number of APIs is higher than MaxAllowedAPICount, the control is marked as Verify. + # This check has been implemented because of the execution time of control in case of large subscription + # In this case, user must follow the FAQ provided in recommendation to check API/Operation level policy + $IsAPILevelPolicyEvaluated = $false + $Index = $Index + 1 + $Message += "`n`r[$($Index)] Scope: API;`tIsCallerIPRestrictionConfigured: Not evaluated;`tTotalApis: $($TotalApis)"; + $Message += "`n`rVerify all the IP range configured at API and Operation level. IP range $($this.ControlSettings.UniversalIPRange) must not be used as this allows access to all possible IPs." } - $Counter = 0 - if($null -ne $this.APIMAPIs) + else { - $this.APIMAPIs | Select-Object ApiId, Name | ForEach-Object { - #Policy Scope: API - if($Counter -ge $MaxApiCount) - { - sleep($SleepTime) - $Counter = 0 + #Policy Scope: API + #Policy Scope: Operation + $IsAPILevelPolicyEvaluated = $true + $APIsWithIPRestriction = 0 + $OperationsWithIPRestriction = 0 + $TotalOperations = 0 + $MaxApiCount = 10 + $SleepTime = 30 + if ([Helpers]::CheckMember($this.ControlSettings, "SleepTime")) + { + $SleepTime = $this.ControlSettings.SleepTime + } + if ([Helpers]::CheckMember($this.ControlSettings, "MaxApiCount")) + { + $MaxApiCount = $this.ControlSettings.MaxApiCount + } + $Counter = 0 + if ($null -ne $this.APIMAPIs) + { + $this.APIMAPIs | Select-Object ApiId, Name | ForEach-Object { + #Policy Scope: API + if ($Counter -ge $MaxApiCount) + { + sleep($SleepTime) + $Counter = 0 + } + $Counter ++ + $APIPolicy = $null + try + { + $APIPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_.ApiId -ErrorAction Stop + } + catch + { + # This block has been intentionally left blank to avoid breaking the code flow + } + + $RestrictedIPs = $null + if (-not [String]::IsNullOrEmpty($APIPolicy)) + { + $RestrictedIPs = $APIPolicy | Select-Xml -XPath "//inbound//ip-filter" | foreach { $_.Node } + } + if ($null -ne $RestrictedIPs) + { + $Policy = "" | Select Scope, ScopeName, ScopeId, Action, AddressRange, Status + $Policy.Scope = "API" + $Policy.ScopeName = $_.Name + $Policy.ScopeId = $_.ApiId + $Policy.Action = $RestrictedIPs.Action + $Policy.AddressRange = $RestrictedIPs | Select-Object Address, Address-Range + $Policy.Status = 'Enabled' + $APIsWithIPRestriction = $APIsWithIPRestriction + 1 + + $RestrictedCallerIPsInfo += $Policy + } + + #Policy Scope: Operation + $Operations = Get-AzApiManagementOperation -Context $this.APIMContext -ApiId $_.ApiId + $TotalOperations = $TotalOperations + ($Operations | Measure-Object).Count + $Operations | ForEach-Object { + $OperationPolicy = $null + try + { + $OperationPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_.ApiId -OperationId $_.OperationId -ErrorAction Stop + } + catch + { + # This block has been intentionally left blank to avoid breaking the code flow + } + + $RestrictedIPs = $null + if (-not [String]::IsNullOrEmpty($OperationPolicy)) + { + $RestrictedIPs = $OperationPolicy | Select-Xml -XPath "//inbound//ip-filter" | foreach { $_.Node } + } + if ($null -ne $RestrictedIPs) + { + $Policy = "" | Select Scope, ScopeName, ScopeId, Action, AddressRange, Status + $Policy.Scope = "Operation" + $Policy.ScopeName = $_.Name + $Policy.ScopeId = $_.OperationId + $Policy.Action = $RestrictedIPs.Action + $Policy.AddressRange = $RestrictedIPs | Select-Object Address, Address-Range + $Policy.Status = 'Enabled' + $OperationsWithIPRestriction = $OperationsWithIPRestriction + 1 + + $RestrictedCallerIPsInfo += $Policy + } + } } - $Counter ++ - $APIPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_.ApiId - $RestrictedIPs = "" - $RestrictedIPs = $APIPolicy | Select-Xml -XPath "//inbound//ip-filter" | foreach { $_.Node } - $Policy = "" | Select Scope, ScopeName, ScopeId, Action, AddressRange, Status - $Policy.Scope = "API" - $Policy.ScopeName = $_.Name - $Policy.ScopeId = $_.ApiId - $Policy.Action = "" - $Policy.AddressRange = "" - if($null -ne $RestrictedIPs) + + $Index = $Index + 1 + if (($APIsWithIPRestriction | Measure-Object).Count -gt 0) { - $Policy.Action = $RestrictedIPs.Action - $Policy.AddressRange = $RestrictedIPs | Select-Object Address, Address-Range - $Policy.Status = 'Enabled' + $Message += "`n`r[$($Index)] Scope: API;`tIsCallerIPRestrictionConfigured: True;`tTotalApis: $($TotalApis);`tAPIsWithIPRestriction: $($APIsWithIPRestriction)" } else { - $Policy.Status = 'Not Enabled' + $Message += "`n`r[$($Index)] Scope: API;`tIsCallerIPRestrictionConfigured: False;`tTotalApis: $($TotalApis);`tAPIsWithIPRestriction: $($APIsWithIPRestriction)" } - $Result += $Policy - - #Policy Scope: Operation - Get-AzApiManagementOperation -Context $this.APIMContext -ApiId $_.ApiId | ForEach-Object { - $OperationPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_.ApiId -OperationId $_.OperationId - $RestrictedIPs = "" - $RestrictedIPs = $APIPolicy | Select-Xml -XPath "//inbound//ip-filter" | foreach { $_.Node } - $Policy = "" | Select Scope, ScopeName, ScopeId, Action, AddressRange, Status - $Policy.Scope = "Operation" - $Policy.ScopeName = $_.Name - $Policy.ScopeId = $_.OperationId - $Policy.Action = "" - $Policy.AddressRange = "" - if($null -ne $RestrictedIPs) - { - $Policy.Action = $RestrictedIPs.Action - $Policy.AddressRange = $RestrictedIPs | Select-Object Address, Address-Range - $Policy.Status = 'Enabled' - } - else - { - $Policy.Status = 'Not Enabled' - } - $Result += $Policy + + $Index = $Index + 1 + if (($OperationsWithIPRestriction | Measure-Object).Count -gt 0) + { + $Message += "`n`r[$($Index)] Scope: Operation;`tIsCallerIPRestrictionConfigured: True;`tTotalOperations: $($TotalOperations);`tOperationsWithIPRestriction: $($OperationsWithIPRestriction)" + } + else + { + $Message += "`n`r[$($Index)] Scope: Operation;`tIsCallerIPRestrictionConfigured: False;`tTotalOperations: $($TotalOperations);`tOperationsWithIPRestriction: $($OperationsWithIPRestriction)" } } } + #Fail control if universal address range 0.0.0.0-255.255.255.255 is used $anyToAnyIPFilter = @() - $allowedIPRange = $Result | Where-Object { $_.Action -eq 'Allow' } - if(($allowedIPRange | Measure-Object).Count -gt 0) + $allowedIPRange = $RestrictedCallerIPsInfo | Where-Object { $_.Action -eq 'Allow' } + if (($allowedIPRange | Measure-Object).Count -gt 0) { $anyToAnyIPFilter = $allowedIPRange | ForEach-Object { $AddressRange = $_.AddressRange[0].'address-range' | Where-Object { - if(($_ | Measure-Object).Count -gt 0) + if (($_ | Measure-Object).Count -gt 0) { (($_.from -eq $this.ControlSettings.IPRangeStartIP -and $_.to -eq $this.ControlSettings.IPRangeEndIP) -or ` - ($_.from -eq $this.ControlSettings.IPRangeEndIP -and $_.to -eq $this.ControlSettings.IPRangeStartIP)) + ($_.from -eq $this.ControlSettings.IPRangeEndIP -and $_.to -eq $this.ControlSettings.IPRangeStartIP)) } }; - if($AddressRange) + if ($AddressRange) { return $_ } } } - if(($anyToAnyIPFilter | Measure-Object).Count -gt 0) + if (($anyToAnyIPFilter | Measure-Object).Count -gt 0) { $controlResult.AddMessage([VerificationResult]::Failed, "Below IP restriction(s) are configured in $($this.ResourceContext.ResourceName) API management instance.", $anyToAnyIPFilter) - $controlResult.SetStateData("Restricted caller IPs",$anyToAnyIPFilter); + $controlResult.SetStateData("Restricted caller IPs", $anyToAnyIPFilter); } - elseif(($Result | Measure-Object).Count -gt 0) + elseif ((($RestrictedCallerIPsInfo | Measure-Object).Count -gt 0) -or $($IsAPILevelPolicyEvaluated -eq $false)) { - $controlResult.AddMessage([VerificationResult]::Verify, "Below IP restriction(s) are configured in $($this.ResourceContext.ResourceName) API management instance.", $Result) - $controlResult.SetStateData("Restricted caller IPs",$Result); + $controlResult.AddMessage([VerificationResult]::Verify, [MessageData]::new($Message)) } else { - $controlResult.AddMessage([VerificationResult]::Verify,"Unable to validate control. Please verify from portal that IP restirction is enabled for APIs.") + $controlResult.AddMessage([VerificationResult]::Verify, "Unable to validate control. Please verify from portal that IP restirction is enabled for APIs.") } } return $controlResult; @@ -623,7 +765,8 @@ class APIManagement: AzSVTBase if(($null -ne $this.APIMContext) -and ($null -ne $this.APIMProducts)) { $GuestGroupUsedInProductList = $this.APIMProducts | ForEach-Object { - if((Get-AzApiManagementGroup -Context $this.APIMContext -ProductId $_.ProductId).GroupId -contains 'guests') + $GroupDetails = Get-AzApiManagementGroup -Context $this.APIMContext -ProductId $_.ProductId + if([Helpers]::CheckMember($GroupDetails,"GroupId") -and $GroupDetails.GroupId -contains 'guests') { $_ } @@ -631,8 +774,8 @@ class APIManagement: AzSVTBase if($null -ne $GuestGroupUsedInProductList) { - $controlResult.AddMessage([VerificationResult]::Verify, "Guest groups is added to below products access control.", $GuestGroupUsedInProductList) - $controlResult.SetStateData("Products open to Guest users",$GuestGroupUsedInProductList); + $controlResult.AddMessage([VerificationResult]::Verify, "Guest groups is added to below products access control.", $GuestGroupUsedInProductList.ProductId) + $controlResult.SetStateData("Products open to Guest users",$GuestGroupUsedInProductList.ProductId); } else { @@ -696,11 +839,12 @@ class APIManagement: AzSVTBase hidden [ControlResult] CheckUserAuthorizationSettingInAPI([ControlResult] $controlResult) { - $APIUserAuth = $this.CheckUserAuthorizationSettingEnabledinAPI(); - if(($APIUserAuth.Disabled | Measure-Object).Count -gt 0) + $this.APIUserAuth = $this.CheckUserAuthorizationSettingEnabledinAPI(); + if(($this.APIUserAuth.Disabled | Measure-Object).Count -gt 0) { - $controlResult.AddMessage([VerificationResult]::Failed, "User Authorization : OAuth 2.0 or OpenID connect is not enabled in below APIs.", $APIUserAuth.Disabled) - $controlResult.SetStateData("User Authorization not enabled in APIs",$APIUserAuth.Disabled); + $APIListWithDisabledUserAuth = $this.APIUserAuth.Disabled | Select ApiId, ServiceUrl + $controlResult.AddMessage([VerificationResult]::Failed, "User Authorization : OAuth 2.0 or OpenID connect is not enabled in below APIs.", $APIListWithDisabledUserAuth) + $controlResult.SetStateData("User Authorization not enabled in APIs", $APIListWithDisabledUserAuth); } else { @@ -711,32 +855,44 @@ class APIManagement: AzSVTBase hidden [ControlResult] CheckJWTValidatePolicyInAPI([ControlResult] $controlResult) { - $APIUserAuth = $this.CheckUserAuthorizationSettingEnabledinAPI(); + $this.APIUserAuth = $this.CheckUserAuthorizationSettingEnabledinAPI(); $JWTValidatePolicyNotFound = @() - if(($APIUserAuth -ne 'ResourceNotFound') -and ($null -ne $this.APIMContext) -and ($null -ne $this.APIMAPIs)) + if(($this.APIUserAuth -ne 'ResourceNotFound') -and ($null -ne $this.APIMContext) -and ($null -ne $this.APIMAPIs)) { - $JWTValidatePolicyNotFound = $this.APIMAPIs | ForEach-Object { - $apiPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_.ApiId - $IsPolicyEnabled = $apiPolicy | Select-Xml -XPath "//inbound//validate-jwt" + $this.APIMAPIs | ForEach-Object { + $apiPolicy = $null + try { + $apiPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_.ApiId -ErrorAction Stop + } + catch { + # This block has been intentionally left blank to avoid breaking the code flow + } + + $IsPolicyEnabled = $null + if (-not [String]::IsNullOrEmpty($apiPolicy)) { + $IsPolicyEnabled = $apiPolicy | Select-Xml -XPath "//inbound//validate-jwt" + } if($null -eq $IsPolicyEnabled) { - $_ + $JWTValidatePolicyNotFound += $_ } } - $Temp = @() - if(($JWTValidatePolicyNotFound | Measure-Object).Count -gt 0 -and ($APIUserAuth.Enabled | Measure-Object).Count -gt 0) + # FailedApiList here is the list of API Ids for which user authentication is enabled but jwt validation is added in policy + $FailedApiList = @() + if(($JWTValidatePolicyNotFound | Measure-Object).Count -gt 0 -and ($this.APIUserAuth.Enabled | Measure-Object).Count -gt 0) { - $Temp = Compare-Object -ReferenceObject $APIUserAuth.Enabled.ApiID -DifferenceObject $JWTValidatePolicyNotFound.ApiId -IncludeEqual | Where-Object { $_.SideIndicator -eq "==" } + $FailedApiList = Compare-Object -ReferenceObject $this.APIUserAuth.Enabled.ApiID -DifferenceObject $JWTValidatePolicyNotFound.ApiId -IncludeEqual | Where-Object { $_.SideIndicator -eq "==" } } - if(($JWTValidatePolicyNotFound | Measure-Object).Count -gt 0 -and ($APIUserAuth.Enabled | Measure-Object).Count -gt 0 -and ($Temp | Measure-Object).Count -gt 0) + if(($JWTValidatePolicyNotFound | Measure-Object).Count -gt 0 -and ($this.APIUserAuth.Enabled | Measure-Object).Count -gt 0 -and ($FailedApiList | Measure-Object).Count -gt 0) { - $controlResult.AddMessage([VerificationResult]::Failed, "JWT Token validation not found for OAuth/OpenID connect authorization.", $Temp) - $controlResult.SetStateData("JWT Token validation not found",$Temp); + $controlResult.AddMessage([VerificationResult]::Failed, "JWT Token validation not found for OAuth/OpenID connect authorization.", $FailedApiList) + $controlResult.SetStateData("JWT Token validation not found",$FailedApiList); } elseif(($JWTValidatePolicyNotFound| Measure-Object).Count -gt 0) { - $controlResult.AddMessage([VerificationResult]::Verify,"The 'validate-jwt' policy is not configured in below APIs.", $JWTValidatePolicyNotFound) - $controlResult.SetStateData("JWT Token validation not found",$JWTValidatePolicyNotFound); + $JWTNotFoundReturnObj = $JWTValidatePolicyNotFound | Select-Object ApiId, ServiceUrl + $controlResult.AddMessage([VerificationResult]::Verify,"The 'validate-jwt' policy is not configured in below APIs.", $JWTNotFoundReturnObj) + $controlResult.SetStateData("JWT Token validation not found", $JWTNotFoundReturnObj); } else { @@ -750,34 +906,38 @@ class APIManagement: AzSVTBase { if( $null -ne $this.APIMContext -and ($null -ne $this.APIMAPIs)) { - $ResourceAppIdURI = [WebRequestHelper]::GetResourceManagerUrl() - $APIUserAuth = "" | Select-Object Enabled, Disabled - $APIUserAuth.Enabled = @() - $APIUserAuth.Disabled = @() - $this.APIMAPIs | ForEach-Object { - $uri=[system.string]::Format($ResourceAppIdURI+"subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.ApiManagement/service/{2}/apis/{3}?api-version=2018-06-01-preview",$this.SubscriptionContext.SubscriptionId,$this.ResourceContext.ResourceGroupName,$this.ResourceContext.ResourceName,$_.ApiId) - $json=$null; - try - { - $json=[WebRequestHelper]::InvokeGetWebRequest($uri); - } - catch - { + if ($null -eq $this.APIUserAuth) + { + $ResourceAppIdURI = [WebRequestHelper]::GetResourceManagerUrl() + $this.APIUserAuth = "" | Select-Object Enabled, Disabled + $this.APIUserAuth.Enabled = @() + $this.APIUserAuth.Disabled = @() + $this.APIMAPIs | ForEach-Object { + $uri=[system.string]::Format($ResourceAppIdURI+"subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.ApiManagement/service/{2}/apis/{3}?api-version=2018-06-01-preview",$this.SubscriptionContext.SubscriptionId,$this.ResourceContext.ResourceGroupName,$this.ResourceContext.ResourceName,$_.ApiId) $json=$null; - } - if(($null -ne $json) -and (($json | Measure-Object).Count -gt 0)) - { - if(([Helpers]::CheckMember($json[0],"properties.authenticationSettings")) -and ([Helpers]::CheckMember($json, "properties.authenticationSettings.oAuth2") -or [Helpers]::CheckMember($json, "properties.authenticationSettings.openid"))) - { - $APIUserAuth.Enabled += $_ + try + { + $json=[WebRequestHelper]::InvokeGetWebRequest($uri); + } + catch + { + $json=$null; } - else + if(($null -ne $json) -and (($json | Measure-Object).Count -gt 0)) { - $APIUserAuth.Disabled += $_ + if(([Helpers]::CheckMember($json[0],"properties.authenticationSettings")) -and ([Helpers]::CheckMember($json, "properties.authenticationSettings.oAuth2") -or [Helpers]::CheckMember($json, "properties.authenticationSettings.openid"))) + { + $this.APIUserAuth.Enabled += $_ + } + else + { + $this.APIUserAuth.Disabled += $_ + } } } } - return $APIUserAuth; + + return $this.APIUserAuth; } else { From 639ee870ea8cbef42248764aba9c5d3183f01e57 Mon Sep 17 00:00:00 2001 From: SINIKI Date: Fri, 10 Jan 2020 11:11:41 +0530 Subject: [PATCH 26/57] Corrected MaxAllowedAPICount value --- src/AzSK/Framework/Configurations/SVT/ControlSettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json index 22c100571..641082930 100644 --- a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json +++ b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json @@ -42,7 +42,7 @@ "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30", "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168" ], - "MaxAllowedAPICount": "30" + "MaxAllowedAPICount": 30 }, "VirtualMachine": { "Windows": { From 6cb0b5d2316f0becc8598598ad8a903442167610 Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Fri, 10 Jan 2020 11:32:21 +0530 Subject: [PATCH 27/57] max object count for resource inventory --- src/AzSK/Framework/Configurations/SVT/ControlSettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json index ce573f9dc..6bef2a716 100644 --- a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json +++ b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json @@ -533,5 +533,5 @@ "RequiredImageTag": "latest" } }, - "MaxResourceInventoryObjectsCount": 5000 + "MaxResourceInventoryObjectsCount": 20000 } From d085ce5a68007c3a81140e3ab1fa85a3885f7d87 Mon Sep 17 00:00:00 2001 From: Tarun Shukla Date: Fri, 10 Jan 2020 12:28:02 +0530 Subject: [PATCH 28/57] Added null check and removed commented code --- .../Core/ContinuousAssurance/CAAutomation.ps1 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 index aef3a0a5e..21631194d 100644 --- a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 +++ b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 @@ -4018,11 +4018,13 @@ class CCAutomation: AzCommandBase hidden [PSObject] GetScanLogsFromStorageAccount($containerName, $scanLogsPrefixPattern) { # Get AzSK storage of the current master sub + $CAScanDataBlobObject = $null $reportsStorageAccount = [UserSubscriptionDataHelper]::GetUserSubscriptionStorage() - $keys = Get-AzStorageAccountKey -ResourceGroupName $reportsStorageAccount.ResourceGroupName -Name $reportsStorageAccount.Name - $currentContext = New-AzStorageContext -StorageAccountName $reportsStorageAccount.Name -StorageAccountKey $keys[0].Value -Protocol Https - #$scanLogsPrefixPattern = $this.AutomationAccount.CoreResourceGroup + "/" + "$($this.SubscriptionContext.SubscriptionId)" + "/" - $CAScanDataBlobObject = Get-AzStorageBlob -Container $containerName -Prefix $scanLogsPrefixPattern -Context $currentContext -ErrorAction SilentlyContinue + if($null -ne $reportsStorageAccount){ + $keys = Get-AzStorageAccountKey -ResourceGroupName $reportsStorageAccount.ResourceGroupName -Name $reportsStorageAccount.Name + $currentContext = New-AzStorageContext -StorageAccountName $reportsStorageAccount.Name -StorageAccountKey $keys[0].Value -Protocol Https + $CAScanDataBlobObject = Get-AzStorageBlob -Container $containerName -Prefix $scanLogsPrefixPattern -Context $currentContext -ErrorAction SilentlyContinue + } return $CAScanDataBlobObject } From f0d0af2f0b248c23c4391aa034eda7913b643098 Mon Sep 17 00:00:00 2001 From: SINIKI Date: Fri, 10 Jan 2020 13:07:23 +0530 Subject: [PATCH 29/57] Updated comments in APIManagement.ps1 --- .../Core/SVT/Services/APIManagement.ps1 | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 b/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 index 6710931fd..e6df425b9 100644 --- a/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 +++ b/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 @@ -343,7 +343,8 @@ class APIManagement: AzSVTBase $apiPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_ -ErrorAction Stop } catch { - # This block has been intentionally left blank to avoid breaking the code flow + # Eat the current exception which typically happens when the command to fetch policy fails due to network issue or if the policy is not accessible from portal + # No need to break execution } $certThumbprint = $null if (-not [String]::IsNullOrEmpty($apiPolicy)) { @@ -413,7 +414,8 @@ class APIManagement: AzSVTBase } catch { - # This block has been intentionally left blank to avoid breaking the code flow + # Eat the current exception which typically happens when the command to fetch policy fails due to network issue or if the policy is not accessible from portal + # No need to break execution } $AllowedOrigins = $null @@ -441,7 +443,8 @@ class APIManagement: AzSVTBase } catch { - # This block has been intentionally left blank to avoid breaking the code flow + # Eat the current exception which typically happens when the command to fetch policy fails due to network issue or if the policy is not accessible from portal + # No need to break execution } $AllowedOrigins = $null @@ -503,7 +506,8 @@ class APIManagement: AzSVTBase } catch { - # This block has been intentionally left blank to avoid breaking the code flow + # Eat the current exception which typically happens when the command to fetch policy fails due to network issue or if the policy is not accessible from portal + # No need to break execution } $RestrictedIPs = $null @@ -520,12 +524,12 @@ class APIManagement: AzSVTBase $Policy.AddressRange = $RestrictedIPs | Select-Object Address, Address-Range $Policy.Status = 'Enabled' $RestrictedCallerIPsInfo += $Policy - $Message += "[$($Index)] Scope: Global;`tIsCallerIPRestrictionConfigured: True" + $Message += "[$($Index)] Scope: Global;`tIsIPRestrictionConfigured: True" } else { - $Message += "[$($Index)] Scope: Global;`tIsCallerIPRestrictionConfigured: False" + $Message += "[$($Index)] Scope: Global;`tIsIPRestrictionConfigured: False" } #Policy Scope: Product @@ -541,7 +545,8 @@ class APIManagement: AzSVTBase } catch { - # This block has been intentionally left blank to avoid breaking the code flow + # Eat the current exception which typically happens when the command to fetch policy fails due to network issue or if the policy is not accessible from portal + # No need to break execution } $RestrictedIPs = $null @@ -569,11 +574,11 @@ class APIManagement: AzSVTBase $Index = $Index + 1 if ($ProductsWithIPFilter -gt 0) { - $Message += "`n`r[$($Index)] Scope: Product;`tIsCallerIPRestrictionConfigured: True;`tTotalProducts: $($TotalProduct);`tProductsWithIPRestriction: $($ProductsWithIPFilter)" + $Message += "`n`r[$($Index)] Scope: Product;`tIsIPRestrictionConfigured: True;`tTotalProducts: $($TotalProduct);`tProductsWithIPRestriction: $($ProductsWithIPFilter)" } else { - $Message += "`n`r[$($Index)] Scope: Product;`tIsCallerIPRestrictionConfigured: False;`tTotalProducts: $($TotalProduct);`tProductsWithIPRestriction: $($ProductsWithIPFilter)" + $Message += "`n`r[$($Index)] Scope: Product;`tIsIPRestrictionConfigured: False;`tTotalProducts: $($TotalProduct);`tProductsWithIPRestriction: $($ProductsWithIPFilter)" } } @@ -585,8 +590,8 @@ class APIManagement: AzSVTBase # In this case, user must follow the FAQ provided in recommendation to check API/Operation level policy $IsAPILevelPolicyEvaluated = $false $Index = $Index + 1 - $Message += "`n`r[$($Index)] Scope: API;`tIsCallerIPRestrictionConfigured: Not evaluated;`tTotalApis: $($TotalApis)"; - $Message += "`n`rVerify all the IP range configured at API and Operation level. IP range $($this.ControlSettings.UniversalIPRange) must not be used as this allows access to all possible IPs." + $Message += "`n`r[$($Index)] Scope: API;`tIsIPRestrictionConfigured: Not evaluated;`tTotalApis: $($TotalApis)"; + $Message += "`n`rThe control could not be evaluated at API and Operations level due to high number of APIs. You will need to verify policies manually." } else { @@ -624,7 +629,8 @@ class APIManagement: AzSVTBase } catch { - # This block has been intentionally left blank to avoid breaking the code flow + # Eat the current exception which typically happens when the command to fetch policy fails due to network issue or if the policy is not accessible from portal + # No need to break execution } $RestrictedIPs = $null @@ -657,7 +663,8 @@ class APIManagement: AzSVTBase } catch { - # This block has been intentionally left blank to avoid breaking the code flow + # Eat the current exception which typically happens when the command to fetch policy fails due to network issue or if the policy is not accessible from portal + # No need to break execution } $RestrictedIPs = $null @@ -684,21 +691,21 @@ class APIManagement: AzSVTBase $Index = $Index + 1 if (($APIsWithIPRestriction | Measure-Object).Count -gt 0) { - $Message += "`n`r[$($Index)] Scope: API;`tIsCallerIPRestrictionConfigured: True;`tTotalApis: $($TotalApis);`tAPIsWithIPRestriction: $($APIsWithIPRestriction)" + $Message += "`n`r[$($Index)] Scope: API;`tIsIPRestrictionConfigured: True;`tTotalApis: $($TotalApis);`tAPIsWithIPRestriction: $($APIsWithIPRestriction)" } else { - $Message += "`n`r[$($Index)] Scope: API;`tIsCallerIPRestrictionConfigured: False;`tTotalApis: $($TotalApis);`tAPIsWithIPRestriction: $($APIsWithIPRestriction)" + $Message += "`n`r[$($Index)] Scope: API;`tIsIPRestrictionConfigured: False;`tTotalApis: $($TotalApis);`tAPIsWithIPRestriction: $($APIsWithIPRestriction)" } $Index = $Index + 1 if (($OperationsWithIPRestriction | Measure-Object).Count -gt 0) { - $Message += "`n`r[$($Index)] Scope: Operation;`tIsCallerIPRestrictionConfigured: True;`tTotalOperations: $($TotalOperations);`tOperationsWithIPRestriction: $($OperationsWithIPRestriction)" + $Message += "`n`r[$($Index)] Scope: Operation;`tIsIPRestrictionConfigured: True;`tTotalOperations: $($TotalOperations);`tOperationsWithIPRestriction: $($OperationsWithIPRestriction)" } else { - $Message += "`n`r[$($Index)] Scope: Operation;`tIsCallerIPRestrictionConfigured: False;`tTotalOperations: $($TotalOperations);`tOperationsWithIPRestriction: $($OperationsWithIPRestriction)" + $Message += "`n`r[$($Index)] Scope: Operation;`tIsIPRestrictionConfigured: False;`tTotalOperations: $($TotalOperations);`tOperationsWithIPRestriction: $($OperationsWithIPRestriction)" } } } @@ -731,6 +738,7 @@ class APIManagement: AzSVTBase } elseif ((($RestrictedCallerIPsInfo | Measure-Object).Count -gt 0) -or $($IsAPILevelPolicyEvaluated -eq $false)) { + $controlResult.AddMessage("Following are the IP policies at various scopes in this APIM instance:") $controlResult.AddMessage([VerificationResult]::Verify, [MessageData]::new($Message)) } else @@ -865,7 +873,8 @@ class APIManagement: AzSVTBase $apiPolicy = Get-AzApiManagementPolicy -Context $this.APIMContext -ApiId $_.ApiId -ErrorAction Stop } catch { - # This block has been intentionally left blank to avoid breaking the code flow + # Eat the current exception which typically happens when the command to fetch policy fails due to network issue or if the policy is not accessible from portal + # No need to break execution } $IsPolicyEnabled = $null @@ -913,6 +922,7 @@ class APIManagement: AzSVTBase $this.APIUserAuth.Enabled = @() $this.APIUserAuth.Disabled = @() $this.APIMAPIs | ForEach-Object { + # REST API to fetch user authorization setting at API level $uri=[system.string]::Format($ResourceAppIdURI+"subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.ApiManagement/service/{2}/apis/{3}?api-version=2018-06-01-preview",$this.SubscriptionContext.SubscriptionId,$this.ResourceContext.ResourceGroupName,$this.ResourceContext.ResourceName,$_.ApiId) $json=$null; try From 3009257a33ec87a254321a5bad31c61f5bce5e8e Mon Sep 17 00:00:00 2001 From: SINIKI Date: Fri, 10 Jan 2020 13:19:07 +0530 Subject: [PATCH 30/57] Resolved review comments. --- src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 b/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 index 69cfa42dc..e1aa90f7c 100644 --- a/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 +++ b/src/AzSK/Framework/Core/SVT/Services/SQLDatabase.ps1 @@ -86,14 +86,16 @@ class SQLDatabase: AzSVTBase hidden [ControlResult] CheckSqlServerAuditing([ControlResult] $controlResult) { $serverAudit = $null - try { + try + { $serverAudit = Get-AzSqlServerAudit -ResourceGroupName $this.ResourceContext.ResourceGroupName -ServerName $this.ResourceContext.ResourceName -ErrorAction Stop $controlResult.AddMessage([MessageData]::new("Current audit status for SQL server [$($this.ResourceContext.ResourceName)]:", $serverAudit)) } catch { + # This block is the catch execption when the storage configured for audit is not found. It either does not exist, associated with a different subscription or you do not have the appropriate credentials to access it. $controlResult.AddMessage("$($_.Exception.Message)") - $controlResult.AddMessage([VerificationResult]::Verify, ""); + $controlResult.VerificationResult = [VerificationResult]::Verify; } if($null -ne $serverAudit){ From 8b23edb9706ba507764b09c0e3dd40d818bedd5c Mon Sep 17 00:00:00 2001 From: SINIKI Date: Fri, 10 Jan 2020 13:27:07 +0530 Subject: [PATCH 31/57] Corrected spell error --- src/AzSK/Framework/Core/SVT/Services/ServiceBus.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/Services/ServiceBus.ps1 b/src/AzSK/Framework/Core/SVT/Services/ServiceBus.ps1 index 526017a9b..d9675ca76 100644 --- a/src/AzSK/Framework/Core/SVT/Services/ServiceBus.ps1 +++ b/src/AzSK/Framework/Core/SVT/Services/ServiceBus.ps1 @@ -199,29 +199,29 @@ class ServiceBus: AzSVTBase { $controlResult.VerificationResult = [VerificationResult]::Failed; - $faliedClients = New-Object -TypeName PSObject + $failedClients = New-Object -TypeName PSObject if(($fullPermissionQueues | Measure-Object).count -gt 0) { - $faliedClients | Add-Member -NotePropertyName FailedQueuesWithFullPermission -NotePropertyValue $fullPermissionQueues + $failedClients | Add-Member -NotePropertyName FailedQueuesWithFullPermission -NotePropertyValue $fullPermissionQueues $controlResult.AddMessage([MessageData]::new("Validate the authorization rules for the Queue are defined with limited permissions.", $fullPermissionQueues)); } if(($noPolicyQueues | Measure-Object).count -gt 0) { - $faliedClients | Add-Member -NotePropertyName FailedQueuesWithNoAccessPolicy -NotePropertyValue $noPolicyQueues + $failedClients | Add-Member -NotePropertyName FailedQueuesWithNoAccessPolicy -NotePropertyValue $noPolicyQueues $controlResult.AddMessage([MessageData]::new("No Authorization rules defined for following Queue. Either Queue is not in use or namespace level access policy is used. Applications (senders/receivers) must not use access policies defined at Service Bus namespace level.", $noPolicyQueues)); } if(($fullPermissionTopics | Measure-Object).count -gt 0) { - $faliedClients | Add-Member -NotePropertyName FailedTopicsWithFullPermission -NotePropertyValue $fullPermissionTopics + $failedClients | Add-Member -NotePropertyName FailedTopicsWithFullPermission -NotePropertyValue $fullPermissionTopics $controlResult.AddMessage([MessageData]::new("Validate the authorization rules for the Topic are defined with limited permissions.", $fullPermissionTopics)); } if(($noPolicyTopics | Measure-Object).count -gt 0) { - $faliedClients | Add-Member -NotePropertyName FailedTopicsWithNoAccessPolicy -NotePropertyValue $noPolicyTopics + $failedClients | Add-Member -NotePropertyName FailedTopicsWithNoAccessPolicy -NotePropertyValue $noPolicyTopics $controlResult.AddMessage([MessageData]::new("No Authorization rules defined for following Topic. Either Topic is not in use or namespace level access policy is used. Applications (senders/receivers) must not use access policies defined at Service Bus namespace level.", $noPolicyTopics)); } - $controlResult.SetStateData("Access policy with minimum required permission must be defined for the Queue/Topic", $faliedClients); + $controlResult.SetStateData("Access policy with minimum required permission must be defined for the Queue/Topic", $failedClients); } else { From f12e0c4128ccc139292301d6e599c67a49776f42 Mon Sep 17 00:00:00 2001 From: Tarun Shukla Date: Fri, 10 Jan 2020 14:03:10 +0530 Subject: [PATCH 32/57] Added check for multiple stoarge account --- src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 index 21631194d..7752bacd6 100644 --- a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 +++ b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 @@ -4020,7 +4020,7 @@ class CCAutomation: AzCommandBase # Get AzSK storage of the current master sub $CAScanDataBlobObject = $null $reportsStorageAccount = [UserSubscriptionDataHelper]::GetUserSubscriptionStorage() - if($null -ne $reportsStorageAccount){ + if($null -ne $reportsStorageAccount -and ($reportsStorageAccount | Measure-Object).Count -eq 1){ $keys = Get-AzStorageAccountKey -ResourceGroupName $reportsStorageAccount.ResourceGroupName -Name $reportsStorageAccount.Name $currentContext = New-AzStorageContext -StorageAccountName $reportsStorageAccount.Name -StorageAccountKey $keys[0].Value -Protocol Https $CAScanDataBlobObject = Get-AzStorageBlob -Container $containerName -Prefix $scanLogsPrefixPattern -Context $currentContext -ErrorAction SilentlyContinue From 4e7948ac49d77d1faa68656d0b3c8aa1931f503e Mon Sep 17 00:00:00 2001 From: Tarun Shukla Date: Fri, 10 Jan 2020 16:08:47 +0530 Subject: [PATCH 33/57] Modified code to fetch scan logs in last 3 days only --- .../Core/ContinuousAssurance/CAAutomation.ps1 | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 index 7752bacd6..9eefc9c0e 100644 --- a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 +++ b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 @@ -2187,9 +2187,6 @@ class CCAutomation: AzCommandBase # Get Scan logs from storage $scanLogsPrefixPattern = $this.AutomationAccount.ResourceGroup + "/" + "$($tgtSubStorageAccount.TargetSubscriptionId)" + "/" $CAScanDataBlobObject = $this.GetScanLogsFromStorageAccount($this.CAScanOutputLogsContainerName,$scanLogsPrefixPattern) - if($null -ne $CAScanDataBlobObject -and ($CAScanDataBlobObject| Measure-Object).Count -gt 0){ - $CAScanDataBlobObject = $CAScanDataBlobObject | Where-Object { $_.LastModified.UtcDateTime -ge [DateTime]::UtcNow.AddDays(-3)} - } if($null -ne $CAScanDataBlobObject -and ($CAScanDataBlobObject| Measure-Object).Count -gt 0) { # Scan logs found in storage for last 3 days @@ -2233,9 +2230,6 @@ class CCAutomation: AzCommandBase # Get AzSK storage of the current sub $scanLogsPrefixPattern = $this.AutomationAccount.ResourceGroup + "/" + "$($this.SubscriptionContext.SubscriptionId)" + "/" $CAScanDataBlobObject = $this.GetScanLogsFromStorageAccount($this.CAScanOutputLogsContainerName,$scanLogsPrefixPattern) - if($null -ne $CAScanDataBlobObject -and ($CAScanDataBlobObject| Measure-Object).Count -gt 0){ - $CAScanDataBlobObject = $CAScanDataBlobObject | Where-Object { $_.LastModified.UtcDateTime -ge [DateTime]::UtcNow.AddDays(-3)} - } if($null -ne $CAScanDataBlobObject -and ($CAScanDataBlobObject| Measure-Object).Count -gt 0) { $isScanLogsPresent = $true @@ -4018,14 +4012,21 @@ class CCAutomation: AzCommandBase hidden [PSObject] GetScanLogsFromStorageAccount($containerName, $scanLogsPrefixPattern) { # Get AzSK storage of the current master sub - $CAScanDataBlobObject = $null + $recentCAScanDataBlobObject = $null $reportsStorageAccount = [UserSubscriptionDataHelper]::GetUserSubscriptionStorage() if($null -ne $reportsStorageAccount -and ($reportsStorageAccount | Measure-Object).Count -eq 1){ + $recentLogLimitInDays = 3 + $dayCounter = 1 $keys = Get-AzStorageAccountKey -ResourceGroupName $reportsStorageAccount.ResourceGroupName -Name $reportsStorageAccount.Name $currentContext = New-AzStorageContext -StorageAccountName $reportsStorageAccount.Name -StorageAccountKey $keys[0].Value -Protocol Https - $CAScanDataBlobObject = Get-AzStorageBlob -Container $containerName -Prefix $scanLogsPrefixPattern -Context $currentContext -ErrorAction SilentlyContinue - } - return $CAScanDataBlobObject + while($dayCounter -le $recentLogLimitInDays -and $recentCAScanDataBlobObject -eq $null){ + $date = [DateTime]::UtcNow.AddDays(-$counter).ToString("yyyyMMdd") + $recentLogsPath = $scanLogsPrefixPattern + "AutomationLogs_" + $date + $recentCAScanDataBlobObject = Get-AzStorageBlob -Container $containerName -Prefix $recentLogsPath -Context $currentContext -ErrorAction SilentlyContinue + $dayCounter += 1 + } + } + return $recentCAScanDataBlobObject } #get App RGs From 2d8d14b923719ab1d9f470183e76cccca9ef5354 Mon Sep 17 00:00:00 2001 From: Tarun Shukla Date: Fri, 10 Jan 2020 16:11:55 +0530 Subject: [PATCH 34/57] Fixed typo --- src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 index 9eefc9c0e..4772a2bd0 100644 --- a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 +++ b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 @@ -4016,7 +4016,7 @@ class CCAutomation: AzCommandBase $reportsStorageAccount = [UserSubscriptionDataHelper]::GetUserSubscriptionStorage() if($null -ne $reportsStorageAccount -and ($reportsStorageAccount | Measure-Object).Count -eq 1){ $recentLogLimitInDays = 3 - $dayCounter = 1 + $dayCounter = 0 $keys = Get-AzStorageAccountKey -ResourceGroupName $reportsStorageAccount.ResourceGroupName -Name $reportsStorageAccount.Name $currentContext = New-AzStorageContext -StorageAccountName $reportsStorageAccount.Name -StorageAccountKey $keys[0].Value -Protocol Https while($dayCounter -le $recentLogLimitInDays -and $recentCAScanDataBlobObject -eq $null){ From 24c111275d5ee81dcc3b122dc73b2352939b7ee8 Mon Sep 17 00:00:00 2001 From: Garima Singh Date: Fri, 10 Jan 2020 16:22:28 +0530 Subject: [PATCH 35/57] Fix for AttestationValue variable not found in CA --- src/AzSK.Framework/Abstracts/CommandBase.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AzSK.Framework/Abstracts/CommandBase.ps1 b/src/AzSK.Framework/Abstracts/CommandBase.ps1 index 9e6c381b6..dfb0e59ad 100644 --- a/src/AzSK.Framework/Abstracts/CommandBase.ps1 +++ b/src/AzSK.Framework/Abstracts/CommandBase.ps1 @@ -183,7 +183,8 @@ else { $controlAttested = $false if( ([FeatureFlightingManager]::GetFeatureStatus("EnableScanAfterAttestation","*"))) { #Global variable "AttestationValue" is set to true when one or more controls are attested in current scan - if (Get-Variable AttestationValue -Scope Global){ + #Ignore if variable AttestationValue is not found + if (Get-Variable AttestationValue -Scope Global -ErrorAction Ignore){ if ( $Global:AttestationValue){ $controlAttested = $true } From ba548ce30ac203fd9938c7a6b1690510cd97697f Mon Sep 17 00:00:00 2001 From: Tarun Shukla Date: Fri, 10 Jan 2020 16:30:21 +0530 Subject: [PATCH 36/57] Updated msg --- src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 index 4772a2bd0..78ea5be00 100644 --- a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 +++ b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 @@ -2240,7 +2240,7 @@ class CCAutomation: AzCommandBase $resolveMsg = "Please contact AzSK support team for a resolution." if($isScanLogsPresent) { - $resultMsg = "Scan logs are present in storage account for last 3 days." + $resultMsg = "AzSK storage account contains logs for recent jobs." $resultStatus = "OK" } else @@ -4020,7 +4020,7 @@ class CCAutomation: AzCommandBase $keys = Get-AzStorageAccountKey -ResourceGroupName $reportsStorageAccount.ResourceGroupName -Name $reportsStorageAccount.Name $currentContext = New-AzStorageContext -StorageAccountName $reportsStorageAccount.Name -StorageAccountKey $keys[0].Value -Protocol Https while($dayCounter -le $recentLogLimitInDays -and $recentCAScanDataBlobObject -eq $null){ - $date = [DateTime]::UtcNow.AddDays(-$counter).ToString("yyyyMMdd") + $date = [DateTime]::UtcNow.AddDays(-$dayCounter).ToString("yyyyMMdd") $recentLogsPath = $scanLogsPrefixPattern + "AutomationLogs_" + $date $recentCAScanDataBlobObject = Get-AzStorageBlob -Container $containerName -Prefix $recentLogsPath -Context $currentContext -ErrorAction SilentlyContinue $dayCounter += 1 From 470d823f0d17c50bb16c653e33ce6ec43adec1e4 Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Fri, 10 Jan 2020 16:49:03 +0530 Subject: [PATCH 37/57] Changes for ASC security alert control and new control for PIM non alt assignments --- .../Configurations/SVT/ControlSettings.json | 6 +- .../SubscriptionCore/SubscriptionCore.json | 18 ++++++ .../SVT/SubscriptionCore/SubscriptionCore.ps1 | 64 ++++++++++++++++++- 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json index 6bef2a716..2205de638 100644 --- a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json +++ b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json @@ -226,7 +226,11 @@ "PIMCAPolicyTags":[], "PIMAppId":"01fc33a7-78ba-4d2f-a4b7-768e336e890e", "ASCAlertsSeverityLevels": [ "High" ], - "ASCAlertsThresholdInDays": 30, + "ASCAlertsThresholdInDays": { + "High": 0, + "Medium": 30 + + }, "WhitelistedMgmtCerts": { "Thumbprints": [], "ApprovedValidityRangeInDays": 732 diff --git a/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json b/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json index 27b38457f..efcf80bc2 100644 --- a/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json +++ b/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json @@ -528,6 +528,24 @@ ], "Enabled": true, "Rationale": "Periodic credential rotation is a good security hygiene practice as, over time, it minimizes the likelihood of data loss/compromise which can arise from key theft/brute forcing/recovery attacks. Credential expiry can also impact availability of existing apps." + }, + { + "ControlID": "Azure_Subscription_Check_NonAlternate_Critical_PIM_Assignments", + "Description": "Ensure non alternate accounts are not assigned critical roles in the subscription.", + "Id": "SubscriptionCore320", + "ControlSeverity": "High", + "Automated": "Yes", + "MethodName": "CheckNonAlternateAccountsinPIMAccess", + "Recommendation": "Go to Azure portal -> Priviledged Identity Management -> Azure Resources -> Select the scope -> Members-> Eligible roles. Remove non alternate accounts that have critical roles assigned in the subscription.", + "Tags": [ + "SDL", + "TCP", + "Automated", + "AuthZ", + "SubscriptionCore" + ], + "Enabled": true, + "Rationale": "" } ] } diff --git a/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 b/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 index 6a2dff336..b694bcda1 100644 --- a/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 +++ b/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 @@ -529,11 +529,12 @@ class SubscriptionCore: AzSVTBase $activeAlerts = ($this.ASCSettings.Alerts | Where-Object {$_.State -eq "Active" }) if(($activeAlerts | Measure-Object).Count -gt 0 ) { - if( [Helpers]::CheckMember($this.ControlSettings, 'ASCAlertsThresholdInDays') -and [Helpers]::CheckMember($this.ControlSettings, 'ASCAlertsSeverityLevels')) + if( [Helpers]::CheckMember($this.ControlSettings, 'ASCAlertsThresholdInDays') ) { $AlertDaysCheck = $this.ControlSettings.ASCAlertsThresholdInDays - $AlertSeverityCheck = $this.ControlSettings.ASCAlertsSeverityLevels - $activeAlerts = $activeAlerts | Where-Object{ ( [System.DateTime]::Parse($_.ReportedTimeUTC).AddDays($AlertDaysCheck) -ge ([System.DateTime]::UtcNow)) -and $_.ReportedSeverity -in $AlertSeverityCheck} + $AlertSeverityCheck = $this.ControlSettings.ASCAlertsThresholdInDays.PSObject.Properties.Name; + $activeAlerts = $activeAlerts | Where-Object {$_.ReportedSeverity -in $AlertSeverityCheck} + $activeAlerts = $activeAlerts | Where-Object{ ( [System.DateTime]::Parse($_.ReportedTimeUTC).AddDays($AlertDaysCheck.($_.ReportedSeverity)) -le ([System.DateTime]::UtcNow)) } if(($activeAlerts | Measure-Object).Count -gt 0) { $controlResult.SetStateData("Active alert in Security Center", ($activeAlerts | Select-Object AlertName, ReportedTimeUTC)); @@ -1512,6 +1513,63 @@ class SubscriptionCore: AzSVTBase } return $controlResult; } + + + hidden [ControlResult] CheckNonAlternateAccountsinPIMAccess([ControlResult] $controlResult) + { + $AltAccountRegX = [string]::Empty; + if($null -eq $this.PIMAssignments) + { + $message=$this.GetPIMRoles(); + } + if($message -ne 'OK') # if there is some while making request message will contain exception + { + + $controlResult.AddMessage("Unable to fetch PIM data, please verify manually.") + $controlResult.AddMessage($message); + return $controlResult; + } + # get the altenate account pattern from org policy control settings + if( [Helpers]::CheckMember($this.ControlSettings,"AlernateAccountRegularExpressionForOrg")) + { + $AltAccountRegX = $this.ControlSettings.AlernateAccountRegularExpressionForOrg + } + else + { + # if unable to get the altenate account pattern from policy control settings, let the control be manual + $controlResult.AddMessage("Unable to get the alternate account pattern for your org. Please verify manually") + return $controlResult; + } + if(($this.PIMAssignments| Measure-Object).Count -gt 0) + { + # get pim assignments for critical roles at subscription level + $PIMAssignmentsForCriticalRoles = $this.PIMAssignments | Where-Object {$_.RoleDefinitionName -in $this.ControlSettings.CriticalPIMRoles.Subscription} + if(($PIMAssignmentsForCriticalRoles | Measure-Object).Count -gt 0) + { + $IdentityName= $PIMAssignmentsForCriticalRoles.DisplayName | Get-Unique + $Users = @(); + # we currently do not have principal names stored in the PIMAssignments object thus need to get the principal name explicitly + $IdentityName | ForEach-Object{ + $Users += Get-AzADUser -DisplayName $_ + } + + + $nonAltPIMAccounts = $Users | Where-Object{$_.UserPrincipalName -notmatch $AltAccountRegX} + if(($nonAltPIMAccounts | Measure-Object).Count -gt 0) + { + $nonAltPIMAccountsWithRoles = $PIMAssignmentsForCriticalRoles | Where-Object{$_.DisplayName -in $nonAltPIMAccounts.DisplayName} + $controlResult.AddMessage([VerificationResult]::Failed, "Non alternate accounts are assigned critical roles") + $controlResult.AddMessage($nonAltPIMAccountsWithRoles) + } + else + { + $controlResult.AddMessage([VerificationResult]::Passed, "No Non alternate accounts are assigned critical roles") + } + } + } + + return $controlResult; + } hidden [void] LoadRBACConfig() { if(($this.MandatoryAccounts | Measure-Object).Count -eq 0 ` From ee7e58790177773fc24e809df1c0a121566c866f Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Fri, 10 Jan 2020 16:54:33 +0530 Subject: [PATCH 38/57] removed non used settings --- src/AzSK/Framework/Configurations/SVT/ControlSettings.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json index ad5ebf880..147b5f1dc 100644 --- a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json +++ b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json @@ -226,8 +226,7 @@ "CheckPIMCAPolicyTags": false, "PIMCAPolicyTags":[], "PIMAppId":"01fc33a7-78ba-4d2f-a4b7-768e336e890e", - "ASCAlertsSeverityLevels": [ "High" ], - "ASCAlertsThresholdInDays": { + "ASCAlertsThresholdInDays": { "High": 0, "Medium": 30 From 2865c34244d33889809b8639b1fdcf41e1eda9e3 Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Fri, 10 Jan 2020 17:38:56 +0530 Subject: [PATCH 39/57] fix --- .../Framework/Configurations/SVT/ControlSettings.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json index 147b5f1dc..178f16602 100644 --- a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json +++ b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json @@ -41,8 +41,7 @@ "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11", "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30", "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168" - ], - "MaxAllowedAPICount": 30 + ] }, "VirtualMachine": { "Windows": { @@ -226,7 +225,8 @@ "CheckPIMCAPolicyTags": false, "PIMCAPolicyTags":[], "PIMAppId":"01fc33a7-78ba-4d2f-a4b7-768e336e890e", - "ASCAlertsThresholdInDays": { + "ASCAlertsSeverityLevels": [ "High" ], + "ASCAlertsThresholdInDays": { "High": 0, "Medium": 30 @@ -537,5 +537,6 @@ "RequiredImageTag": "latest" } }, - "MaxResourceInventoryObjectsCount": 20000 + "MaxResourceInventoryObjectsCount": 20000, + "AlernateAccountRegularExpressionForOrg": "" } From 704e6a9037b1097cfa6f5d86d6d19d3791d0ae1d Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Fri, 10 Jan 2020 19:03:52 +0530 Subject: [PATCH 40/57] fixing the build issue --- .../Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 b/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 index ce82233e2..fbd0c3718 100644 --- a/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 +++ b/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 @@ -1518,6 +1518,7 @@ class SubscriptionCore: AzSVTBase hidden [ControlResult] CheckNonAlternateAccountsinPIMAccess([ControlResult] $controlResult) { $AltAccountRegX = [string]::Empty; + $message = [string]::Empty; if($null -eq $this.PIMAssignments) { $message=$this.GetPIMRoles(); From 00b9f4a7d9936581d633694764cb67d03a2dd790 Mon Sep 17 00:00:00 2001 From: RohitYadav-msft Date: Fri, 10 Jan 2020 19:51:16 +0530 Subject: [PATCH 41/57] Azure_AppService_Deploy_Use_Latest_Version --- src/AzSK/Framework/Configurations/ARMChecker/ARMControls.json | 4 ++-- src/OSSConfiguration/ARMControls.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AzSK/Framework/Configurations/ARMChecker/ARMControls.json b/src/AzSK/Framework/Configurations/ARMChecker/ARMControls.json index 09eb83337..4ad2a5dea 100644 --- a/src/AzSK/Framework/Configurations/ARMChecker/ARMControls.json +++ b/src/AzSK/Framework/Configurations/ARMChecker/ARMControls.json @@ -529,10 +529,10 @@ "recommendation": "Run command 'Set-AzWebApp -Name '' -ResourceGroupName '' -NetFrameworkVersion 'v4.7''. Run 'Get-Help Set-AzWebApp -full' for more help.", "severity": "Low", "jsonPath": [ "$.properties.siteConfig.netFrameworkVersion", "$.properties.netFrameworkVersion" ], - "matchType": "StringSingleToken", + "matchType": "RegExpressionSingleToken", "data": { "type": "Allow", - "value": "v4.7", + "pattern": "^(v4.0|v4.7)$", "isCaseSensitive": false } }, diff --git a/src/OSSConfiguration/ARMControls.json b/src/OSSConfiguration/ARMControls.json index 32d7fc8f5..33badb746 100644 --- a/src/OSSConfiguration/ARMControls.json +++ b/src/OSSConfiguration/ARMControls.json @@ -529,10 +529,10 @@ "recommendation": "Run command 'Set-AzWebApp -Name '' -ResourceGroupName '' -NetFrameworkVersion 'v4.7''. Run 'Get-Help Set-AzWebApp -full' for more help.", "severity": "Low", "jsonPath": [ "$.properties.siteConfig.netFrameworkVersion", "$.properties.netFrameworkVersion" ], - "matchType": "StringSingleToken", + "matchType": "RegExpressionSingleToken", "data": { "type": "Allow", - "value": "v4.7", + "pattern": "^(v4.0|v4.7)$", "isCaseSensitive": false } }, From 9428bbb5175e6a40dedf619c7e8a1988b7e03975 Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Mon, 13 Jan 2020 09:56:42 +0530 Subject: [PATCH 42/57] Bug fix for resourceInventory limiting to only scannable resources --- src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 b/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 index afe745f93..3eed2a913 100644 --- a/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 +++ b/src/AzSK/Framework/Listeners/AzResourceInventoryListener.ps1 @@ -43,7 +43,7 @@ class AzResourceInventoryListener: ListenerBase if($resources.Count -gt $maxResourceCount) { $resources= [ResourceInventory]::FilteredResources - [AIOrgTelemetryHelper]::TrackEvent("Raw Resource Inventory Aborted", $resources.Count, $null) + [AIOrgTelemetryHelper]::PublishEvent( "Raw Resource Inventory Aborted",@{"TotalResources"= $([ResourceInventory]::RawResources |Measure-Object).Count;"AzSKScanableResources"=$(([ResourceInventory]::FilteredResources| Measure-Object).Count); "SubscriptionId"=$SubscriptionId;"PartialScanIdentifier"=$this.PartialScanIdentifier;}, $null) } $resourceGroups = Get-AzResourceGroup $resourceDetails = @(); From d36ba920a5677f928c7d26a30fe93676b106aff7 Mon Sep 17 00:00:00 2001 From: Tarun Shukla Date: Mon, 13 Jan 2020 11:19:20 +0530 Subject: [PATCH 43/57] Updated the messgaes for GCA changes. --- .../Framework/Core/ContinuousAssurance/CAAutomation.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 index 78ea5be00..de5ed0462 100644 --- a/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 +++ b/src/AzSK/Framework/Core/ContinuousAssurance/CAAutomation.ps1 @@ -2126,7 +2126,7 @@ class CCAutomation: AzCommandBase } #Check 12.2 Suspended job in last 3 days elseif($null -ne $suspendedJobs -and ($suspendedJobs|Measure-Object).Count -gt 0){ - $failMsg = "One or more CA scanning automation runbook (job) has been in suspended state in the last 3 days." + $failMsg = "One or more automation runbook jobs have been suspended in the last 3 days." $resolvemsg = "Please contact AzSK support team for a resolution." $resultMsg = "$failMsg`r`n$resolvemsg" $resultStatus = "Warning" @@ -2159,7 +2159,7 @@ class CCAutomation: AzCommandBase #region: Step 13: Check if storage account rec'd scan logs in last 3 days $stepCount++ - $checkDescription = "Inspecting AzSK storage account contains logs for recent jobs." + $checkDescription = "Inspecting AzSK storage account for scan logs from recent jobs." $tgtSubsLogsStatus = @() $isScanLogsPresent = $true if($this.IsCentralScanModeOn) @@ -2240,12 +2240,12 @@ class CCAutomation: AzCommandBase $resolveMsg = "Please contact AzSK support team for a resolution." if($isScanLogsPresent) { - $resultMsg = "AzSK storage account contains logs for recent jobs." + $resultMsg = "AzSK storage account contains scan logs for recent jobs as expected." $resultStatus = "OK" } else { - $failMsg = "Scan logs are missing in storage account for last 3 days." + $failMsg = "CA scan logs are missing in storage account for last 3 days." $resultMsg = "$failMsg`r`n$resolvemsg" $resultStatus = "Failed" $shouldReturn = $true From e23f49a8bcf247586754f46d405d63c241e11144 Mon Sep 17 00:00:00 2001 From: Garima Singh Date: Mon, 13 Jan 2020 11:54:06 +0530 Subject: [PATCH 44/57] BOM char fix for ResourceFlat API --- src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 | 6 +----- src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 b/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 index c2ae55cec..1baf86a7f 100644 --- a/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 +++ b/src/AzSK.Framework/Helpers/AIOrgTelemetryHelper.ps1 @@ -514,11 +514,7 @@ static [void] PublishEvent([System.Collections.ArrayList] $servicescantelemetryE catch{ # Error while sending events to telemetry. Encode content to UTF8 and make API call again to handle BOM/special characters if (($null -ne $eventJson)-and ($eventJson.length -gt 0)) { - if([Helpers]::CheckMember($_.Exception,"Response.StatusCode")){ - if ($_.Exception.Response.StatusCode -eq "BadRequest") { - [AIOrgTelemetryHelper]::PostUTF8Content($uri, $eventJson); - } - } + [AIOrgTelemetryHelper]::PostUTF8Content($uri, $eventJson); } } } diff --git a/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 b/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 index 6fda5907d..640e55071 100644 --- a/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 +++ b/src/AzSK.Framework/Helpers/RemoteApiHelper.ps1 @@ -24,11 +24,7 @@ class RemoteApiHelper { catch { #Error while sending events to Database. Encode content to UTF8 and make API call again to handle BOM/special characters if (($null -ne $content)-and ($content.length -gt 0)) { - if([Helpers]::CheckMember($_.Exception,"Response.StatusCode")){ - if ($_.Exception.Response.StatusCode -eq "BadRequest") { - [RemoteApiHelper]::PostUTF8Content($uri, $content, "application/json") - } - } + return [RemoteApiHelper]::PostUTF8Content($uri, $content, "application/json") } return "ERROR" } From 8e12cc45e6ede503d97f5bd2186adb212cbf6631 Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Mon, 13 Jan 2020 14:01:47 +0530 Subject: [PATCH 45/57] Bug fix for Subscription New control and ErNetwork Nic object properties not found --- .../SubscriptionCore/SubscriptionCore.json | 8 +- src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 | 22 ++--- .../SVT/SubscriptionCore/SubscriptionCore.ps1 | 95 ++++++++++--------- 3 files changed, 67 insertions(+), 58 deletions(-) diff --git a/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json b/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json index efcf80bc2..9952f1e6b 100644 --- a/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json +++ b/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json @@ -530,13 +530,13 @@ "Rationale": "Periodic credential rotation is a good security hygiene practice as, over time, it minimizes the likelihood of data loss/compromise which can arise from key theft/brute forcing/recovery attacks. Credential expiry can also impact availability of existing apps." }, { - "ControlID": "Azure_Subscription_Check_NonAlternate_Critical_PIM_Assignments", - "Description": "Ensure non alternate accounts are not assigned critical roles in the subscription.", + "ControlID": "Azure_Subscription_Use_Only_Alt_Credentials", + "Description": "Ensure that critical operations in the subscription are conducted using alternate (ALT) credentials", "Id": "SubscriptionCore320", "ControlSeverity": "High", "Automated": "Yes", "MethodName": "CheckNonAlternateAccountsinPIMAccess", - "Recommendation": "Go to Azure portal -> Priviledged Identity Management -> Azure Resources -> Select the scope -> Members-> Eligible roles. Remove non alternate accounts that have critical roles assigned in the subscription.", + "Recommendation": "Go to Azure portal -> Priviledged Identity Management -> Azure Resources -> Select the scope -> Members-> Eligible roles and verify the non alernate accounts. Ensure that only alternate accounts are used as members of critical roles in the subscription. Do not use day to day user accounts.", "Tags": [ "SDL", "TCP", @@ -545,7 +545,7 @@ "SubscriptionCore" ], "Enabled": true, - "Rationale": "" + "Rationale": "The regular / day to day use accounts are subject to a lot of credential theft attacks due to various activities that a user conducts using such accounts (e.g., browsing the web, clicking on email links, etc.). A user account that gets compromised (say via a phishing attack) immediately subjects the entire cloud subscription to risk if it is a member of critical roles in the subscription. Use of smartcard-backed alternate (SC-ALT) accounts instead protects the cloud subscriptions from this risk. Moreover, for complete protection, all sensitive access must be done using a secure admin workstation (SAW) and Azure Privileged Identity Management (PIM)." } ] } diff --git a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 index c3f10c341..690f69eb4 100644 --- a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 +++ b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 @@ -47,7 +47,7 @@ class SVTIaasBase: AzSVTBase if($null -ne $ipc -and ($ipc.IpConfigurations | Measure-Object).Count -gt 0) { - $this.vNetNics = $nics| Where-Object{($_.IpConfigurations.Id) -in $ipc.IpConfigurations.Id} + $this.vNetNics = $nics | Where-Object{($_.IpConfigurations.Id) -in $ipc.IpConfigurations.Id } } } @@ -102,7 +102,7 @@ class SVTIaasBase: AzSVTBase try { Set-Variable -Name nic -Scope Local -Value $_ - $out = ""| Select-Object NICName, VMName, VMId, PrimaryStatus, NetworkSecurityGroupName,NetworkSecurityGroupId, PublicIpAddress, PrivateIpAddress, EnableIPForwarding, IpConfigurations + $out = ""| Select-Object NICName, VMName, VMId, PrimaryStatus, NetworkSecurityGroupName,NetworkSecurityGroupId, PublicIpAddress, PrivateIpAddress, EnableIPForwarding, IpConfigurations $out.NICName = $nic.Name $out.IpConfigurations = $nic.IpConfigurations $out.EnableIPForwarding = $nic.EnableIPForwarding @@ -114,8 +114,7 @@ class SVTIaasBase: AzSVTBase $NICPublicIpAddresses = $nic.ipconfigurations.PublicIpAddress $PrivateIpAddresses = $nic.ipconfigurations.PrivateIpAddress - $PublicIpAddresses =@() - if(($PublicIpAddresses |Measure-Object).Count -gt 0) + if(($NICPublicIpAddresses |Measure-Object).Count -gt 0) { $NICPublicIpAddresses | ForEach-Object{ try @@ -139,7 +138,7 @@ class SVTIaasBase: AzSVTBase } else { - $nicproperties.IpConfigurations | ForEach-Object{ + $nic.IpConfigurations | ForEach-Object{ Set-Variable -Name ipconfiguration -Scope Local -Value $_ try { @@ -161,24 +160,25 @@ class SVTIaasBase: AzSVTBase $out.PrivateIpAddress = ([System.String]::Join(";",$PrivateIpAddresses)) - if(($nicproperties | Get-Member -Name "VirtualMachine") -and $nicproperties.VirtualMachine ) + if(($nic | Get-Member -Name "VirtualMachine") -and $nic.VirtualMachine ) { - $vmresource = Get-AzResource -ResourceId $nicproperties.VirtualMachine.Id + $vmresource = Get-AzResource -ResourceId $nic.VirtualMachine.Id $out.VMName = $vmresource.Name } else { $out.VMName = "" } - if($null -ne ($nicproperties | Get-Member primary)) + if($null -ne ($nic | Get-Member primary)) { - $out.PrimaryStatus = $nicproperties.primary + $out.PrimaryStatus = $nic.primary } - if(($nicproperties | Get-Member -Name "NetworkSecurityGroup") -and $nicproperties.NetworkSecurityGroup) + if(($nic | Get-Member -Name "NetworkSecurityGroup") -and $nic.NetworkSecurityGroup) { - $nsgresource = Get-AzResource -ResourceId $nicproperties.NetworkSecurityGroup.Id + $nsgresource = Get-AzResource -ResourceId $nic.NetworkSecurityGroup.Id $out.NetworkSecurityGroupName = $nsgresource.Name } + $this.vNetNicsOutput += $out } catch diff --git a/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 b/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 index fbd0c3718..f394852b6 100644 --- a/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 +++ b/src/AzSK/Framework/Core/SVT/SubscriptionCore/SubscriptionCore.ps1 @@ -1517,57 +1517,66 @@ class SubscriptionCore: AzSVTBase hidden [ControlResult] CheckNonAlternateAccountsinPIMAccess([ControlResult] $controlResult) { - $AltAccountRegX = [string]::Empty; - $message = [string]::Empty; - if($null -eq $this.PIMAssignments) - { - $message=$this.GetPIMRoles(); - } - if($message -ne 'OK') # if there is some while making request message will contain exception + if($this.HasGraphAPIAccess) { + $AltAccountRegX = [string]::Empty; + $message = [string]::Empty; + if($null -eq $this.PIMAssignments) + { + $message=$this.GetPIMRoles(); + } + if($message -ne 'OK') # if there is some while making request message will contain exception + { - $controlResult.AddMessage("Unable to fetch PIM data, please verify manually.") - $controlResult.AddMessage($message); + $controlResult.AddMessage("Unable to fetch PIM data, please verify manually.") + $controlResult.AddMessage($message); + return $controlResult; + } + # get the altenate account pattern from org policy control settings + if( [Helpers]::CheckMember($this.ControlSettings,"AlernateAccountRegularExpressionForOrg")) + { + $AltAccountRegX = $this.ControlSettings.AlernateAccountRegularExpressionForOrg + } + else + { + # if unable to get the altenate account pattern from policy control settings, let the control be manual + $controlResult.AddMessage("Unable to get the alternate account pattern for your org. Please verify manually") return $controlResult; - } - # get the altenate account pattern from org policy control settings - if( [Helpers]::CheckMember($this.ControlSettings,"AlernateAccountRegularExpressionForOrg")) - { - $AltAccountRegX = $this.ControlSettings.AlernateAccountRegularExpressionForOrg - } - else - { - # if unable to get the altenate account pattern from policy control settings, let the control be manual - $controlResult.AddMessage("Unable to get the alternate account pattern for your org. Please verify manually") - return $controlResult; - } - if(($this.PIMAssignments| Measure-Object).Count -gt 0) - { - # get pim assignments for critical roles at subscription level - $PIMAssignmentsForCriticalRoles = $this.PIMAssignments | Where-Object {$_.RoleDefinitionName -in $this.ControlSettings.CriticalPIMRoles.Subscription} - if(($PIMAssignmentsForCriticalRoles | Measure-Object).Count -gt 0) + } + if(($this.PIMAssignments| Measure-Object).Count -gt 0) { - $IdentityName= $PIMAssignmentsForCriticalRoles.DisplayName | Get-Unique - $Users = @(); - # we currently do not have principal names stored in the PIMAssignments object thus need to get the principal name explicitly - $IdentityName | ForEach-Object{ - $Users += Get-AzADUser -DisplayName $_ - } - - - $nonAltPIMAccounts = $Users | Where-Object{$_.UserPrincipalName -notmatch $AltAccountRegX} - if(($nonAltPIMAccounts | Measure-Object).Count -gt 0) + # get pim assignments for critical roles at subscription level + $PIMAssignmentsForCriticalRoles = $this.PIMAssignments | Where-Object {$_.RoleDefinitionName -in $this.ControlSettings.CriticalPIMRoles.Subscription} + if(($PIMAssignmentsForCriticalRoles | Measure-Object).Count -gt 0) { - $nonAltPIMAccountsWithRoles = $PIMAssignmentsForCriticalRoles | Where-Object{$_.DisplayName -in $nonAltPIMAccounts.DisplayName} - $controlResult.AddMessage([VerificationResult]::Failed, "Non alternate accounts are assigned critical roles") - $controlResult.AddMessage($nonAltPIMAccountsWithRoles) - } - else - { - $controlResult.AddMessage([VerificationResult]::Passed, "No Non alternate accounts are assigned critical roles") + $IdentityName= $PIMAssignmentsForCriticalRoles.DisplayName | Get-Unique + $Users = @(); + # we currently do not have principal names stored in the PIMAssignments object thus need to get the principal name explicitly + $IdentityName | ForEach-Object{ + + $Users += Get-AzADUser -DisplayName $_ + } + + + $nonAltPIMAccounts = $Users | Where-Object{$_.UserPrincipalName -notmatch $AltAccountRegX} + if(($nonAltPIMAccounts | Measure-Object).Count -gt 0) + { + $nonAltPIMAccountsWithRoles = $PIMAssignmentsForCriticalRoles | Where-Object{$_.DisplayName -in $nonAltPIMAccounts.DisplayName} + $controlResult.AddMessage([VerificationResult]::Failed, "Non alternate accounts are assigned critical roles") + $controlResult.AddMessage($nonAltPIMAccountsWithRoles) + } + else + { + $controlResult.AddMessage([VerificationResult]::Passed, "No Non alternate accounts are assigned critical roles") + } } } } + else + { + $controlResult.CurrentSessionContext.Permissions.HasRequiredAccess = $false; + $controlResult.AddMessage([VerificationResult]::Manual, "Not able to query Graph API. Please verify manually."); + } return $controlResult; } From 0490df428628fbf4632613fb55940f8961ad83aa Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Mon, 13 Jan 2020 14:30:42 +0530 Subject: [PATCH 46/57] changed the severity of control and minor fix --- .../Configurations/SVT/SubscriptionCore/SubscriptionCore.json | 2 +- src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json b/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json index 9952f1e6b..4dc6d665d 100644 --- a/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json +++ b/src/AzSK/Framework/Configurations/SVT/SubscriptionCore/SubscriptionCore.json @@ -533,7 +533,7 @@ "ControlID": "Azure_Subscription_Use_Only_Alt_Credentials", "Description": "Ensure that critical operations in the subscription are conducted using alternate (ALT) credentials", "Id": "SubscriptionCore320", - "ControlSeverity": "High", + "ControlSeverity": "Medium", "Automated": "Yes", "MethodName": "CheckNonAlternateAccountsinPIMAccess", "Recommendation": "Go to Azure portal -> Priviledged Identity Management -> Azure Resources -> Select the scope -> Members-> Eligible roles and verify the non alernate accounts. Ensure that only alternate accounts are used as members of critical roles in the subscription. Do not use day to day user accounts.", diff --git a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 index 690f69eb4..35efc2f12 100644 --- a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 +++ b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 @@ -47,7 +47,7 @@ class SVTIaasBase: AzSVTBase if($null -ne $ipc -and ($ipc.IpConfigurations | Measure-Object).Count -gt 0) { - $this.vNetNics = $nics | Where-Object{($_.IpConfigurations.Id) -in $ipc.IpConfigurations.Id } + $this.vNetNics += $nics| Where-Object{($_.IpConfigurations.Id) -in $ipc.IpConfigurations.Id } } } From 9d1e619c45bef88bdc0afdd699b442722f6f6ec3 Mon Sep 17 00:00:00 2001 From: SINIKI <38354733+SINIKI@users.noreply.github.com> Date: Mon, 13 Jan 2020 14:37:40 +0530 Subject: [PATCH 47/57] Corrected variable name --- src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 b/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 index e6df425b9..4fac59725 100644 --- a/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 +++ b/src/AzSK/Framework/Core/SVT/Services/APIManagement.ps1 @@ -564,7 +564,7 @@ class APIManagement: AzSVTBase $Policy.Action = $RestrictedIPs.Action $Policy.AddressRange = $RestrictedIPs | Select-Object Address, Address-Range $Policy.Status = 'Enabled' - $ProductWithIPFilter = $ProductWithIPFilter + 1 + $ProductsWithIPFilter = $ProductsWithIPFilter + 1 $RestrictedCallerIPsInfo += $Policy } @@ -955,4 +955,4 @@ class APIManagement: AzSVTBase } } -} \ No newline at end of file +} From 607b111a94a011de79776b5deda6ae774760fae5 Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Tue, 14 Jan 2020 01:24:47 +0530 Subject: [PATCH 48/57] Bug fix for NICs with more than one IpConfigurations not getting listed --- src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 index 35efc2f12..7ccae1c48 100644 --- a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 +++ b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 @@ -44,10 +44,13 @@ class SVTIaasBase: AzSVTBase { $nics = Get-AzNetworkInterface #-ResourceGroupName $rgname $ipc = $VNetSubnets| Select-Object -Property 'IpConfigurations' -ExpandProperty 'IpConfigurations' - + + if($null -ne $ipc -and ($ipc.IpConfigurations | Measure-Object).Count -gt 0) { - $this.vNetNics += $nics| Where-Object{($_.IpConfigurations.Id) -in $ipc.IpConfigurations.Id } + $NICIpConfigs = $ipc.IpConfigurations.Id | Where-Object{$_ -in $nics.IpConfigurations.Id} + $resId = ($nics | Select-Object @{Name= 'ResourceId'; Expression = {$_.Id}}, @{Name="IpConfigurationId"; Expression={ $_.IpConfigurations | Select-Object Id }} |Select-Object -Property * -ExcludeProperty IpConfigurations -ExpandProperty IpConfigurationId | Where-Object{$_.Id -in $NICIpConfigs}).ResourceId + $this.VNetNics += $nics | Where-Object{$_.Id -in $resId} } } @@ -110,24 +113,24 @@ class SVTIaasBase: AzSVTBase $PrivateIpAddresses = @() if([FeatureFlightingManager]::GetFeatureStatus("EnableVnetFixForSub",$($this.SubscriptionContext.SubscriptionId))) { - - - $NICPublicIpAddresses = $nic.ipconfigurations.PublicIpAddress - $PrivateIpAddresses = $nic.ipconfigurations.PrivateIpAddress + + $NICPublicIpAddresses = @(); + $NICPublicIpAddresses += $nic.ipconfigurations | Where-Object {$null -ne $_.PublicIpAddress} + $PrivateIpAddresses += $nic.ipconfigurations.PrivateIpAddress if(($NICPublicIpAddresses |Measure-Object).Count -gt 0) { $NICPublicIpAddresses | ForEach-Object{ try { - $IPResource = Get-AzResource -ResourceId $_.Id + $IPResource = Get-AzResource -ResourceId $_.PublicIpAddress.Id $pubResourceName = Get-AzPublicIpAddress -Name $IPResource.Name -ResourceGroupName $IPResource.ResourceGroupName $PublicIpAddresses += $pubResourceName.IpAddress } catch { - $this.vNetPIPIssues += $nic + $this.vNetPIPIssues += $nic.IpConfigurations } From 124555237e99d6919c75e15e0d362d4dd749d56f Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Tue, 14 Jan 2020 09:42:59 +0530 Subject: [PATCH 49/57] null check added --- src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 index 7ccae1c48..78cf86f20 100644 --- a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 +++ b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 @@ -49,8 +49,12 @@ class SVTIaasBase: AzSVTBase if($null -ne $ipc -and ($ipc.IpConfigurations | Measure-Object).Count -gt 0) { $NICIpConfigs = $ipc.IpConfigurations.Id | Where-Object{$_ -in $nics.IpConfigurations.Id} - $resId = ($nics | Select-Object @{Name= 'ResourceId'; Expression = {$_.Id}}, @{Name="IpConfigurationId"; Expression={ $_.IpConfigurations | Select-Object Id }} |Select-Object -Property * -ExcludeProperty IpConfigurations -ExpandProperty IpConfigurationId | Where-Object{$_.Id -in $NICIpConfigs}).ResourceId - $this.VNetNics += $nics | Where-Object{$_.Id -in $resId} + $NICresources = ($nics | Select-Object @{Name= 'ResourceId'; Expression = {$_.Id}}, @{Name="IpConfigurationId"; Expression={ $_.IpConfigurations | Select-Object Id }} |Select-Object -Property * -ExcludeProperty IpConfigurations -ExpandProperty IpConfigurationId | Where-Object{$_.Id -in $NICIpConfigs}) + if(($NICresources | Measure-Object).Count -gt 0) + { + $resourceIds = $NICresources.ResourceId + $this.VNetNics += $nics | Where-Object{$_.Id -in $resourceIds} + } } } From 4f8cad2566c63496de632a40c714f50d7eb4dfcc Mon Sep 17 00:00:00 2001 From: Aboli-msft <52916530+Aboli-msft@users.noreply.github.com> Date: Tue, 14 Jan 2020 11:48:36 +0530 Subject: [PATCH 50/57] Adding vNet feature flag --- .../Framework/Configurations/FeatureFlighting.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/AzSK/Framework/Configurations/FeatureFlighting.json b/src/AzSK/Framework/Configurations/FeatureFlighting.json index 2797e628a..acce3668d 100644 --- a/src/AzSK/Framework/Configurations/FeatureFlighting.json +++ b/src/AzSK/Framework/Configurations/FeatureFlighting.json @@ -173,6 +173,15 @@ "UnderPreview": false, "IsEnabled": true }, + { + "Name": "EnableVnetFixForSub", + "Description": "", + "Sources": [ "*" ], + "EnabledForSubs": [], + "DisabledForSubs": [], + "UnderPreview": false, + "IsEnabled": true + }, { "Name": "EnableDetailedResourceTrackerTelemetry", "Description": "", @@ -192,4 +201,4 @@ "IsEnabled": true } ] -} \ No newline at end of file +} From 0274b07bcf13f64e4462b97b25d127800caad6ef Mon Sep 17 00:00:00 2001 From: Aboli-msft <52916530+Aboli-msft@users.noreply.github.com> Date: Tue, 14 Jan 2020 11:54:52 +0530 Subject: [PATCH 51/57] Update ControlSettings.json --- src/AzSK/Framework/Configurations/SVT/ControlSettings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json index b702064f2..cced2b33e 100644 --- a/src/AzSK/Framework/Configurations/SVT/ControlSettings.json +++ b/src/AzSK/Framework/Configurations/SVT/ControlSettings.json @@ -41,7 +41,8 @@ "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11", "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30", "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168" - ] + ], + "MaxAllowedAPICount": 30 }, "VirtualMachine": { "Windows": { From de4875683dc81b5cc6fa0a7ea1451884264779f8 Mon Sep 17 00:00:00 2001 From: "Ritika Singh (Tata Consultancy Services Ltd)" Date: Thu, 16 Jan 2020 11:20:56 +0530 Subject: [PATCH 52/57] Generate pdf fix for express route and GCS --- src/AzSK/SVT/SVT.ps1 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/AzSK/SVT/SVT.ps1 b/src/AzSK/SVT/SVT.ps1 index 0fa977056..87f40dd33 100644 --- a/src/AzSK/SVT/SVT.ps1 +++ b/src/AzSK/SVT/SVT.ps1 @@ -548,9 +548,11 @@ function Get-AzSKExpressRouteNetworkSecurityStatus $DoNotOpenOutputFolder, [GeneratePDF] - [Parameter(Mandatory = $false, HelpMessage = "Enables users to generate PDF file for reports.")] + [ValidateSet("Landscape", "portrait")] + [Parameter(Mandatory = $false)] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::None, + $GeneratePDF = [GeneratePDF]::portrait, + [switch] [Parameter(Mandatory = $false, HelpMessage = "Switch to specify whether to generate script to fix the control or not.")] @@ -746,9 +748,11 @@ function Get-AzSKControlsStatus $DoNotOpenOutputFolder, [GeneratePDF] + [ValidateSet("Landscape", "portrait")] [Parameter(Mandatory = $false)] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::None, + $GeneratePDF = [GeneratePDF]::portrait, + [switch] [Parameter(Mandatory = $false)] From 2c729897266df4801f19c7efeffadb41e0fafab5 Mon Sep 17 00:00:00 2001 From: "Ritika Singh (Tata Consultancy Services Ltd)" Date: Thu, 16 Jan 2020 11:31:15 +0530 Subject: [PATCH 53/57] Update in CommandBase file for none parameter --- src/AzSK.Framework/Abstracts/CommandBase.ps1 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/AzSK.Framework/Abstracts/CommandBase.ps1 b/src/AzSK.Framework/Abstracts/CommandBase.ps1 index c3ea2c7c0..1735d7534 100644 --- a/src/AzSK.Framework/Abstracts/CommandBase.ps1 +++ b/src/AzSK.Framework/Abstracts/CommandBase.ps1 @@ -150,10 +150,9 @@ else { $GeneratePDFReport = $this.InvocationContext.BoundParameters["GeneratePDF"]; try { if (-not [string]::IsNullOrEmpty($folderpath)) { + switch ($GeneratePDFReport) { - None { - # Do nothing - } + Landscape { [AzSKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $true); } From bc0d75b72f4a2ad2a367cd13752fad1d0f35304e Mon Sep 17 00:00:00 2001 From: "Ritika Singh (Tata Consultancy Services Ltd)" Date: Thu, 16 Jan 2020 12:01:37 +0530 Subject: [PATCH 54/57] 'P' for portrait made to uppercase in command parameter --- src/AzSK/SVT/SVT.ps1 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/AzSK/SVT/SVT.ps1 b/src/AzSK/SVT/SVT.ps1 index 87f40dd33..4ed692f19 100644 --- a/src/AzSK/SVT/SVT.ps1 +++ b/src/AzSK/SVT/SVT.ps1 @@ -161,10 +161,10 @@ function Get-AzSKAzureServicesSecurityStatus $DoNotOpenOutputFolder, [GeneratePDF] - [ValidateSet("Landscape", "portrait")] + [ValidateSet("Landscape", "Portrait")] [Parameter(Mandatory = $false)] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::portrait, + $GeneratePDF = [GeneratePDF]::Portrait, [switch] [Parameter(Mandatory = $false)] @@ -380,10 +380,10 @@ function Get-AzSKSubscriptionSecurityStatus $DoNotOpenOutputFolder, [GeneratePDF] - [ValidateSet("Landscape", "portrait")] + [ValidateSet("Landscape", "Portrait")] [Parameter(Mandatory = $false)] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::portrait, + $GeneratePDF = [GeneratePDF]::Portrait, [switch] [Parameter(Mandatory = $false)] @@ -548,10 +548,10 @@ function Get-AzSKExpressRouteNetworkSecurityStatus $DoNotOpenOutputFolder, [GeneratePDF] - [ValidateSet("Landscape", "portrait")] + [ValidateSet("Landscape", "Portrait")] [Parameter(Mandatory = $false)] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::portrait, + $GeneratePDF = [GeneratePDF]::Portrait, [switch] @@ -748,10 +748,10 @@ function Get-AzSKControlsStatus $DoNotOpenOutputFolder, [GeneratePDF] - [ValidateSet("Landscape", "portrait")] + [ValidateSet("Landscape", "Portrait")] [Parameter(Mandatory = $false)] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::portrait, + $GeneratePDF = [GeneratePDF]::Portrait, [switch] From cf476844fe9b26da2d9388c63906d5cf389d7e25 Mon Sep 17 00:00:00 2001 From: "Ritika Singh (Tata Consultancy Services Ltd)" Date: Thu, 16 Jan 2020 13:17:20 +0530 Subject: [PATCH 55/57] Get-AzSKExpressRouteNetworkSecurityStatus --- src/AzSK.Framework/Models/Enums.ps1 | 1 + src/AzSK/SVT/SVT.ps1 | 17 +++++++---------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/AzSK.Framework/Models/Enums.ps1 b/src/AzSK.Framework/Models/Enums.ps1 index 95f392ad4..a720e9cde 100644 --- a/src/AzSK.Framework/Models/Enums.ps1 +++ b/src/AzSK.Framework/Models/Enums.ps1 @@ -92,6 +92,7 @@ enum MonitoringSolutionInstallationOption enum GeneratePDF { + None Landscape Portrait } diff --git a/src/AzSK/SVT/SVT.ps1 b/src/AzSK/SVT/SVT.ps1 index 4ed692f19..274d58524 100644 --- a/src/AzSK/SVT/SVT.ps1 +++ b/src/AzSK/SVT/SVT.ps1 @@ -161,10 +161,9 @@ function Get-AzSKAzureServicesSecurityStatus $DoNotOpenOutputFolder, [GeneratePDF] - [ValidateSet("Landscape", "Portrait")] [Parameter(Mandatory = $false)] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::Portrait, + $GeneratePDF = [GeneratePDF]::None, [switch] [Parameter(Mandatory = $false)] @@ -380,10 +379,9 @@ function Get-AzSKSubscriptionSecurityStatus $DoNotOpenOutputFolder, [GeneratePDF] - [ValidateSet("Landscape", "Portrait")] [Parameter(Mandatory = $false)] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::Portrait, + $GeneratePDF = [GeneratePDF]::None, [switch] [Parameter(Mandatory = $false)] @@ -548,10 +546,9 @@ function Get-AzSKExpressRouteNetworkSecurityStatus $DoNotOpenOutputFolder, [GeneratePDF] - [ValidateSet("Landscape", "Portrait")] - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, HelpMessage = "Enables users to generate PDF file for reports.")] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::Portrait, + $GeneratePDF = [GeneratePDF]::None, [switch] @@ -590,9 +587,10 @@ function Get-AzSKExpressRouteNetworkSecurityStatus } } - + Get-AzSKAzureServicesSecurityStatus -SubscriptionId $SubscriptionId -ResourceGroupNames $erResourceGroups -ResourceName $ResourceName ` -ResourceTypeName ([SVTMapping]::ERvNetTypeName) -ControlIds $ControlIds -FilterTags $FilterTags -ExcludeTags $ExcludeTags -DoNotOpenOutputFolder:$DoNotOpenOutputFolder -AttestControls $ControlsToAttest -GeneratePDF $GeneratePDF -GenerateFixScript:$GenerateFixScript -ExcludeControlIds $ExcludeControlIds + } function Get-AzSKControlsStatus @@ -748,10 +746,9 @@ function Get-AzSKControlsStatus $DoNotOpenOutputFolder, [GeneratePDF] - [ValidateSet("Landscape", "Portrait")] [Parameter(Mandatory = $false)] [Alias("gpdf","pdf")] - $GeneratePDF = [GeneratePDF]::Portrait, + $GeneratePDF = [GeneratePDF]::None, [switch] From cd16b8f864772acc7de4da950aa7a8d8690208e9 Mon Sep 17 00:00:00 2001 From: "Ritika Singh (Tata Consultancy Services Ltd)" Date: Thu, 16 Jan 2020 13:26:42 +0530 Subject: [PATCH 56/57] generate --- src/AzSK.Framework/Abstracts/CommandBase.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AzSK.Framework/Abstracts/CommandBase.ps1 b/src/AzSK.Framework/Abstracts/CommandBase.ps1 index 1735d7534..ae292a6e6 100644 --- a/src/AzSK.Framework/Abstracts/CommandBase.ps1 +++ b/src/AzSK.Framework/Abstracts/CommandBase.ps1 @@ -152,7 +152,9 @@ else { if (-not [string]::IsNullOrEmpty($folderpath)) { switch ($GeneratePDFReport) { - + None { + # Do nothing + } Landscape { [AzSKPDFExtension]::GeneratePDF($folderpath, $this.SubscriptionContext, $this.InvocationContext, $true); } From 4bc9b04c6a3d532fd17a292144529907bf9df3cb Mon Sep 17 00:00:00 2001 From: "Zhilmil Gupta (Tata Consultancy Services Ltd)" Date: Thu, 16 Jan 2020 15:58:37 +0530 Subject: [PATCH 57/57] Rolling back Vnet changes --- src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 | 150 ++++++-------------- 1 file changed, 45 insertions(+), 105 deletions(-) diff --git a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 index 78cf86f20..aca8a0598 100644 --- a/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 +++ b/src/AzSK/Framework/Core/SVT/SVTIaasBase.ps1 @@ -38,67 +38,39 @@ class SVTIaasBase: AzSVTBase hidden [PSObject[]] GetvNetNics($VNetSubnets) { - if([FeatureFlightingManager]::GetFeatureStatus("EnableVnetFixForSub",$($this.SubscriptionContext.SubscriptionId))) + if (-not $this.vNetNics) { - if (-not $this.vNetNics) - { - $nics = Get-AzNetworkInterface #-ResourceGroupName $rgname - $ipc = $VNetSubnets| Select-Object -Property 'IpConfigurations' -ExpandProperty 'IpConfigurations' - - - if($null -ne $ipc -and ($ipc.IpConfigurations | Measure-Object).Count -gt 0) + $this.vNetNicsWIssues = @(); + $VNetSubnets | ForEach-Object{ + Set-Variable -Name currentsubnet -Scope Local -Value $_ + if($null -ne $currentsubnet.IpConfigurations ) { - $NICIpConfigs = $ipc.IpConfigurations.Id | Where-Object{$_ -in $nics.IpConfigurations.Id} - $NICresources = ($nics | Select-Object @{Name= 'ResourceId'; Expression = {$_.Id}}, @{Name="IpConfigurationId"; Expression={ $_.IpConfigurations | Select-Object Id }} |Select-Object -Property * -ExcludeProperty IpConfigurations -ExpandProperty IpConfigurationId | Where-Object{$_.Id -in $NICIpConfigs}) - if(($NICresources | Measure-Object).Count -gt 0) - { - $resourceIds = $NICresources.ResourceId - $this.VNetNics += $nics | Where-Object{$_.Id -in $resourceIds} - } - } - } - - return $this.vNetNics; - - } - else - { - if (-not $this.vNetNics) - { - $this.vNetNicsWIssues = @(); - $VNetSubnets | ForEach-Object{ - Set-Variable -Name currentsubnet -Scope Local -Value $_ - if($null -ne $currentsubnet.IpConfigurations ) - { - $currentsubnet.IpConfigurations | ForEach-Object{ - Set-Variable -Name currentipconfig -Scope Local -Value $_ - if($currentipconfig.Id.Contains("Microsoft.Network/networkInterfaces")) - { - $currentipconfig = $currentipconfig.Id.ToLower() - $nicresourceid = $currentipconfig.Substring(0,$currentipconfig.LastIndexOf("ipconfigurations")-1) - try - { - # - $nic = Get-AzResource -ResourceId $nicresourceid - $this.vNetNics += $nic - } - catch - { - $this.vNetNicsWIssues += $nicresourceid; - } - } + $currentsubnet.IpConfigurations | ForEach-Object{ + Set-Variable -Name currentipconfig -Scope Local -Value $_ + if($currentipconfig.Id.Contains("Microsoft.Network/networkInterfaces")) + { + $currentipconfig = $currentipconfig.Id.ToLower() + $nicresourceid = $currentipconfig.Substring(0,$currentipconfig.LastIndexOf("ipconfigurations")-1) + try + { + # + $nic = Get-AzResource -ResourceId $nicresourceid + $this.vNetNics += $nic + } + catch + { + $this.vNetNicsWIssues += $nicresourceid; + } } } } } - } + } return $this.vNetNics; } hidden [PSObject[]] GetvnetNicsProperties($vNetNics) { - - if(-not $this.vNetNicsOutput) { if($null -ne $vNetNics ) @@ -106,86 +78,54 @@ class SVTIaasBase: AzSVTBase $this.vNetPIPIssues = @(); $tempVNetNICS = [array]($vNetNics) $tempVNetNICS | ForEach-Object{ + Set-Variable -Name nic -Scope Local -Value $_ + Set-Variable -Name nicproperties -Scope Local -Value $_.Properties try { - Set-Variable -Name nic -Scope Local -Value $_ - $out = ""| Select-Object NICName, VMName, VMId, PrimaryStatus, NetworkSecurityGroupName,NetworkSecurityGroupId, PublicIpAddress, PrivateIpAddress, EnableIPForwarding, IpConfigurations + $out = ""| Select-Object NICName, VMName, VMId, PrimaryStatus, NetworkSecurityGroupName,NetworkSecurityGroupId, PublicIpAddress, PrivateIpAddress, EnableIPForwarding, IpConfigurations $out.NICName = $nic.Name - $out.IpConfigurations = $nic.IpConfigurations - $out.EnableIPForwarding = $nic.EnableIPForwarding + $out.IpConfigurations = $nicproperties.IpConfigurations + $out.EnableIPForwarding = $nicproperties.EnableIPForwarding $PublicIpAddresses = @() $PrivateIpAddresses = @() - if([FeatureFlightingManager]::GetFeatureStatus("EnableVnetFixForSub",$($this.SubscriptionContext.SubscriptionId))) - { - - $NICPublicIpAddresses = @(); - $NICPublicIpAddresses += $nic.ipconfigurations | Where-Object {$null -ne $_.PublicIpAddress} - $PrivateIpAddresses += $nic.ipconfigurations.PrivateIpAddress - if(($NICPublicIpAddresses |Measure-Object).Count -gt 0) + $nicproperties.IpConfigurations | ForEach-Object{ + Set-Variable -Name ipconfiguration -Scope Local -Value $_ + try { - $NICPublicIpAddresses | ForEach-Object{ - try - { - - $IPResource = Get-AzResource -ResourceId $_.PublicIpAddress.Id + if(($ipconfiguration | Get-Member -Name "Properties") -and ($ipconfiguration.Properties | Get-Member -Name "PublicIpAddress") -and $ipconfiguration.Properties.PublicIpAddress) + { + $IPResource = Get-AzResource -ResourceId $ipconfiguration.Properties.PublicIpAddress.Id $pubResourceName = Get-AzPublicIpAddress -Name $IPResource.Name -ResourceGroupName $IPResource.ResourceGroupName $PublicIpAddresses += $pubResourceName.IpAddress - } - catch - { - - $this.vNetPIPIssues += $nic.IpConfigurations - } - - - } - } - - - } - else - { - $nic.IpConfigurations | ForEach-Object{ - Set-Variable -Name ipconfiguration -Scope Local -Value $_ - try - { - if(($ipconfiguration | Get-Member -Name "Properties") -and ($ipconfiguration.Properties | Get-Member -Name "PublicIpAddress") -and $ipconfiguration.Properties.PublicIpAddress) - { - $IPResource = Get-AzResource -ResourceId $ipconfiguration.Properties.PublicIpAddress.Id - $pubResourceName = Get-AzPublicIpAddress -Name $IPResource.Name -ResourceGroupName $IPResource.ResourceGroupName - $PublicIpAddresses += $pubResourceName.IpAddress - } - $PrivateIpAddresses += $ipconfiguration.Properties.PrivateIpAddress - } - catch - { - $this.vNetPIPIssues += $ipconfiguration } + $PrivateIpAddresses += $ipconfiguration.Properties.PrivateIpAddress + } + catch + { + $this.vNetPIPIssues += $ipconfiguration } } $out.PublicIpAddress = ([System.String]::Join(";",$PublicIpAddresses)) $out.PrivateIpAddress = ([System.String]::Join(";",$PrivateIpAddresses)) - - if(($nic | Get-Member -Name "VirtualMachine") -and $nic.VirtualMachine ) + if(($nicproperties | Get-Member -Name "VirtualMachine") -and $nicproperties.VirtualMachine ) { - $vmresource = Get-AzResource -ResourceId $nic.VirtualMachine.Id + $vmresource = Get-AzResource -ResourceId $nicproperties.VirtualMachine.Id $out.VMName = $vmresource.Name } else { $out.VMName = "" } - if($null -ne ($nic | Get-Member primary)) + if($null -ne ($nicproperties | Get-Member primary)) { - $out.PrimaryStatus = $nic.primary + $out.PrimaryStatus = $nicproperties.primary } - if(($nic | Get-Member -Name "NetworkSecurityGroup") -and $nic.NetworkSecurityGroup) + if(($nicproperties | Get-Member -Name "NetworkSecurityGroup") -and $nicproperties.NetworkSecurityGroup) { - $nsgresource = Get-AzResource -ResourceId $nic.NetworkSecurityGroup.Id + $nsgresource = Get-AzResource -ResourceId $nicproperties.NetworkSecurityGroup.Id $out.NetworkSecurityGroupName = $nsgresource.Name } - $this.vNetNicsOutput += $out } catch @@ -198,4 +138,4 @@ class SVTIaasBase: AzSVTBase } return $this.vNetNicsOutput; } -} +} \ No newline at end of file