BurpSuiteDeploy.psm1
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13 # $ExecutionContext.SessionState.Module.OnRemove = { # [DeploymentCache]::Deployments = @() # [ScanConfigurationCache]::ScanConfigurations = @() # [SiteTreeCache]::SiteTree = $null # } class DeploymentCache { static [object[]] $Deployments = @() static [object] Get([string]$resourceId) { return @([DeploymentCache]::Deployments | Where-Object {$_.ResourceId -eq $resourceId})[0] } static [object] Get() { return [DeploymentCache]::Deployments } static [void] Set([object]$deployment) { [DeploymentCache]::Deployments += $deployment } static [void] Init() { [DeploymentCache]::Deployments = @() } } class ScanConfigurationCache { static [object[]] $ScanConfigurations = @() static [object] Get([string]$name) { return @([ScanConfigurationCache]::ScanConfigurations | Where-Object { $_.name -eq $name })[0] } static [void] Reload() { [ScanConfigurationCache]::Init() } static [void] Init() { [ScanConfigurationCache]::ScanConfigurations = @(Get-BurpSuiteScanConfiguration) } } class ScheduleItemCache { static [object[]] $ScheduleItems static [object] Get([string]$siteId) { return @([ScheduleItemCache]::ScheduleItems | Where-Object { $_.site.id -eq $siteId }) } static [void] Reload() { [ScheduleItemCache]::Init() } static [void] Init() { [ScheduleItemCache]::ScheduleItems = @(Get-BurpSuiteScheduleItem -Fields id, schedule, site) } } class SiteTreeCache { static [object] $SiteTree static [object] Get([string]$parentId, [string]$name, [string]$type) { if ($type -eq 'Folders') { return @([SiteTreeCache]::SiteTree.Folders | Where-Object { ($_.name -eq $name) -and ($_.parent_id -eq $parentId) })[0] } return @([SiteTreeCache]::SiteTree.Sites | Where-Object { ($_.name -eq $name) -and ($_.parent_id -eq $parentId) })[0] } static [object] Get([string]$id, [string]$type) { if ($type -eq 'Folders') { return @([SiteTreeCache]::SiteTree.Folders | Where-Object { $_.id -eq $id })[0] } return @([SiteTreeCache]::SiteTree.Sites | Where-Object { $_.id -eq $id })[0] } static [void] Reload() { [SiteTreeCache]::Init() } static [void] Init() { [SiteTreeCache]::SiteTree = Get-BurpSuiteSiteTree } } class Deployment { [string] $Id [string] $ResourceId [string] $ProvisioningState [object] $Properties [string] $ProvisioningError } class Resource { [string] $ResourceId [string] $ResourceType [string] $Name [object] $Properties [string[]] $DependsOn } class ProvisioningState { static [string] $Succeeded = 'Succeeded' static [string] $Error = 'Error' } class Util { static [object] GetResourceId([string]$name, [string]$type) { $resourceId = @($type.TrimEnd("/"), $name.TrimStart("/")) -join "/" return $resourceId } static [object] GetResourceId([string]$name, [string]$type, [string]$parentType) { $resourceId = @($parentType.TrimEnd("/"), (($name.TrimStart("/")) -split "/")[0], $type.TrimEnd("/"), (($name.TrimStart("/")) -split "/")[-1]) -join "/" return $resourceId } static [object] GetResourceType([string]$type) { $resourceType = $type.TrimEnd("/") return $resourceType } static [object] GetResourceType([string]$type, [string]$parentType) { $resourceType = @($parentType.TrimEnd("/"), $type.TrimStart("/")) -join "/" return $resourceType } static [object] GetResourceName([string]$name) { $resourceName = (($name.TrimStart("/")) -split "/")[-1] return $resourceName } } # Idea from http://stackoverflow.com/questions/7468707/deep-copy-a-dictionary-hashtable-in-powershell # borrowed from http://stackoverflow.com/questions/8982782/does-anyone-have-a-dependency-graph-and-topological-sorting-code-snippet-for-pow function _cloneObject { [cmdletbinding()] param( [object] $InputObject ) # # NOTE: Use Export-Clixml/Import-Clixml instead of BinaryFormatter # https://learn.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide#binaryformatter-security-vulnerabilities # # $memoryStream = new-object IO.MemoryStream # $binaryFormatter = new-object Runtime.Serialization.Formatters.Binary.BinaryFormatter # $binaryFormatter.Serialize($memoryStream, $InputObject) # $memoryStream.Position = 0 # $binaryFormatter.Deserialize($memoryStream) $tempFile = New-TemporaryFile $InputObject | Export-Clixml -Path $tempFile.FullName $clonedObject = Import-Clixml -Path $tempFile.FullName Remove-Item -Path $tempFile.FullName -Force $clonedObject } function _convertToHashtable { [OutputType([System.Collections.Hashtable])] [cmdletbinding()] param( [object]$InputObject ) $ht = @{} $InputObject | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object { $ht.$_ = $InputObject.$_ } $ht } function _createTempFile { [cmdletbinding()] param( [object] $InputObject ) $tempFile = New-TemporaryFile if (-not ([string]::IsNullOrEmpty($InputObject))) { Out-File -NoNewline -InputObject $InputObject -FilePath $tempFile } $tempFile } function _sortDeployment { [OutputType([System.Object[]])] [cmdletbinding()] param( [object[]] $Resources ) $order = @{} foreach ($resource in $Resources) { if ($resource.dependsOn) { if (-not $order.ContainsKey($resource.ResourceId)) { $order.add($resource.ResourceId, $resource.dependsOn) } } } if ($order.Keys.Count -gt 0) { $deployOrder = _sortTopologically $order _sortWithCustomList -InputObject $Resources -Property ResourceId -CustomList $deployOrder } else { $Resources } } # Thanks to http://stackoverflow.com/questions/8982782/does-anyone-have-a-dependency-graph-and-topological-sorting-code-snippet-for-pow # Input is a hashtable of @{ID = @(Depended,On,IDs);...} function _sortTopologically { [OutputType([System.Collections.ArrayList])] [cmdletbinding()] param( [Parameter(Mandatory = $true, Position = 0)] [hashtable] $EdgeList ) # Make sure we can use HashSet Add-Type -AssemblyName System.Core # Clone it so as to not alter original $currentEdgeList = [hashtable] (_cloneObject $EdgeList) # algorithm from http://en.wikipedia.org/wiki/Topological_sorting#Algorithms $topologicallySortedElements = New-Object System.Collections.ArrayList $setOfAllNodesWithNoIncomingEdges = New-Object System.Collections.Queue $fasterEdgeList = @{} # Keep track of all nodes in case they put it in as an edge destination but not source $allTheNodes = New-Object -TypeName System.Collections.Generic.HashSet[object] -ArgumentList (, [object[]] $currentEdgeList.Keys) foreach ($currentNode in $currentEdgeList.Keys) { $currentDestinationNodes = [array] $currentEdgeList[$currentNode] if ($currentDestinationNodes.Length -eq 0) { $setOfAllNodesWithNoIncomingEdges.Enqueue($currentNode) } foreach ($currentDestinationNode in $currentDestinationNodes) { if (!$allTheNodes.Contains($currentDestinationNode)) { [void] $allTheNodes.Add($currentDestinationNode) } } # Take this time to convert them to a HashSet for faster operation $currentDestinationNodes = New-Object -TypeName System.Collections.Generic.HashSet[object] -ArgumentList (, [object[]] $currentDestinationNodes ) [void] $fasterEdgeList.Add($currentNode, $currentDestinationNodes) } # Now let's reconcile by adding empty dependencies for source nodes they didn't tell us about foreach ($currentNode in $allTheNodes) { if (!$currentEdgeList.ContainsKey($currentNode)) { [void] $currentEdgeList.Add($currentNode, (New-Object -TypeName System.Collections.Generic.HashSet[object])) $setOfAllNodesWithNoIncomingEdges.Enqueue($currentNode) } } $currentEdgeList = $fasterEdgeList while ($setOfAllNodesWithNoIncomingEdges.Count -gt 0) { $currentNode = $setOfAllNodesWithNoIncomingEdges.Dequeue() [void] $currentEdgeList.Remove($currentNode) [void] $topologicallySortedElements.Add($currentNode) foreach ($currentEdgeSourceNode in $currentEdgeList.Keys) { $currentNodeDestinations = $currentEdgeList[$currentEdgeSourceNode] if ($currentNodeDestinations.Contains($currentNode)) { [void] $currentNodeDestinations.Remove($currentNode) if ($currentNodeDestinations.Count -eq 0) { [void] $setOfAllNodesWithNoIncomingEdges.Enqueue($currentEdgeSourceNode) } } } } if ($currentEdgeList.Count -gt 0) { throw "Graph has at least one cycle!" } return $topologicallySortedElements } # Thanks to http://stackoverflow.com/questions/8982782/does-anyone-have-a-dependency-graph-and-topological-sorting-code-snippet-for-pow # Input is a hashtable of @{ID = @(Depended,On,IDs);...} function _sortWithCustomList { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")] Param ( [parameter(ValueFromPipeline = $true)] [PSObject] $InputObject, [parameter(Position = 1)] [String] $Property, [parameter()] [Object[]] $CustomList ) begin { # convert customList (array) to hash $hash = @{} $rank = 0 $customList | Select-Object -Unique | ForEach-Object { $key = $_ $hash.Add($key, $rank) $rank++ } # create script block for sorting # items not in custom list will be last in sort order $sortOrder = { $key = if ($Property) { $_.$Property } else { $_ } $rank = $hash[$key] if ($null -ne $rank) { $rank } else { [System.Double]::PositiveInfinity } } # create a place to collect objects from pipeline # (I don't know how to match behavior of Sort's InputObject parameter) $objects = @() } process { $objects += $InputObject } end { $objects | Sort-Object -Property $sortOrder } } function _tryGetProperty { param( [object] $InputObject, [string] $PropertyName ) if ((@($InputObject.PSObject.Properties.Match($PropertyName)).Count -gt 0) -and ($null -ne $InputObject.$PropertyName)) { return $InputObject.$PropertyName } return $null } function _testIsExpression { [cmdletbinding()] param( [object] $InputString ) $InputString -match '^\[.+\]$' } function _resolveExpression { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")] [cmdletbinding()] param( [object] $inputString, [hashtable] $variables, [object[]] $resources ) if (-not (_testIsExpression -InputString $inputString)) { return $inputString } $safeCommands = @('variables', 'concat', 'resourceId', 'reference') $parsedString = (($inputString -replace '^\[', '') -replace '\]$', '') $expression = [scriptblock]::Create("return ($parsedString)") function variables([string]$name) { ($variables[$name]) } function concat([string[]]$arguments) { ($arguments -join '') } function resourceId([string[]]$segments) { $resourceId = ( $segments | ForEach-Object { if (-not ([string]::IsNullOrEmpty($_))) { $_.TrimEnd("/") } } ) -join '/' $resourceId } function reference([string]$resourceId) { $resources | Where-Object { $_.ResourceId -eq $resourceId } } $ast = [System.Management.Automation.Language.Parser]::ParseInput($parsedString, [ref]$null, [ref]$null) $commandsAst = $ast.FindAll( { ($args[0] -is [System.Management.Automation.Language.CommandAst]) ` -and $args[0].GetCommandName() -notin $safeCommands }, $true) if ($commandsAst.Count -eq 0) { & $expression } } function Get-BurpSuiteResource { [CmdletBinding(HelpUri = 'https://github.com/juniinacio/BurpSuiteDeploy', ConfirmImpact = 'Low')] Param ( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [ValidateScript( { Test-Path -Path $_ -PathType Leaf -ErrorAction Stop })] [string[]] $TemplateFile ) begin { } process { try { $resources = foreach($path in $TemplateFile) { $path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path) $template = ConvertFrom-Json -InputObject (Get-Content -Path $path -Raw | Out-String) foreach($resource in $template.resources) { [Resource]@{ ResourceId = [Util]::GetResourceId($resource.name, $resource.type) ResourceType = [Util]::GetResourceType($resource.type) Name = $resource.name Properties = $resource.Properties DependsOn = $resource.dependsOn } if ($null -ne (_tryGetProperty -InputObject $resource -PropertyName 'resources')) { foreach($childResource in $resource.resources) { [Resource]@{ ResourceId = [Util]::GetResourceId($childResource.name, $childResource.type, $resource.type) ResourceType = [Util]::GetResourceType($childResource.type, $resource.type) Name = [Util]::GetResourceName($childResource.name) Properties = $childResource.Properties DependsOn = $childResource.dependsOn } } } } } if (-not $resources) { throw "No resources processed. Something went wrong." } _sortDeployment -Resources $resources } catch { throw } } end { } } function Invoke-BurpSuiteDeploy { [CmdletBinding(SupportsShouldProcess = $true, HelpUri = 'https://github.com/juniinacio/BurpSuiteDeploy', ConfirmImpact = 'Medium')] Param ( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [ValidateScript( { Test-Path -Path $_ -PathType Leaf -ErrorAction Stop })] [string[]] $TemplateFile, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [string] $Uri, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [string] $APIKey ) begin { try { Connect-BurpSuite -Uri $Uri -APIKey $APIKey } catch { throw } } process { try { if ($PSCmdlet.ShouldProcess("Deploy", $TemplateFile)) { $resources = Get-BurpSuiteResource -TemplateFile $TemplateFile $deployments = $resources | Invoke-BurpSuiteResource -Confirm:$false if (@($deployments).ProvisioningState -contains [ProvisioningState]::Error) { Write-Error -Message "Provisioning of one or more resources completed with errors." } $deployments } } catch { throw } } end { try { Disconnect-BurpSuite } catch { throw } } } function Invoke-BurpSuiteResource { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] [CmdletBinding(SupportsShouldProcess = $true, HelpUri = 'https://github.com/juniinacio/BurpSuiteDeploy', ConfirmImpact = 'Medium')] Param ( [parameter(ValueFromPipeline = $True, Mandatory = $True)] [object]$InputObject ) begin { [SiteTreeCache]::Init() [ScanConfigurationCache]::Init() [ScheduleItemCache]::Init() } process { try { Write-Verbose "Deploying resource $($InputObject.ResourceId)`..." if ($PSCmdlet.ShouldProcess("Deploy", $InputObject.ResourceId)) { switch ($InputObject.ResourceType) { 'BurpSuite/Sites' { $resource = [SiteTreeCache]::Get(0, $InputObject.Name, 'Sites') if ($null -eq $resource) { Write-Verbose "Creating site $($InputObject.Name)`..." $scanConfigurationIds = @() foreach ($scanConfigurationId in $InputObject.Properties.scanConfigurationIds) { if ((_testIsExpression -InputString $scanConfigurationId)) { $resolvedScanConfigurationId = _resolveExpression -inputString $scanConfigurationId -variables @{} -resources ([DeploymentCache]::Deployments) if ($null -eq $resolvedScanConfigurationId) { throw "Could not resolve dependency expression $scanConfigurationId`." } $scanConfigurationIds += $resolvedScanConfigurationId } else { $scanConfigurationIds += $scanConfigurationId } } $parameters = @{ ParentId = "0" Name = $InputObject.Name ScopeV2 = $InputObject.Properties.scopeV2 ScanConfigurationIds = $scanConfigurationIds } if ($null -ne ($InputObject.Properties.emailRecipients)) { $parameters.EmailRecipients = $InputObject.Properties.emailRecipients } if ($null -ne ($InputObject.Properties.applicationLogins)) { if ($null -ne ($InputObject.Properties.applicationLogins.loginCredentials)) { $loginCredentials = @() foreach ($loginCredential in $InputObject.Properties.applicationLogins.loginCredentials) { $loginCredentials += [PSCustomObject]@{ Label = $loginCredential.Label; Credential = (New-Object System.Management.Automation.PSCredential ($loginCredential.Username, $(ConvertTo-SecureString $loginCredential.Password -AsPlainText -Force))) } } $parameters.LoginCredentials = $loginCredentials } if ($null -ne ($InputObject.Properties.applicationLogins.recordedLogins)) { $recordedLogins = @() foreach ($recordedLogin in $InputObject.Properties.applicationLogins.recordedLogins) { $recordedLogins += [PSCustomObject]@{ Label = $recordedLogin.Label; FilePath = (_createTempFile -InputObject $recordedLogin.script).FullName } } $parameters.RecordedLogins = $recordedLogins } } $resource = New-BurpSuiteSite @parameters [SiteTreeCache]::Reload() } else { Write-Verbose "Updating site $($InputObject.Name)`..." if ($null -ne ($InputObject.Properties.scopeV2)) { Write-Verbose " Updating site scopes..." $parameters = @{ StartUrls = @($InputObject.Properties.scopeV2.startUrls) InScopeUrlPrefixes = @($InputObject.Properties.scopeV2.inScopeUrlPrefixes) OutOfScopeUrlPrefixes = @($InputObject.Properties.scopeV2.outOfScopeUrlPrefixes) } if ($null -ne ($InputObject.Properties.scopeV2.protocolOptions)) { $parameters.ProtocolOptions = $InputObject.Properties.scopeV2.protocolOptions } Update-BurpSuiteSiteScope -SiteId $resource.Id @parameters Start-Sleep -Seconds 1 } if ($null -ne ($InputObject.Properties.scanConfigurationIds)) { Write-Verbose " Updating scan configuration..." $scanConfigurationIds = @() foreach ($scanConfigurationId in $InputObject.Properties.scanConfigurationIds) { if ((_testIsExpression -InputString $scanConfigurationId)) { $resolvedScanConfigurationId = _resolveExpression -inputString $scanConfigurationId -variables @{} -resources ([DeploymentCache]::Deployments) if ($null -eq $resolvedScanConfigurationId) { throw "Could not resolve dependency expression $scanConfigurationId`." } $scanConfigurationIds += $resolvedScanConfigurationId } else { $scanConfigurationIds += $scanConfigurationId } } Update-BurpSuiteSiteScanConfiguration -Id $resource.Id -ScanConfigurationIds $scanConfigurationIds Start-Sleep -Seconds 1 } [SiteTreeCache]::Reload() $resource = [SiteTreeCache]::Get(0, $InputObject.Name, 'Sites') if ($null -ne ($InputObject.Properties.applicationLogins)) { if ($null -ne ($InputObject.Properties.applicationLogins.loginCredentials)) { Write-Verbose " Updating application logins..." foreach ($loginCredential in $InputObject.Properties.applicationLogins.loginCredentials) { $appPass = ConvertTo-SecureString -String $loginCredential.password -AsPlainText -Force $appCredential = New-Object -TypeName PSCredential -ArgumentList $loginCredential.username, $appPass $appLogin = $resource.application_logins.login_credentials | Where-Object { $_.label -eq $loginCredential.label } if ($null -eq $appLogin) { New-BurpSuiteSiteLoginCredential -SiteId $resource.id -Label $loginCredential.label -Credential $appCredential | Out-Null } else { Update-BurpSuiteSiteLoginCredential -Id $appLogin.id -Credential $appCredential } Start-Sleep -Seconds 1 } } if ($null -ne ($InputObject.Properties.applicationLogins.recordedLogins)) { Write-Verbose " Updating recorded logins..." foreach ($recordedLogin in $InputObject.Properties.applicationLogins.recordedLogins) { $appLogin = $resource.application_logins.recorded_logins | Where-Object { $_.label -eq $recordedLogin.label } if ($null -eq $appLogin) { New-BurpSuiteSiteRecordedLogin -SiteId $resource.id -Label $recordedLogin.label -FilePath (_createTempFile -InputObject $recordedLogin.script).FullName | Out-Null } Start-Sleep -Seconds 1 } } } if ($null -ne ($InputObject.Properties.emailRecipients)) { Write-Verbose " Updating email recipients..." foreach ($emailRecipient in $InputObject.Properties.emailRecipients) { $emailRec = $resource.email_recipients | Where-Object { $_.email -eq $emailRecipient.email } if ($null -eq $emailRec) { New-BurpSuiteSiteEmailRecipient -SiteId $resource.id -EmailRecipient $emailRecipient.email | Out-Null } else { Update-BurpSuiteSiteEmailRecipient -Id $emailRec.id -Email $emailRecipient.email } Start-Sleep -Seconds 1 } } } } 'BurpSuite/Folders' { $resource = [SiteTreeCache]::Get(0, $InputObject.Name, 'Folders') if ($null -eq $resource) { Write-Verbose "Creating folder $($InputObject.Name)`..." $resource = New-BurpSuiteFolder -ParentId 0 -Name $InputObject.Name [SiteTreeCache]::Reload() Start-Sleep -Seconds 1 } } 'BurpSuite/Folders/Sites' { $parentResourceId = ($InputObject.ResourceId -split '/' | Select-Object -First 3) -join '/' $parentResource = [DeploymentCache]::Get($parentResourceId) if ($null -ne $parentResource) { $resource = [SiteTreeCache]::Get($parentResource.Id, $InputObject.Name, 'Sites') if ($null -eq $resource) { Write-Verbose "Creating site $($InputObject.Name), parent id $($parentResource.Id)`..." $scanConfigurationIds = @() foreach ($scanConfigurationId in $InputObject.Properties.scanConfigurationIds) { if ((_testIsExpression -InputString $scanConfigurationId)) { $resolvedScanConfigurationId = _resolveExpression -inputString $scanConfigurationId -variables @{} -resources ([DeploymentCache]::Deployments) if ($null -eq $resolvedScanConfigurationId) { throw "Could not resolve dependency expression $scanConfigurationId`." } $scanConfigurationIds += $resolvedScanConfigurationId } else { $scanConfigurationIds += $scanConfigurationId } } $parameters = @{ ParentId = $parentResource.Id Name = $InputObject.Name ScopeV2 = $InputObject.Properties.scopeV2 ScanConfigurationIds = $scanConfigurationIds } if ($null -ne ($InputObject.Properties.emailRecipients)) { $parameters.EmailRecipients = $InputObject.Properties.emailRecipients } if ($null -ne ($InputObject.Properties.applicationLogins)) { if ($null -ne ($InputObject.Properties.applicationLogins.loginCredentials)) { $loginCredentials = @() foreach ($loginCredential in $InputObject.Properties.applicationLogins.loginCredentials) { $loginCredentials += [PSCustomObject]@{ Label = $loginCredential.Label; Credential = (New-Object System.Management.Automation.PSCredential ($loginCredential.Username, $(ConvertTo-SecureString $loginCredential.Password -AsPlainText -Force))) } } $parameters.LoginCredentials = $loginCredentials } if ($null -ne ($InputObject.Properties.applicationLogins.recordedLogins)) { $recordedLogins = @() foreach ($recordedLogin in $InputObject.Properties.applicationLogins.recordedLogins) { $recordedLogins += [PSCustomObject]@{ Label = $recordedLogin.Label; FilePath = (_createTempFile -InputObject $recordedLogin.script).FullName } } $parameters.RecordedLogins = $recordedLogins } } $resource = New-BurpSuiteSite @parameters [SiteTreeCache]::Reload() } else { Write-Verbose "Updating site $($InputObject.Name), parent id $($parentResource.Id)`..." if ($null -ne ($InputObject.Properties.scopeV2)) { Write-Verbose " Updating site scopes..." $parameters = @{ StartUrls = @($InputObject.Properties.scopeV2.startUrls) InScopeUrlPrefixes = @($InputObject.Properties.scopeV2.inScopeUrlPrefixes) OutOfScopeUrlPrefixes = @($InputObject.Properties.scopeV2.outOfScopeUrlPrefixes) } if ($null -ne ($InputObject.Properties.scopeV2.protocolOptions)) { $parameters.ProtocolOptions = $InputObject.Properties.scopeV2.protocolOptions } Update-BurpSuiteSiteScope -SiteId $resource.Id @parameters Start-Sleep -Seconds 1 } if ($null -ne ($InputObject.Properties.scanConfigurationIds)) { Write-Verbose " Updating scan configurations..." $scanConfigurationIds = @() foreach ($scanConfigurationId in $InputObject.Properties.scanConfigurationIds) { if ((_testIsExpression -InputString $scanConfigurationId)) { $resolvedScanConfigurationId = _resolveExpression -inputString $scanConfigurationId -variables @{} -resources ([DeploymentCache]::Deployments) if ($null -eq $resolvedScanConfigurationId) { throw "Could not resolve dependency expression $scanConfigurationId`." } $scanConfigurationIds += $resolvedScanConfigurationId } else { $scanConfigurationIds += $scanConfigurationId } } Update-BurpSuiteSiteScanConfiguration -Id $resource.Id -ScanConfigurationIds $scanConfigurationIds Start-Sleep -Seconds 1 } [SiteTreeCache]::Reload() $resource = [SiteTreeCache]::Get($parentResource.Id, $InputObject.Name, 'Sites') if ($null -ne ($InputObject.Properties.applicationLogins)) { if ($null -ne ($InputObject.Properties.applicationLogins.loginCredentials)) { Write-Verbose " Updating application logins..." foreach ($loginCredential in $InputObject.Properties.applicationLogins.loginCredentials) { $appPass = ConvertTo-SecureString -String $loginCredential.password -AsPlainText -Force $appCredential = New-Object -TypeName PSCredential -ArgumentList $loginCredential.username, $appPass $appLogin = $resource.application_logins.login_credentials | Where-Object { $_.label -eq $loginCredential.label } if ($null -eq $appLogin) { New-BurpSuiteSiteLoginCredential -SiteId $resource.id -Label $loginCredential.label -Credential $appCredential | Out-Null } else { Update-BurpSuiteSiteLoginCredential -Id $appLogin.id -Credential $appCredential } Start-Sleep -Seconds 1 } } if ($null -ne ($InputObject.Properties.applicationLogins.recordedLogins)) { Write-Verbose " Updating recorded logins..." foreach ($recordedLogin in $InputObject.Properties.applicationLogins.recordedLogins) { $appLogin = $resource.application_logins.recorded_logins | Where-Object { $_.label -eq $recordedLogin.label } if ($null -eq $appLogin) { New-BurpSuiteSiteRecordedLogin -SiteId $resource.id -Label $recordedLogin.label -FilePath (_createTempFile -InputObject $recordedLogin.script).FullName | Out-Null } Start-Sleep -Seconds 1 } } } if ($null -ne ($InputObject.Properties.emailRecipients)) { Write-Verbose " Updating email recipients..." foreach ($emailRecipient in $InputObject.Properties.emailRecipients) { $emailRec = $resource.email_recipients | Where-Object { $_.email -eq $emailRecipient.email } if ($null -eq $emailRec) { New-BurpSuiteSiteEmailRecipient -SiteId $resource.id -EmailRecipient $emailRecipient.email | Out-Null } else { Update-BurpSuiteSiteEmailRecipient -Id $emailRec.id -Email $emailRecipient.email } Start-Sleep -Seconds 1 } } } } else { throw "Resource $($InputObject.ResourceId) parent could not be determined." } } 'BurpSuite/ScanConfigurations' { $tempFile = _createTempFile -InputObject $InputObject.Properties.scanConfigurationFragmentJson $resource = [ScanConfigurationCache]::Get($InputObject.Name) if ($null -eq $resource) { Write-Verbose "Creating scan configuration $($InputObject.Name)`..." $resource = New-BurpSuiteScanConfiguration -Name $InputObject.Name -FilePath $tempFile.FullName [ScanConfigurationCache]::Reload() } else { Write-Verbose "Updating scan configuration $($InputObject.Name)`..." Update-BurpSuiteScanConfiguration -Id $resource.Id -FilePath $tempFile.FullName } Start-Sleep -Seconds 1 } 'BurpSuite/ScheduleItems' { $siteId = $InputObject.Properties.siteId if ((_testIsExpression -InputString $siteId)) { $resolvedSiteId = _resolveExpression -inputString $siteId -variables @{} -resources ([DeploymentCache]::Deployments) if ($null -eq $resolvedSiteId) { throw "Could not resolve expression $siteId`." } $siteId = $resolvedSiteId } $site = [SiteTreeCache]::Get($siteId, 'Sites') if ($null -eq $site) { throw "Could not find site with resource id $siteId`." } $scanConfigurationIds = _tryGetProperty -InputObject $InputObject.Properties -PropertyName 'scanConfigurationIds' if ($null -eq $scanConfigurationIds) { $scanConfigurationIds = $site.scan_configurations.id } $resolvedScanConfigurationIds = @() foreach ($scanConfigurationId in $scanConfigurationIds) { if ((_testIsExpression -InputString $scanConfigurationId)) { $resolvedScanConfigurationId = _resolveExpression -inputString $scanConfigurationId -variables @{} -resources ([DeploymentCache]::Deployments) if ($null -eq $resolvedScanConfigurationId) { throw "Could not resolve expression $scanConfigurationId`." } $resolvedScanConfigurationIds += $resolvedScanConfigurationId } else { $resolvedScanConfigurationIds += $scanConfigurationId } } $recurrenceRule = _tryGetProperty -InputObject $InputObject.Properties.schedule -PropertyName 'rRule' if (-not ([string]::IsNullOrEmpty($recurrenceRule))) { $resource = [ScheduleItemCache]::Get($siteId) | Where-Object { $_.schedule.rrule -eq $recurrenceRule } | Select-Object -First 1 } else { $resource = $null } if ($null -eq $resource) { Write-Verbose "Creating schedule item $($InputObject.Name), site $siteId`..." $parameters = @{} $recurrenceRule = _tryGetProperty -InputObject $InputObject.Properties.schedule -PropertyName 'rRule' if (-not ([string]::IsNullOrEmpty($recurrenceRule))) { $parameters.rrule = $recurrenceRule } $initialRunTime = _tryGetProperty -InputObject $InputObject.Properties.schedule -PropertyName 'initialRunTime' if (-not ([string]::IsNullOrEmpty($initialRunTime))) { $dateTimeNow = Get-Date $initialRunTimeDate = [DateTime]::SpecifyKind($initialRunTime, [DateTimeKind]::Utc) # Correct initial run time if date is in past if ($initialRunTimeDate -lt $dateTimeNow) { $dateTimeTomorrow = $dateTimeNow.AddHours(24) $newInitialRunTimeDate = (Get-Date -Day $dateTimeTomorrow.Day -Month $dateTimeTomorrow.Month -Year $dateTimeTomorrow.Year -Hour $initialRunTimeDate.Hour -Minute $initialRunTimeDate.Minute -Second $initialRunTimeDate.Second) $newInitialRunTimeDate = [DateTime]::SpecifyKind($newInitialRunTimeDate, [DateTimeKind]::Utc) $initialRunTime = Get-Date -Date $newInitialRunTimeDate -Format o } $parameters.initialRunTime = $initialRunTime } $schedule = [PSCustomObject]$parameters $resource = New-BurpSuiteScheduleItem -SiteId $siteId -ScanConfigurationIds $resolvedScanConfigurationIds -Schedule $schedule [ScheduleItemCache]::Reload() } Start-Sleep -Seconds 1 } default { throw "Unknown resource type." } } $deployment = [Deployment]@{ Id = $resource.Id ResourceId = $InputObject.ResourceId ProvisioningState = [ProvisioningState]::Succeeded Properties = $resource } } } catch { $deployment = [Deployment]@{ Id = $resource.Id ResourceId = $InputObject.ResourceId ProvisioningState = [ProvisioningState]::Error ProvisioningError = $_.Exception.Message.ToString() Properties = $resource } } [DeploymentCache]::Set($deployment) $deployment } end { [DeploymentCache]::Init() } } |